aboutsummaryrefslogtreecommitdiff
path: root/ports/winnt/libntp/termios.c
diff options
context:
space:
mode:
Diffstat (limited to 'ports/winnt/libntp/termios.c')
-rw-r--r--ports/winnt/libntp/termios.c789
1 files changed, 789 insertions, 0 deletions
diff --git a/ports/winnt/libntp/termios.c b/ports/winnt/libntp/termios.c
new file mode 100644
index 000000000000..3fe4e4859c01
--- /dev/null
+++ b/ports/winnt/libntp/termios.c
@@ -0,0 +1,789 @@
+/* This file implements system calls that are not compatible with UNIX */
+/* Moved to libntp/termios.c */
+
+#include <config.h>
+#include <io.h>
+#include <stdio.h>
+
+#include "ntp.h"
+#include "ntp_tty.h"
+#include "lib_strbuf.h"
+#include "ntp_assert.h"
+#include "win32_io.h"
+
+#define MAX_SERIAL 255 /* COM1: - COM255: */
+
+typedef struct comhandles_tag {
+ HANDLE h;
+ size_t opens;
+ HANDLE * dupes;
+} comhandles;
+
+comhandles * hnds; /* handle/dupes array */
+size_t c_hnds; /* current array size */
+
+/*
+ * common_serial_open ensures duplicate opens of the same port
+ * work by duplicating the handle for the 2nd open, allowing
+ * refclock_atom to share a GPS refclock's comm port.
+ */
+HANDLE common_serial_open(
+ char * dev,
+ char ** pwindev
+ )
+{
+ char * windev;
+ HANDLE handle;
+ size_t unit;
+ size_t prev_c_hnds;
+ size_t opens;
+ char * pch;
+
+ /*
+ * This is odd, but we'll take any unix device path
+ * by looking for the initial '/' and strip off everything
+ * before the final digits, then translate that to COM__:
+ * maintaining backward compatibility with NTP practice of
+ * mapping unit 0 to the nonfunctional COM0:
+ *
+ * To ease the job of taking the windows COMx: device names
+ * out of reference clocks, we'll also work with those
+ * equanimously.
+ */
+
+ TRACE(1, ("common_serial_open given %s\n", dev));
+
+ pch = NULL;
+ if ('/' == dev[0]) {
+ pch = dev + strlen(dev) - 1;
+
+ if (isdigit(pch[0])) {
+ while (isdigit(pch[0])) {
+ pch--;
+ }
+ pch++;
+ }
+ TRACE(1, ("common_serial_open skipped to ending digits leaving %s\n", pch));
+ } else if ('c' == tolower(dev[0])
+ && 'o' == tolower(dev[1])
+ && 'm' == tolower(dev[2])) {
+ pch = dev + 3;
+ TRACE(1, ("common_serial_open skipped COM leaving %s\n", pch));
+ }
+
+ if (!pch || !isdigit(pch[0])) {
+ TRACE(1, ("not a digit: %s\n", pch ? pch : "[NULL]"));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (1 != sscanf(pch, "%d", &unit)
+ || unit > MAX_SERIAL
+ || unit < 0) {
+ TRACE(1, ("sscanf failure of %s\n", pch));
+ return INVALID_HANDLE_VALUE;
+ }
+
+
+ if (c_hnds < unit + 1) {
+ prev_c_hnds = c_hnds;
+ c_hnds = unit + 1;
+ /* round up to closest multiple of 4 to avoid churn */
+ c_hnds = (c_hnds + 3) & ~3;
+ hnds = erealloc_zero(hnds, c_hnds * sizeof(hnds[0]),
+ prev_c_hnds * sizeof(hnds[0]));
+ }
+
+ if (NULL == hnds[unit].h) {
+ NTP_ENSURE(0 == hnds[unit].opens);
+ LIB_GETBUF(windev);
+ snprintf(windev, LIB_BUFLENGTH, "\\\\.\\COM%d", unit);
+ TRACE(1, ("windows device %s\n", windev));
+ *pwindev = windev;
+ hnds[unit].h =
+ CreateFile(
+ windev,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, /* sharing prohibited */
+ NULL, /* default security */
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ NULL);
+ if (INVALID_HANDLE_VALUE == hnds[unit].h)
+ hnds[unit].h = NULL;
+ }
+
+ if (NULL != hnds[unit].h) {
+ /* think handle = dup(hnds[unit].h); */
+ DuplicateHandle(
+ GetCurrentProcess(),
+ hnds[unit].h,
+ GetCurrentProcess(),
+ &handle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS
+ );
+ hnds[unit].opens++;
+ opens = hnds[unit].opens;
+ hnds[unit].dupes = erealloc(hnds[unit].dupes, opens *
+ sizeof(hnds[unit].dupes[0]));
+ hnds[unit].dupes[opens - 1] = handle;
+ return handle;
+ }
+
+ return INVALID_HANDLE_VALUE;
+}
+
+
+/*
+ * closeserial() is used in place of close by ntpd refclock I/O for ttys
+ */
+int
+closeserial(int fd)
+{
+ HANDLE h;
+ BOOL found;
+ size_t u;
+ size_t d;
+
+ h = (HANDLE)_get_osfhandle(fd);
+ if (INVALID_HANDLE_VALUE == h) {
+ errno = EBADF;
+ return -1;
+ }
+
+ d = 0; /* silence potent. uninit. warning */
+ found = FALSE;
+ for (u = 0; u < c_hnds; u++) {
+ for (d = 0; d < hnds[u].opens; d++) {
+ if (hnds[u].dupes[d] == h) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (found) {
+ hnds[u].opens--;
+ if (d < hnds[u].opens)
+ memmove(&hnds[u].dupes[d],
+ &hnds[u].dupes[d + 1],
+ hnds[u].opens - d *
+ sizeof(hnds[u].dupes[d]));
+ if (0 == hnds[u].opens) {
+ CloseHandle(hnds[u].h);
+ hnds[u].h = NULL;
+ }
+ }
+
+ return close(fd);
+}
+
+/*
+ * isserialhandle() -- check if a handle is a COM port handle
+ */
+int isserialhandle(
+ HANDLE h
+ )
+{
+ size_t u;
+ size_t d;
+
+
+ for (u = 0; u < c_hnds; u++)
+ for (d = 0; d < hnds[u].opens; d++)
+ if (hnds[u].dupes[d] == h)
+ return TRUE;
+ return FALSE;
+}
+
+
+/*
+ * tty_open - open serial port for refclock special uses
+ *
+ * This routine opens a serial port for and returns the
+ * file descriptor if success and -1 if failure.
+ */
+int tty_open(
+ char *dev, /* device name pointer */
+ int access, /* O_RDWR */
+ int mode /* unused */
+ )
+{
+ HANDLE Handle;
+ char * windev;
+
+ /*
+ * open communication port handle
+ */
+ windev = NULL;
+ Handle = common_serial_open(dev, &windev);
+ windev = (windev)
+ ? windev
+ : dev;
+
+ if (Handle == INVALID_HANDLE_VALUE) {
+ msyslog(LOG_ERR, "tty_open: device %s CreateFile error: %m", windev);
+ errno = EMFILE; /* lie, lacking conversion from GetLastError() */
+ return -1;
+ }
+
+ return (int)_open_osfhandle((int)Handle, _O_TEXT);
+}
+
+
+/*
+ * refclock_open - open serial port for reference clock
+ *
+ * This routine opens a serial port for I/O and sets default options. It
+ * returns the file descriptor or -1 indicating failure.
+ */
+int refclock_open(
+ char * dev, /* device name pointer */
+ u_int speed, /* serial port speed (code) */
+ u_int flags /* line discipline flags */
+ )
+{
+ char * windev;
+ HANDLE h;
+ COMMTIMEOUTS timeouts;
+ DCB dcb;
+ DWORD dwEvtMask;
+ int fd;
+ int translate;
+
+ /*
+ * open communication port handle
+ */
+ windev = NULL;
+ h = common_serial_open(dev, &windev);
+ windev = (windev) ? windev : dev;
+
+ if (INVALID_HANDLE_VALUE == h) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "CreateFile(%s) error: %m",
+ windev);
+ )
+ return -1;
+ }
+
+ /* Change the input/output buffers to be large. */
+ if (!SetupComm(h, 1024, 1024)) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "SetupComm(%s) error: %m",
+ windev);
+ )
+ return -1;
+ }
+
+ dcb.DCBlength = sizeof(dcb);
+
+ if (!GetCommState(h, &dcb)) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "GetCommState(%s) error: %m",
+ windev);
+ )
+ return -1;
+ }
+
+ switch (speed) {
+
+ case B300:
+ dcb.BaudRate = 300;
+ break;
+
+ case B1200:
+ dcb.BaudRate = 1200;
+ break;
+
+ case B2400:
+ dcb.BaudRate = 2400;
+ break;
+
+ case B4800:
+ dcb.BaudRate = 4800;
+ break;
+
+ case B9600:
+ dcb.BaudRate = 9600;
+ break;
+
+ case B19200:
+ dcb.BaudRate = 19200;
+ break;
+
+ case B38400:
+ dcb.BaudRate = 38400;
+ break;
+
+ case B57600:
+ dcb.BaudRate = 57600;
+ break;
+
+ case B115200:
+ dcb.BaudRate = 115200;
+ break;
+
+ default:
+ msyslog(LOG_ERR, "%s unsupported bps code %u", windev,
+ speed);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ dcb.fBinary = TRUE;
+ dcb.fParity = TRUE;
+ dcb.fOutxCtsFlow = 0;
+ dcb.fOutxDsrFlow = 0;
+ dcb.fDtrControl = DTR_CONTROL_ENABLE;
+ dcb.fDsrSensitivity = 0;
+ dcb.fTXContinueOnXoff = TRUE;
+ dcb.fOutX = 0;
+ dcb.fInX = 0;
+ dcb.fErrorChar = 0;
+ dcb.fNull = 0;
+ dcb.fRtsControl = RTS_CONTROL_DISABLE;
+ dcb.fAbortOnError = 0;
+ dcb.ByteSize = 8;
+ dcb.StopBits = ONESTOPBIT;
+ dcb.Parity = NOPARITY;
+ dcb.ErrorChar = 0;
+ dcb.EofChar = 0;
+ if (LDISC_RAW & flags)
+ dcb.EvtChar = 0;
+ else
+ dcb.EvtChar = '\r';
+
+ if (!SetCommState(h, &dcb)) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "SetCommState(%s) error: %m",
+ windev);
+ )
+ return -1;
+ }
+
+ /* watch out for CR (dcb.EvtChar) as well as the CD line */
+ dwEvtMask = EV_RLSD;
+ if (LDISC_RAW & flags)
+ dwEvtMask |= EV_RXCHAR;
+ else
+ dwEvtMask |= EV_RXFLAG;
+ if (!SetCommMask(h, dwEvtMask)) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "SetCommMask(%s) error: %m",
+ windev);
+ )
+ return -1;
+ }
+
+ /* configure the handle to never block */
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+ timeouts.ReadTotalTimeoutMultiplier = 0;
+ timeouts.ReadTotalTimeoutConstant = 0;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+
+ if (!SetCommTimeouts(h, &timeouts)) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "Device %s SetCommTimeouts error: %m",
+ windev);
+ )
+ return -1;
+ }
+
+ translate = (LDISC_RAW & flags)
+ ? 0
+ : _O_TEXT;
+ fd = _open_osfhandle((int)h, translate);
+ /* refclock_open() long returned 0 on failure, avoid it. */
+ if (0 == fd) {
+ fd = _dup(0);
+ _close(0);
+ }
+
+ return fd;
+}
+
+
+int
+ioctl_tiocmget(
+ HANDLE h,
+ int *pi
+ )
+{
+ DWORD dw;
+
+ if (!GetCommModemStatus(h, &dw)) {
+ errno = ENOTTY;
+ return -1;
+ }
+
+ *pi = ((dw & MS_CTS_ON) ? TIOCM_CTS : 0)
+ | ((dw & MS_DSR_ON) ? TIOCM_DSR : 0)
+ | ((dw & MS_RLSD_ON) ? TIOCM_CAR : 0)
+ | ((dw & MS_RING_ON) ? TIOCM_RI : 0);
+
+ return 0;
+}
+
+
+int
+ioctl_tiocmset(
+ HANDLE h,
+ int *pi
+ )
+{
+ BOOL failed;
+ int result;
+
+ failed = !EscapeCommFunction(
+ h,
+ (*pi & TIOCM_RTS)
+ ? SETRTS
+ : CLRRTS
+ );
+
+ if (!failed)
+ failed = !EscapeCommFunction(
+ h,
+ (*pi & TIOCM_DTR)
+ ? SETDTR
+ : CLRDTR
+ );
+
+ if (failed) {
+ errno = ENOTTY;
+ result = -1;
+ } else
+ result = 0;
+
+ return result;
+}
+
+
+int
+ioctl(
+ int fd,
+ int op,
+ int *pi
+ )
+{
+ HANDLE h;
+ int result;
+ int modctl;
+
+ h = (HANDLE)_get_osfhandle(fd);
+
+ if (INVALID_HANDLE_VALUE == h) {
+ /* errno already set */
+ return -1;
+ }
+
+ switch (op) {
+
+ case TIOCMGET:
+ result = ioctl_tiocmget(h, pi);
+ break;
+
+ case TIOCMSET:
+ result = ioctl_tiocmset(h, pi);
+ break;
+
+ case TIOCMBIC:
+ result = ioctl_tiocmget(h, &modctl);
+ if (result < 0)
+ return result;
+ modctl &= ~(*pi);
+ result = ioctl_tiocmset(h, &modctl);
+ break;
+
+ case TIOCMBIS:
+ result = ioctl_tiocmget(h, &modctl);
+ if (result < 0)
+ return result;
+ modctl |= *pi;
+ result = ioctl_tiocmset(h, &modctl);
+ break;
+
+ default:
+ errno = EINVAL;
+ result = -1;
+ }
+
+ return result;
+}
+
+
+int
+tcsetattr(
+ int fd,
+ int optional_actions,
+ const struct termios * tios
+ )
+{
+ DCB dcb;
+ HANDLE h;
+
+ UNUSED_ARG(optional_actions);
+
+ h = (HANDLE)_get_osfhandle(fd);
+
+ if (INVALID_HANDLE_VALUE == h) {
+ /* errno already set */
+ return -1;
+ }
+
+ dcb.DCBlength = sizeof(dcb);
+ if (!GetCommState(h, &dcb)) {
+ errno = ENOTTY;
+ return -1;
+ }
+
+ switch (max(tios->c_ospeed, tios->c_ispeed)) {
+
+ case B300:
+ dcb.BaudRate = 300;
+ break;
+
+ case B1200:
+ dcb.BaudRate = 1200;
+ break;
+
+ case B2400:
+ dcb.BaudRate = 2400;
+ break;
+
+ case B4800:
+ dcb.BaudRate = 4800;
+ break;
+
+ case B9600:
+ dcb.BaudRate = 9600;
+ break;
+
+ case B19200:
+ dcb.BaudRate = 19200;
+ break;
+
+ case B38400:
+ dcb.BaudRate = 38400;
+ break;
+
+ case B57600:
+ dcb.BaudRate = 57600;
+ break;
+
+ case B115200:
+ dcb.BaudRate = 115200;
+ break;
+
+ default:
+ msyslog(LOG_ERR, "unsupported serial baud rate");
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (tios->c_cflag & CSIZE) {
+
+ case CS5:
+ dcb.ByteSize = 5;
+ break;
+
+ case CS6:
+ dcb.ByteSize = 6;
+ break;
+
+ case CS7:
+ dcb.ByteSize = 7;
+ break;
+
+ case CS8:
+ dcb.ByteSize = 8;
+ break;
+
+ default:
+ msyslog(LOG_ERR, "unsupported serial word size");
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (PARENB & tios->c_cflag) {
+ dcb.fParity = TRUE;
+ dcb.Parity = (tios->c_cflag & PARODD)
+ ? ODDPARITY
+ : EVENPARITY;
+ } else {
+ dcb.fParity = FALSE;
+ dcb.Parity = NOPARITY;
+ }
+
+ dcb.StopBits = (CSTOPB & tios->c_cflag)
+ ? TWOSTOPBITS
+ : ONESTOPBIT;
+
+ if (!SetCommState(h, &dcb)) {
+ errno = ENOTTY;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+tcgetattr(
+ int fd,
+ struct termios *tios
+ )
+{
+ DCB dcb;
+ HANDLE h;
+
+ h = (HANDLE)_get_osfhandle(fd);
+
+ if (INVALID_HANDLE_VALUE == h) {
+ /* errno already set */
+ return -1;
+ }
+
+ dcb.DCBlength = sizeof(dcb);
+
+ if (!GetCommState(h, &dcb)) {
+ errno = ENOTTY;
+ return -1;
+ }
+
+ /* Set c_ispeed & c_ospeed */
+
+ switch (dcb.BaudRate) {
+
+ case 300:
+ tios->c_ispeed = tios->c_ospeed = B300;
+ break;
+
+ case 1200:
+ tios->c_ispeed = tios->c_ospeed = B1200;
+ break;
+
+ case 2400:
+ tios->c_ispeed = tios->c_ospeed = B2400;
+ break;
+
+ case 4800:
+ tios->c_ispeed = tios->c_ospeed = B4800;
+ break;
+
+ case 9600:
+ tios->c_ispeed = tios->c_ospeed = B9600;
+ break;
+
+ case 19200:
+ tios->c_ispeed = tios->c_ospeed = B19200;
+ break;
+
+ case 38400:
+ tios->c_ispeed = tios->c_ospeed = B38400;
+ break;
+
+ case 57600:
+ tios->c_ispeed = tios->c_ospeed = B57600;
+ break;
+
+ case 115200:
+ tios->c_ispeed = tios->c_ospeed = B115200;
+ break;
+
+ default:
+ tios->c_ispeed = tios->c_ospeed = B9600;
+ }
+
+
+ switch (dcb.ByteSize) {
+ case 5:
+ tios->c_cflag = CS5;
+ break;
+
+ case 6:
+ tios->c_cflag = CS6;
+ break;
+
+ case 7:
+ tios->c_cflag = CS7;
+ break;
+
+ case 8:
+ default:
+ tios->c_cflag = CS8;
+ }
+
+ if (dcb.fParity) {
+ tios->c_cflag |= PARENB;
+
+ if (ODDPARITY == dcb.Parity)
+ tios->c_cflag |= PARODD;
+ }
+
+ if (TWOSTOPBITS == dcb.StopBits)
+ tios->c_cflag |= CSTOPB;
+
+ tios->c_iflag = 0;
+ tios->c_lflag = 0;
+ tios->c_line = 0;
+ tios->c_oflag = 0;
+
+ return 0;
+}
+
+
+int
+tcflush(
+ int fd,
+ int mode
+ )
+{
+ HANDLE h;
+ BOOL success;
+ DWORD flags;
+ int result;
+
+ h = (HANDLE)_get_osfhandle(fd);
+
+ if (INVALID_HANDLE_VALUE == h) {
+ /* errno already set */
+ return -1;
+ }
+
+ switch (mode) {
+
+ case TCIFLUSH:
+ flags = PURGE_RXCLEAR;
+ break;
+
+ case TCOFLUSH:
+ flags = PURGE_TXABORT;
+ break;
+
+ case TCIOFLUSH:
+ flags = PURGE_RXCLEAR | PURGE_TXABORT;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ success = PurgeComm(h, flags);
+
+ if (success)
+ result = 0;
+ else {
+ errno = ENOTTY;
+ result = -1;
+ }
+
+ return result;
+}
+