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