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