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