slcand.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  2. * slcand.c - userspace daemon for serial line CAN interface driver SLCAN
  3. *
  4. * Copyright (c) 2009 Robert Haddon <robert.haddon@verari.com>
  5. * Copyright (c) 2009 Verari Systems Inc.
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. *
  21. * Send feedback to <linux-can@vger.kernel.org>
  22. *
  23. */
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <unistd.h>
  28. #include <sys/types.h>
  29. #include <sys/stat.h>
  30. #include <sys/socket.h>
  31. #include <fcntl.h>
  32. #include <syslog.h>
  33. #include <errno.h>
  34. #include <pwd.h>
  35. #include <signal.h>
  36. #include <sys/ioctl.h>
  37. #include <net/if.h>
  38. #include <termios.h>
  39. #include <linux/tty.h>
  40. #include <linux/sockios.h>
  41. #include <linux/serial.h>
  42. #include <stdarg.h>
  43. /* Change this to whatever your daemon is called */
  44. #define DAEMON_NAME "slcand"
  45. /* Change this to the user under which to run */
  46. #define RUN_AS_USER "root"
  47. /* The length of ttypath buffer */
  48. #define TTYPATH_LENGTH 256
  49. /* UART flow control types */
  50. #define FLOW_NONE 0
  51. #define FLOW_HW 1
  52. #define FLOW_SW 2
  53. static void fake_syslog(int priority, const char *format, ...)
  54. {
  55. va_list ap;
  56. printf("[%d] ", priority);
  57. va_start(ap, format);
  58. vprintf(format, ap);
  59. va_end(ap);
  60. printf("\n");
  61. }
  62. typedef void (*syslog_t)(int priority, const char *format, ...);
  63. static syslog_t syslogger = syslog;
  64. void print_usage(char *prg)
  65. {
  66. fprintf(stderr, "\nUsage: %s [options] <tty> [canif-name]\n\n", prg);
  67. fprintf(stderr, "Options: -o (send open command 'O\\r')\n");
  68. fprintf(stderr, " -c (send close command 'C\\r')\n");
  69. fprintf(stderr, " -f (read status flags with 'F\\r' to reset error states)\n");
  70. fprintf(stderr, " -l (send listen only command 'L\\r', overrides -o)\n");
  71. fprintf(stderr, " -s <speed> (set CAN speed 0..8)\n");
  72. fprintf(stderr, " -S <speed> (set UART speed in baud)\n");
  73. fprintf(stderr, " -t <type> (set UART flow control type 'hw' or 'sw')\n");
  74. fprintf(stderr, " -b <btr> (set bit time register value)\n");
  75. fprintf(stderr, " -F (stay in foreground; no daemonize)\n");
  76. fprintf(stderr, " -h (show this help page)\n");
  77. fprintf(stderr, "\nExamples:\n");
  78. fprintf(stderr, "slcand -o -c -f -s6 ttyUSB0\n");
  79. fprintf(stderr, "slcand -o -c -f -s6 ttyUSB0 can0\n");
  80. fprintf(stderr, "slcand -o -c -f -s6 /dev/ttyUSB0\n");
  81. fprintf(stderr, "\n");
  82. exit(EXIT_FAILURE);
  83. }
  84. static int slcand_running;
  85. static int exit_code;
  86. static char ttypath[TTYPATH_LENGTH];
  87. static void child_handler(int signum)
  88. {
  89. switch (signum) {
  90. case SIGUSR1:
  91. /* exit parent */
  92. exit(EXIT_SUCCESS);
  93. break;
  94. case SIGALRM:
  95. case SIGCHLD:
  96. syslogger(LOG_NOTICE, "received signal %i on %s", signum, ttypath);
  97. exit_code = EXIT_FAILURE;
  98. slcand_running = 0;
  99. break;
  100. case SIGINT:
  101. case SIGTERM:
  102. syslogger(LOG_NOTICE, "received signal %i on %s", signum, ttypath);
  103. exit_code = EXIT_SUCCESS;
  104. slcand_running = 0;
  105. break;
  106. }
  107. }
  108. static int look_up_uart_speed(long int s)
  109. {
  110. switch (s) {
  111. case 9600:
  112. return B9600;
  113. case 19200:
  114. return B19200;
  115. case 38400:
  116. return B38400;
  117. case 57600:
  118. return B57600;
  119. case 115200:
  120. return B115200;
  121. case 230400:
  122. return B230400;
  123. case 460800:
  124. return B460800;
  125. case 500000:
  126. return B500000;
  127. case 576000:
  128. return B576000;
  129. case 921600:
  130. return B921600;
  131. case 1000000:
  132. return B1000000;
  133. case 1152000:
  134. return B1152000;
  135. case 1500000:
  136. return B1500000;
  137. case 2000000:
  138. return B2000000;
  139. #ifdef B2500000
  140. case 2500000:
  141. return B2500000;
  142. #endif
  143. #ifdef B3000000
  144. case 3000000:
  145. return B3000000;
  146. #endif
  147. #ifdef B3500000
  148. case 3500000:
  149. return B3500000;
  150. #endif
  151. #ifdef B3710000
  152. case 3710000
  153. return B3710000;
  154. #endif
  155. #ifdef B4000000
  156. case 4000000:
  157. return B4000000;
  158. #endif
  159. default:
  160. return -1;
  161. }
  162. }
  163. int main(int argc, char *argv[])
  164. {
  165. char *tty = NULL;
  166. char const *devprefix = "/dev/";
  167. char *name = NULL;
  168. char buf[IFNAMSIZ+1];
  169. struct termios tios;
  170. speed_t old_ispeed;
  171. speed_t old_ospeed;
  172. int opt;
  173. int send_open = 0;
  174. int send_close = 0;
  175. int send_listen = 0;
  176. int send_read_status_flags = 0;
  177. char *speed = NULL;
  178. char *uart_speed_str = NULL;
  179. long int uart_speed = 0;
  180. int flow_type = FLOW_NONE;
  181. char *btr = NULL;
  182. int run_as_daemon = 1;
  183. char *pch;
  184. int ldisc = N_SLCAN;
  185. int fd;
  186. ttypath[0] = '\0';
  187. while ((opt = getopt(argc, argv, "ocfls:S:t:b:?hF")) != -1) {
  188. switch (opt) {
  189. case 'o':
  190. send_open = 1;
  191. break;
  192. case 'c':
  193. send_close = 1;
  194. break;
  195. case 'f':
  196. send_read_status_flags = 1;
  197. break;
  198. case 'l':
  199. send_listen = 1;
  200. break;
  201. case 's':
  202. speed = optarg;
  203. if (strlen(speed) > 1)
  204. print_usage(argv[0]);
  205. break;
  206. case 'S':
  207. uart_speed_str = optarg;
  208. errno = 0;
  209. uart_speed = strtol(uart_speed_str, NULL, 10);
  210. if (errno)
  211. print_usage(argv[0]);
  212. if (look_up_uart_speed(uart_speed) == -1) {
  213. fprintf(stderr, "Unsupported UART speed (%lu)\n", uart_speed);
  214. exit(EXIT_FAILURE);
  215. }
  216. break;
  217. case 't':
  218. if (!strcmp(optarg, "hw")) {
  219. flow_type = FLOW_HW;
  220. } else if (!strcmp(optarg, "sw")) {
  221. flow_type = FLOW_SW;
  222. } else {
  223. fprintf(stderr, "Unsupported flow type (%s)\n", optarg);
  224. exit(EXIT_FAILURE);
  225. }
  226. break;
  227. case 'b':
  228. btr = optarg;
  229. if (strlen(btr) > 6)
  230. print_usage(argv[0]);
  231. break;
  232. case 'F':
  233. run_as_daemon = 0;
  234. break;
  235. case 'h':
  236. case '?':
  237. default:
  238. print_usage(argv[0]);
  239. break;
  240. }
  241. }
  242. if (!run_as_daemon)
  243. syslogger = fake_syslog;
  244. /* Initialize the logging interface */
  245. openlog(DAEMON_NAME, LOG_PID, LOG_LOCAL5);
  246. /* Parse serial device name and optional can interface name */
  247. tty = argv[optind];
  248. if (NULL == tty)
  249. print_usage(argv[0]);
  250. name = argv[optind + 1];
  251. /* Prepare the tty device name string */
  252. pch = strstr(tty, devprefix);
  253. if (pch != tty)
  254. snprintf(ttypath, TTYPATH_LENGTH, "%s%s", devprefix, tty);
  255. else
  256. snprintf(ttypath, TTYPATH_LENGTH, "%s", tty);
  257. syslogger(LOG_INFO, "starting on TTY device %s", ttypath);
  258. /* Daemonize */
  259. if (run_as_daemon) {
  260. if (daemon(0, 0)) {
  261. syslogger(LOG_ERR, "failed to daemonize");
  262. exit(EXIT_FAILURE);
  263. }
  264. }
  265. else {
  266. /* Trap signals that we expect to receive */
  267. signal(SIGINT, child_handler);
  268. signal(SIGTERM, child_handler);
  269. }
  270. /* */
  271. slcand_running = 1;
  272. /* Now we are a daemon -- do the work for which we were paid */
  273. fd = open(ttypath, O_RDWR | O_NONBLOCK | O_NOCTTY);
  274. if (fd < 0) {
  275. syslogger(LOG_NOTICE, "failed to open TTY device %s\n", ttypath);
  276. perror(ttypath);
  277. exit(EXIT_FAILURE);
  278. }
  279. /* Configure baud rate */
  280. memset(&tios, 0, sizeof(struct termios));
  281. if (tcgetattr(fd, &tios) < 0) {
  282. syslogger(LOG_NOTICE, "failed to get attributes for TTY device %s: %s\n", ttypath, strerror(errno));
  283. exit(EXIT_FAILURE);
  284. }
  285. // Because of a recent change in linux - https://patchwork.kernel.org/patch/9589541/
  286. // we need to set low latency flag to get proper receive latency
  287. struct serial_struct snew;
  288. ioctl (fd, TIOCGSERIAL, &snew);
  289. snew.flags |= ASYNC_LOW_LATENCY;
  290. ioctl (fd, TIOCSSERIAL, &snew);
  291. /* Get old values for later restore */
  292. old_ispeed = cfgetispeed(&tios);
  293. old_ospeed = cfgetospeed(&tios);
  294. /* Reset UART settings */
  295. cfmakeraw(&tios);
  296. tios.c_iflag &= ~IXOFF;
  297. tios.c_cflag &= ~CRTSCTS;
  298. /* Baud Rate */
  299. cfsetispeed(&tios, look_up_uart_speed(uart_speed));
  300. cfsetospeed(&tios, look_up_uart_speed(uart_speed));
  301. /* Flow control */
  302. if (flow_type == FLOW_HW)
  303. tios.c_cflag |= CRTSCTS;
  304. else if (flow_type == FLOW_SW)
  305. tios.c_iflag |= (IXON | IXOFF);
  306. /* apply changes */
  307. if (tcsetattr(fd, TCSADRAIN, &tios) < 0)
  308. syslogger(LOG_NOTICE, "Cannot set attributes for device \"%s\": %s!\n", ttypath, strerror(errno));
  309. if (speed) {
  310. sprintf(buf, "C\rS%s\r", speed);
  311. write(fd, buf, strlen(buf));
  312. }
  313. if (btr) {
  314. sprintf(buf, "C\rs%s\r", btr);
  315. write(fd, buf, strlen(buf));
  316. }
  317. if (send_read_status_flags) {
  318. sprintf(buf, "F\r");
  319. write(fd, buf, strlen(buf));
  320. }
  321. if (send_listen) {
  322. sprintf(buf, "L\r");
  323. write(fd, buf, strlen(buf));
  324. } else if (send_open) {
  325. sprintf(buf, "O\r");
  326. write(fd, buf, strlen(buf));
  327. }
  328. /* set slcan like discipline on given tty */
  329. if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
  330. perror("ioctl TIOCSETD");
  331. exit(EXIT_FAILURE);
  332. }
  333. /* retrieve the name of the created CAN netdevice */
  334. if (ioctl(fd, SIOCGIFNAME, buf) < 0) {
  335. perror("ioctl SIOCGIFNAME");
  336. exit(EXIT_FAILURE);
  337. }
  338. syslogger(LOG_NOTICE, "attached TTY %s to netdevice %s\n", ttypath, buf);
  339. /* try to rename the created netdevice */
  340. if (name) {
  341. struct ifreq ifr;
  342. int s = socket(PF_INET, SOCK_DGRAM, 0);
  343. if (s < 0)
  344. perror("socket for interface rename");
  345. else {
  346. strncpy(ifr.ifr_name, buf, IFNAMSIZ);
  347. strncpy(ifr.ifr_newname, name, IFNAMSIZ);
  348. if (ioctl(s, SIOCSIFNAME, &ifr) < 0) {
  349. syslogger(LOG_NOTICE, "netdevice %s rename to %s failed\n", buf, name);
  350. perror("ioctl SIOCSIFNAME rename");
  351. exit(EXIT_FAILURE);
  352. } else
  353. syslogger(LOG_NOTICE, "netdevice %s renamed to %s\n", buf, name);
  354. close(s);
  355. }
  356. }
  357. /* The Big Loop */
  358. while (slcand_running)
  359. sleep(1); /* wait 1 second */
  360. /* Reset line discipline */
  361. syslogger(LOG_INFO, "stopping on TTY device %s", ttypath);
  362. ldisc = N_TTY;
  363. if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
  364. perror("ioctl TIOCSETD");
  365. exit(EXIT_FAILURE);
  366. }
  367. if (send_close) {
  368. sprintf(buf, "C\r");
  369. write(fd, buf, strlen(buf));
  370. }
  371. /* Reset old rates */
  372. cfsetispeed(&tios, old_ispeed);
  373. cfsetospeed(&tios, old_ospeed);
  374. /* apply changes */
  375. if (tcsetattr(fd, TCSADRAIN, &tios) < 0)
  376. syslogger(LOG_NOTICE, "Cannot set attributes for device \"%s\": %s!\n", ttypath, strerror(errno));
  377. /* Finish up */
  378. syslogger(LOG_NOTICE, "terminated on %s", ttypath);
  379. closelog();
  380. return exit_code;
  381. }