options-initialize.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
  2. // Copyright (C) 2013, 2016 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 <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <sys/types.h>
  20. #include <unistd.h>
  21. #include <grp.h>
  22. #include <pwd.h>
  23. #include <vector>
  24. #include "multiplex-mappers-internal.h"
  25. #include "framebuffer-internal.h"
  26. #include "gpio.h"
  27. namespace rgb_matrix {
  28. RuntimeOptions::RuntimeOptions() :
  29. #ifdef RGB_SLOWDOWN_GPIO
  30. gpio_slowdown(RGB_SLOWDOWN_GPIO),
  31. #else
  32. gpio_slowdown(1),
  33. #endif
  34. daemon(0), // Don't become a daemon by default.
  35. drop_privileges(1), // Encourage good practice: drop privileges by default.
  36. do_gpio_init(true)
  37. {
  38. // Nothing to see here.
  39. }
  40. namespace {
  41. typedef char** argv_iterator;
  42. #define OPTION_PREFIX "--led-"
  43. #define OPTION_PREFIX_LEN strlen(OPTION_PREFIX)
  44. static bool ConsumeBoolFlag(const char *flag_name, const argv_iterator &pos,
  45. bool *result_value) {
  46. const char *option = *pos;
  47. if (strncmp(option, OPTION_PREFIX, OPTION_PREFIX_LEN) != 0)
  48. return false;
  49. option += OPTION_PREFIX_LEN;
  50. bool value_to_set = true;
  51. if (strncmp(option, "no-", 3) == 0) {
  52. value_to_set = false;
  53. option += 3;
  54. }
  55. if (strcmp(option, flag_name) != 0)
  56. return false; // not consumed.
  57. *result_value = value_to_set;
  58. return true;
  59. }
  60. static bool ConsumeIntFlag(const char *flag_name,
  61. argv_iterator &pos, const argv_iterator end,
  62. int *result_value, int *error) {
  63. const char *option = *pos;
  64. if (strncmp(option, OPTION_PREFIX, OPTION_PREFIX_LEN) != 0)
  65. return false;
  66. option += OPTION_PREFIX_LEN;
  67. const size_t flag_len = strlen(flag_name);
  68. if (strncmp(option, flag_name, flag_len) != 0)
  69. return false; // not consumed.
  70. const char *value;
  71. if (option[flag_len] == '=') // --option=42 # value in same arg
  72. value = option + flag_len + 1;
  73. else if (pos + 1 < end) { // --option 42 # value in next arg
  74. value = *(++pos);
  75. } else {
  76. fprintf(stderr, "Parameter expected after %s%s\n",
  77. OPTION_PREFIX, flag_name);
  78. ++*error;
  79. return true; // consumed, but error.
  80. }
  81. char *end_value = NULL;
  82. int val = strtol(value, &end_value, 10);
  83. if (!*value || *end_value) {
  84. fprintf(stderr, "Couldn't parse parameter %s%s=%s "
  85. "(Expected decimal number but '%s' looks funny)\n",
  86. OPTION_PREFIX, flag_name, value, end_value);
  87. ++*error;
  88. return true; // consumed, but error
  89. }
  90. *result_value = val;
  91. return true; // consumed.
  92. }
  93. // The resulting value is allocated.
  94. static bool ConsumeStringFlag(const char *flag_name,
  95. argv_iterator &pos, const argv_iterator end,
  96. const char **result_value, int *error) {
  97. const char *option = *pos;
  98. if (strncmp(option, OPTION_PREFIX, OPTION_PREFIX_LEN) != 0)
  99. return false;
  100. option += OPTION_PREFIX_LEN;
  101. const size_t flag_len = strlen(flag_name);
  102. if (strncmp(option, flag_name, flag_len) != 0)
  103. return false; // not consumed.
  104. const char *value;
  105. if (option[flag_len] == '=') // --option=hello # value in same arg
  106. value = option + flag_len + 1;
  107. else if (pos + 1 < end) { // --option hello # value in next arg
  108. value = *(++pos);
  109. } else {
  110. fprintf(stderr, "Parameter expected after %s%s\n",
  111. OPTION_PREFIX, flag_name);
  112. ++*error;
  113. *result_value = NULL;
  114. return true; // consumed, but error.
  115. }
  116. *result_value = strdup(value); // This will leak, but no big deal.
  117. return true;
  118. }
  119. static bool FlagInit(int &argc, char **&argv,
  120. RGBMatrix::Options *mopts,
  121. RuntimeOptions *ropts,
  122. bool remove_consumed_options) {
  123. argv_iterator it = &argv[0];
  124. argv_iterator end = it + argc;
  125. std::vector<char*> unused_options;
  126. unused_options.push_back(*it++); // Not interested in program name
  127. bool bool_scratch;
  128. int err = 0;
  129. bool posix_end_option_seen = false; // end of options '--'
  130. for (/**/; it < end; ++it) {
  131. posix_end_option_seen |= (strcmp(*it, "--") == 0);
  132. if (!posix_end_option_seen) {
  133. if (ConsumeStringFlag("gpio-mapping", it, end,
  134. &mopts->hardware_mapping, &err))
  135. continue;
  136. if (ConsumeStringFlag("rgb-sequence", it, end,
  137. &mopts->led_rgb_sequence, &err))
  138. continue;
  139. if (ConsumeStringFlag("pixel-mapper", it, end,
  140. &mopts->pixel_mapper_config, &err))
  141. continue;
  142. if (ConsumeStringFlag("panel-type", it, end,
  143. &mopts->panel_type, &err))
  144. continue;
  145. if (ConsumeIntFlag("rows", it, end, &mopts->rows, &err))
  146. continue;
  147. if (ConsumeIntFlag("cols", it, end, &mopts->cols, &err))
  148. continue;
  149. if (ConsumeIntFlag("chain", it, end, &mopts->chain_length, &err))
  150. continue;
  151. if (ConsumeIntFlag("parallel", it, end, &mopts->parallel, &err))
  152. continue;
  153. if (ConsumeIntFlag("multiplexing", it, end, &mopts->multiplexing, &err))
  154. continue;
  155. if (ConsumeIntFlag("brightness", it, end, &mopts->brightness, &err))
  156. continue;
  157. if (ConsumeIntFlag("scan-mode", it, end, &mopts->scan_mode, &err))
  158. continue;
  159. if (ConsumeIntFlag("pwm-bits", it, end, &mopts->pwm_bits, &err))
  160. continue;
  161. if (ConsumeIntFlag("pwm-lsb-nanoseconds", it, end,
  162. &mopts->pwm_lsb_nanoseconds, &err))
  163. continue;
  164. if (ConsumeIntFlag("pwm-dither-bits", it, end,
  165. &mopts->pwm_dither_bits, &err))
  166. continue;
  167. if (ConsumeIntFlag("row-addr-type", it, end,
  168. &mopts->row_address_type, &err))
  169. continue;
  170. if (ConsumeIntFlag("limit-refresh", it, end,
  171. &mopts->limit_refresh_rate_hz, &err))
  172. continue;
  173. if (ConsumeBoolFlag("show-refresh", it, &mopts->show_refresh_rate))
  174. continue;
  175. if (ConsumeBoolFlag("inverse", it, &mopts->inverse_colors))
  176. continue;
  177. // We don't have a swap_green_blue option anymore, but we simulate the
  178. // flag for a while.
  179. bool swap_green_blue;
  180. if (ConsumeBoolFlag("swap-green-blue", it, &swap_green_blue)) {
  181. if (strlen(mopts->led_rgb_sequence) == 3) {
  182. char *new_sequence = strdup(mopts->led_rgb_sequence);
  183. new_sequence[0] = mopts->led_rgb_sequence[0];
  184. new_sequence[1] = mopts->led_rgb_sequence[2];
  185. new_sequence[2] = mopts->led_rgb_sequence[1];
  186. mopts->led_rgb_sequence = new_sequence; // leaking. Ignore.
  187. }
  188. continue;
  189. }
  190. bool allow_hardware_pulsing = !mopts->disable_hardware_pulsing;
  191. if (ConsumeBoolFlag("hardware-pulse", it, &allow_hardware_pulsing)) {
  192. mopts->disable_hardware_pulsing = !allow_hardware_pulsing;
  193. continue;
  194. }
  195. bool request_help = false;
  196. if (ConsumeBoolFlag("help", it, &request_help) && request_help) {
  197. // In that case, we pretend to have failure in parsing, which will
  198. // trigger printing the usage(). Typically :)
  199. return false;
  200. }
  201. //-- Runtime options.
  202. if (ConsumeIntFlag("slowdown-gpio", it, end, &ropts->gpio_slowdown, &err))
  203. continue;
  204. if (ropts->daemon >= 0 && ConsumeBoolFlag("daemon", it, &bool_scratch)) {
  205. ropts->daemon = bool_scratch ? 1 : 0;
  206. continue;
  207. }
  208. if (ropts->drop_privileges >= 0 &&
  209. ConsumeBoolFlag("drop-privs", it, &bool_scratch)) {
  210. ropts->drop_privileges = bool_scratch ? 1 : 0;
  211. continue;
  212. }
  213. if (strncmp(*it, OPTION_PREFIX, OPTION_PREFIX_LEN) == 0) {
  214. fprintf(stderr, "Option %s starts with %s but it is unknown. Typo?\n",
  215. *it, OPTION_PREFIX);
  216. }
  217. }
  218. unused_options.push_back(*it);
  219. }
  220. if (err > 0) {
  221. return false;
  222. }
  223. if (remove_consumed_options) {
  224. // Success. Re-arrange flags to only include the ones not consumed.
  225. argc = (int) unused_options.size();
  226. for (int i = 0; i < argc; ++i) {
  227. argv[i] = unused_options[i];
  228. }
  229. }
  230. return true;
  231. }
  232. } // anonymous namespace
  233. bool ParseOptionsFromFlags(int *argc, char ***argv,
  234. RGBMatrix::Options *m_opt_in,
  235. RuntimeOptions *rt_opt_in,
  236. bool remove_consumed_options) {
  237. if (argc == NULL || argv == NULL) {
  238. fprintf(stderr, "Called ParseOptionsFromFlags() without argc/argv\n");
  239. return false;
  240. }
  241. // Replace NULL arguments with some scratch-space.
  242. RGBMatrix::Options scratch_matrix;
  243. RGBMatrix::Options *mopt = (m_opt_in != NULL) ? m_opt_in : &scratch_matrix;
  244. RuntimeOptions scratch_rt;
  245. RuntimeOptions *ropt = (rt_opt_in != NULL) ? rt_opt_in : &scratch_rt;
  246. return FlagInit(*argc, *argv, mopt, ropt, remove_consumed_options);
  247. }
  248. static std::string CreateAvailableMultiplexString(
  249. const internal::MuxMapperList &m) {
  250. std::string result;
  251. char buffer[256];
  252. for (size_t i = 0; i < m.size(); ++i) {
  253. if (i != 0) result.append("; ");
  254. snprintf(buffer, sizeof(buffer), "%d=%s", (int) i+1, m[i]->GetName());
  255. result.append(buffer);
  256. }
  257. return result;
  258. }
  259. void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &d,
  260. const RuntimeOptions &r) {
  261. const internal::MuxMapperList &muxers
  262. = internal::GetRegisteredMultiplexMappers();
  263. std::vector<std::string> mapper_names = GetAvailablePixelMappers();
  264. std::string available_mappers;
  265. for (size_t i = 0; i < mapper_names.size(); ++i) {
  266. if (i != 0) available_mappers.append(", ");
  267. available_mappers.append("\"").append(mapper_names[i]).append("\"");
  268. }
  269. fprintf(out,
  270. "\t--led-gpio-mapping=<name> : Name of GPIO mapping used. Default \"%s\"\n"
  271. "\t--led-rows=<rows> : Panel rows. Typically 8, 16, 32 or 64."
  272. " (Default: %d).\n"
  273. "\t--led-cols=<cols> : Panel columns. Typically 32 or 64. "
  274. "(Default: %d).\n"
  275. "\t--led-chain=<chained> : Number of daisy-chained panels. "
  276. "(Default: %d).\n"
  277. "\t--led-parallel=<parallel> : Parallel chains. range=1..3 "
  278. #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE
  279. "(6 for CM3) "
  280. #endif
  281. "(Default: %d).\n"
  282. "\t--led-multiplexing=<0..%d> : Mux type: 0=direct; %s (Default: 0)\n"
  283. "\t--led-pixel-mapper : Semicolon-separated list of pixel-mappers to arrange pixels.\n"
  284. "\t Optional params after a colon e.g. \"U-mapper;Rotate:90\"\n"
  285. "\t Available: %s. Default: \"\"\n"
  286. "\t--led-pwm-bits=<1..%d> : PWM bits (Default: %d).\n"
  287. "\t--led-brightness=<percent>: Brightness in percent (Default: %d).\n"
  288. "\t--led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced "
  289. "(Default: %d).\n"
  290. "\t--led-row-addr-type=<0..4>: 0 = default; 1 = AB-addressed panels; 2 = direct row select; 3 = ABC-addressed panels; 4 = ABC Shift + DE direct "
  291. "(Default: 0).\n"
  292. "\t--led-%sshow-refresh : %show refresh rate.\n"
  293. "\t--led-limit-refresh=<Hz> : Limit refresh rate to this frequency in Hz. Useful to keep a\n"
  294. "\t constant refresh rate on loaded system. 0=no limit. Default: %d\n"
  295. "\t--led-%sinverse "
  296. ": Switch if your matrix has inverse colors %s.\n"
  297. "\t--led-rgb-sequence : Switch if your matrix has led colors "
  298. "swapped (Default: \"RGB\")\n"
  299. "\t--led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB "
  300. "(Default: %d)\n"
  301. "\t--led-pwm-dither-bits=<0..2> : Time dithering of lower bits "
  302. "(Default: 0)\n"
  303. "\t--led-%shardware-pulse : %sse hardware pin-pulse generation.\n"
  304. "\t--led-panel-type=<name> : Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'\n",
  305. d.hardware_mapping,
  306. d.rows, d.cols, d.chain_length, d.parallel,
  307. (int) muxers.size(), CreateAvailableMultiplexString(muxers).c_str(),
  308. available_mappers.c_str(),
  309. internal::Framebuffer::kBitPlanes, d.pwm_bits,
  310. d.brightness, d.scan_mode,
  311. d.show_refresh_rate ? "no-" : "", d.show_refresh_rate ? "Don't s" : "S",
  312. d.limit_refresh_rate_hz,
  313. d.inverse_colors ? "no-" : "", d.inverse_colors ? "off" : "on",
  314. d.pwm_lsb_nanoseconds,
  315. !d.disable_hardware_pulsing ? "no-" : "",
  316. !d.disable_hardware_pulsing ? "Don't u" : "U");
  317. fprintf(out, "\t--led-slowdown-gpio=<0..4>: "
  318. "Slowdown GPIO. Needed for faster Pis/slower panels "
  319. "(Default: %d).\n", r.gpio_slowdown);
  320. if (r.daemon >= 0) {
  321. const bool on = (r.daemon > 0);
  322. fprintf(out,
  323. "\t--led-%sdaemon : "
  324. "%sake the process run in the background as daemon.\n",
  325. on ? "no-" : "", on ? "Don't m" : "M");
  326. }
  327. if (r.drop_privileges >= 0) {
  328. const bool on = (r.drop_privileges > 0);
  329. fprintf(out,
  330. "\t--led-%sdrop-privs : %srop privileges from 'root' "
  331. "after initializing the hardware.\n",
  332. on ? "no-" : "", on ? "Don't d" : "D");
  333. }
  334. }
  335. bool RGBMatrix::Options::Validate(std::string *err_in) const {
  336. std::string scratch;
  337. std::string *err = err_in ? err_in : &scratch;
  338. bool success = true;
  339. if (rows < 8 || rows > 64 || rows % 2 != 0) {
  340. err->append("Invalid number or rows per panel (--led-rows). "
  341. "Should be in range of [8..64] and divisible by 2.\n");
  342. success = false;
  343. }
  344. if (cols < 16) {
  345. err->append("Invlid number of columns for panel (--led-cols). "
  346. "Typically that is something like 32 or 64\n");
  347. success = false;
  348. }
  349. if (chain_length < 1) {
  350. err->append("Chain-length outside usable range.\n");
  351. success = false;
  352. }
  353. const internal::MuxMapperList &muxers
  354. = internal::GetRegisteredMultiplexMappers();
  355. if (multiplexing < 0 || multiplexing > (int)muxers.size()) {
  356. err->append("Multiplexing can only be one of 0=normal; ")
  357. .append(CreateAvailableMultiplexString(muxers));
  358. success = false;
  359. }
  360. if (row_address_type < 0 || row_address_type > 4) {
  361. err->append("Row address type values can be 0 (default), 1 (AB addressing), 2 (direct row select), 3 (ABC address), 4 (ABC Shift + DE direct).\n");
  362. success = false;
  363. }
  364. #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE
  365. const bool is_cm = (strcmp(hardware_mapping, "compute-module") == 0);
  366. #else
  367. const bool is_cm = false;
  368. #endif
  369. if (parallel < 1 || parallel > (is_cm ? 6 : 3)) {
  370. err->append("Parallel outside usable range (1..3 allowed"
  371. #ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE
  372. ", up to 6 only for CM3"
  373. #endif
  374. ").\n");
  375. success = false;
  376. }
  377. if (brightness < 1 || brightness > 100) {
  378. err->append("Brightness outside usable range (Percent 1..100 allowed).\n");
  379. success = false;
  380. }
  381. if (pwm_bits <= 0 || pwm_bits > internal::Framebuffer::kBitPlanes) {
  382. char buffer[256];
  383. snprintf(buffer, sizeof(buffer),
  384. "Invalid range of pwm-bits (1..%d allowed).\n",
  385. internal::Framebuffer::kBitPlanes);
  386. err->append(buffer);
  387. success = false;
  388. }
  389. if (scan_mode < 0 || scan_mode > 1) {
  390. err->append("Invalid scan mode (0 or 1 allowed).\n");
  391. success = false;
  392. }
  393. if (pwm_lsb_nanoseconds < 50 || pwm_lsb_nanoseconds > 3000) {
  394. err->append("Invalid range of pwm-lsb-nanoseconds (50..3000 allowed).\n");
  395. success = false;
  396. }
  397. if (pwm_dither_bits < 0 || pwm_dither_bits > 2) {
  398. err->append("Inavlid range of pwm-dither-bits (0..2 allowed).\n");
  399. success = false;
  400. }
  401. if (led_rgb_sequence == NULL || strlen(led_rgb_sequence) != 3) {
  402. err->append("led-sequence needs to be three characters long.\n");
  403. success = false;
  404. } else {
  405. if ((!strchr(led_rgb_sequence, 'R') && !strchr(led_rgb_sequence, 'r'))
  406. || (!strchr(led_rgb_sequence, 'G') && !strchr(led_rgb_sequence, 'g'))
  407. || (!strchr(led_rgb_sequence, 'B') && !strchr(led_rgb_sequence, 'b'))) {
  408. err->append("led-sequence needs to contain all of letters 'R', 'G' "
  409. "and 'B'\n");
  410. success = false;
  411. }
  412. }
  413. if (!success && !err_in) {
  414. // If we didn't get a string to write to, we write things to stderr.
  415. fprintf(stderr, "%s", err->c_str());
  416. }
  417. return success;
  418. }
  419. } // namespace rgb_matrix