LIRC libraries: /tmp/work/src/lirc-0.10.2/lib/serial.c Source File

LIRC libraries Linux Infrared Remote Control

Loading...
Searching...
No Matches
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
52static 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
327tty_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 {
350 log_perror_err(
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) {
399 log_perror_err(
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) {
410 log_perror_err(
411 "chdir() to \"%s\" failed", dirname);
412 if (unlink(filename) == -1) {
413 log_perror_err(
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) {
423 log_perror_err(
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) {
433 log_perror_err(
434 "could not delete file \"%s\"",
435 filename);
436 /* FALLTHROUGH */
437 }
438 return 0;
439 }
440 }
441 }
442 return 1;
443}
444
445 int tty_delete_lock(void)
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) {
495 log_perror_err(
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}
tty_write
int tty_write(int fd, char byte)
Write a single byte to serial device.
Definition: serial.c:539
tty_set
int tty_set(int fd, int rts, int dtr)
Set RTS and DTR control lines.
Definition: serial.c:511
tty_reset
int tty_reset(int fd)
Set the cfmakeraw termio options.
Definition: serial.c:54
tty_setbaud
int tty_setbaud(int fd, int baud)
Set the speed a.
Definition: serial.c:120
tty_read
int tty_read(int fd, char *byte)
Read a single byte from serial device.
Definition: serial.c:557
tty_setdtr
int tty_setdtr(int fd, int enable)
Set/clear DTR control line.
Definition: serial.c:93
tty_write_echo
int tty_write_echo(int fd, char byte)
Write a single byte and check the echo from remote party.
Definition: serial.c:577
tty_delete_lock
int tty_delete_lock(void)
Delete any legacy lock(s) owned by this process.
Definition: serial.c:445
tty_create_lock
int tty_create_lock(const char *name)
Creates a lock file of the type /var/local/LCK.
Definition: serial.c:302
tty_clear
int tty_clear(int fd, int rts, int dtr)
Clear RTS and DTR control lines.
Definition: serial.c:525
tty_setrtscts
int tty_setrtscts(int fd, int enable)
Set/clear CTS control line.
Definition: serial.c:72
tty_setcsize
int tty_setcsize(int fd, int csize)
Set the character size.
Definition: serial.c:265
log_trace
#define log_trace(fmt,...)
Log a trace message.
Definition: lirc_log.h:129
log_debug
#define log_debug(fmt,...)
Log a debug message.
Definition: lirc_log.h:124
log_perror_debug
#define log_perror_debug(fmt,...)
perror wrapper logging with level LIRC_DEBUG.
Definition: lirc_log.h:99
log_perror_err
#define log_perror_err(fmt,...)
perror wrapper logging with level LIRC_ERROR.
Definition: lirc_log.h:89
log_error
#define log_error(fmt,...)
Log an error message.
Definition: lirc_log.h:104
log_perror_warn
#define log_perror_warn(fmt,...)
perror wrapper logging with level LIRC_WARNING.
Definition: lirc_log.h:94
logchannel_t
logchannel_t
Log channels used to filter messages.
Definition: lirc_log.h:53
log_warn
#define log_warn(fmt,...)
Log a warning message.
Definition: lirc_log.h:109