LIRC libraries
LinuxInfraredRemoteControl
serial.c
Go to the documentation of this file.
1 /****************************************************************************
2 ** serial.c ****************************************************************
3 ****************************************************************************
4 *
5 * common routines for hardware that uses the standard serial port driver
6 *
7 * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
8 *
9 */
10 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #ifndef LIRC_LOCKDIR
22 #define LIRC_LOCKDIR "/var/lock/lockdev"
23 #endif
24 
25 
26 #include <limits.h>
27 #include <poll.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <termios.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <dirent.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/ioctl.h>
42 
43 #if defined __linux__
44 #include <linux/serial.h> /* for 'struct serial_struct' to set custom
45  * baudrates */
46 #endif
47 
48 #include "lirc/lirc_log.h"
49 #include "lirc/curl_poll.h"
50 
51 
52 static const logchannel_t logchannel = LOG_LIB;
53 
54 int tty_reset(int fd)
55 {
56  struct termios options;
57 
58  if (tcgetattr(fd, &options) == -1) {
59  log_trace("tty_reset(): tcgetattr() failed");
60  log_perror_debug("tty_reset()");
61  return 0;
62  }
63 #ifdef DILOS
64  /* cfmakeraw */
65  options.c_iflag &= ~(IMAXBEL|IXOFF|INPCK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IGNPAR);
66  options.c_iflag |= IGNBRK;
67  options.c_oflag &= ~OPOST;
68  options.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN|NOFLSH|TOSTOP|PENDIN);
69  options.c_cflag &= ~(CSIZE|PARENB);
70  options.c_cflag |= CS8|CREAD;
71  options.c_cc[VMIN] = 1;
72  options.c_cc[VTIME] = 0;
73 #else /* DILOS */
74  cfmakeraw(&options);
75 #endif /* DILOS */
76  if (tcsetattr(fd, TCSAFLUSH, &options) == -1) {
77  log_trace("tty_reset(): tcsetattr() failed");
78  log_perror_debug("tty_reset()");
79  return 0;
80  }
81  return 1;
82 }
83 
84 int tty_setrtscts(int fd, int enable)
85 {
86  struct termios options;
87 
88  if (tcgetattr(fd, &options) == -1) {
89  log_trace("%s: tcgetattr() failed", __func__);
90  log_perror_debug(__func__);
91  return 0;
92  }
93  if (enable)
94  options.c_cflag |= CRTSCTS;
95  else
96  options.c_cflag &= ~CRTSCTS;
97  if (tcsetattr(fd, TCSAFLUSH, &options) == -1) {
98  log_trace("%s: tcsetattr() failed", __func__);
99  log_perror_debug(__func__);
100  return 0;
101  }
102  return 1;
103 }
104 
105 int tty_setdtr(int fd, int enable)
106 {
107  int cmd, sts;
108 
109  if (ioctl(fd, TIOCMGET, &sts) < 0) {
110  log_trace("%s: ioctl(TIOCMGET) failed", __func__);
111  log_perror_debug(__func__);
112  return 0;
113  }
114  if (((sts & TIOCM_DTR) == 0) && enable) {
115  log_trace("%s: 0->1", __func__);
116  } else if ((!enable) && (sts & TIOCM_DTR)) {
117  log_trace("%s: 1->0", __func__);
118  }
119  if (enable)
120  cmd = TIOCMBIS;
121  else
122  cmd = TIOCMBIC;
123  sts = TIOCM_DTR;
124  if (ioctl(fd, cmd, &sts) < 0) {
125  log_trace("%s: ioctl(TIOCMBI(S|C)) failed", __func__);
126  log_perror_debug(__func__);
127  return 0;
128  }
129  return 1;
130 }
131 
132 int tty_setbaud(int fd, int baud)
133 {
134  struct termios options;
135  int speed;
136 
137 #if defined __linux__
138  int use_custom_divisor = 0;
139  struct serial_struct serinfo;
140 #endif
141 
142  switch (baud) {
143  case 300:
144  speed = B300;
145  break;
146  case 1200:
147  speed = B1200;
148  break;
149  case 2400:
150  speed = B2400;
151  break;
152  case 4800:
153  speed = B4800;
154  break;
155  case 9600:
156  speed = B9600;
157  break;
158  case 19200:
159  speed = B19200;
160  break;
161  case 38400:
162  speed = B38400;
163  break;
164  case 57600:
165  speed = B57600;
166  break;
167  case 115200:
168  speed = B115200;
169  break;
170 #ifdef B230400
171  case 230400:
172  speed = B230400;
173  break;
174 #endif
175 #ifdef B460800
176  case 460800:
177  speed = B460800;
178  break;
179 #endif
180 #ifdef B500000
181  case 500000:
182  speed = B500000;
183  break;
184 #endif
185 #ifdef B576000
186  case 576000:
187  speed = B576000;
188  break;
189 #endif
190 #ifdef B921600
191  case 921600:
192  speed = B921600;
193  break;
194 #endif
195 #ifdef B1000000
196  case 1000000:
197  speed = B1000000;
198  break;
199 #endif
200 #ifdef B1152000
201  case 1152000:
202  speed = B1152000;
203  break;
204 #endif
205 #ifdef B1500000
206  case 1500000:
207  speed = B1500000;
208  break;
209 #endif
210 #ifdef B2000000
211  case 2000000:
212  speed = B2000000;
213  break;
214 #endif
215 #ifdef B2500000
216  case 2500000:
217  speed = B2500000;
218  break;
219 #endif
220 #ifdef B3000000
221  case 3000000:
222  speed = B3000000;
223  break;
224 #endif
225 #ifdef B3500000
226  case 3500000:
227  speed = B3500000;
228  break;
229 #endif
230 #ifdef B4000000
231  case 4000000:
232  speed = B4000000;
233  break;
234 #endif
235  default:
236 #if defined __linux__
237  speed = B38400;
238  use_custom_divisor = 1;
239  break;
240 #else
241  log_trace("tty_setbaud(): bad baud rate %d", baud);
242  return 0;
243 #endif
244  }
245  if (tcgetattr(fd, &options) == -1) {
246  log_trace("tty_setbaud(): tcgetattr() failed");
247  log_perror_debug("tty_setbaud()");
248  return 0;
249  }
250  (void)cfsetispeed(&options, speed);
251  (void)cfsetospeed(&options, speed);
252  if (tcsetattr(fd, TCSAFLUSH, &options) == -1) {
253  log_trace("tty_setbaud(): tcsetattr() failed");
254  log_perror_debug("tty_setbaud()");
255  return 0;
256  }
257 #if defined __linux__
258  if (use_custom_divisor) {
259  if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) {
260  log_trace("tty_setbaud(): TIOCGSERIAL failed");
261  log_perror_debug("tty_setbaud()");
262  return 0;
263  }
264  serinfo.flags &= ~ASYNC_SPD_MASK;
265  serinfo.flags |= ASYNC_SPD_CUST;
266  serinfo.custom_divisor = serinfo.baud_base / baud;
267  if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0) {
268  log_trace("tty_setbaud(): TIOCSSERIAL failed");
269  log_perror_debug("tty_setbaud()");
270  return 0;
271  }
272  }
273 #endif
274  return 1;
275 }
276 
277 int tty_setcsize(int fd, int csize)
278 {
279  struct termios options;
280  int size;
281 
282  switch (csize) {
283  case 5:
284  size = CS5;
285  break;
286  case 6:
287  size = CS6;
288  break;
289  case 7:
290  size = CS7;
291  break;
292  case 8:
293  size = CS8;
294  break;
295  default:
296  log_trace("tty_setcsize(): bad csize rate %d", csize);
297  return 0;
298  }
299  if (tcgetattr(fd, &options) == -1) {
300  log_trace("tty_setcsize(): tcgetattr() failed");
301  log_perror_debug("tty_setcsize()");
302  return 0;
303  }
304  options.c_cflag &= ~CSIZE;
305  options.c_cflag |= size;
306  if (tcsetattr(fd, TCSAFLUSH, &options) == -1) {
307  log_trace("tty_setcsize(): tcsetattr() failed");
308  log_perror_debug("tty_setcsize()");
309  return 0;
310  }
311  return 1;
312 }
313 
314 int tty_create_lock(const char* name)
315 {
316  char filename[FILENAME_MAX + 1];
317  char symlink[FILENAME_MAX + 1];
318  char cwd[FILENAME_MAX + 1];
319  const char* last;
320  const char* s;
321  char id[10 + 1 + 1];
322  int lock;
323  int len;
324 
325  strcpy(filename, LIRC_LOCKDIR "/LCK..");
326 
327  last = strrchr(name, '/');
328  if (last != NULL)
329  s = last + 1;
330  else
331  s = name;
332 
333  if (strlen(filename) + strlen(s) > FILENAME_MAX) {
334  log_error("invalid filename \"%s%s\"", filename, s);
335  return 0;
336  }
337  strcat(filename, s);
338 
339 tty_create_lock_retry:
340  len = snprintf(id, 10 + 1 + 1, "%10d\n", getpid());
341  if (len == -1) {
342  log_error("invalid pid \"%d\"", getpid());
343  return 0;
344  }
345  lock = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0644);
346  if (lock == -1) {
347  log_perror_err("could not create lock file \"%s\"", filename);
348  lock = open(filename, O_RDONLY);
349  if (lock != -1) {
350  pid_t otherpid;
351 
352  id[10 + 1] = 0;
353  if (read(lock, id, 10 + 1) == 10 + 1 && read(lock, id, 1) == 0
354  && sscanf(id, "%d\n", &otherpid) > 0) {
355  if (kill(otherpid, 0) == -1 && errno == ESRCH) {
356  log_warn("detected stale lockfile %s", filename);
357  close(lock);
358  if (unlink(filename) != -1) {
359  log_warn("stale lockfile removed");
360  goto tty_create_lock_retry;
361  } else {
363  "could not remove stale lockfile");
364  }
365  return 0;
366  }
367  log_error("%s is locked by PID %d", name, otherpid);
368  } else {
369  log_error("invalid lockfile %s encountered", filename);
370  }
371  close(lock);
372  }
373  return 0;
374  }
375  if (write(lock, id, len) != len) {
376  log_perror_err("could not write pid to lock file");
377  close(lock);
378  if (unlink(filename) == -1)
379  log_perror_err("could not delete file \"%s\"", filename);
380  /* FALLTHROUGH */
381  return 0;
382  }
383  if (close(lock) == -1) {
384  log_perror_err("could not close lock file");
385  if (unlink(filename) == -1)
386  log_perror_err("could not delete file \"%s\"", filename);
387  /* FALLTHROUGH */
388  return 0;
389  }
390 
391  len = readlink(name, symlink, FILENAME_MAX);
392  if (len == -1) {
393  if (errno != EINVAL) { /* symlink */
394  log_perror_err("readlink() failed for \"%s\"", name);
395  if (unlink(filename) == -1) {
396  log_perror_err("could not delete file \"%s\"",
397  filename);
398  /* FALLTHROUGH */
399  }
400  return 0;
401  }
402  } else {
403  symlink[len] = 0;
404 
405  if (last) {
406  char dirname[FILENAME_MAX + 1];
407 
408  if (getcwd(cwd, FILENAME_MAX) == NULL) {
409  log_perror_err("getcwd() failed");
410  if (unlink(filename) == -1) {
412  "could not delete file \"%s\"",
413  filename);
414  /* FALLTHROUGH */
415  }
416  return 0;
417  }
418 
419  strcpy(dirname, name);
420  dirname[strlen(name) - strlen(last)] = 0;
421  if (chdir(dirname) == -1) {
423  "chdir() to \"%s\" failed", dirname);
424  if (unlink(filename) == -1) {
426  "could not delete file \"%s\"",
427  filename);
428  /* FALLTHROUGH */
429  }
430  return 0;
431  }
432  }
433  if (tty_create_lock(symlink) == -1) {
434  if (unlink(filename) == -1) {
436  "could not delete file \"%s\"", filename);
437  /* FALLTHROUGH */
438  }
439  return 0;
440  }
441  if (last) {
442  if (chdir(cwd) == -1) {
443  log_perror_err("chdir() to \"%s\" failed", cwd);
444  if (unlink(filename) == -1) {
446  "could not delete file \"%s\"",
447  filename);
448  /* FALLTHROUGH */
449  }
450  return 0;
451  }
452  }
453  }
454  return 1;
455 }
456 
458 {
459  DIR* dp;
460  struct dirent* ep;
461  int lock;
462  int len;
463  char id[20] = { '\0' };
464  char filename[FILENAME_MAX + 1];
465  long pid;
466  int retval = 1;
467 
468  dp = opendir(LIRC_LOCKDIR);
469  if (dp != NULL) {
470  while ((ep = readdir(dp))) {
471  if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) {
472  retval = 0;
473  continue;
474  }
475  strcpy(filename, LIRC_LOCKDIR "/");
476  if (strlen(filename) + strlen(ep->d_name) > FILENAME_MAX) {
477  retval = 0;
478  continue;
479  }
480  strcat(filename, ep->d_name);
481  if (strstr(filename, "LCK..") == NULL) {
482  log_debug("Ignoring non-LCK.. logfile %s",
483  filename);
484  retval = 0;
485  continue;
486  }
487  lock = open(filename, O_RDONLY);
488  if (lock == -1) {
489  retval = 0;
490  continue;
491  }
492  len = read(lock, id, sizeof(id) - 1);
493  close(lock);
494  if (len <= 0) {
495  retval = 0;
496  continue;
497  }
498  pid = strtol(id, NULL, 10);
499  if (pid == LONG_MIN || pid == LONG_MAX || pid == 0) {
500  log_debug("Can't parse lockfile %s (ignored)",
501  filename);
502  retval = 0;
503  continue;
504  }
505  if (pid == getpid()) {
506  if (unlink(filename) == -1) {
508  "could not delete file \"%s\"",
509  filename);
510  retval = 0;
511  continue;
512  }
513  }
514  }
515  closedir(dp);
516  } else {
517  log_error("could not open directory \"" LIRC_LOCKDIR "\"");
518  return 0;
519  }
520  return retval;
521 }
522 
523 int tty_set(int fd, int rts, int dtr)
524 {
525  int mask;
526 
527  mask = rts ? TIOCM_RTS : 0;
528  mask |= dtr ? TIOCM_DTR : 0;
529  if (ioctl(fd, TIOCMBIS, &mask) == -1) {
530  log_trace("tty_set(): ioctl() failed");
531  log_perror_warn("tty_set()");
532  return 0;
533  }
534  return 1;
535 }
536 
537 int tty_clear(int fd, int rts, int dtr)
538 {
539  int mask;
540 
541  mask = rts ? TIOCM_RTS : 0;
542  mask |= dtr ? TIOCM_DTR : 0;
543  if (ioctl(fd, TIOCMBIC, &mask) == -1) {
544  log_perror_debug("tty_clear()");
545  log_trace("tty_clear(): ioctl() failed");
546  return 0;
547  }
548  return 1;
549 }
550 
551 int tty_write(int fd, char byte)
552 {
553  if (write(fd, &byte, 1) != 1) {
554  log_trace("tty_write(): write() failed");
555  log_perror_debug("tty_write()");
556  return -1;
557  }
558  /* wait until the stop bit of Control Byte is sent
559  * (for 9600 baud rate, it takes about 100 msec */
560  usleep(100 * 1000);
561 
562  /* we don't wait because tcdrain() does this for us */
563  /* tcdrain(fd); */
564  /* FIXME! but unfortunately this does not seem to be
565  * implemented in 2.0.x kernels ... */
566  return 1;
567 }
568 
569 int tty_read(int fd, char* byte)
570 {
571  struct pollfd pfd = {.fd = fd, .events = POLLIN, .revents = 0};
572  int ret;
573 
574  ret = poll(&pfd, 1, 1000); /* 1 second timeout. */
575  if (ret == 0) {
576  log_error("tty_read(): timeout");
577  return -1; /* received nothing, bad */
578  } else if (ret != 1) {
579  log_perror_debug("tty_read(): curl_poll() failed");
580  return -1;
581  }
582  if (read(fd, byte, 1) != 1) {
583  log_perror_debug("tty_read(): read() failed");
584  return -1;
585  }
586  return 1;
587 }
588 
589 int tty_write_echo(int fd, char byte)
590 {
591  char reply;
592 
593  if (tty_write(fd, byte) == -1)
594  return -1;
595  if (tty_read(fd, &reply) == -1)
596  return -1;
597  log_trace("sent: A%u D%01x reply: A%u D%01x", (((unsigned int)(unsigned char)byte) & 0xf0) >> 4,
598  ((unsigned int)(unsigned char)byte) & 0x0f, (((unsigned int)(unsigned char)reply) & 0xf0) >> 4,
599  ((unsigned int)(unsigned char)reply) & 0x0f);
600  if (byte != reply)
601  log_error("Command mismatch.");
602  return 1;
603 }
int tty_setrtscts(int fd, int enable)
Definition: serial.c:84
#define log_debug(fmt,...)
Definition: lirc_log.h:124
#define log_perror_debug(fmt,...)
Definition: lirc_log.h:99
int tty_setdtr(int fd, int enable)
Definition: serial.c:105
int tty_delete_lock(void)
Definition: serial.c:457
#define log_warn(fmt,...)
Definition: lirc_log.h:109
logchannel_t
Definition: lirc_log.h:53
int tty_reset(int fd)
Definition: serial.c:54
int tty_create_lock(const char *name)
Definition: serial.c:314
#define log_error(fmt,...)
Definition: lirc_log.h:104
int tty_write(int fd, char byte)
Definition: serial.c:551
int tty_clear(int fd, int rts, int dtr)
Definition: serial.c:537
int tty_setcsize(int fd, int csize)
Definition: serial.c:277
#define log_trace(fmt,...)
Definition: lirc_log.h:129
#define log_perror_err(fmt,...)
Definition: lirc_log.h:89
int tty_write_echo(int fd, char byte)
Definition: serial.c:589
int tty_read(int fd, char *byte)
Definition: serial.c:569
int tty_setbaud(int fd, int baud)
Definition: serial.c:132
#define log_perror_warn(fmt,...)
Definition: lirc_log.h:94
int tty_set(int fd, int rts, int dtr)
Definition: serial.c:523