led-matrix.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
  2. // Copyright (C) 2013 Henner Zeller <h.zeller@acm.org>
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation version 2.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <http://gnu.org/licenses/gpl-2.0.txt>
  15. #include "led-matrix.h"
  16. #include <assert.h>
  17. #include <grp.h>
  18. #include <pwd.h>
  19. #include <math.h>
  20. #include <pthread.h>
  21. #include <stdint.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/time.h>
  26. #include <sys/types.h>
  27. #include <time.h>
  28. #include <unistd.h>
  29. #include "gpio.h"
  30. #include "thread.h"
  31. #include "framebuffer-internal.h"
  32. #include "multiplex-mappers-internal.h"
  33. // Leave this in here for a while. Setting things from old defines.
  34. #if defined(ADAFRUIT_RGBMATRIX_HAT)
  35. # error "ADAFRUIT_RGBMATRIX_HAT has long been deprecated. Please use the Options struct or --led-gpio-mapping=adafruit-hat commandline flag"
  36. #endif
  37. #if defined(ADAFRUIT_RGBMATRIX_HAT_PWM)
  38. # error "ADAFRUIT_RGBMATRIX_HAT_PWM has long been deprecated. Please use the Options struct or --led-gpio-mapping=adafruit-hat-pwm commandline flag"
  39. #endif
  40. namespace rgb_matrix {
  41. // Implementation details of RGBmatrix.
  42. class RGBMatrix::Impl {
  43. class UpdateThread;
  44. friend class UpdateThread;
  45. public:
  46. // Create an RGBMatrix.
  47. //
  48. // Needs an initialized GPIO object and configuration options from the
  49. // RGBMatrix::Options struct.
  50. //
  51. // If you pass an GPIO object (which has to be Init()ialized), it will start // the internal thread to start the screen immediately.
  52. //
  53. // If you need finer control over when the refresh thread starts (which you
  54. // might when you become a daemon), pass NULL here and see SetGPIO() method.
  55. //
  56. // The resulting canvas is (options.rows * options.parallel) high and
  57. // (32 * options.chain_length) wide.
  58. Impl(GPIO *io, const Options &options);
  59. ~Impl();
  60. // Used to be there to help user delay initialization of thread starting,
  61. // these days only used internally.
  62. void SetGPIO(GPIO *io, bool start_thread = true);
  63. bool StartRefresh();
  64. FrameCanvas *CreateFrameCanvas();
  65. FrameCanvas *SwapOnVSync(FrameCanvas *other, unsigned framerate_fraction);
  66. bool ApplyPixelMapper(const PixelMapper *mapper);
  67. bool SetPWMBits(uint8_t value);
  68. uint8_t pwmbits(); // return the pwm-bits of the currently active buffer.
  69. void set_luminance_correct(bool on);
  70. bool luminance_correct() const;
  71. // Set brightness in percent for all created FrameCanvas. 1%..100%.
  72. // This will only affect newly set pixels.
  73. void SetBrightness(uint8_t brightness);
  74. uint8_t brightness();
  75. uint64_t RequestInputs(uint64_t);
  76. uint64_t AwaitInputChange(int timeout_ms);
  77. uint64_t RequestOutputs(uint64_t output_bits);
  78. void OutputGPIO(uint64_t output_bits);
  79. void Clear();
  80. private:
  81. friend class RGBMatrix;
  82. // Apply pixel mappers that have been passed down via a configuration
  83. // string.
  84. void ApplyNamedPixelMappers(const char *pixel_mapper_config,
  85. int chain, int parallel);
  86. Options params_;
  87. bool do_luminance_correct_;
  88. FrameCanvas *active_;
  89. GPIO *io_;
  90. Mutex active_frame_sync_;
  91. UpdateThread *updater_;
  92. std::vector<FrameCanvas*> created_frames_;
  93. internal::PixelDesignatorMap *shared_pixel_mapper_;
  94. uint64_t user_output_bits_;
  95. };
  96. using namespace internal;
  97. // Pump pixels to screen. Needs to be high priority real-time because jitter
  98. class RGBMatrix::Impl::UpdateThread : public Thread {
  99. public:
  100. UpdateThread(GPIO *io, FrameCanvas *initial_frame,
  101. int pwm_dither_bits, bool show_refresh,
  102. int limit_refresh_hz)
  103. : io_(io), show_refresh_(show_refresh),
  104. target_frame_usec_(limit_refresh_hz < 1 ? 0 : 1e6/limit_refresh_hz),
  105. running_(true),
  106. current_frame_(initial_frame), next_frame_(NULL),
  107. requested_frame_multiple_(1) {
  108. pthread_cond_init(&frame_done_, NULL);
  109. pthread_cond_init(&input_change_, NULL);
  110. switch (pwm_dither_bits) {
  111. case 0:
  112. start_bit_[0] = 0; start_bit_[1] = 0;
  113. start_bit_[2] = 0; start_bit_[3] = 0;
  114. break;
  115. case 1:
  116. start_bit_[0] = 0; start_bit_[1] = 1;
  117. start_bit_[2] = 0; start_bit_[3] = 1;
  118. break;
  119. case 2:
  120. start_bit_[0] = 0; start_bit_[1] = 1;
  121. start_bit_[2] = 2; start_bit_[3] = 2;
  122. break;
  123. }
  124. }
  125. void Stop() {
  126. MutexLock l(&running_mutex_);
  127. running_ = false;
  128. }
  129. virtual void Run() {
  130. unsigned frame_count = 0;
  131. unsigned low_bit_sequence = 0;
  132. uint32_t largest_time = 0;
  133. gpio_bits_t last_gpio_bits = 0;
  134. // Let's start measure max time only after a we were running for a few
  135. // seconds to not pick up start-up glitches.
  136. static const int kHoldffTimeUs = 2000 * 1000;
  137. uint32_t initial_holdoff_start = GetMicrosecondCounter();
  138. bool max_measure_enabled = false;
  139. while (running()) {
  140. const uint32_t start_time_us = GetMicrosecondCounter();
  141. current_frame_->framebuffer()
  142. ->DumpToMatrix(io_, start_bit_[low_bit_sequence % 4]);
  143. // SwapOnVSync() exchange.
  144. {
  145. MutexLock l(&frame_sync_);
  146. // Do fast equality test first (likely due to frame_count reset).
  147. if (frame_count == requested_frame_multiple_
  148. || frame_count % requested_frame_multiple_ == 0) {
  149. // We reset to avoid frame hick-up every couple of weeks
  150. // run-time iff requested_frame_multiple_ is not a factor of 2^32.
  151. frame_count = 0;
  152. if (next_frame_ != NULL) {
  153. current_frame_ = next_frame_;
  154. next_frame_ = NULL;
  155. }
  156. pthread_cond_signal(&frame_done_);
  157. }
  158. }
  159. // Read input bits.
  160. const gpio_bits_t inputs = io_->Read();
  161. if (inputs != last_gpio_bits) {
  162. last_gpio_bits = inputs;
  163. MutexLock l(&input_sync_);
  164. gpio_inputs_ = inputs;
  165. pthread_cond_signal(&input_change_);
  166. }
  167. ++frame_count;
  168. ++low_bit_sequence;
  169. if (target_frame_usec_) {
  170. while ((GetMicrosecondCounter() - start_time_us) < target_frame_usec_) {
  171. // busy wait. We have our dedicated core, so ok to burn cycles.
  172. }
  173. }
  174. const uint32_t end_time_us = GetMicrosecondCounter();
  175. if (show_refresh_) {
  176. uint32_t usec = end_time_us - start_time_us;
  177. printf("\b\b\b\b\b\b\b\b%6.1fHz", 1e6 / usec);
  178. if (usec > largest_time && max_measure_enabled) {
  179. largest_time = usec;
  180. const float lowest_hz = 1e6 / largest_time;
  181. printf(" (lowest: %.1fHz)"
  182. "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", lowest_hz);
  183. } else {
  184. // Don't measure at startup, as times will be janky.
  185. max_measure_enabled = (end_time_us - initial_holdoff_start) > kHoldffTimeUs;
  186. }
  187. }
  188. }
  189. }
  190. FrameCanvas *SwapOnVSync(FrameCanvas *other, unsigned frame_fraction) {
  191. MutexLock l(&frame_sync_);
  192. FrameCanvas *previous = current_frame_;
  193. next_frame_ = other;
  194. requested_frame_multiple_ = frame_fraction;
  195. frame_sync_.WaitOn(&frame_done_);
  196. return previous;
  197. }
  198. gpio_bits_t AwaitInputChange(int timeout_ms) {
  199. MutexLock l(&input_sync_);
  200. input_sync_.WaitOn(&input_change_, timeout_ms);
  201. return gpio_inputs_;
  202. }
  203. private:
  204. inline bool running() {
  205. MutexLock l(&running_mutex_);
  206. return running_;
  207. }
  208. GPIO *const io_;
  209. const bool show_refresh_;
  210. const uint32_t target_frame_usec_;
  211. uint32_t start_bit_[4];
  212. Mutex running_mutex_;
  213. bool running_;
  214. Mutex input_sync_;
  215. pthread_cond_t input_change_;
  216. gpio_bits_t gpio_inputs_;
  217. Mutex frame_sync_;
  218. pthread_cond_t frame_done_;
  219. FrameCanvas *current_frame_;
  220. FrameCanvas *next_frame_;
  221. unsigned requested_frame_multiple_;
  222. };
  223. // Some defaults. See options-initialize.cc for the command line parsing.
  224. RGBMatrix::Options::Options() :
  225. // Historically, we provided these options only as #defines. Make sure that
  226. // things still behave as before if someone has set these.
  227. // At some point: remove them from the Makefile. Later: remove them here.
  228. #ifdef DEFAULT_HARDWARE
  229. hardware_mapping(DEFAULT_HARDWARE),
  230. #else
  231. hardware_mapping("regular"),
  232. #endif
  233. rows(32), cols(32), chain_length(1), parallel(1),
  234. pwm_bits(internal::Framebuffer::kDefaultBitPlanes),
  235. #ifdef LSB_PWM_NANOSECONDS
  236. pwm_lsb_nanoseconds(LSB_PWM_NANOSECONDS),
  237. #else
  238. pwm_lsb_nanoseconds(130),
  239. #endif
  240. pwm_dither_bits(0),
  241. brightness(100),
  242. #ifdef RGB_SCAN_INTERLACED
  243. scan_mode(1),
  244. #else
  245. scan_mode(0),
  246. #endif
  247. row_address_type(0),
  248. multiplexing(0),
  249. #ifdef DISABLE_HARDWARE_PULSES
  250. disable_hardware_pulsing(true),
  251. #else
  252. disable_hardware_pulsing(false),
  253. #endif
  254. #ifdef SHOW_REFRESH_RATE
  255. show_refresh_rate(true),
  256. #else
  257. show_refresh_rate(false),
  258. #endif
  259. #ifdef INVERSE_RGB_DISPLAY_COLORS
  260. inverse_colors(true),
  261. #else
  262. inverse_colors(false),
  263. #endif
  264. led_rgb_sequence("RGB"),
  265. pixel_mapper_config(NULL),
  266. panel_type(NULL),
  267. #ifdef FIXED_FRAME_MICROSECONDS
  268. limit_refresh_rate_hz(1e6 / FIXED_FRAME_MICROSECONDS)
  269. #else
  270. limit_refresh_rate_hz(0)
  271. #endif
  272. {
  273. // Nothing to see here.
  274. }
  275. #define DEBUG_MATRIX_OPTIONS 0
  276. #if DEBUG_MATRIX_OPTIONS
  277. static void PrintOptions(const RGBMatrix::Options &o) {
  278. #define P_INT(val) fprintf(stderr, "%s : %d\n", #val, o.val)
  279. #define P_STR(val) fprintf(stderr, "%s : %s\n", #val, o.val)
  280. #define P_BOOL(val) fprintf(stderr, "%s : %s\n", #val, o.val ? "true":"false")
  281. P_STR(hardware_mapping);
  282. P_INT(rows);
  283. P_INT(cols);
  284. P_INT(chain_length);
  285. P_INT(parallel);
  286. P_INT(pwm_bits);
  287. P_INT(pwm_lsb_nanoseconds);
  288. P_INT(pwm_dither_bits);
  289. P_INT(brightness);
  290. P_INT(scan_mode);
  291. P_INT(row_address_type);
  292. P_INT(multiplexing);
  293. P_BOOL(disable_hardware_pulsing);
  294. P_BOOL(show_refresh_rate);
  295. P_BOOL(inverse_colors);
  296. P_STR(led_rgb_sequence);
  297. P_STR(pixel_mapper_config);
  298. P_STR(panel_type);
  299. P_INT(limit_refresh_rate_hz);
  300. #undef P_INT
  301. #undef P_STR
  302. #undef P_BOOL
  303. }
  304. #endif // DEBUG_MATRIX_OPTIONS
  305. RGBMatrix::Impl::Impl(GPIO *io, const Options &options)
  306. : params_(options), io_(NULL), updater_(NULL), shared_pixel_mapper_(NULL),
  307. user_output_bits_(0) {
  308. assert(params_.Validate(NULL));
  309. #if DEBUG_MATRIX_OPTIONS
  310. PrintOptions(params_);
  311. #endif
  312. const MultiplexMapper *multiplex_mapper = NULL;
  313. if (params_.multiplexing > 0) {
  314. const MuxMapperList &multiplexers = GetRegisteredMultiplexMappers();
  315. if (params_.multiplexing <= (int) multiplexers.size()) {
  316. // TODO: we could also do a find-by-name here, but not sure if worthwhile
  317. multiplex_mapper = multiplexers[params_.multiplexing - 1];
  318. }
  319. }
  320. if (multiplex_mapper) {
  321. // The multiplexers might choose to have a different physical layout.
  322. // We need to configure that first before setting up the hardware.
  323. multiplex_mapper->EditColsRows(&params_.cols, &params_.rows);
  324. }
  325. Framebuffer::InitHardwareMapping(params_.hardware_mapping);
  326. active_ = CreateFrameCanvas();
  327. active_->Clear();
  328. SetGPIO(io, true);
  329. // We need to apply the mapping for the panels first.
  330. ApplyPixelMapper(multiplex_mapper);
  331. // .. followed by higher level mappers that might arrange panels.
  332. ApplyNamedPixelMappers(options.pixel_mapper_config,
  333. params_.chain_length, params_.parallel);
  334. }
  335. RGBMatrix::Impl::~Impl() {
  336. if (updater_) {
  337. updater_->Stop();
  338. updater_->WaitStopped();
  339. }
  340. delete updater_;
  341. // Make sure LEDs are off.
  342. active_->Clear();
  343. if (io_) active_->framebuffer()->DumpToMatrix(io_, 0);
  344. for (size_t i = 0; i < created_frames_.size(); ++i) {
  345. delete created_frames_[i];
  346. }
  347. delete shared_pixel_mapper_;
  348. }
  349. RGBMatrix::~RGBMatrix() {
  350. delete impl_;
  351. }
  352. uint64_t RGBMatrix::Impl::RequestInputs(uint64_t bits) {
  353. return io_->RequestInputs(bits);
  354. }
  355. uint64_t RGBMatrix::Impl::RequestOutputs(uint64_t output_bits) {
  356. uint64_t success_bits = io_->InitOutputs(output_bits);
  357. user_output_bits_ |= success_bits;
  358. return success_bits;
  359. }
  360. void RGBMatrix::Impl::OutputGPIO(uint64_t output_bits) {
  361. io_->WriteMaskedBits(output_bits, user_output_bits_);
  362. }
  363. void RGBMatrix::Impl::ApplyNamedPixelMappers(const char *pixel_mapper_config,
  364. int chain, int parallel) {
  365. if (pixel_mapper_config == NULL || strlen(pixel_mapper_config) == 0)
  366. return;
  367. char *const writeable_copy = strdup(pixel_mapper_config);
  368. const char *const end = writeable_copy + strlen(writeable_copy);
  369. char *s = writeable_copy;
  370. while (s < end) {
  371. char *const semicolon = strchrnul(s, ';');
  372. *semicolon = '\0';
  373. char *optional_param_start = strchr(s, ':');
  374. if (optional_param_start) {
  375. *optional_param_start++ = '\0';
  376. }
  377. if (*s == '\0' && optional_param_start && *optional_param_start != '\0') {
  378. fprintf(stderr, "Stray parameter ':%s' without mapper name ?\n", optional_param_start);
  379. }
  380. if (*s) {
  381. ApplyPixelMapper(FindPixelMapper(s, chain, parallel, optional_param_start));
  382. }
  383. s = semicolon + 1;
  384. }
  385. free(writeable_copy);
  386. }
  387. void RGBMatrix::Impl::SetGPIO(GPIO *io, bool start_thread) {
  388. if (io != NULL && io_ == NULL) {
  389. io_ = io;
  390. Framebuffer::InitGPIO(io_, params_.rows, params_.parallel,
  391. !params_.disable_hardware_pulsing,
  392. params_.pwm_lsb_nanoseconds, params_.pwm_dither_bits,
  393. params_.row_address_type);
  394. Framebuffer::InitializePanels(io_, params_.panel_type,
  395. params_.cols * params_.chain_length);
  396. }
  397. if (start_thread) {
  398. StartRefresh();
  399. }
  400. }
  401. bool RGBMatrix::Impl::StartRefresh() {
  402. if (updater_ == NULL && io_ != NULL) {
  403. updater_ = new UpdateThread(io_, active_, params_.pwm_dither_bits,
  404. params_.show_refresh_rate,
  405. params_.limit_refresh_rate_hz);
  406. // If we have multiple processors, the kernel
  407. // jumps around between these, creating some global flicker.
  408. // So let's tie it to the last CPU available.
  409. // The Raspberry Pi2 has 4 cores, our attempt to bind it to
  410. // core #3 will succeed.
  411. // The Raspberry Pi1 only has one core, so this affinity
  412. // call will simply fail and we keep using the only core.
  413. updater_->Start(99, (1<<3)); // Prio: high. Also: put on last CPU.
  414. }
  415. return updater_ != NULL;
  416. }
  417. FrameCanvas *RGBMatrix::Impl::CreateFrameCanvas() {
  418. FrameCanvas *result =
  419. new FrameCanvas(new Framebuffer(params_.rows,
  420. params_.cols * params_.chain_length,
  421. params_.parallel,
  422. params_.scan_mode,
  423. params_.led_rgb_sequence,
  424. params_.inverse_colors,
  425. &shared_pixel_mapper_));
  426. if (created_frames_.empty()) {
  427. // First time. Get defaults from initial Framebuffer.
  428. do_luminance_correct_ = result->framebuffer()->luminance_correct();
  429. }
  430. result->framebuffer()->SetPWMBits(params_.pwm_bits);
  431. result->framebuffer()->set_luminance_correct(do_luminance_correct_);
  432. result->framebuffer()->SetBrightness(params_.brightness);
  433. created_frames_.push_back(result);
  434. return result;
  435. }
  436. FrameCanvas *RGBMatrix::Impl::SwapOnVSync(FrameCanvas *other,
  437. unsigned frame_fraction) {
  438. if (frame_fraction == 0) frame_fraction = 1; // correct user error.
  439. if (!updater_) return NULL;
  440. FrameCanvas *const previous = updater_->SwapOnVSync(other, frame_fraction);
  441. if (other) active_ = other;
  442. return previous;
  443. }
  444. uint64_t RGBMatrix::Impl::AwaitInputChange(int timeout_ms) {
  445. if (!updater_) return 0;
  446. return updater_->AwaitInputChange(timeout_ms);
  447. }
  448. bool RGBMatrix::Impl::SetPWMBits(uint8_t value) {
  449. const bool success = active_->framebuffer()->SetPWMBits(value);
  450. if (success) {
  451. params_.pwm_bits = value;
  452. }
  453. return success;
  454. }
  455. uint8_t RGBMatrix::Impl::pwmbits() { return params_.pwm_bits; }
  456. // Map brightness of output linearly to input with CIE1931 profile.
  457. void RGBMatrix::Impl::set_luminance_correct(bool on) {
  458. active_->framebuffer()->set_luminance_correct(on);
  459. do_luminance_correct_ = on;
  460. }
  461. bool RGBMatrix::Impl::luminance_correct() const {
  462. return do_luminance_correct_;
  463. }
  464. void RGBMatrix::Impl::SetBrightness(uint8_t brightness) {
  465. for (size_t i = 0; i < created_frames_.size(); ++i) {
  466. created_frames_[i]->framebuffer()->SetBrightness(brightness);
  467. }
  468. params_.brightness = brightness;
  469. }
  470. uint8_t RGBMatrix::Impl::brightness() {
  471. return params_.brightness;
  472. }
  473. bool RGBMatrix::Impl::ApplyPixelMapper(const PixelMapper *mapper) {
  474. if (mapper == NULL) return true;
  475. using internal::PixelDesignatorMap;
  476. const int old_width = shared_pixel_mapper_->width();
  477. const int old_height = shared_pixel_mapper_->height();
  478. int new_width, new_height;
  479. if (!mapper->GetSizeMapping(old_width, old_height, &new_width, &new_height)) {
  480. return false;
  481. }
  482. PixelDesignatorMap *new_mapper = new PixelDesignatorMap(
  483. new_width, new_height, shared_pixel_mapper_->GetFillColorBits());
  484. for (int y = 0; y < new_height; ++y) {
  485. for (int x = 0; x < new_width; ++x) {
  486. int orig_x = -1, orig_y = -1;
  487. mapper->MapVisibleToMatrix(old_width, old_height,
  488. x, y, &orig_x, &orig_y);
  489. if (orig_x < 0 || orig_y < 0 ||
  490. orig_x >= old_width || orig_y >= old_height) {
  491. fprintf(stderr, "Error in PixelMapper: (%d, %d) -> (%d, %d) [range: "
  492. "%dx%d]\n", x, y, orig_x, orig_y, old_width, old_height);
  493. continue;
  494. }
  495. const internal::PixelDesignator *orig_designator;
  496. orig_designator = shared_pixel_mapper_->get(orig_x, orig_y);
  497. *new_mapper->get(x, y) = *orig_designator;
  498. }
  499. }
  500. delete shared_pixel_mapper_;
  501. shared_pixel_mapper_ = new_mapper;
  502. return true;
  503. }
  504. // -- Public interface of RGBMatrix. Delegate everything to impl_
  505. static bool drop_privs(const char *priv_user, const char *priv_group) {
  506. uid_t ruid, euid, suid;
  507. if (getresuid(&ruid, &euid, &suid) >= 0) {
  508. if (euid != 0) // not root anyway. No priv dropping.
  509. return true;
  510. }
  511. struct group *g = getgrnam(priv_group);
  512. if (g == NULL) {
  513. perror("group lookup.");
  514. return false;
  515. }
  516. if (setresgid(g->gr_gid, g->gr_gid, g->gr_gid) != 0) {
  517. perror("setresgid()");
  518. return false;
  519. }
  520. struct passwd *p = getpwnam(priv_user);
  521. if (p == NULL) {
  522. perror("user lookup.");
  523. return false;
  524. }
  525. if (setresuid(p->pw_uid, p->pw_uid, p->pw_uid) != 0) {
  526. perror("setresuid()");
  527. return false;
  528. }
  529. return true;
  530. }
  531. RGBMatrix *RGBMatrix::CreateFromOptions(const RGBMatrix::Options &options,
  532. const RuntimeOptions &runtime_options) {
  533. std::string error;
  534. if (!options.Validate(&error)) {
  535. fprintf(stderr, "%s\n", error.c_str());
  536. return NULL;
  537. }
  538. // For the Pi4, we might need 2, maybe up to 4. Let's open up to 5.
  539. if (runtime_options.gpio_slowdown < 0 || runtime_options.gpio_slowdown > 5) {
  540. fprintf(stderr, "--led-slowdown-gpio=%d is outside usable range\n",
  541. runtime_options.gpio_slowdown);
  542. return NULL;
  543. }
  544. static GPIO io; // This static var is a little bit icky.
  545. if (runtime_options.do_gpio_init
  546. && !io.Init(runtime_options.gpio_slowdown)) {
  547. fprintf(stderr, "Must run as root to be able to access /dev/mem\n"
  548. "Prepend 'sudo' to the command\n");
  549. return NULL;
  550. }
  551. if (runtime_options.daemon > 0 && daemon(1, 0) != 0) {
  552. perror("Failed to become daemon");
  553. }
  554. RGBMatrix::Impl *result = new RGBMatrix::Impl(NULL, options);
  555. // Allowing daemon also means we are allowed to start the thread now.
  556. const bool allow_daemon = !(runtime_options.daemon < 0);
  557. if (runtime_options.do_gpio_init)
  558. result->SetGPIO(&io, allow_daemon);
  559. // TODO(hzeller): if we disallow daemon, then we might also disallow
  560. // drop privileges: we can't drop privileges until we have created the
  561. // realtime thread that usually requires root to be established.
  562. // Double check and document.
  563. if (runtime_options.drop_privileges > 0) {
  564. drop_privs("daemon", "daemon");
  565. }
  566. return new RGBMatrix(result);
  567. }
  568. // Public interface.
  569. RGBMatrix *RGBMatrix::CreateFromFlags(int *argc, char ***argv,
  570. RGBMatrix::Options *m_opt_in,
  571. RuntimeOptions *rt_opt_in,
  572. bool remove_consumed_options) {
  573. RGBMatrix::Options scratch_matrix;
  574. RGBMatrix::Options *mopt = (m_opt_in != NULL) ? m_opt_in : &scratch_matrix;
  575. RuntimeOptions scratch_rt;
  576. RuntimeOptions *ropt = (rt_opt_in != NULL) ? rt_opt_in : &scratch_rt;
  577. if (!ParseOptionsFromFlags(argc, argv, mopt, ropt, remove_consumed_options))
  578. return NULL;
  579. return CreateFromOptions(*mopt, *ropt);
  580. }
  581. FrameCanvas *RGBMatrix::CreateFrameCanvas() {
  582. return impl_->CreateFrameCanvas();
  583. }
  584. FrameCanvas *RGBMatrix::SwapOnVSync(FrameCanvas *other,
  585. unsigned framerate_fraction) {
  586. return impl_->SwapOnVSync(other, framerate_fraction);
  587. }
  588. bool RGBMatrix::ApplyPixelMapper(const PixelMapper *mapper) {
  589. return impl_->ApplyPixelMapper(mapper);
  590. }
  591. bool RGBMatrix::SetPWMBits(uint8_t value) { return impl_->SetPWMBits(value); }
  592. uint8_t RGBMatrix::pwmbits() { return impl_->pwmbits(); }
  593. void RGBMatrix::set_luminance_correct(bool on) {
  594. return impl_->set_luminance_correct(on);
  595. }
  596. bool RGBMatrix::luminance_correct() const { return impl_->luminance_correct(); }
  597. void RGBMatrix::SetBrightness(uint8_t brightness) {
  598. impl_->SetBrightness(brightness);
  599. }
  600. uint8_t RGBMatrix::brightness() { return impl_->brightness(); }
  601. uint64_t RGBMatrix::RequestInputs(uint64_t all_interested_bits) {
  602. return impl_->RequestInputs(all_interested_bits);
  603. }
  604. uint64_t RGBMatrix::AwaitInputChange(int timeout_ms) {
  605. return impl_->AwaitInputChange(timeout_ms);
  606. }
  607. uint64_t RGBMatrix::RequestOutputs(uint64_t all_interested_bits) {
  608. return impl_->RequestOutputs(all_interested_bits);
  609. }
  610. void RGBMatrix::OutputGPIO(uint64_t output_bits) {
  611. impl_->OutputGPIO(output_bits);
  612. }
  613. bool RGBMatrix::StartRefresh() { return impl_->StartRefresh(); }
  614. // -- Implementation of RGBMatrix Canvas: delegation to ContentBuffer
  615. int RGBMatrix::width() const {
  616. return impl_->active_->width();
  617. }
  618. int RGBMatrix::height() const {
  619. return impl_->active_->height();
  620. }
  621. void RGBMatrix::SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) {
  622. impl_->active_->SetPixel(x, y, red, green, blue);
  623. }
  624. void RGBMatrix::Clear() {
  625. impl_->active_->Clear();
  626. }
  627. void RGBMatrix::Fill(uint8_t red, uint8_t green, uint8_t blue) {
  628. impl_->active_->Fill(red, green, blue);
  629. }
  630. // FrameCanvas implementation of Canvas
  631. FrameCanvas::~FrameCanvas() { delete frame_; }
  632. int FrameCanvas::width() const { return frame_->width(); }
  633. int FrameCanvas::height() const { return frame_->height(); }
  634. void FrameCanvas::SetPixel(int x, int y,
  635. uint8_t red, uint8_t green, uint8_t blue) {
  636. frame_->SetPixel(x, y, red, green, blue);
  637. }
  638. void FrameCanvas::Clear() { return frame_->Clear(); }
  639. void FrameCanvas::Fill(uint8_t red, uint8_t green, uint8_t blue) {
  640. frame_->Fill(red, green, blue);
  641. }
  642. bool FrameCanvas::SetPWMBits(uint8_t value) { return frame_->SetPWMBits(value); }
  643. uint8_t FrameCanvas::pwmbits() { return frame_->pwmbits(); }
  644. // Map brightness of output linearly to input with CIE1931 profile.
  645. void FrameCanvas::set_luminance_correct(bool on) { frame_->set_luminance_correct(on); }
  646. bool FrameCanvas::luminance_correct() const { return frame_->luminance_correct(); }
  647. void FrameCanvas::SetBrightness(uint8_t brightness) { frame_->SetBrightness(brightness); }
  648. uint8_t FrameCanvas::brightness() { return frame_->brightness(); }
  649. void FrameCanvas::Serialize(const char **data, size_t *len) const {
  650. frame_->Serialize(data, len);
  651. }
  652. bool FrameCanvas::Deserialize(const char *data, size_t len) {
  653. return frame_->Deserialize(data, len);
  654. }
  655. void FrameCanvas::CopyFrom(const FrameCanvas &other) {
  656. frame_->CopyFrom(other.frame_);
  657. }
  658. } // end namespace rgb_matrix