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