piu_main.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. /*
  2. * Copyright (c) 2019 Clementine Computing LLC.
  3. *
  4. * This file is part of PopuFare.
  5. *
  6. * PopuFare is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * PopuFare is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <poll.h>
  23. #include <unistd.h>
  24. #include <errno.h>
  25. #include <string.h>
  26. #include <signal.h>
  27. #include <time.h>
  28. #include "../common/common_defs.h"
  29. #include "../commhub/commhub.h"
  30. #include "../commhub/client_utils.h"
  31. #define PIU_MINDER_VERSION "1.0.1"
  32. int piu_fd = -1;
  33. int hub_fd = -1;
  34. device_test_vector piu_test_vector = {
  35. .dev_id = "rider_ui",
  36. .init_string = "/0:\r/1:\r/m:09 00 03 42 5A 44 56\r",
  37. .n_reply_lines = 4,
  38. .reply_strings = (char *[]){NULL, NULL, "/OK:m:09 00 03 42 5A 44 56", "/M:^"},
  39. };
  40. char diag[DIAG_BUFFER_SIZE] = {0};
  41. #define PIU_NUM_LINES 2
  42. static char *piu_set_line_cmd[PIU_NUM_LINES] = {"/0:", "/1:"};
  43. static piu_message def_msgs[PIU_NUM_LINES] = {{0}};
  44. static piu_message cur_msgs[PIU_NUM_LINES] = {{0}};
  45. int update_piu_display(int line) {
  46. piu_message *msg;
  47. if( (line >= PIU_NUM_LINES) || (line < 0) ) {
  48. return -1;
  49. }
  50. if(piu_fd < 0) {
  51. return -1;
  52. }
  53. msg = (cur_msgs[line].seconds == 0)?(def_msgs + line):(cur_msgs + line);
  54. // write the command prefix
  55. //
  56. write(piu_fd, piu_set_line_cmd[line], 3);
  57. // write the actual message
  58. //
  59. write(piu_fd, msg->message, strlen(msg->message));
  60. // write the CR to signal EOL
  61. //
  62. write(piu_fd, "\r", 1);
  63. //printf("%s%s\n",piu_set_line_cmd[line], msg->message);
  64. return 0;
  65. }
  66. message_callback_return handle_display_message(struct message_record *msg, void *param) {
  67. piu_message *cmd = (piu_message *) msg->payload;
  68. piu_message *tgt = NULL;
  69. // obtain a timestamp for NOW
  70. //
  71. time_t now = time(NULL);
  72. int touched = 0;
  73. // if we have an update for a line that doesn't exist, ignore it
  74. //
  75. if( (cmd->line >= PIU_NUM_LINES) || (cmd->line < 0) ) {
  76. return MESSAGE_HANDLED_CONT;
  77. }
  78. // if we're updating the default message
  79. //
  80. if(cmd->seconds == 0) {
  81. // get a pointer to our message target
  82. //
  83. tgt = def_msgs + cmd->line;
  84. // just do it...
  85. //
  86. memcpy(tgt, cmd, sizeof(piu_message));
  87. // force terminate the string just in case
  88. //
  89. tgt->message[PIU_MESSAGE_LEN - 1] = '\0';
  90. // priority is meaningless for default messages
  91. //
  92. tgt->priority = 0;
  93. //if the active message on this line has expired, a change in
  94. // default message will need to trigger an update
  95. //
  96. if(cur_msgs[cmd->line].seconds < now) {
  97. touched = 1;
  98. }
  99. }
  100. else
  101. {
  102. // get a pointer to our message target
  103. //
  104. tgt = cur_msgs + cmd->line;
  105. // If the current message has expired or will expire this second (thus bringing up the default message)
  106. // OR the new message is of higher priority then we need to do the repacement
  107. //
  108. if( (tgt->seconds <= now) || (cmd->priority >= tgt->priority) ) {
  109. // perform the copy
  110. //
  111. memcpy(tgt, cmd, sizeof(piu_message));
  112. // force terminate the string just in case
  113. //
  114. tgt->message[PIU_MESSAGE_LEN - 1] = '\0';
  115. // set the expiration time on the new message to the timestamp of NOW
  116. // plus the desired duration
  117. //
  118. tgt->seconds = now + cmd->seconds;
  119. touched = 1;
  120. }
  121. }
  122. // if we have made any changes...
  123. //
  124. if(touched) {
  125. update_piu_display(cmd->line);
  126. }
  127. return MESSAGE_HANDLED_CONT;
  128. }
  129. int handle_message_timeouts() {
  130. int i;
  131. time_t now = time(NULL);
  132. // For each display line
  133. //
  134. for(i = 0; i < PIU_NUM_LINES; i++) {
  135. // Check to see if it has an active current message
  136. //
  137. if(cur_msgs[i].seconds != 0) {
  138. // If so, see if that message has passed its expiration time
  139. //
  140. if(cur_msgs[i].seconds < now) {
  141. // if it has...
  142. //
  143. cur_msgs[i].seconds = 0;
  144. // flag it inactive
  145. // and update the PIU
  146. //
  147. update_piu_display(i);
  148. }
  149. }
  150. }
  151. return 0;
  152. }
  153. #ifdef BEEP_WITH_MAGSTRIPE_READER
  154. char dup_notify_str[MAX_PAYLOAD_LENGTH] = {0};
  155. long long dup_notify_usec = 0;
  156. message_callback_return handle_DIU_beep(struct message_record *msg, void *param) {
  157. int is_dup;
  158. long long dup_usec_delta = 0;
  159. if(strncmp(msg->payload, dup_notify_str, MAX_PAYLOAD_LENGTH)) {
  160. strncpy(dup_notify_str, msg->payload, MAX_PAYLOAD_LENGTH - 1);
  161. dup_notify_str[MAX_PAYLOAD_LENGTH - 1] = '\0';
  162. is_dup = 0;
  163. dup_notify_usec = 0;
  164. }
  165. else {
  166. is_dup = 1;
  167. dup_usec_delta = get_usec_time() - dup_notify_usec;
  168. dup_notify_usec = get_usec_time();
  169. }
  170. switch(msg->payload[0])
  171. {
  172. case LOGLEVEL_EVENT:
  173. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
  174. PIU_ACK_BEEP(piu_fd);
  175. }
  176. break;
  177. case LOGLEVEL_REJECT:
  178. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
  179. PIU_ERROR_BEEP(piu_fd);
  180. }
  181. break;
  182. case LOGLEVEL_ACCEPT:
  183. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
  184. PIU_ACK_BEEP(piu_fd);
  185. }
  186. break;
  187. case LOGLEVEL_ERROR:
  188. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
  189. PIU_CRITICAL_BEEP(piu_fd);
  190. }
  191. break;
  192. default:
  193. break;
  194. }
  195. return MESSAGE_HANDLED_CONT;
  196. }
  197. #endif
  198. //-------------------------
  199. void maintain_ipc_hub_connect(char *progname) {
  200. struct message_record outgoing_msg;
  201. // if we have no connection to the IPC hub
  202. if(hub_fd < 0) {
  203. // try and get one
  204. //
  205. hub_fd = connect_to_message_server(progname);
  206. if(hub_fd >= 0)
  207. {
  208. // Subscribe to the default status messages
  209. //
  210. subscribe_to_default_messages(hub_fd);
  211. // Subscribe to our specific message
  212. //
  213. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_PIU_MESSAGE, strlen(MAILBOX_PIU_MESSAGE));
  214. send_message(hub_fd,&outgoing_msg);
  215. #ifdef BEEP_WITH_MAGSTRIPE_READER
  216. // if BEEP_WITH_MAGSTRIPE_READER is defined, that means our diu board's speaker/amp is FUCKING BROKEN and we
  217. // need to intercept DIU messages and induce the magstripe reader on the PIU to beep when they occur even
  218. // though it is a stupid idea to do so since any other activity on the magstripe reader (like swiping a card...)
  219. // will cause it to either miss the card or disrupt the beeping.
  220. //
  221. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_DRIVER_NOTIFY, strlen(MAILBOX_DRIVER_NOTIFY));
  222. send_message(hub_fd,&outgoing_msg);
  223. #endif
  224. }
  225. else
  226. {
  227. fprintf(stderr, "Cannot connect to IPC hub!\n");
  228. }
  229. }
  230. }
  231. int main(int argc, char **argv) {
  232. char line[LINE_BUFFER_SIZE] = {0};
  233. struct message_record incoming_msg;
  234. struct message_record outgoing_msg;
  235. struct pollfd fds[2];
  236. int nfd;
  237. int poll_return;
  238. int read_return;
  239. int i, j;
  240. #ifdef DEBUG_PRINT
  241. long long int _usec_now, _usec_prv, _usec_del;
  242. _usec_now = get_usec_time();
  243. _usec_prv = _usec_now;
  244. _usec_del = 60000000;
  245. #endif
  246. long long int _usec_ratelimit_now, _usec_ratelimit_prv, _usec_ratelimit_del;
  247. _usec_ratelimit_now = get_usec_time();
  248. _usec_ratelimit_prv = _usec_ratelimit_now;
  249. _usec_ratelimit_del = 1000000;
  250. if ( (argc>1) && (
  251. (strncmp(argv[1], "-h", 3)==0) ||
  252. (strncmp(argv[1], "-v", 3)==0) ) ) {
  253. printf("piu_minder version: %s\n", PIU_MINDER_VERSION);
  254. exit(0);
  255. }
  256. configure_signal_handlers(argv[0]);
  257. maintain_ipc_hub_connect(argv[0]);
  258. // Register our defualt system message processing callbacks
  259. //
  260. register_system_status_callbacks();
  261. // Register our module specific message processing callbacks
  262. //
  263. register_dispatch_callback(MAILBOX_PIU_MESSAGE, CALLBACK_USER(1), &handle_display_message, NULL);
  264. #ifdef BEEP_WITH_MAGSTRIPE_READER
  265. register_dispatch_callback(MAILBOX_DRIVER_NOTIFY, CALLBACK_USER(2), &handle_DIU_beep, NULL);
  266. #endif
  267. // loop until we get asked to exit...
  268. //
  269. while( exit_request_status == EXIT_REQUEST_NONE ) {
  270. RESET_WATCHDOG();
  271. #ifdef DEBUG_PRINT
  272. _usec_now = get_usec_time();
  273. if ((_usec_now - _usec_prv) > _usec_del) {
  274. printf("[%lli] piu_minder: heartbeat\n", get_usec_time());
  275. _usec_prv = _usec_now;
  276. }
  277. #endif
  278. maintain_ipc_hub_connect(argv[0]);
  279. if(piu_fd < 0) {
  280. piu_fd = open_rs232_device(PASSENGER_UI_PORT, USE_DEFAULT_BAUD, RS232_LINE);
  281. if(piu_fd >= 0) {
  282. read_return = test_and_init_device(piu_fd, &piu_test_vector, diag);
  283. if(read_return) {
  284. fprintf(stderr, "PIU Init Failed on %s: %s\n", PASSENGER_UI_PORT, diag);
  285. close(piu_fd);
  286. piu_fd = -1;
  287. }
  288. // If we successfully connected...
  289. //
  290. else {
  291. // iterate through each line
  292. //
  293. for(j = 0; j < PIU_NUM_LINES; j++) {
  294. // and update the display to be current
  295. //
  296. update_piu_display(j);
  297. }
  298. }
  299. }
  300. else {
  301. _usec_ratelimit_now = get_usec_time();
  302. if ((_usec_ratelimit_now - _usec_ratelimit_prv) > _usec_ratelimit_del) {
  303. fprintf(stderr, "Cannot open serial port %s for PIU!\n", PASSENGER_UI_PORT);
  304. _usec_ratelimit_prv = _usec_ratelimit_now;
  305. }
  306. }
  307. }
  308. nfd = 0;
  309. if(hub_fd >= 0) {
  310. fds[nfd].fd = hub_fd;
  311. fds[nfd].events = POLLIN;
  312. fds[nfd].revents = 0;
  313. nfd++;
  314. }
  315. if(piu_fd >= 0) {
  316. fds[nfd].fd = piu_fd;
  317. fds[nfd].events = POLLIN;
  318. fds[nfd].revents = 0;
  319. nfd++;
  320. }
  321. // if we have any file descriptors, poll them
  322. //
  323. if(nfd > 0) {
  324. poll_return = poll(fds, nfd, POLL_TIMEOUT);
  325. }
  326. // otherwise, whistle and look busy
  327. //
  328. else {
  329. poll_return = 0;
  330. sleep(1);
  331. }
  332. //--------------------------------------------------------------------------------
  333. // No matter what poll() returned, we need to do some basic background work here...
  334. //
  335. //--------------------------------------------------------------------------------
  336. handle_message_timeouts();
  337. //--------------------------------------------------------------------------------
  338. // if poll didn't net us any work to do,
  339. //
  340. if(poll_return < 1) {
  341. //lets try again
  342. //
  343. continue;
  344. }
  345. for(i = 0; i < nfd; i++) {
  346. //If we're looking at the PIU...
  347. //
  348. if( fds[i].fd == piu_fd ) {
  349. // if poll says our serial port has become bogus...
  350. //
  351. if(fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) {
  352. fprintf(stderr, "This is very odd... Poll returned flags %d on our serial port...\n", fds[i].revents);
  353. // close it
  354. //
  355. close(piu_fd);
  356. // flag it invalid
  357. piu_fd = -1;
  358. // and break out of the for loop to allow the while to cycle
  359. //
  360. break;
  361. }
  362. if(fds[i].revents & POLLIN) {
  363. read_return = read(fds[i].fd, line, sizeof(line));
  364. if(read_return > 0) {
  365. char *trav = line;
  366. line[read_return] = '\0';
  367. strip_crlf(line);
  368. // advance until EOL or we hit our start sentinel
  369. //
  370. while(*trav && (*trav != '/') ) {
  371. trav++;
  372. }
  373. // Check to see that our address header is intact...
  374. //
  375. if( (trav[0] == '/') && (trav[2] == ':') ) {
  376. switch(trav[1]) {
  377. case 'M':
  378. // advance past the header
  379. //
  380. trav += 3;
  381. // Ignore ACK message from the magstripe reader
  382. //
  383. if(strcmp(trav, "^")) {
  384. prepare_message(&outgoing_msg, MAILBOX_TOKEN_MAG, trav, strlen(trav) + 1);
  385. send_message(hub_fd, &outgoing_msg);
  386. }
  387. break;
  388. case 'R':
  389. // advance past the header
  390. //
  391. trav += 3;
  392. // Ignore null reads and
  393. // spurious 1-bit reads
  394. //
  395. if ( (strcmp(trav,"0|0")!=0) &&
  396. (strcmp(trav,"1|0")!=0) &&
  397. (strcmp(trav,"1|1")!=0) ) {
  398. prepare_message(&outgoing_msg, MAILBOX_TOKEN_RFID, trav, strlen(trav) + 1);
  399. send_message(hub_fd, &outgoing_msg);
  400. }
  401. break;
  402. // handle warnings
  403. //
  404. case '*':
  405. // debugs
  406. //
  407. case '#':
  408. // and errors
  409. //
  410. case '!':
  411. // send them all to the log server
  412. //
  413. format_log_message(&outgoing_msg, trav[1], "PIU Reports: %s", trav + 3);
  414. send_message(hub_fd, &outgoing_msg);
  415. // but in the case of errors, send them to the driver too
  416. //
  417. if(trav[1] == '!') {
  418. format_driver_message(&outgoing_msg, trav[1], "PIU Reports: %s", trav + 3);
  419. send_message(hub_fd, &outgoing_msg);
  420. }
  421. break;
  422. // ignore any message addresses that we don't know what to do with
  423. //
  424. default:
  425. printf("Ignoring command \"%s\"\n", trav);
  426. break;
  427. }
  428. }
  429. else {
  430. //printf("Ignoring non-command line \"%s\"\n", trav);
  431. }
  432. }
  433. else
  434. {
  435. fprintf(stderr, "Read from %s returned %d!\n", PASSENGER_UI_PORT, read_return);
  436. //close it
  437. close(piu_fd);
  438. //flag it invalid
  439. piu_fd = -1;
  440. //and break out of the for loop to allow the while to cycle
  441. break;
  442. }
  443. }
  444. }
  445. //If we're looking at the IPC hub...
  446. else if( fds[i].fd == hub_fd )
  447. {
  448. //if poll says our connection to the IPC hub has died...
  449. if(fds[i].revents & (POLLHUP | POLLERR | POLLNVAL))
  450. {
  451. //complain
  452. fprintf(stderr, "The connection to the IPC hub has gone away...\n");
  453. //close it
  454. close(hub_fd);
  455. //flag it dead
  456. hub_fd = -1;
  457. //break out of the for loop
  458. break;
  459. }
  460. // if we have mail in any of our mailboxes...
  461. //
  462. if(fds[i].revents & POLLIN) {
  463. read_return = get_message(hub_fd, &incoming_msg);
  464. if(read_return < 0) {
  465. // complain
  466. //
  467. fprintf(stderr, "The connection to the IPC hub has gone away...\n");
  468. // close it
  469. //
  470. close(hub_fd);
  471. // flag it dead
  472. //
  473. hub_fd = -1;
  474. // break out of the for loop
  475. //
  476. break;
  477. }
  478. else {
  479. message_callback_return msg_status;
  480. msg_status = process_message(&incoming_msg);
  481. if (msg_status < 0) { }
  482. }
  483. }
  484. }
  485. }
  486. }
  487. printf("Exiting.\n");
  488. close(piu_fd);
  489. close(hub_fd);
  490. return 0;
  491. }