diff options
Diffstat (limited to 'spectro/icoms_ux.c')
-rw-r--r-- | spectro/icoms_ux.c | 350 |
1 files changed, 205 insertions, 145 deletions
diff --git a/spectro/icoms_ux.c b/spectro/icoms_ux.c index 7fb7359..9e79d7f 100644 --- a/spectro/icoms_ux.c +++ b/spectro/icoms_ux.c @@ -1,5 +1,5 @@ - /* Unix icoms and serial I/O class */ +/* Unix icoms and serial I/O class */ /* * Argyll Color Correction System @@ -18,6 +18,8 @@ TTBD: */ +#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) + #include <sys/types.h> /* Include sys/select.h ? */ #include <sys/stat.h> #include <fcntl.h> @@ -27,62 +29,50 @@ /* select() defined, but not poll(), so emulate poll() */ #if defined(FD_CLR) && !defined(POLLIN) -#include "pollem.h" -#define poll_x pollem +# include "pollem.h" +# define poll_x pollem #else -#include <sys/poll.h> /* Else assume poll() is native */ -#define poll_x poll +# include <sys/poll.h> /* Else assume poll() is native */ +# define poll_x poll #endif -#ifdef __APPLE__ +#ifdef UNIX_APPLE //#include <stdbool.h> -#include <sys/sysctl.h> -#include <sys/param.h> -#include <CoreFoundation/CoreFoundation.h> -#include <IOKit/IOKitLib.h> -#include <IOKit/serial/IOSerialKeys.h> -#include <IOKit/IOBSD.h> -#include <mach/mach_init.h> -#include <mach/task_policy.h> -#endif /* __APPLE__ */ - -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) -instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *); -#endif /* ENABLE_SERIAL */ +# include <sys/sysctl.h> +# include <sys/param.h> +# include <CoreFoundation/CoreFoundation.h> +# include <IOKit/IOKitLib.h> +# include <IOKit/serial/IOSerialKeys.h> +# include <IOKit/IOBSD.h> +# include <mach/mach_init.h> +# include <mach/task_policy.h> +#endif /* UNIX_APPLE */ -/* Create and return a list of available serial ports or USB instruments for this system */ -/* return icom error */ -int icompaths_refresh_paths(icompaths *p) { - int rv, usbend = 0; - int i,j; - a1logd(p->log, 8, "icoms_get_paths: called\n"); +instType fast_ser_inst_type(icoms *p, int tryhard, void *, void *); - /* Clear any existing paths */ - p->clear(p); +/* Add paths to serial connected device. */ +/* Return an icom error */ +int serial_get_paths(icompaths *p, icom_type mask) { + int rv; -#ifdef ENABLE_USB - if ((rv = hid_get_paths(p)) != ICOM_OK) - return rv; - if ((rv = usb_get_paths(p)) != ICOM_OK) - return rv; -#endif /* ENABLE_USB */ - usbend = p->npaths; + a1logd(p->log, 7, "serial_get_paths: called with mask %d\n",mask); -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) -#ifdef __APPLE__ +#ifdef UNIX_APPLE /* Search the OSX registry for serial ports */ - { + if (mask & (icomt_serial | icomt_fastserial | icomt_btserial)) { kern_return_t kstat; mach_port_t mp; /* Master IO port */ CFMutableDictionaryRef sdict; /* Serial Port dictionary */ io_iterator_t mit; /* Matching itterator */ io_object_t ioob; /* Serial object found */ + a1logd(p->log, 6, "serial_get_paths: looking up serial ports services\n"); + /* Get dictionary of serial ports */ if ((sdict = IOServiceMatching(kIOSerialBSDServiceValue)) == NULL) { a1loge(p->log, ICOM_SYS, "IOServiceMatching returned a NULL dictionary\n"); - return ICOM_OK; + return ICOM_OK; /* Hmm. There are no serial ports ? */ } /* Set value to match to RS232 type serial */ @@ -98,7 +88,7 @@ int icompaths_refresh_paths(icompaths *p) { /* Find all the matching serial ports */ for (;;) { char pname[200]; - icom_ser_attr sattr = icom_normal; + icom_type dctype = icomt_unknown; CFTypeRef dfp; /* Device file path */ @@ -114,36 +104,50 @@ int icompaths_refresh_paths(icompaths *p) { if (!CFStringGetCString(dfp, pname, 100, kCFStringEncodingASCII)) goto continue2; - /* Ignore infra red port or Bluetooth, or any other noise */ + a1logd(p->log, 8, "serial_get_paths: checking '%s'\n",pname); + + /* Ignore infra red port or any other noise */ if (strstr(pname, "IrDA") != NULL || strstr(pname, "Dialup") != NULL - || strstr(pname, "Bluetooth") != NULL) + || strstr(pname, "PDA-Sync") != NULL) goto continue2; /* Would be nice to identify FTDI serial ports more specifically ? */ if (strstr(pname, "usbserial") != NULL) - sattr |= icom_fast; + dctype |= icomt_fastserial; + + if (strstr(pname, "Bluetooth") != NULL + || strstr(pname, "JETI") != NULL) { + dctype |= icomt_fastserial; + dctype |= icomt_btserial; + } #ifndef ENABLE_SERIAL - if (sattr & icom_fast) { /* Only add fast ports if !ENABLE_SERIAL */ + if (dctype & icomt_fastserial) { /* Only add fast ports if !ENABLE_SERIAL */ #endif - /* Add the port to the list */ - p->add_serial(p, pname, pname, sattr); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s' attr 0x%x\n",pname, sattr); + if (((mask & icomt_serial) && !(dctype & icomt_fastserial)) + || ((mask & icomt_fastserial) && (dctype & icomt_fastserial) + && !(dctype & icomt_btserial)) + || ((mask & icomt_btserial) && (dctype & icomt_btserial))) { + /* Add the port to the list */ + p->add_serial(p, pname, pname, dctype); + a1logd(p->log, 8, "serial_get_paths: Added path '%s' dctype 0x%x\n",pname, dctype); + } #ifndef ENABLE_SERIAL } #endif /* If fast, try and identify it */ - if (sattr & icom_fast) { + if (dctype & icomt_fastserial) { icompath *path; icoms *icom; if ((path = p->get_last_path(p)) != NULL && (icom = new_icoms(path, p->log)) != NULL) { instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); if (itype != instUnknown) - icompaths_set_serial_itype(path, itype); + icompaths_set_serial_itype(path, itype); /* And set category */ icom->del(icom); } + a1logd(p->log, 8, "serial_get_paths: Identified '%s' dctype 0x%x\n",inst_sname(path->itype),path->dctype); } continue2: CFRelease(dfp); @@ -152,8 +156,9 @@ int icompaths_refresh_paths(icompaths *p) { } IOObjectRelease(mit); /* Release the itterator */ } -#else - /* Other UNIX like systems */ + +#else /* Other UNIX like systems */ + /* Many are crude and list every available device name, whether */ /* it's usable or not. Do any UNIX systems have a mechanism for listing */ /* serial ports ?? */ @@ -203,12 +208,16 @@ int icompaths_refresh_paths(icompaths *p) { */ /* Search for devices that match the pattern /dev/ttyS[0-9]* and /dev/ttyUSB* */ - /* We will assume that ttyUSB* ports includes FTDI ports */ - { + /* We will assume that ttyUSB* ports includes FTDI ports. */ + /* Bluetooth ports are named ttyHS* or rfcomm* ?? */ + + if (mask & (icomt_serial | icomt_fastserial | icomt_bt)) { DIR *dd; struct dirent *de; char *dirn = "/dev/"; + a1logd(p->log, 6, "serial_get_paths: looking up serial port devices\n"); + if ((dd = opendir(dirn)) == NULL) { a1loge(p->log, ICOM_SYS, "failed to open directory \"%s\"\n",dirn); return ICOM_OK; @@ -217,10 +226,13 @@ int icompaths_refresh_paths(icompaths *p) { for (;;) { int fd; char *dpath; - icom_ser_attr sattr = icom_normal; + icom_type dctype = icomt_unknown; - if ((de = readdir(dd)) == NULL) + if ((de = readdir(dd)) == NULL) { break; + } + + a1logd(p->log, 8, "serial_get_paths: checking '%s'\n",de->d_name); if (!( #if defined(__FreeBSD__) || defined(__OpenBSD__) @@ -231,27 +243,32 @@ int icompaths_refresh_paths(icompaths *p) { /* Presumably Linux.. */ ( strncmp(de->d_name, "ttyS", 4) == 0 && de->d_name[4] >= '0' && de->d_name[4] <= '9') - || ( strncmp(de->d_name, "ttyUSB", 5) == 0) + || ( strncmp(de->d_name, "ttyUSB", 6) == 0) + || ( strncmp(de->d_name, "ttyHS", 5) == 0) + || ( strncmp(de->d_name, "rfcomm", 6) == 0) #endif )) continue; if ((dpath = (char *)malloc(strlen(dirn) + strlen(de->d_name) + 1)) == NULL) { closedir(dd); - a1loge(p->log, ICOM_SYS, "icompaths_refresh_paths() malloc failed!\n"); + a1loge(p->log, ICOM_SYS, "icompaths_refresh_paths_sel() malloc failed!\n"); return ICOM_SYS; } strcpy(dpath, dirn); strcat(dpath, de->d_name); /* See if the serial port is real */ - if (strncmp(de->d_name, "ttyUSB", 5) != 0) { + if (strncmp(de->d_name, "ttyUSB", 6) != 0 + && strncmp(de->d_name, "ttyHS", 5) != 0 + && strncmp(de->d_name, "rfcomm", 6) != 0) { /* Hmm. This is probably a bad idea - it can upset other */ /* programs that use the serial ports ? */ if ((fd = open(dpath, O_RDONLY | O_NOCTTY | O_NONBLOCK)) < 0) { - a1logd(p->log, 8, "icoms_get_paths: failed to open serial \"%s\" - not real\n",dpath); + a1logd(p->log, 8, "serial_get_paths: failed to open serial \"%s\" r/o - not real\n",dpath); free(dpath); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ continue; } /* On linux we could do a @@ -267,94 +284,83 @@ int icompaths_refresh_paths(icompaths *p) { */ close(fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ + a1logd(p->log, 8, "serial_get_paths: open'd serial \"%s\" r/o - assume real\n",dpath); } - a1logd(p->log, 8, "icoms_get_paths: open'd serial \"%s\" - assume real\n",dpath); - if (strncmp(de->d_name, "ttyUSB", 5) == 0) - sattr |= icom_fast; + if (strncmp(de->d_name, "ttyUSB", 6) == 0 + || strncmp(de->d_name, "ttyHS", 5) == 0 + || strncmp(de->d_name, "rfcomm", 6) == 0) + dctype |= icomt_fastserial; + + if (strncmp(de->d_name, "rfcomm", 6) == 0) { + dctype |= icomt_fastserial; + dctype |= icomt_btserial; + } #ifndef ENABLE_SERIAL - if (sattr & icom_fast) { /* Only add fast ports if !ENABLE_SERIAL */ + if (dctype & icomt_fastserial) { /* Only add fast ports if !ENABLE_SERIAL */ #endif - /* Add the path to the list */ - p->add_serial(p, dpath, dpath, 0); - a1logd(p->log, 8, "icoms_get_paths: Added path '%s' attr 0x%x\n",dpath,sattr); + if (((mask & icomt_serial) && !(dctype & icomt_fastserial)) + || ((mask & icomt_fastserial) && (dctype & icomt_fastserial) + && !(dctype & icomt_btserial)) + || ((mask & icomt_btserial) && (dctype & icomt_btserial))) { + /* Add the port to the list */ + p->add_serial(p, dpath, dpath, dctype); + a1logd(p->log, 8, "serial_get_paths: Added path '%s' dctype 0x%x\n",dpath, dctype); + } #ifndef ENABLE_SERIAL } #endif free(dpath); /* If fast, try and identify it */ - if (sattr & icom_fast) { + if (dctype & icomt_fastserial) { icompath *path; icoms *icom; if ((path = p->get_last_path(p)) != NULL && (icom = new_icoms(path, p->log)) != NULL) { instType itype = fast_ser_inst_type(icom, 0, NULL, NULL); if (itype != instUnknown) - icompaths_set_serial_itype(path, itype); + icompaths_set_serial_itype(path, itype); /* And set category */ icom->del(icom); } + a1logd(p->log, 8, "serial_get_paths: Identified '%s' dctype 0x%x\n",inst_sname(path->itype),path->dctype); } } closedir(dd); } -#endif /* ! __APPLE__ */ -#endif /* ENABLE_SERIAL */ - - /* Sort the serial /dev keys so people don't get confused... */ - /* Sort identified instruments ahead of unknown serial ports */ - for (i = usbend; i < (p->npaths-1); i++) { - for (j = i+1; j < p->npaths; j++) { - if ((p->paths[i]->itype == instUnknown && p->paths[j]->itype != instUnknown) - || (((p->paths[i]->itype == instUnknown && p->paths[j]->itype == instUnknown) - || (p->paths[i]->itype != instUnknown && p->paths[j]->itype != instUnknown)) - && strcmp(p->paths[i]->name, p->paths[j]->name) > 0)) { - icompath *tt = p->paths[i]; - p->paths[i] = p->paths[j]; - p->paths[j] = tt; - } - } - } +#endif /* ! UNIX_APPLE */ + return ICOM_OK; } /* -------------------------------------------------------------------- */ -/* Close the port */ -static void icoms_close_port(icoms *p) { - if (p->is_open) { -#ifdef ENABLE_USB - if (p->usbd) { - usb_close_port(p); - } else if (p->hidd) { - hid_close_port(p); - } -#endif -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - if (p->fd != -1) { - close(p->fd); - p->fd = -1; - } -#endif /* ENABLE_SERIAL */ - p->is_open = 0; - } +/* Is the serial port actually open ? */ +int serial_is_open(icoms *p) { + return p->fd != -1; } -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) +/* Close the serial port */ +void serial_close_port(icoms *p) { -static int icoms_ser_write(icoms *p, char *wbuf, int nwch, double tout); -static int icoms_ser_read(icoms *p, char *rbuf, int bsize, int *bread, - char *tc, int ntc, double tout); + if (p->is_open && p->fd != -1) { + close(p->fd); + p->fd = -1; + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ + } +} +/* -------------------------------------------------------------------- */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE # ifndef IOSSIOSPEED # define IOSSIOSPEED _IOW('T', 2, speed_t) # endif #endif -#if defined(__APPLE__) || defined(__OpenBSD__) +#if defined(UNIX_APPLE) || defined(__OpenBSD__) # ifndef B921600 # define B921600 921600 # endif @@ -364,14 +370,14 @@ static int icoms_ser_read(icoms *p, char *rbuf, int bsize, int *bread, /* This always re-opens the port */ /* return icom error */ static int -icoms_set_ser_port( +icoms_set_ser_port_ex( icoms *p, flow_control fc, baud_rate baud, parity parity, stop_bits stop, -word_length word -) { +word_length word, +int delayms) { /* Delay after open in msec */ int rv; struct termios tio; speed_t speed = 0; @@ -379,15 +385,18 @@ word_length word a1logd(p->log, 8, "icoms_set_ser_port: About to set port characteristics:\n" " Port name = %s\n" " Flow control = %d\n" - " Baud Rate = %d\n" + " Baud Rate = %s\n" " Parity = %d\n" " Stop bits = %d\n" " Word length = %d\n" - ,p->name ,fc ,baud ,parity ,stop ,word); + " Open delay = %d ms\n" + ,p->name ,fc ,baud_rate_to_str(baud) ,parity ,stop ,word, delayms); - if (p->is_open) /* Close it and re-open it */ + if (p->is_open) { /* Close it and re-open it */ + a1logd(p->log, 8, "icoms_set_ser_port: closing port\n"); p->close_port(p); + } if (p->port_type(p) == icomt_serial) { @@ -410,10 +419,52 @@ word_length word a1logd(p->log, 8, "icoms_set_ser_port: about to open serial port '%s'\n",p->spath); if ((p->fd = open(p->spath, O_RDWR | O_NOCTTY )) < 0) { - a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' failed with %d (%s)\n",p->spath,p->fd,strerror(errno)); + a1logd(p->log, 1, "icoms_set_ser_port: open port '%s' r/w failed with %d (%s)\n",p->spath,p->fd,strerror(errno)); + + if (delayms < 160) /* Seems to need at least 80 msec for many drivers */ + delayms = 160; + + msec_sleep(delayms); /* For Bluetooth */ + +#ifdef NEVER /* See what supplementary groups we are a member of */ + { + int j, ngroups = 16; + gid_t *groups = (gid_t *)malloc (ngroups * sizeof(gid_t)); + struct passwd *pw = getpwuid(getuid()); + + if (groups == NULL) { + a1logd(p->log, 0, "icoms_set_ser_port: malloc of sgroups failed\n"); + goto fail; + } + if (pw == NULL) { + a1logd(p->log, 0, "icoms_set_ser_port: getpwuid failed\n"); + goto fail; + } + + if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) < 0) { + groups = realloc(groups, ngroups * sizeof(gid_t)); + if (groups == NULL) { + a1logd(p->log, 0, "icoms_set_ser_port: realloc of sgroups failed\n"); + goto fail; + } + getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + } + a1logd(p->log, 0, "icoms_set_ser_port: ngroups = %d\n", ngroups); + for (j = 0; j < ngroups; j++) { + struct group *gr = getgrgid(groups[j]); + if (gr != NULL) + a1logd(p->log, 0, "icoms_set_ser_port: %d: gid %d\n", j,groups[j]); + else + a1logd(p->log, 0, "icoms_set_ser_port: %d: gid %d (%s)\n", j,groups[j],gr->gr_name); + } + fail:; + } +#endif return ICOM_SYS; } + /* O_NONBLOCK O_SYNC */ + p->is_open = 1; } @@ -448,6 +499,7 @@ word_length word switch (p->fc) { case fc_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal flow control %d\n",p->fc); return ICOM_SYS; case fc_XonXOff: @@ -459,7 +511,7 @@ word_length word break; case fc_Hardware: /* Use RTS/CTS bi-directional flow control */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE tio.c_cflag |= CCTS_OFLOW; tio.c_cflag |= CRTS_IFLOW; #else @@ -468,7 +520,7 @@ word_length word break; case fc_HardwareDTR: /* Use DTR/DSR bi-directional flow control */ -#ifdef __APPLE__ +#ifdef UNIX_APPLE tio.c_cflag |= CDSR_OFLOW; tio.c_cflag |= CDTR_IFLOW; #else @@ -484,6 +536,7 @@ word_length word switch (p->py) { case parity_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal parity setting %d\n",p->py); return ICOM_SYS; break; @@ -504,6 +557,7 @@ word_length word switch (p->sb) { case stop_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal stop bits %d\n",p->sb); return ICOM_SYS; case stop_1: @@ -516,6 +570,7 @@ word_length word switch (p->wl) { case length_nc: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal word length %d\n",p->wl); return ICOM_SYS; case length_5: @@ -572,6 +627,7 @@ word_length word break; default: close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal baud rate! (0x%x)\n",p->br); return ICOM_SYS; } @@ -589,11 +645,13 @@ word_length word #endif if ((rv = cfsetispeed(&tio, speed)) < 0) { close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: cfsetispeed failed with '%s'\n", strerror(errno)); return ICOM_SYS; } if ((rv = cfsetospeed(&tio, speed)) < 0) { close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: cfsetospeed failed with '%s'\n", strerror(errno)); return ICOM_SYS; } @@ -601,11 +659,13 @@ word_length word /* Make change immediately */ if ((rv = tcsetattr(p->fd, TCSANOW, &tio)) < 0) { close(p->fd); + msec_sleep(100); /* Improves reliability of USB<->Serial converters */ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: tcsetattr failed with '%s'\n", strerror(errno)); return ICOM_SYS; } tcflush(p->fd, TCIOFLUSH); /* Discard any current in/out data */ + msec_sleep(50); /* Improves reliability of USB<->Serial converters */ p->write = icoms_ser_write; p->read = icoms_ser_read; @@ -616,6 +676,29 @@ word_length word return ICOM_OK; } +/* Set the serial port characteristics */ +/* This always re-opens the port */ +/* return an icom error */ +static int +icoms_set_ser_port( +icoms *p, +flow_control fc, +baud_rate baud, +parity parity, +stop_bits stop, +word_length word) +{ + return icoms_set_ser_port_ex(p, fc, baud, parity, stop, word, 0); +} + +/* ------------------------------ */ +/* Could add read flush function, to discard all read data using + + tcflush(fd, TCIFLUSH); + +*/ + + /* ---------------------------------------------------------------------------------*/ /* Serial write/read */ @@ -623,7 +706,7 @@ word_length word /* Data will be written up to the terminating nul */ /* Return relevant error status bits */ /* Set the icoms lserr value */ -static int +int icoms_ser_write( icoms *p, char *wbuf, /* null terminated unless nwch > 0 */ @@ -699,6 +782,8 @@ double tout retrv |= ICOM_TO; } +// tcdrain(p->fd); + a1logd(p->log, 8, "icoms_ser_write: took %d msec, returning ICOM err 0x%x\n",etime - stime,retrv); p->lserr = retrv; return p->lserr; @@ -707,7 +792,7 @@ double tout /* Read characters into the buffer */ /* Return string will be terminated with a nul */ /* return icom error */ -static int +int icoms_ser_read( icoms *p, char *rbuf, /* Buffer to store characters read */ @@ -826,30 +911,5 @@ double tout /* Time out in seconds */ return p->lserr; } -#endif /* ENABLE_SERIAL */ - -/* ---------------------------------------------------------------------------------*/ - -/* Destroy ourselves */ -static void -icoms_del(icoms *p) { - a1logd(p->log, 8, "icoms_del: called\n"); - if (p->is_open) { - a1logd(p->log, 8, "icoms_del: closing port\n"); - p->close_port(p); - } -#ifdef ENABLE_USB - usb_del_usb(p); - hid_del_hid(p); -#endif -#if defined(ENABLE_SERIAL) || defined(ENABLE_FAST_SERIAL) - if (p->spath != NULL) - free(p->spath); -#endif - p->log = del_a1log(p->log); - if (p->name != NULL) - free(p->name); - p->log = del_a1log(p->log); - free (p); -} +#endif /* ENABLE_SERIAL || ENABLE_FAST_SERIAL*/ |