1 
2 /* Unix icoms and serial I/O class */
3 
4 /*
5  * Argyll Color Correction System
6  *
7  * Author: Graeme W. Gill
8  * Date:   18/11/2000
9  *
10  * Copyright 1997 - 2013 Graeme W. Gill
11  * All rights reserved.
12  *
13  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
14  * see the License2.txt file for licencing details.
15  */
16 
17 /*
18     TTBD:
19  */
20 
21 #if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL)
22 
23 #include <sys/types.h>      /* Include sys/select.h ? */
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <termios.h>
27 #include <dirent.h>
28 #include <string.h>
29 
30 /* select() defined, but not poll(), so emulate poll() */
31 #if defined(FD_CLR) && !defined(POLLIN)
32 # include "pollem.h"
33 # define poll_x pollem
34 #else
35 # include <sys/poll.h>	/* Else assume poll() is native */
36 # define poll_x poll
37 #endif
38 
39 #ifdef UNIX_APPLE
40 //#include <stdbool.h>
41 # include <sys/sysctl.h>
42 # include <sys/param.h>
43 # include <CoreFoundation/CoreFoundation.h>
44 # include <IOKit/IOKitLib.h>
45 # include <IOKit/serial/IOSerialKeys.h>
46 # include <IOKit/IOBSD.h>
47 # include <mach/mach_init.h>
48 # include <mach/task_policy.h>
49 #endif /* UNIX_APPLE */
50 
51 
52 instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *);
53 
54 /* Add paths to serial connected device. */
55 /* Return an icom error */
serial_get_paths(icompaths * p,icom_type mask)56 int serial_get_paths(icompaths *p, icom_type mask) {
57 	int rv;
58 
59 	a1logd(p->log, 7, "serial_get_paths: called with mask %d\n",mask);
60 
61 #ifdef UNIX_APPLE
62 	/* Search the OSX registry for serial ports */
63 	if (mask & (icomt_serial | icomt_fastserial | icomt_btserial)) {
64 	    kern_return_t kstat;
65 	    mach_port_t mp;						/* Master IO port */
66 	    CFMutableDictionaryRef sdict;		/* Serial Port  dictionary */
67 		io_iterator_t mit;					/* Matching itterator */
68 		io_object_t ioob;					/* Serial object found */
69 
70 		a1logd(p->log, 6, "serial_get_paths: looking up serial ports services\n");
71 
72 		/* Get dictionary of serial ports */
73     	if ((sdict = IOServiceMatching(kIOSerialBSDServiceValue)) == NULL) {
74         	a1loge(p->log, ICOM_SYS, "IOServiceMatching returned a NULL dictionary\n");
75 			return ICOM_OK;		/* Hmm. There are no serial ports ? */
76 		}
77 
78 		/* Set value to match to RS232 type serial */
79         CFDictionarySetValue(sdict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type));
80 
81 		/* Init itterator to find matching types. Consumes sdict reference */
82 		if ((kstat = IOServiceGetMatchingServices(kIOMasterPortDefault, sdict, &mit))
83 		                                                                     != KERN_SUCCESS) {
84         	a1loge(p->log, ICOM_SYS, "IOServiceGetMatchingServices returned %d\n", kstat);
85 			return ICOM_SYS;
86 		}
87 
88 		/* Find all the matching serial ports */
89 		for (;;) {
90 			char pname[200];
91 			icom_type dctype = icomt_unknown;
92 
93 	        CFTypeRef dfp;		/* Device file path */
94 
95 		    if ((ioob = IOIteratorNext(mit)) == 0)
96 				break;
97 
98 		    /* Get the callout device's path (/dev/cu.xxxxx). */
99 			if ((dfp = IORegistryEntryCreateCFProperty(ioob, CFSTR(kIOCalloutDeviceKey),
100 			                                      kCFAllocatorDefault, 0)) == NULL)
101 				goto continue1;
102 
103 			/* Convert from CF string to C string */
104 			if (!CFStringGetCString(dfp, pname, 100, kCFStringEncodingASCII))
105 				goto continue2;
106 
107 			a1logd(p->log, 8, "serial_get_paths: checking '%s'\n",pname);
108 
109 			/* Ignore infra red port or any other noise */
110 			if (strstr(pname, "IrDA") != NULL
111 			 || strstr(pname, "Dialup") != NULL
112 			 || strstr(pname, "PDA-Sync") != NULL)
113 				goto continue2;
114 
115 			/* Would be nice to identify FTDI serial ports more specifically ? */
116 			if (strstr(pname, "usbserial") != NULL)
117 				dctype |= icomt_fastserial;
118 
119 			if (strstr(pname, "Bluetooth") != NULL
120 			 || strstr(pname, "JETI") != NULL) {
121 				dctype |= icomt_fastserial;
122 				dctype |= icomt_btserial;
123 			}
124 
125 #ifndef ENABLE_SERIAL
126 			if (dctype & icomt_fastserial) {		/* Only add fast ports if !ENABLE_SERIAL */
127 #endif
128 			if (((mask & icomt_serial) && !(dctype & icomt_fastserial))
129 			 || ((mask & icomt_fastserial) && (dctype & icomt_fastserial)
130 			                               && !(dctype & icomt_btserial))
131 			 || ((mask & icomt_btserial) && (dctype & icomt_btserial))) {
132 				/* Add the port to the list */
133 				p->add_serial(p, pname, pname, dctype);
134 				a1logd(p->log, 8, "serial_get_paths: Added path '%s' dctype 0x%x\n",pname, dctype);
135 			}
136 #ifndef ENABLE_SERIAL
137 			}
138 #endif
139 			/* If fast, try and identify it */
140 			if (dctype & icomt_fastserial) {
141 				icompath *path;
142 				icoms *icom;
143 				if ((path = p->get_last_path(p)) != NULL
144 				 && (icom = new_icoms(path, p->log)) != NULL) {
145 					instType itype = fast_ser_inst_type(icom, 0, NULL, NULL);
146 					if (itype != instUnknown)
147 						icompaths_set_serial_itype(path, itype);	/* And set category */
148 					icom->del(icom);
149 				}
150 				a1logd(p->log, 8, "serial_get_paths: Identified '%s' dctype 0x%x\n",inst_sname(path->itype),path->dctype);
151 			}
152 		continue2:
153             CFRelease(dfp);
154 		continue1:
155 		    IOObjectRelease(ioob);		/* Release found object */
156 		}
157 	    IOObjectRelease(mit);			/* Release the itterator */
158 	}
159 
160 #else /* Other UNIX like systems */
161 
162 	/* Many are crude and list every available device name, whether */
163 	/* it's usable or not. Do any UNIX systems have a mechanism for listing */
164 	/* serial ports ?? */
165 
166 	/* On Linux, the list in /proc/tty/driver/serial may indicate */
167 	/* which are real or not (if "uart:unknown" then not real) */
168 	/* e.g.:
169 
170 		0: uart:16550A port:000003F8 irq:4 tx:3 rx:1755 brk:1 RTS|DTR
171 		1: uart:16550A port:000002F8 irq:3 tx:11 rx:3 brk:3
172 		2: uart:unknown port:000003E8 irq:4
173 		3: uart:unknown port:000002E8 irq:3
174 		4: uart:unknown port:00000000 irq:0
175 		5: uart:unknown port:00000000 irq:0
176 		6: uart:unknown port:00000000 irq:0
177 		7: uart:unknown port:00000000 irq:0
178 
179 		but the permissions don't allow looking at this.
180 	 */
181 	/* (This info is similar to what is returned by "setserial -g /dev/ttyS*", */
182 	/*  and "setserial -gb /dev/ttyS*" returns just the real ports.) */
183 	/* None of this can distinguish if one is the mouse. */
184 
185 	/* From "QTSerialPort": */
186 	/*
187 		Constant         Used By         Naming Convention
188 		----------       -------------   ------------------------
189 		_TTY_WIN_        Windows         COM1, COM2
190 		_TTY_IRIX_       SGI/IRIX        /dev/ttyf1, /dev/ttyf2
191 		_TTY_HPUX_       HP-UX           /dev/tty1p0, /dev/tty2p0
192 		_TTY_SUN_        SunOS/Solaris   /dev/ttya, /dev/ttyb
193 		_TTY_DIGITAL_    Digital UNIX    /dev/tty01, /dev/tty02
194 		_TTY_FREEBSD_    FreeBSD         /dev/ttyd0, /dev/ttyd1
195 		_TTY_LINUX_      Linux           /dev/ttyS0, /dev/ttyS1
196 		<none>           Linux           /dev/ttyS0, /dev/ttyS1
197 		                 Linux           /dev/ttyUSB0, /dev/ttyUSB1
198 	*/
199 
200 	/*
201 		"Most program set a lock in /var/lock/LCK..tty<XX> on Linux ?
202 		<http://sunsite.ualberta.ca/LDP/LDP/nag2/x-087-2-serial.devices.html>
203 		<http://docs.freebsd.org/info/uucp/uucp.info.UUCP_Lock_Files.html>
204 
205 		We should really use the lock files to avoid treading on
206 		other programs toes. We assume at the moment that the user
207 		only picks a serial port with an instrument on it.
208 	*/
209 
210 	/* Search for devices that match the pattern /dev/ttyS[0-9]* and /dev/ttyUSB* */
211 	/* We will assume that ttyUSB* ports includes FTDI ports. */
212 	/* Bluetooth ports are named ttyHS* or rfcomm* ?? */
213 
214 	if (mask & (icomt_serial | icomt_fastserial | icomt_bt)) {
215 		DIR *dd;
216 		struct dirent *de;
217 		char *dirn = "/dev/";
218 
219 		a1logd(p->log, 6, "serial_get_paths: looking up serial port devices\n");
220 
221 		if ((dd = opendir(dirn)) == NULL) {
222 			a1loge(p->log, ICOM_SYS, "failed to open directory \"%s\"\n",dirn);
223 			return ICOM_OK;
224 		}
225 
226 		for (;;) {
227 			int fd;
228 			char *dpath;
229 			icom_type dctype = icomt_unknown;
230 
231 			if ((de = readdir(dd)) == NULL) {
232 				break;
233 			}
234 
235 			a1logd(p->log, 8, "serial_get_paths: checking '%s'\n",de->d_name);
236 
237 			if (!(
238 #if defined(__FreeBSD__) || defined(__OpenBSD__)
239 			   /* This should match uart & USB devs. */
240 				( strncmp (de->d_name, "cua", 3) == 0
241 				&& strlen (de->d_name) < 7)
242 #else
243 				/* Presumably Linux.. */
244 			    (   strncmp(de->d_name, "ttyS", 4) == 0
245 			     && de->d_name[4] >= '0' && de->d_name[4] <= '9')
246 			 || (   strncmp(de->d_name, "ttyUSB", 6) == 0)
247 			 || (   strncmp(de->d_name, "ttyHS", 5) == 0)
248 			 || (   strncmp(de->d_name, "rfcomm", 6) == 0)
249 #endif
250 			))
251 				continue;
252 
253 			if ((dpath = (char *)malloc(strlen(dirn) + strlen(de->d_name) + 1)) == NULL) {
254 				closedir(dd);
255 				a1loge(p->log, ICOM_SYS, "icompaths_refresh_paths_sel() malloc failed!\n");
256 				return ICOM_SYS;
257 			}
258 			strcpy(dpath, dirn);
259 			strcat(dpath, de->d_name);
260 
261 			/* See if the serial port is real */
262 			if (strncmp(de->d_name, "ttyUSB", 6) != 0
263 			 && strncmp(de->d_name, "ttyHS", 5) != 0
264 			 && strncmp(de->d_name, "rfcomm", 6) != 0) {
265 
266 				/* Hmm. This is probably a bad idea - it can upset other */
267 				/* programs that use the serial ports ? */
268 				if ((fd = open(dpath, O_RDONLY | O_NOCTTY | O_NONBLOCK)) < 0) {
269 					a1logd(p->log, 8, "serial_get_paths: failed to open serial \"%s\" r/o - not real\n",dpath);
270 					free(dpath);
271 					msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
272 					continue;
273 				}
274 				/* On linux we could do a
275 					struct serial_struct serinfo;
276 
277 					serinfo.reserved_char[0] = 0;
278 
279 					if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0
280 						|| serinfo.type == PORT_UNKNOWN) {
281 						free(dpath);
282 						continue;
283 					}
284 
285 				 */
286 				close(fd);
287 				msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
288 				a1logd(p->log, 8, "serial_get_paths: open'd serial \"%s\" r/o - assume real\n",dpath);
289 			}
290 
291 			if (strncmp(de->d_name, "ttyUSB", 6) == 0
292 			 || strncmp(de->d_name, "ttyHS", 5) == 0
293 			 || strncmp(de->d_name, "rfcomm", 6) == 0)
294 				dctype |= icomt_fastserial;
295 
296 			if (strncmp(de->d_name, "rfcomm", 6) == 0) {
297 				dctype |= icomt_fastserial;
298 				dctype |= icomt_btserial;
299 			}
300 
301 #ifndef ENABLE_SERIAL
302 			if (dctype & icomt_fastserial) {		/* Only add fast ports if !ENABLE_SERIAL */
303 #endif
304 			if (((mask & icomt_serial) && !(dctype & icomt_fastserial))
305 			 || ((mask & icomt_fastserial) && (dctype & icomt_fastserial)
306 			                               && !(dctype & icomt_btserial))
307 			 || ((mask & icomt_btserial) && (dctype & icomt_btserial))) {
308 				/* Add the port to the list */
309 				p->add_serial(p, dpath, dpath, dctype);
310 				a1logd(p->log, 8, "serial_get_paths: Added path '%s' dctype 0x%x\n",dpath, dctype);
311 			}
312 #ifndef ENABLE_SERIAL
313 			}
314 #endif
315 			free(dpath);
316 
317 			/* If fast, try and identify it */
318 			if (dctype & icomt_fastserial) {
319 				icompath *path;
320 				icoms *icom;
321 				if ((path = p->get_last_path(p)) != NULL
322 				 && (icom = new_icoms(path, p->log)) != NULL) {
323 					instType itype = fast_ser_inst_type(icom, 0, NULL, NULL);
324 					if (itype != instUnknown)
325 						icompaths_set_serial_itype(path, itype);	/* And set category */
326 					icom->del(icom);
327 				}
328 				a1logd(p->log, 8, "serial_get_paths: Identified '%s' dctype 0x%x\n",inst_sname(path->itype),path->dctype);
329 			}
330 		}
331 		closedir(dd);
332 	}
333 #endif /* ! UNIX_APPLE */
334 
335 	return ICOM_OK;
336 }
337 
338 /* -------------------------------------------------------------------- */
339 
340 /* Is the serial port actually open ? */
serial_is_open(icoms * p)341 int serial_is_open(icoms *p) {
342 	return p->fd != -1;
343 }
344 
345 /* Close the serial port */
serial_close_port(icoms * p)346 void serial_close_port(icoms *p) {
347 
348 	if (p->is_open && p->fd != -1) {
349 		close(p->fd);
350 		p->fd = -1;
351 		msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
352 	}
353 }
354 
355 /* -------------------------------------------------------------------- */
356 
357 #ifdef UNIX_APPLE
358 # ifndef IOSSIOSPEED
359 #  define IOSSIOSPEED    _IOW('T', 2, speed_t)
360 # endif
361 #endif
362 
363 #if defined(UNIX_APPLE) || defined(__OpenBSD__)
364 # ifndef B921600
365 #  define B921600 921600
366 # endif
367 #endif
368 
369 /* Set the serial port number and characteristics */
370 /* This always re-opens the port */
371 /* return icom error */
372 static int
icoms_set_ser_port_ex(icoms * p,flow_control fc,baud_rate baud,parity parity,stop_bits stop,word_length word,int delayms)373 icoms_set_ser_port_ex(
374 icoms       *p,
375 flow_control fc,
376 baud_rate	 baud,
377 parity		 parity,
378 stop_bits	 stop,
379 word_length	 word,
380 int          delayms) {		/* Delay after open in msec */
381 	int rv;
382 	struct termios tio;
383 	speed_t speed = 0;
384 
385 	a1logd(p->log, 8, "icoms_set_ser_port: About to set port characteristics:\n"
386 		              "       Port name = %s\n"
387 		              "       Flow control = %d\n"
388 		              "       Baud Rate = %s\n"
389 		              "       Parity = %d\n"
390 		              "       Stop bits = %d\n"
391 		              "       Word length = %d\n"
392 	                  "       Open delay = %d ms\n"
393 		              ,p->name ,fc ,baud_rate_to_str(baud) ,parity ,stop ,word, delayms);
394 
395 
396 	if (p->is_open) { 	/* Close it and re-open it */
397 		a1logd(p->log, 8, "icoms_set_ser_port: closing port\n");
398 		p->close_port(p);
399 	}
400 
401 	if (p->port_type(p) == icomt_serial) {
402 
403 		a1logd(p->log, 8, "icoms_set_ser_port: Make sure serial port is open\n");
404 
405 		if (fc != fc_nc)
406 			p->fc = fc;
407 		if (baud != baud_nc)
408 			p->br = baud;
409 		if (parity != parity_nc)
410 			p->py = parity;
411 		if (stop != stop_nc)
412 			p->sb = stop;
413 		if (word != length_nc)
414 			p->wl = word;
415 
416 		/* Make sure the port is open */
417 		if (!p->is_open) {
418 
419 			a1logd(p->log, 8, "icoms_set_ser_port: about to open serial port '%s'\n",p->spath);
420 
421 			if ((p->fd = open(p->spath, O_RDWR | O_NOCTTY )) < 0) {
422 				a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' r/w failed with %d (%s)\n",p->spath,p->fd,strerror(errno));
423 
424 			if (delayms < 160)		/* Seems to need at least 80 msec for many drivers */
425 				delayms = 160;
426 
427 			msec_sleep(delayms);	/* For Bluetooth */
428 
429 #ifdef NEVER	/* See what supplementary groups we are a member of */
430 		{
431 			int j, ngroups = 16;
432 			gid_t *groups = (gid_t *)malloc (ngroups * sizeof(gid_t));
433 			struct passwd *pw = getpwuid(getuid());
434 
435 			if (groups == NULL) {
436 				a1logd(p->log, 0, "icoms_set_ser_port: malloc of sgroups failed\n");
437 				goto fail;
438 			}
439 			if (pw == NULL) {
440 				a1logd(p->log, 0, "icoms_set_ser_port: getpwuid failed\n");
441 				goto fail;
442 			}
443 
444 			if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) < 0) {
445 				groups = realloc(groups, ngroups * sizeof(gid_t));
446 				if (groups == NULL) {
447 					a1logd(p->log, 0, "icoms_set_ser_port: realloc of sgroups failed\n");
448 					goto fail;
449 				}
450 				getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
451 			}
452 			a1logd(p->log, 0, "icoms_set_ser_port: ngroups = %d\n", ngroups);
453 			for (j = 0; j < ngroups; j++) {
454 				struct group *gr = getgrgid(groups[j]);
455 				if (gr != NULL)
456 					a1logd(p->log, 0, "icoms_set_ser_port: %d: gid %d\n", j,groups[j]);
457 				else
458 					a1logd(p->log, 0, "icoms_set_ser_port: %d: gid %d (%s)\n", j,groups[j],gr->gr_name);
459 			}
460 			  fail:;
461 		}
462 #endif
463 				return ICOM_SYS;
464 			}
465 
466 			/* O_NONBLOCK O_SYNC */
467 
468 			p->is_open = 1;
469 		}
470 
471 		if ((rv = tcgetattr(p->fd, &tio)) < 0) {
472 			a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: tcgetattr on '%s' failed with %d (%s)\n",p->spath,p->fd,strerror(errno));
473 			return ICOM_SYS;
474 		}
475 
476 		/* Clear everything in the tio, and just set what we want */
477 		memset(&tio, 0, sizeof(struct termios));
478 
479 		/* Turn on basic configuration: */
480 		tio.c_iflag |= (
481 					  IGNBRK		/* Ignore Break */
482 					   );
483 
484 		tio.c_oflag |= ( 0 );
485 
486 		tio.c_cflag |= (
487 					  CREAD		/* Enable the receiver */
488 					| CLOCAL	/* Ignore modem control lines */
489 						);
490 
491 		tio.c_lflag |= (
492 					     0		/* Non-canonical input mode */
493 						);
494 
495 		/* And configure: */
496 		tio.c_cc[VTIME] = 1;		/* 0.1 second timeout */
497 		tio.c_cc[VMIN] = 64;		/* Comfortably less than _PF_MAX_INPUT */
498 
499 		switch (p->fc) {
500 			case fc_nc:
501 				close(p->fd);
502 				msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
503 				a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal flow control %d\n",p->fc);
504 				return ICOM_SYS;
505 			case fc_XonXOff:
506 				/* Use Xon/Xoff bi-directional flow control */
507 				tio.c_iflag |= IXON;		/* Enable XON/XOFF flow control on output */
508 				tio.c_iflag |= IXOFF;		/* Enable XON/XOFF flow control on input */
509 				tio.c_cc[VSTART] = 0x11;	/* ^Q */
510 				tio.c_cc[VSTOP] = 0x13;		/* ^S */
511 				break;
512 			case fc_Hardware:
513 				/* Use RTS/CTS bi-directional flow control */
514 #ifdef UNIX_APPLE
515 				tio.c_cflag |= CCTS_OFLOW;
516 				tio.c_cflag |= CRTS_IFLOW;
517 #else
518 				tio.c_cflag |= CRTSCTS;
519 #endif
520 				break;
521 			case fc_HardwareDTR:
522 				/* Use DTR/DSR bi-directional flow control */
523 #ifdef UNIX_APPLE
524 				tio.c_cflag |= CDSR_OFLOW;
525 				tio.c_cflag |= CDTR_IFLOW;
526 #else
527 #ifdef CDTRDSR	/* Hmm. Not all Linux's support this... */
528 				tio.c_cflag |= CDTRDSR;
529 #endif
530 #endif
531 				break;
532 			default:
533 				break;
534 		}
535 
536 		switch (p->py) {
537 			case parity_nc:
538 				close(p->fd);
539 				msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
540 				a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal parity setting %d\n",p->py);
541 				return ICOM_SYS;
542 				break;
543 			case parity_none:
544 				tio.c_iflag &= ~INPCK;		/* Disable input parity checking */
545 				break;
546 			case parity_odd:
547 				tio.c_iflag |= INPCK;		/* Enable input parity checking */
548 				tio.c_cflag |= PARENB;		/* Enable input and output parity checking */
549 				tio.c_cflag |= PARODD;		/* Input and output parity is odd */
550 				break;
551 			case parity_even:
552 				tio.c_iflag |= INPCK;		/* Enable input parity checking */
553 				tio.c_cflag |= PARENB;		/* Enable input and output parity checking */
554 				break;
555 		}
556 
557 		switch (p->sb) {
558 			case stop_nc:
559 				close(p->fd);
560 				msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
561 				a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal stop bits %d\n",p->sb);
562 				return ICOM_SYS;
563 			case stop_1:
564 				break;		/* defaults to 1 */
565 			case stop_2:
566 				tio.c_cflag |= CSTOPB;
567 				break;
568 		}
569 
570 		switch (p->wl) {
571 			case length_nc:
572 				close(p->fd);
573 				msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
574 				a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal word length %d\n",p->wl);
575 				return ICOM_SYS;
576 			case length_5:
577 				tio.c_cflag |= CS5;
578 				break;
579 			case length_6:
580 				tio.c_cflag |= CS6;
581 				break;
582 			case length_7:
583 				tio.c_cflag |= CS7;
584 				break;
585 			case length_8:
586 				tio.c_cflag |= CS8;
587 				break;
588 		}
589 
590 		/* Set the baud rate */
591 		switch (p->br) {
592 			case baud_110:
593 				speed = B110;
594 				break;
595 			case baud_300:
596 				speed = B300;
597 				break;
598 			case baud_600:
599 				speed = B600;
600 				break;
601 			case baud_1200:
602 				speed = B1200;
603 				break;
604 			case baud_2400:
605 				speed = B2400;
606 				break;
607 			case baud_4800:
608 				speed = B4800;
609 				break;
610 			case baud_9600:
611 				speed = B9600;
612 				break;
613 			case baud_19200:
614 				speed = B19200;
615 				break;
616 			case baud_38400:
617 				speed = B38400;
618 				break;
619 			case baud_57600:
620 				speed = B57600;
621 				break;
622 			case baud_115200:
623 				speed = B115200;
624 				break;
625 			default:
626 				close(p->fd);
627 				msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
628 				a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal baud rate! (0x%x)\n",p->br);
629 				return ICOM_SYS;
630 		}
631 
632 		tcflush(p->fd, TCIOFLUSH);			/* Discard any current in/out data */
633 
634 #ifdef NEVER
635 		// for osx >= 10.4 ? or >= 10.3 ??
636 		// Doesn't actually seem to be needed... ??
637 		if (speed > B115200) {
638 			if (ioctl(p->fd, IOSSIOSPEED, &speed ) == -1)
639 				printf( "Error %d calling ioctl( ..., IOSSIOSPEED, ... )\n", errno );
640 
641 		}
642 #endif
643 		if ((rv = cfsetispeed(&tio,  speed)) < 0) {
644 			close(p->fd);
645 			msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
646 			a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: cfsetispeed failed with '%s'\n", strerror(errno));
647 			return ICOM_SYS;
648 		}
649 		if ((rv = cfsetospeed(&tio,  speed)) < 0) {
650 			close(p->fd);
651 			msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
652 			a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: cfsetospeed failed with '%s'\n", strerror(errno));
653 			return ICOM_SYS;
654 		}
655 
656 		/* Make change immediately */
657 		if ((rv = tcsetattr(p->fd, TCSANOW, &tio)) < 0) {
658 			close(p->fd);
659 			msec_sleep(100);		/* Improves reliability of USB<->Serial converters */
660 			a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: tcsetattr failed with '%s'\n", strerror(errno));
661 			return ICOM_SYS;
662 		}
663 
664 		tcflush(p->fd, TCIOFLUSH);			/* Discard any current in/out data */
665 		msec_sleep(50);		/* Improves reliability of USB<->Serial converters */
666 
667 		p->write = icoms_ser_write;
668 		p->read = icoms_ser_read;
669 
670 	}
671 	a1logd(p->log, 8, "icoms_set_ser_port: port characteristics set ok\n");
672 
673 	return ICOM_OK;
674 }
675 
676 /* Set the serial port characteristics */
677 /* This always re-opens the port */
678 /* return an icom error */
679 static int
icoms_set_ser_port(icoms * p,flow_control fc,baud_rate baud,parity parity,stop_bits stop,word_length word)680 icoms_set_ser_port(
681 icoms *p,
682 flow_control fc,
683 baud_rate	 baud,
684 parity		 parity,
685 stop_bits	 stop,
686 word_length	 word)
687 {
688 	return icoms_set_ser_port_ex(p, fc, baud, parity, stop, word, 0);
689 }
690 
691 /* ------------------------------ */
692 /* Could add read flush function, to discard all read data using
693 
694 	tcflush(fd, TCIFLUSH);
695 
696 */
697 
698 
699 /* ---------------------------------------------------------------------------------*/
700 /* Serial write/read */
701 
702 /* Write the characters in the buffer out */
703 /* Data will be written up to the terminating nul */
704 /* Return relevant error status bits */
705 /* Set the icoms lserr value */
706 int
icoms_ser_write(icoms * p,char * wbuf,int nwch,double tout)707 icoms_ser_write(
708 icoms *p,
709 char *wbuf,			/* null terminated unless nwch > 0 */
710 int nwch,			/* if > 0, number of characters to write */
711 double tout
712 ) {
713 	int len, wbytes;
714 	long ttop, top;				/* Total timeout period, timeout period */
715 	unsigned int stime, etime;	/* Start and end times of USB operation */
716 	struct pollfd pa[1];		/* Poll array to monitor serial write and stdin */
717 	int nfd = 1;				/* Number of fd's to poll */
718 	struct termios origs, news;
719 	int retrv = ICOM_OK;
720 
721 	a1logd(p->log, 8, "\nicoms_ser_write: writing '%s'\n",
722 	       nwch > 0 ? icoms_tohex((unsigned char *)wbuf, nwch) : icoms_fix(wbuf));
723 
724 	if (!p->is_open) {
725 		a1loge(p->log, ICOM_SYS, "icoms_ser_write: device not initialised\n");
726 		p->lserr = ICOM_SYS;
727 		return p->lserr;
728 	}
729 
730 	/* Setup to wait for serial output not block */
731 	pa[0].fd = p->fd;
732 	pa[0].events = POLLOUT;
733 	pa[0].revents = 0;
734 
735 	if (nwch != 0)
736 		len = nwch;
737 	else
738 		len = strlen(wbuf);
739 
740 	ttop = (int)(tout * 1000.0 + 0.5);        /* Total timeout period in msecs */
741 
742 	a1logd(p->log, 8, "\nicoms_ser_write: ep 0x%x, bytes %d, ttop %d, quant %d\n", p->rd_ep, len, ttop, p->rd_qa);
743 
744 	etime = stime = msec_time();
745 
746 	/* Until data is all written or we time out */
747 	for (top = ttop; top > 0 && len > 0;) {
748 
749 		if (poll_x(pa, nfd, top) > 0) {		/* Wait for something */
750 
751 			if (pa[0].revents != 0) {
752 				if (pa[0].revents != POLLOUT) {
753 					a1loge(p->log, ICOM_SYS, "icoms_ser_write: poll returned "
754 					                     "unexpected value 0x%x",pa[0].revents);
755 					p->lserr = ICOM_SYS;
756 					return p->lserr;
757 				}
758 
759 				/* We can write it without blocking */
760 				wbytes = write(p->fd, wbuf, len);
761 				if (wbytes < 0) {
762 					a1logd(p->log, 8, "icoms_ser_write: write failed with %d\n",wbytes);
763 					retrv |= ICOM_SERW;
764 					break;
765 
766 				} else if (wbytes > 0) {
767 					a1logd(p->log, 8, "icoms_ser_write: wrote %d bytes\n",wbytes);
768 					len -= wbytes;
769 					wbuf += wbytes;
770 				}
771 			}
772 		}
773 		etime = msec_time();
774 		top = ttop - (etime - stime);	/* Remaining time */
775 	}
776 
777 	if (top <= 0) {			/* Must have timed out */
778 		a1logd(p->log, 8, "icoms_ser_write: timeout, took %d msec out of %d\n",etime - stime,ttop);
779 		retrv |= ICOM_TO;
780 	}
781 
782 //	tcdrain(p->fd);
783 
784 	a1logd(p->log, 8, "icoms_ser_write: took %d msec, returning ICOM err 0x%x\n",etime - stime,retrv);
785 	p->lserr = retrv;
786 	return p->lserr;
787 }
788 
789 /* Read characters into the buffer */
790 /* Return string will be terminated with a nul */
791 /* return icom error */
792 int
icoms_ser_read(icoms * p,char * rbuf,int bsize,int * pbread,char * tc,int ntc,double tout)793 icoms_ser_read(
794 icoms *p,
795 char *rbuf,			/* Buffer to store characters read */
796 int bsize,			/* Buffer size */
797 int *pbread,		/* Bytes read (not including forced '\000') */
798 char *tc,			/* Terminating characers, NULL for none or char count mode */
799 int ntc,			/* Number of terminating characters or char count needed, if 0 use bsize */
800 double tout			/* Time out in seconds */
801 ) {
802 	int j, rbytes;
803 	long ttop, top;			/* Total timeout period, timeout period */
804 	unsigned int stime, etime;		/* Start and end times of USB operation */
805 	struct pollfd pa[1];		/* Poll array to monitor serial read and stdin */
806 	int nfd = 1;				/* Number of fd's to poll */
807 	struct termios origs, news;
808 	char *rrbuf = rbuf;		/* Start of return buffer */
809 	int bread = 0;
810 	int retrv = ICOM_OK;
811 	int nreads;				/* Number of reads performed */
812 
813 	if (!p->is_open) {
814 		a1loge(p->log, ICOM_SYS, "icoms_ser_read: device not initialised\n");
815 		p->lserr = ICOM_SYS;
816 		return p->lserr;
817 	}
818 
819 	if (bsize < 3) {
820 		a1loge(p->log, ICOM_SYS, "icoms_ser_read: given too small a buffer\n");
821 		p->lserr = ICOM_SYS;
822 		return p->lserr;
823 	}
824 
825 	for (j = 0; j < bsize; j++)
826 		rbuf[j] = 0;
827 
828 	ttop = (int)(tout * 1000.0 + 0.5);        /* Total timeout period in msecs */
829 
830 	a1logd(p->log, 8, "\nicoms_ser_read: bytes %d, ttop %d, ntc %d\n", bsize, ttop, ntc);
831 
832 	/* Wait for serial input to have data */
833 	pa[0].fd = p->fd;
834 	pa[0].events = POLLIN | POLLPRI;
835 	pa[0].revents = 0;
836 
837 	bsize -=1;			/* Allow space for forced null */
838 
839 	/* Until data is all read, we time out, or the user aborts */
840 	etime = stime = msec_time();
841 	j = (tc == NULL && ntc <= 0) ? -1 : 0;
842 
843 	/* Until data is all read or we time out */
844 	for (top = ttop, nreads = 0; top > 0 && bsize > 0 && j < ntc ;) {
845 
846 		if (poll_x(pa, nfd, top) > 0) {
847 			if (pa[0].revents != 0) {
848 				int btr;
849 				if (pa[0].revents != POLLIN &&  pa[0].revents != POLLPRI) {
850 					a1loge(p->log, ICOM_SYS, "icoms_ser_read: poll on serin returned "
851 					                     "unexpected value 0x%x",pa[0].revents);
852 					p->lserr = ICOM_SYS;
853 					return p->lserr;
854 				}
855 
856 				/* We have data to read from input */
857 				rbytes = read(p->fd, rbuf, bsize);
858 				if (rbytes < 0) {
859 					a1logd(p->log, 8, "icoms_ser_read: read failed with %d, rbuf = '%s'\n",rbytes,icoms_fix(rrbuf));
860 					retrv |= ICOM_SERR;
861 					break;
862 
863 				} else if (rbytes > 0) {
864 					a1logd(p->log, 8, "icoms_ser_read: read %d bytes, rbuf = '%s'\n",rbytes,icoms_fix(rrbuf));
865 
866 					bsize -= rbytes;
867 					if (tc != NULL) {
868 						while(rbytes--) {	/* Count termination characters */
869 							char ch = *rbuf++, *tcp = tc;
870 
871 							while(*tcp != '\000') {
872 								if (ch == *tcp)
873 									j++;
874 								tcp++;
875 							}
876 						}
877 						a1logd(p->log, 8, "icoms_ser_read: tc count %d\n",j);
878 					} else {
879 						if (ntc > 0)
880 							j += rbytes;
881 						rbuf += rbytes;
882 					}
883 				}
884 			}
885 		}
886 		etime = msec_time();
887 		top = ttop - (etime - stime);	/* Remaining time */
888 	}
889 
890 	*rbuf = '\000';
891 	a1logd(p->log, 8, "icoms_ser_read: read %d total bytes with %d reads\n",rbuf - rrbuf, nreads);
892 	if (pbread != NULL)
893 		*pbread = (rbuf - rrbuf);
894 
895 	/* If ran out of time and not completed */
896 	a1logd(p->log, 8, "icoms_ser_read: took %d msec\n",etime - stime);
897 	if (top <= 0 && bsize > 0 && j < ntc) {
898 		a1logd(p->log, 8, "icoms_ser_read: timeout, took %d msec out of %d\n",etime - stime,ttop);
899 		retrv |= ICOM_TO;
900 	}
901 
902 	a1logd(p->log, 8, "icoms_ser_read: took %d msec, returning '%s' ICOM err 0x%x\n",
903 	       etime - stime, tc == NULL && ntc > 0
904 			                          ? icoms_tohex((unsigned char *)rrbuf, rbuf - rrbuf)
905 	                                  : icoms_fix(rrbuf), retrv);
906 
907 	p->lserr = retrv;
908 	return p->lserr;
909 }
910 
911 #endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL*/
912 
913