2
/* Unix serial I/O class */
5
* Argyll Color Correction System
7
* Author: Graeme W. Gill
10
* Copyright 1997 - 2010 Graeme W. Gill
11
* All rights reserved.
13
* This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
14
* see the License2.txt file for licencing details.
23
#include <sys/types.h> /* Include sys/select.h ? */
36
#include "copyright.h"
41
#include "insttypes.h"
50
# define errout stderr
51
# define DBG(xx) fprintf(errout, xx )
52
# define DBGF(xx) fprintf xx
54
# define errout stderr
59
/* select() defined, but not poll(), so emulate poll() */
60
#if defined(FD_CLR) && !defined(POLLIN)
64
#include <sys/poll.h> /* Else assume poll() is native */
69
//#include <stdbool.h>
70
#include <sys/sysctl.h>
71
#include <sys/param.h>
72
#include <CoreFoundation/CoreFoundation.h>
73
#include <IOKit/IOKitLib.h>
74
#include <IOKit/serial/IOSerialKeys.h>
75
#include <IOKit/IOBSD.h>
76
#include <mach/mach_init.h>
77
#include <mach/task_policy.h>
78
#endif /* __APPLE__ */
80
/* Return the port type */
81
static icom_type icoms_port_type(
91
/* Create and return a list of available serial ports or USB instruments for this system */
99
/* Free any old list */
100
if (p->paths != NULL) {
101
for (i = 0; i < p->npaths; i++) {
102
if (p->paths[i]->path != NULL)
103
free(p->paths[i]->path);
105
if (p->paths[i]->dev != NULL)
106
usb_del_usb_device(p->paths[i]->dev);
107
if (p->paths[i]->hev != NULL)
108
hid_del_hid_device(p->paths[i]->hev);
109
#endif /* ENABLE_USB */
123
/* Search the OSX registry for serial ports */
126
mach_port_t mp; /* Master IO port */
127
CFMutableDictionaryRef sdict; /* Serial Port dictionary */
128
io_iterator_t mit; /* Matching itterator */
129
io_object_t ioob; /* Serial object found */
131
/* Get dictionary of serial ports */
132
if ((sdict = IOServiceMatching(kIOSerialBSDServiceValue)) == NULL) {
133
warning("IOServiceMatching returned a NULL dictionary");
136
/* Set value to match to RS232 type serial */
137
CFDictionarySetValue(sdict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type));
139
/* Init itterator to find matching types. Consumes sdict reference */
140
if ((kstat = IOServiceGetMatchingServices(kIOMasterPortDefault, sdict, &mit))
142
error("IOServiceGetMatchingServices returned %d\n", kstat);
144
/* Find all the matching serial ports */
148
CFTypeRef dfp; /* Device file path */
150
if ((ioob = IOIteratorNext(mit)) == 0)
153
/* Get the callout device's path (/dev/cu.xxxxx). */
154
if ((dfp = IORegistryEntryCreateCFProperty(ioob, CFSTR(kIOCalloutDeviceKey),
155
kCFAllocatorDefault, 0)) == NULL)
158
/* Convert from CF string to C string */
159
if (!CFStringGetCString(dfp, pname, 100, kCFStringEncodingASCII))
162
/* Ignore infra red port or Bluetooth, or any other noise */
163
if (strstr(pname, "IrDA") != NULL
164
|| strstr(pname, "Dialup") != NULL
165
|| strstr(pname, "Bluetooth") != NULL)
168
/* Add the port to the list */
169
if (p->paths == NULL) {
170
if ((p->paths = (icompath **)calloc(sizeof(icompath *), 1 + 1)) == NULL)
171
error("icoms: calloc failed!");
173
if ((p->paths = (icompath **)realloc(p->paths,
174
sizeof(icompath *) * (p->npaths + 2))) == NULL)
175
error("icoms: realloc failed!");
176
p->paths[p->npaths+1] = NULL;
178
if ((p->paths[p->npaths] = malloc(sizeof(icompath))) == NULL)
179
error("icoms: malloc failed!");
180
if ((p->paths[p->npaths]->path = strdup(pname)) == NULL)
181
error("icoms: strdup failed!");
183
p->paths[p->npaths]->dev = NULL;
184
p->paths[p->npaths]->hev = NULL;
185
#endif /* ENABLE_USB */
187
p->paths[p->npaths] = NULL;
192
IOObjectRelease(ioob); /* Release found object */
194
IOObjectRelease(mit); /* Release the itterator */
197
/* Other UNIX like systems */
198
/* Many are crude and list every available device name, whether */
199
/* it's usable or not. Do any UNIX systems have a mechanism for listing */
200
/* serial ports ?? */
202
/* On Linux, the list in /proc/tty/driver/serial may indicate */
203
/* which are real or not (if "uart:unknown" then not real) */
206
0: uart:16550A port:000003F8 irq:4 tx:3 rx:1755 brk:1 RTS|DTR
207
1: uart:16550A port:000002F8 irq:3 tx:11 rx:3 brk:3
208
2: uart:unknown port:000003E8 irq:4
209
3: uart:unknown port:000002E8 irq:3
210
4: uart:unknown port:00000000 irq:0
211
5: uart:unknown port:00000000 irq:0
212
6: uart:unknown port:00000000 irq:0
213
7: uart:unknown port:00000000 irq:0
215
but the permissions don't allow looking at this.
217
/* (This info is similar to what is returned by "setserial -g /dev/ttyS*", */
218
/* and "setserial -gb /dev/ttyS*" returns just the real ports.) */
219
/* None of this can distinguish if one is the mouse. */
221
/* From "QTSerialPort": */
223
Constant Used By Naming Convention
224
---------- ------------- ------------------------
225
_TTY_WIN_ Windows COM1, COM2
226
_TTY_IRIX_ SGI/IRIX /dev/ttyf1, /dev/ttyf2
227
_TTY_HPUX_ HP-UX /dev/tty1p0, /dev/tty2p0
228
_TTY_SUN_ SunOS/Solaris /dev/ttya, /dev/ttyb
229
_TTY_DIGITAL_ Digital UNIX /dev/tty01, /dev/tty02
230
_TTY_FREEBSD_ FreeBSD /dev/ttyd0, /dev/ttyd1
231
_TTY_LINUX_ Linux /dev/ttyS0, /dev/ttyS1
232
<none> Linux /dev/ttyS0, /dev/ttyS1
233
Linux /dev/ttyUSB0, /dev/ttyUSB1
237
"Most program set a lock in /var/lock/LCK..tty<XX> on Linux ?
238
<http://sunsite.ualberta.ca/LDP/LDP/nag2/x-087-2-serial.devices.html>
239
<http://docs.freebsd.org/info/uucp/uucp.info.UUCP_Lock_Files.html>
241
We should really use the lock files to avoid treading on
242
other programs toes. We assume at the moment that the user
243
only picks a serial port with an instrument on it.
246
/* Search for devices that match the pattern /dev/ttyS[0-9]* and /dev/ttyUSB* */
250
char *dirn = "/dev/";
252
if ((dd = opendir(dirn)) == NULL) {
253
// DBGF((errout,"failed to open directory \"%s\"\n",dirn));
254
if (p->debug) fprintf(errout,"failed to open directory \"%s\"\n",dirn);
262
if ((de = readdir(dd)) == NULL)
267
/* This should match uart & USB devs. */
268
( strncmp (de->d_name, "cua", 3) == 0
269
&& strlen (de->d_name) < 7)
271
/* Presumably Linux.. */
272
( strncmp(de->d_name, "ttyS", 4) == 0
273
&& de->d_name[4] >= '0' && de->d_name[4] <= '9')
274
|| ( strncmp(de->d_name, "ttyUSB", 5) == 0)
279
if ((dpath = (char *)malloc(strlen(dirn) + strlen(de->d_name) + 1)) == NULL) {
281
error("icoms: malloc failed!");
284
strcat(dpath, de->d_name);
286
/* See if the serial port is real */
287
if (strncmp(de->d_name, "ttyUSB", 5) != 0) {
289
/* Hmm. This is probably a bad idea - it can upset other */
290
/* programs that use the serial ports ? */
291
if ((fd = open(dpath, O_RDONLY | O_NOCTTY | O_NONBLOCK)) < 0) {
292
if (p->debug) fprintf(errout,"failed to open serial \"%s\"\n",dpath);
296
/* On linux we could do a
297
struct serial_struct serinfo;
299
serinfo.reserved_char[0] = 0;
301
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0
302
|| serinfo.type == PORT_UNKNOWN) {
310
if (p->debug) fprintf(errout,"managed to open serial \"%s\"\n",dpath);
312
/* Add the path to the list */
313
if (p->paths == NULL) {
314
if ((p->paths = (icompath **)calloc(sizeof(icompath *), 1 + 1)) == NULL) {
317
error("icoms: calloc failed!");
320
if ((p->paths = (icompath **)realloc(p->paths,
321
sizeof(icompath *) * (p->npaths + 2))) == NULL) {
324
error("icoms: realloc failed!");
326
p->paths[p->npaths+1] = NULL;
328
if ((p->paths[p->npaths] = malloc(sizeof(icompath))) == NULL) {
331
error("icoms: malloc failed!");
333
p->paths[p->npaths]->path = dpath;
335
p->paths[p->npaths]->dev = NULL;
336
p->paths[p->npaths]->hev = NULL;
337
#endif /* ENABLE_USB */
339
p->paths[p->npaths] = NULL;
343
#endif /* ! __APPLE__ */
344
#endif /* ENABLE_SERIAL */
346
/* Sort the /dev keys so people don't get confused... */
347
for (i = usbend; i < (p->npaths-1); i++) {
348
for (j = i+1; j < p->npaths; j++) {
349
if (strcmp(p->paths[i]->path, p->paths[j]->path) > 0) {
350
icompath *tt = p->paths[i];
351
p->paths[i] = p->paths[j];
362
static void icoms_close_port(icoms *p) {
366
} else if (p->is_hid) {
377
static int icoms_ser_write(icoms *p, char *wbuf, double tout);
378
static int icoms_ser_read(icoms *p, char *rbuf, int bsize, char tc, int ntc, double tout);
380
/* Set the serial port number and characteristics */
384
int port, /* serial com port, 1 - N. 0 for no change */
395
fprintf(stderr,"icoms: About to set port characteristics:\n");
396
fprintf(stderr," Port = %d\n",port);
397
fprintf(stderr," Flow control = %d\n",fc);
398
fprintf(stderr," Baud Rate = %d\n",baud);
399
fprintf(stderr," Parity = %d\n",parity);
400
fprintf(stderr," Stop bits = %d\n",stop);
401
fprintf(stderr," Word length = %d\n",word);
405
if (p->is_open && port != p->port) { /* If port number changes */
410
if (p->is_usb_portno(p, port) == instUnknown) {
416
if (parity != parity_nc)
420
if (word != length_nc)
423
/* Make sure the port is open */
426
if (p->ppath != NULL) {
427
if (p->ppath->path != NULL)
428
free(p->ppath->path);
433
if (p->paths == NULL)
436
if (port <= 0 || port > p->npaths)
437
error("icoms - set_ser_port: port number out of range!");
439
if ((p->ppath = malloc(sizeof(icompath))) == NULL)
440
error("malloc() failed on com port path");
441
*p->ppath = *p->paths[port-1]; /* Structure copy */
442
if ((p->ppath->path = strdup(p->paths[port-1]->path)) == NULL)
443
error("strdup() failed on com port path");
446
if (p->debug) fprintf(stderr,"icoms: About to open port '%s'\n",p->ppath->path);
448
if ((p->fd = open(p->ppath->path, O_RDWR | O_NOCTTY )) < 0)
449
error("Opening COM port '%s' failed with '%s'",p->ppath->path, strerror(errno));
450
/* O_NONBLOCK O_SYNC */
451
if (p->debug) fprintf(stderr,"icoms: Opened port OK, fd = %d\n",p->fd);
455
if (tcgetattr(p->fd, &tio) < 0) {
456
error("tcgetattr failed with '%s' on serial port '%s'", strerror(errno),p->ppath->path);
459
/* Clear everything in the tio, and just set what we want */
460
memset(&tio, 0, sizeof(struct termios));
462
/* Turn on basic configuration: */
464
IGNBRK /* Ignore Break */
467
tio.c_oflag |= ( 0 );
470
CREAD /* Enable the receiver */
471
| CLOCAL /* Ignore modem control lines */
475
0 /* Non-canonical input mode */
479
tio.c_cc[VTIME] = 1; /* 0.1 second timeout */
480
tio.c_cc[VMIN] = 64; /* Comfortably less than _PF_MAX_INPUT */
484
error("icoms - set_ser_port: illegal flow control!");
487
/* Use Xon/Xoff bi-directional flow control */
488
tio.c_iflag |= IXON; /* Enable XON/XOFF flow control on output */
489
tio.c_iflag |= IXOFF; /* Enable XON/XOFF flow control on input */
490
tio.c_cc[VSTART] = 0x11; /* ^Q */
491
tio.c_cc[VSTOP] = 0x13; /* ^S */
494
/* Use RTS/CTS bi-directional flow control */
496
tio.c_cflag |= CCTS_OFLOW;
497
tio.c_cflag |= CRTS_IFLOW;
499
tio.c_cflag |= CRTSCTS;
508
error("icoms - set_ser_port: illegal parity setting!");
511
tio.c_iflag &= ~INPCK; /* Disable input parity checking */
514
tio.c_iflag |= INPCK; /* Enable input parity checking */
515
tio.c_cflag |= PARENB; /* Enable input and output parity checking */
516
tio.c_cflag |= PARODD; /* Input and output parity is odd */
519
tio.c_iflag |= INPCK; /* Enable input parity checking */
520
tio.c_cflag |= PARENB; /* Enable input and output parity checking */
526
error("icoms - set_ser_port: illegal stop bits!");
529
break; /* defaults to 1 */
531
tio.c_cflag |= CSTOPB;
537
error("icoms - set_ser_port: illegal word length!");
552
/* Set the baud rate */
588
error("icoms - set_ser_port: illegal baud rate!");
592
tcflush(p->fd, TCIOFLUSH); /* Discard any current in/out data */
594
if (cfsetispeed(&tio, speed) < 0)
595
error("cfsetispeed failed with '%s'", strerror(errno));
596
if (cfsetospeed(&tio, speed) < 0)
597
error("cfsetospeed failed with '%s'", strerror(errno));
599
/* Make change immediately */
600
if (tcsetattr(p->fd, TCSANOW, &tio) < 0)
601
error("tcsetattr failed with '%s' on '%s'", strerror(errno), p->ppath->path);
603
tcflush(p->fd, TCIOFLUSH); /* Discard any current in/out data */
605
p->write = icoms_ser_write;
606
p->read = icoms_ser_read;
609
if (p->debug) fprintf(stderr,"icoms: port characteristics set ok\n");
612
/* ---------------------------------------------------------------------------------*/
613
/* Serial write/read */
615
/* Write the characters in the buffer out */
616
/* Data will be written up to the terminating nul */
617
/* Return relevant error status bits */
625
long toc, i, top; /* Timout count, counter, timeout period */
626
struct pollfd pa[2]; /* Poll array to monitor serial write and stdin */
627
struct termios origs, news;
629
if (p->debug) fprintf(stderr,"About to write '%s' ",icoms_fix(wbuf));
631
error("icoms_write: not initialised");
633
/* Configure stdin to be ready with just one character */
634
if (tcgetattr(STDIN_FILENO, &origs) < 0)
635
error("tcgetattr failed with '%s' on stdin", strerror(errno));
637
news.c_lflag &= ~(ICANON | ECHO);
638
news.c_cc[VTIME] = 0;
640
if (tcsetattr(STDIN_FILENO,TCSANOW, &news) < 0)
641
error("tcsetattr failed with '%s' on stdin", strerror(errno));
643
/* Wait for serial output not block */
645
pa[0].events = POLLOUT;
648
/* Wait for stdin to have a character */
649
pa[1].fd = STDIN_FILENO;
650
pa[1].events = POLLIN | POLLPRI;
653
/* Until timed out, aborted, or transmitted */
655
tout *= 1000.0; /* Timout in msec */
658
top = 100; /* Timeout period in msecs */
659
toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */
663
/* Until data is all written, we time out, or the user aborts */
664
for(i = toc; i > 0 && len > 0;) {
665
if (poll_x(pa, 2, top) > 0) {
666
if (pa[0].revents != 0) {
667
if (pa[0].revents != POLLOUT)
668
error("poll on serial out returned unexpected value 0x%x",pa[0].revents);
670
/* We can write it without blocking */
671
if ((wbytes = write(p->fd, wbuf, len)) < 0) {
672
p->lerr |= ICOM_SERW;
674
} else if (wbytes > 0) {
680
if (pa[1].revents != 0) {
682
if (pa[1].revents != POLLIN && pa[1].revents != POLLPRI)
683
error("poll on stdin returned unexpected value 0x%x",pa[1].revents);
684
/* Check for user abort */
685
if (read(STDIN_FILENO, tb, 10) > 0 && p->uih[tb[0]] != ICOM_OK) {
687
p->lerr = p->uih[tb[0]];
688
if (p->uih[tb[0]] == ICOM_USER
689
|| p->uih[tb[0]] == ICOM_TERM
690
|| p->uih[tb[0]] == ICOM_TRIG
691
|| p->uih[tb[0]] == ICOM_CMND)
696
i--; /* timeout (or error!) */
699
if (i <= 0) { /* Timed out */
704
if (tcsetattr(STDIN_FILENO, TCSANOW, &origs) < 0)
705
error("tcsetattr failed with '%s' on stdin", strerror(errno));
707
if (p->debug) fprintf(stderr,"ICOM err 0x%x\n",p->lerr);
711
/* Read characters into the buffer */
712
/* Return string will be terminated with a nul */
716
char *rbuf, /* Buffer to store characters read */
717
int bsize, /* Buffer size */
718
char tc, /* Terminating characer */
719
int ntc, /* Number of terminating characters */
720
double tout /* Time out in seconds */
723
long j, toc, i, top; /* Timout count, counter, timeout period */
724
struct pollfd pa[2]; /* Poll array to monitor serial read and stdin */
725
struct termios origs, news;
726
char *rrbuf = rbuf; /* Start of return buffer */
728
if (p->debug) fprintf(stderr,"icoms: Read called\n");
730
error("icoms_read: not initialised");
733
error("icoms_read given too small a buffer");
736
/* The Prolific 2303 USB<->serial seems to choke on this, */
737
/* so we just put up with the 100msec delay at the end of each reply. */
738
if (tc != p->tc) { /* Set the termination char */
741
if (tcgetattr(p->fd, &tio) < 0)
742
error("tcgetattr failed with '%s' on '%s'", strerror(errno),p->ppath->path);
746
/* Make change immediately */
747
tcflush(p->fd, TCIFLUSH);
748
if (tcsetattr(p->fd, TCSANOW, &tio) < 0)
749
error("tcsetattr failed with '%s' on '%s'", strerror(errno),p->ppath->path);
755
/* Configure stdin to be ready with just one character */
756
if (tcgetattr(STDIN_FILENO, &origs) < 0)
757
error("ycgetattr failed with '%s' on stdin", strerror(errno));
759
news.c_lflag &= ~(ICANON | ECHO);
760
news.c_cc[VTIME] = 0;
762
if (tcsetattr(STDIN_FILENO,TCSANOW, &news) < 0)
763
error("tcsetattr failed with '%s' on stdin", strerror(errno));
765
/* Wait for serial input to have data */
767
pa[0].events = POLLIN | POLLPRI;
770
/* Wait for stdin to have a character */
771
pa[1].fd = STDIN_FILENO;
772
pa[1].events = POLLIN | POLLPRI;
775
bsize--; /* Allow space for null */
776
tout *= 1000.0; /* Timout in msec */
779
top = 100; /* Timeout period in msecs */
780
toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */
784
/* Until data is all read, we time out, or the user aborts */
785
for(i = toc, j = 0; i > 0 && bsize > 1 && j < ntc ;) {
787
if (poll_x(pa, 2, top) > 0) {
788
if (pa[0].revents != 0) {
789
if (pa[0].revents != POLLIN && pa[0].revents != POLLPRI)
790
error("poll on serial in returned unexpected value 0x%x",pa[0].revents);
792
/* We have data to read from input */
793
if ((rbytes = read(p->fd, rbuf, bsize)) < 0) {
794
p->lerr |= ICOM_SERR;
796
} else if (rbytes > 0) {
797
i = toc; /* Reset time */
799
while(rbytes--) { /* Count termination characters */
805
if (pa[1].revents != 0) {
807
if (pa[1].revents != POLLIN && pa[1].revents != POLLPRI)
808
error("poll on stdin returned unexpected value 0x%x",pa[1].revents);
809
/* Check for user abort */
810
if (read(STDIN_FILENO, tb, 10) > 0 && p->uih[tb[0]] != ICOM_OK) {
812
p->lerr = p->uih[tb[0]];
813
if (p->uih[tb[0]] == ICOM_USER
814
|| p->uih[tb[0]] == ICOM_TERM
815
|| p->uih[tb[0]] == ICOM_TRIG
816
|| p->uih[tb[0]] == ICOM_CMND)
821
i--; /* We timed out (or error!) */
826
if (i <= 0) { /* timed out */
829
if (p->debug) fprintf(stderr,"icoms: About to return read '%s' ICOM err 0x%x\n",icoms_fix(rrbuf),p->lerr);
832
if (tcsetattr(STDIN_FILENO, TCSANOW, &origs) < 0)
833
error("tcsetattr failed with '%s' on stdin", strerror(errno));
835
if (p->debug) fprintf(stderr,"icoms: Read returning with 0x%x\n",p->lerr);
840
/* ---------------------------------------------------------------------------------*/
842
/* Destroy ourselves */
844
icoms_del(icoms *p) {
845
if (p->debug) fprintf(stderr,"icoms: delete called\n");
847
if (p->debug) fprintf(stderr,"icoms: closing port\n");
850
if (p->paths != NULL) {
852
for (i = 0; i < p->npaths; i++) {
853
if (p->paths[i]->path != NULL)
854
free(p->paths[i]->path);
856
if (p->paths[i]->dev != NULL)
857
usb_del_usb_device(p->paths[i]->dev);
858
if (p->paths[i]->hev != NULL)
859
hid_del_hid_device(p->paths[i]->hev);
860
#endif /* ENABLE_USB */
865
if (p->ppath != NULL) {
866
if (p->ppath->path != NULL)
867
free(p->ppath->path);
876
if ((p = (icoms *)calloc(sizeof(icoms), 1)) == NULL)
877
error("icoms: malloc failed!");
879
/* Init things to null values */
890
p->close_port = icoms_close_port;
892
p->port_type = icoms_port_type;
893
p->get_paths = icoms_get_paths;
894
p->set_ser_port = icoms_set_ser_port;
900
usb_set_usb_methods(p);
901
hid_set_hid_methods(p);