summaryrefslogtreecommitdiff
path: root/spectro/icoms_nt.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/icoms_nt.c')
-rw-r--r--spectro/icoms_nt.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/spectro/icoms_nt.c b/spectro/icoms_nt.c
new file mode 100644
index 0000000..ef364c4
--- /dev/null
+++ b/spectro/icoms_nt.c
@@ -0,0 +1,563 @@
+
+ /* Windows NT serial I/O class */
+
+/*
+ * Argyll Color Correction System
+ *
+ * Author: Graeme W. Gill
+ * Date: 28/9/97
+ *
+ * Copyright 1997 - 2013 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
+ * see the License2.txt file for licencing details.
+ */
+
+#include <conio.h>
+
+/* Create and return a list of available serial ports or USB instruments for this system. */
+/* We look at the registry key "HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM" */
+/* to determine serial ports, and use libusb to discover USB instruments. */
+/* 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;
+ LONG stat;
+ HKEY sch; /* Serial coms handle */
+
+ a1logd(p->log, 8, "icoms_get_paths: called\n");
+
+ /* Clear any existing paths */
+ p->clear(p);
+
+#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, 6, "icoms_get_paths: got %d paths, looking up the registry for serial ports\n",p->npaths);
+
+#ifdef ENABLE_SERIAL
+ /* Look in the registry for serial ports */
+ if ((stat = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM",
+ 0, KEY_READ, &sch)) != ERROR_SUCCESS) {
+ a1logd(p->log, 1, "icoms_get_paths: There don't appear to be any serial ports\n");
+ return ICOM_OK; /* Maybe they have USB ports */
+ }
+
+ /* Look at all the values in this key */
+ a1logd(p->log, 8, "icoms_get_paths: looking through all the values in the SERIALCOMM key\n");
+
+ for (i = 0; ; i++) {
+ char valname[500];
+ DWORD vnsize = 500;
+ DWORD vtype;
+ char value[500];
+ DWORD vsize = 500;
+
+ stat = RegEnumValue(
+ sch, /* handle to key to enumerate */
+ i, /* index of subkey to enumerate */
+ valname, /* address of buffer for value name */
+ &vnsize, /* address for size of value name buffer */
+ NULL, /* reserved */
+ &vtype, /* Address of value type */
+ value, /* Address of value buffer */
+ &vsize /* Address of value buffer size */
+ );
+ if (stat == ERROR_NO_MORE_ITEMS) {
+ a1logd(p->log, 8, "icoms_get_paths: got ERROR_NO_MORE_ITEMS\n");
+ break;
+ }
+ if (stat == ERROR_MORE_DATA /* Hmm. Should expand buffer size */
+ || stat != ERROR_SUCCESS) {
+ a1logw(p->log, "icoms_get_paths: RegEnumValue failed with %d\n",stat);
+ break;
+ }
+ valname[500-1] = '\000';
+ value[500-1] = '\000';
+
+ if (vtype != REG_SZ) {
+ a1logw(p->log, "icoms_get_paths: RegEnumValue didn't return stringz type\n");
+ continue;
+ }
+
+ /* Add the port to the list */
+ p->add_serial(p, value, value);
+ a1logd(p->log, 8, "icoms_get_paths: Added path '%s'\n",value);
+ }
+ if ((stat = RegCloseKey(sch)) != ERROR_SUCCESS) {
+ a1logw(p->log, "icoms_get_paths: RegCloseKey failed with %d\n",stat);
+ }
+#endif /* ENABLE_SERIAL */
+
+ /* Sort the COM keys so people don't get confused... */
+ a1logd(p->log, 6, "icoms_get_paths: we now have %d entries and are about to sort them\n",p->npaths);
+ for (i = usbend; i < (p->npaths-1); i++) {
+ for (j = i+1; j < p->npaths; j++) {
+ if (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;
+ }
+ }
+ }
+
+ a1logd(p->log, 8, "icoms_get_paths: returning %d paths and ICOM_OK\n",p->npaths);
+
+ 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
+#ifdef ENABLE_SERIAL
+ if (p->phandle != NULL) {
+ CloseHandle(p->phandle);
+ }
+#endif /* ENABLE_SERIAL */
+ p->is_open = 0;
+ }
+}
+
+#ifdef ENABLE_SERIAL
+
+static int icoms_ser_write(icoms *p, char *wbuf, double tout);
+static int icoms_ser_read(icoms *p, char *rbuf, int bsize, char tc, int ntc, double tout);
+
+/* 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)
+{
+ 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"
+ " Parity = %d\n"
+ " Stop bits = %d\n"
+ " Word length = %d\n"
+ ,p->name ,fc ,baud ,parity ,stop ,word);
+
+ if (p->is_open)
+ p->close_port(p);
+
+ if (p->port_type(p) == icomt_serial) {
+ DCB dcb;
+
+ a1logd(p->log, 8, "icoms_set_ser_port: Make sure serial port is open\n");
+
+ if (fc != fc_nc)
+ p->fc = fc;
+ if (baud != baud_nc)
+ p->br = baud;
+ if (parity != parity_nc)
+ p->py = parity;
+ if (stop != stop_nc)
+ p->sb = stop;
+ if (word != length_nc)
+ p->wl = word;
+
+ /* Make sure the port is open */
+ if (!p->is_open) {
+ a1logd(p->log, 8, "icoms_set_ser_port: about to open serial port '%s'\n",p->spath);
+
+ if ((p->phandle = CreateFile(
+ p->spath,
+ GENERIC_READ|GENERIC_WRITE,
+ 0, /* Exclusive access */
+ NULL, /* No security */
+ OPEN_EXISTING, /* Does not make sense to create */
+ 0, /* No overlapped I/O */
+ NULL) /* NULL template */
+ ) == INVALID_HANDLE_VALUE) {
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: open port '%s' failed with LastError %d\n",p->spath,GetLastError());
+ return ICOM_SYS;
+ }
+ p->is_open = 1;
+ }
+
+ if (GetCommState(p->phandle, &dcb) == FALSE) {
+ CloseHandle(p->phandle);
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: reading state '%s' failed with LastError %d\n",p->spath,GetLastError());
+ return ICOM_SYS;
+ }
+
+ /* Set misc stuff to default */
+ dcb.fBinary = TRUE; /* Binary mode is only one that works */
+ dcb.fOutxCtsFlow = FALSE; /* Not using Cts flow control */
+ dcb.fOutxDsrFlow = FALSE; /* Not using Dsr Flow control */
+ dcb.fDtrControl = DTR_CONTROL_ENABLE; /* Enable DTR during connection */
+ dcb.fDsrSensitivity = FALSE; /* Not using Dsr Flow control */
+ dcb.fTXContinueOnXoff = TRUE; /* */
+ dcb.fOutX = FALSE; /* No default Xon/Xoff flow control */
+ dcb.fInX = FALSE; /* No default Xon/Xoff flow control */
+ dcb.fErrorChar = FALSE;
+ dcb.fNull = FALSE;
+ dcb.fRtsControl = RTS_CONTROL_ENABLE; /* Turn RTS on during connection */
+ dcb.fAbortOnError = TRUE;
+
+ switch (p->fc) {
+ case fc_nc:
+ CloseHandle(p->phandle);
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal flow control %d\n",p->fc);
+ return ICOM_SYS;
+ case fc_XonXOff:
+ /* Use Xon/Xoff bi-directional flow control */
+ dcb.fOutX = TRUE;
+ dcb.fInX = TRUE;
+ dcb.XonChar = 0x11; /* ^Q */
+ dcb.XoffChar = 0x13; /* ^S */
+ // dcb.fTXContinueOnXoff = FALSE; /* Stop transmitting if input is full */
+ break;
+ case fc_Hardware:
+ /* Use RTS/CTS bi-directional flow control */
+ dcb.fOutxCtsFlow = TRUE;
+ dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
+ break;
+ default:
+ break;
+ }
+
+ switch (p->py) {
+ case parity_nc:
+ CloseHandle(p->phandle);
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal parity setting %d\n",p->py);
+ return ICOM_SYS;
+ case parity_none:
+ dcb.fParity = FALSE;
+ dcb.Parity = NOPARITY;
+ break;
+ case parity_odd:
+ dcb.fParity = TRUE;
+ dcb.Parity = ODDPARITY;
+ break;
+ case parity_even:
+ dcb.fParity = TRUE;
+ dcb.Parity = EVENPARITY;
+ break;
+ default:
+ break;
+ }
+
+ switch (p->sb) {
+ case stop_nc:
+ CloseHandle(p->phandle);
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal stop bits %d\n",p->sb);
+ return ICOM_SYS;
+ case stop_1:
+ dcb.StopBits = ONESTOPBIT;
+ break;
+ case stop_2:
+ dcb.StopBits = TWOSTOPBITS;
+ break;
+ }
+
+ switch (p->wl) {
+ case length_nc:
+ CloseHandle(p->phandle);
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal word length %d\n",p->wl);
+ return ICOM_SYS;
+ case length_5:
+ dcb.ByteSize = 5;
+ break;
+ case length_6:
+ dcb.ByteSize = 6;
+ break;
+ case length_7:
+ dcb.ByteSize = 7;
+ break;
+ case length_8:
+ dcb.ByteSize = 8;
+ break;
+ }
+
+ switch (p->br) {
+ case baud_110:
+ dcb.BaudRate = CBR_110;
+ break;
+ case baud_300:
+ dcb.BaudRate = CBR_300;
+ break;
+ case baud_600:
+ dcb.BaudRate = CBR_600;
+ break;
+ case baud_1200:
+ dcb.BaudRate = CBR_1200;
+ break;
+ case baud_2400:
+ dcb.BaudRate = CBR_2400;
+ break;
+ case baud_4800:
+ dcb.BaudRate = CBR_4800;
+ break;
+ case baud_9600:
+ dcb.BaudRate = CBR_9600;
+ break;
+ case baud_14400:
+ dcb.BaudRate = CBR_14400;
+ break;
+ case baud_19200:
+ dcb.BaudRate = CBR_19200;
+ break;
+ case baud_38400:
+ dcb.BaudRate = CBR_38400;
+ break;
+ case baud_57600:
+ dcb.BaudRate = CBR_57600;
+ break;
+ case baud_115200:
+ dcb.BaudRate = CBR_115200;
+ break;
+ default:
+ CloseHandle(p->phandle);
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: illegal baud rate! (0x%x)\n",p->br);
+ return ICOM_SYS;
+ }
+
+ PurgeComm(p->phandle, PURGE_TXCLEAR | PURGE_RXCLEAR);
+
+ if (!SetCommState(p->phandle, &dcb)) {
+ CloseHandle(p->phandle);
+ a1loge(p->log, ICOM_SYS, "icoms_set_ser_port: SetCommState failed with LastError %d\n",
+ GetLastError());
+ return ICOM_SYS;
+ }
+
+ PurgeComm(p->phandle, PURGE_TXCLEAR | PURGE_RXCLEAR);
+
+ p->write = icoms_ser_write;
+ p->read = icoms_ser_read;
+
+ }
+ a1logd(p->log, 8, "icoms_set_ser_port: port characteristics set ok\n");
+
+ return ICOM_OK;
+}
+
+/* ---------------------------------------------------------------------------------*/
+/* Serial write/read */
+
+/* Write the characters in the buffer out */
+/* Data will be written up to the terminating nul */
+/* Return relevant error status bits */
+/* Set the icoms lserr value */
+static int
+icoms_ser_write(
+icoms *p,
+char *wbuf,
+double tout)
+{
+ COMMTIMEOUTS tmo;
+ DWORD wbytes;
+ int c, len;
+ long toc, i, top; /* Timout count, counter, timeout period */
+ int rv = ICOM_OK;
+
+ a1logd(p->log, 8, "icoms_ser_write: About to write '%s' ",icoms_fix(wbuf));
+ if (!p->is_open) {
+ a1loge(p->log, ICOM_SYS, "icoms_ser_write: device not initialised\n");
+ p->lserr = rv = ICOM_SYS;
+ return rv;
+ }
+
+ len = strlen(wbuf);
+ tout *= 1000.0; /* Timout in msec */
+
+ top = 100; /* Timeout period in msecs */
+ toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */
+ if (toc < 1)
+ toc = 1;
+
+ /* Set the timout value */
+ tmo.ReadIntervalTimeout = top;
+ tmo.ReadTotalTimeoutMultiplier = 0;
+ tmo.ReadTotalTimeoutConstant = top;
+ tmo.WriteTotalTimeoutMultiplier = top;
+ tmo.WriteTotalTimeoutConstant = 0;
+ if (!SetCommTimeouts(p->phandle, &tmo)) {
+ a1loge(p->log, ICOM_SYS, "icoms_ser_write: SetCommTimeouts failed with %d\n",GetLastError());
+ p->lserr = rv = ICOM_SYS;
+ return rv;
+ }
+
+ /* Until data is all written or we time out */
+ for (i = toc; i > 0 && len > 0;) {
+ if (!WriteFile(p->phandle, wbuf, len, &wbytes, NULL)) {
+ DWORD errs;
+ if (!ClearCommError(p->phandle,&errs,NULL))
+ error("Write to COM port failed, and Clear error failed");
+ if (errs & CE_BREAK)
+ rv |= ICOM_BRK;
+ if (errs & CE_FRAME)
+ rv |= ICOM_FER;
+ if (errs & CE_RXPARITY)
+ rv |= ICOM_PER;
+ if (errs & CE_RXOVER)
+ rv |= ICOM_OER;
+ break;
+ } else if (wbytes == 0) {
+ i--; /* Timeout */
+ } else if (wbytes > 0) { /* Account for bytes done */
+ i = toc;
+ len -= wbytes;
+ wbuf += len;
+ }
+ }
+ if (i <= 0) { /* Timed out */
+ rv |= ICOM_TO;
+ }
+ a1logd(p->log, 8, "icoms_ser_write: returning ICOM err 0x%x\n",rv);
+
+ p->lserr = rv;
+ return rv;
+}
+
+
+/* Read characters into the buffer */
+/* Return string will be terminated with a nul */
+static int
+icoms_ser_read(
+icoms *p,
+char *rbuf, /* Buffer to store characters read */
+int bsize, /* Buffer size */
+char tc, /* Terminating characer */
+int ntc, /* Number of terminating characters */
+double tout /* Time out in seconds */
+) {
+ COMMTIMEOUTS tmo;
+ DWORD rbytes;
+ int c, j;
+ long toc, i, top; /* Timout count, counter, timeout period */
+ char *rrbuf = rbuf; /* Start of return buffer */
+ DCB dcb;
+ int rv = ICOM_OK;
+
+ if (p->phandle == NULL) {
+ a1loge(p->log, ICOM_SYS, "icoms_read: device not initialised\n");
+ p->lserr = rv = ICOM_SYS;
+ return rv;
+ }
+
+ if (bsize < 3) {
+ a1loge(p->log, ICOM_SYS, "icoms_read: given too small a buffer\n");
+ p->lserr = rv = ICOM_SYS;
+ return rv;
+ }
+
+#ifdef NEVER
+ /* The Prolific 2303 USB<->serial seems to choke on this, */
+ /* so we just put up with a 100msec delay at the end of each */
+ /* reply. */
+ if (GetCommState(p->phandle, &dcb) == FALSE) {
+ a1loge(p->log, ICOM_SYS, "icoms_ser_read: GetCommState failed with %d\n",GetLastError());
+ p->lserr = rv = ICOM_SYS;
+ return rv;
+ }
+
+ dcb.EofChar = tc;
+
+ if (!SetCommState(p->phandle, &dcb)) {
+ a1loge(p->log, ICOM_SYS, "icoms_ser_read: SetCommState failed %d\n",GetLastError());
+ p->lserr = rv = ICOM_SYS;
+ return rv;
+ }
+#endif
+ p->tc = tc;
+
+ tout *= 1000.0; /* Timout in msec */
+ bsize--; /* Allow space for null */
+
+ top = 100; /* Timeout period in msecs */
+ toc = (int)(tout/top + 0.5); /* Number of timout periods in timeout */
+ if (toc < 1)
+ toc = 1;
+
+ /* Set the timout value */
+ tmo.ReadIntervalTimeout = top;
+ tmo.ReadTotalTimeoutMultiplier = 0;
+ tmo.ReadTotalTimeoutConstant = top;
+ tmo.WriteTotalTimeoutMultiplier = top;
+ tmo.WriteTotalTimeoutConstant = 0;
+ if (!SetCommTimeouts(p->phandle, &tmo)) {
+ a1loge(p->log, ICOM_SYS, "icoms_ser_read: SetCommTimeouts failed with %d\n",GetLastError());
+ p->lserr = rv = ICOM_SYS;
+ return rv;
+ }
+
+ /* Until data is all read or we time out */
+ for (i = toc, j = 0; i > 0 && bsize > 1 && j < ntc ;) {
+ if (!ReadFile(p->phandle, rbuf, bsize, &rbytes, NULL)) {
+ DWORD errs;
+ if (!ClearCommError(p->phandle,&errs,NULL))
+ error("Read from COM port failed, and Clear error failed");
+ if (errs & CE_BREAK)
+ rv |= ICOM_BRK;
+ if (errs & CE_FRAME)
+ rv |= ICOM_FER;
+ if (errs & CE_RXPARITY)
+ rv |= ICOM_PER;
+ if (errs & CE_RXOVER)
+ rv |= ICOM_OER;
+ break;
+ } else if (rbytes == 0) {
+ i--; /* Timeout */
+ } else if (rbytes > 0) { /* Account for bytes done */
+ i = toc;
+ bsize -= rbytes;
+ while(rbytes--) { /* Count termination characters */
+ if (*rbuf++ == tc)
+ j++;
+ }
+ }
+ }
+ if (i <= 0) { /* timed out */
+ rv |= ICOM_TO;
+ }
+ *rbuf = '\000';
+ a1logd(p->log, 8, "icoms_ser_read: returning '%s' ICOM err 0x%x\n",icoms_fix(rrbuf),rv);
+
+ p->lserr = rv;
+ return rv;
+}
+
+#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
+ p->log = del_a1log(p->log); /* unref */
+ free (p);
+}
+