diff options
Diffstat (limited to 'ports/winnt/libntp/termios.c')
-rw-r--r-- | ports/winnt/libntp/termios.c | 311 |
1 files changed, 191 insertions, 120 deletions
diff --git a/ports/winnt/libntp/termios.c b/ports/winnt/libntp/termios.c index 9596593b7575a..22b06b4718179 100644 --- a/ports/winnt/libntp/termios.c +++ b/ports/winnt/libntp/termios.c @@ -11,16 +11,135 @@ #include "ntp_assert.h" #include "win32_io.h" +#include "ntp_iocplmem.h" +#include "ntp_iocpltypes.h" + +/* ------------------------------------------------------------------- + * COM port management + * + * com port handling needs some special functionality, especially for + * PPS support. There are things that are shared by the Windows Kernel + * on device level, not handle level. These include IOCPL membership, + * event wait slot, ... It's also no so simple to open a device a + * second time, and so we must manage the handles on open com ports + * in userland. Well, partially. + */ #define MAX_SERIAL 255 /* COM1: - COM255: */ +#define MAX_COMDUP 8 /* max. allowed number of dupes per device */ typedef struct comhandles_tag { - HANDLE h; - size_t opens; - HANDLE * dupes; + uint16_t unit; /* COMPORT number */ + uint16_t nhnd; /* number of open handles */ + char * comName;/* windows device name */ + DevCtx_t * devCtx; /* shared device context */ + HANDLE htab[MAX_COMDUP]; /* OS handles */ } comhandles; -comhandles * hnds; /* handle/dupes array */ -size_t c_hnds; /* current array size */ +comhandles ** tab_comh; /* device data table */ +size_t num_comh; /* current used array size */ +size_t max_comh; /* current allocated array size */ + +/* lookup a COM unit by a handle + * Scans all used units for a matching handle. Returns the slot + * or NULL on failure. + * + * If 'phidx' is given, the index in the slots handle table that + * holds the handle is also returned. + * + * This a simple 2d table scan. But since we don't expect to have + * hundreds of com ports open, this should be no problem. + */ +static comhandles* +lookup_com_handle( + HANDLE h, + size_t * phidx + ) +{ + size_t tidx, hidx; + comhandles * slot; + for (tidx = 0; tidx < num_comh; ++tidx) { + slot = tab_comh[tidx]; + for (hidx = 0; hidx < slot->nhnd; ++hidx) { + if (slot->htab[hidx] == h) { + if (phidx != NULL) + *phidx = hidx; + return slot; + } + } + } + return NULL; +} + +/* lookup the list of COM units by unit number. This will always return + * a valid location -- eventually the table gets expanded, and a new + * entry is returned. In that case, the structure is set up with all + * entries valid and *no* file handles yet. + */ +static comhandles* +insert_com_unit( + uint16_t unit +) +{ + size_t tidx; + comhandles * slot; + + /* search for matching entry and return if found */ + for (tidx = 0; tidx < num_comh; ++tidx) + if (tab_comh[tidx]->unit == unit) + return tab_comh[tidx]; + + /* search failed. make sure we can add a new slot */ + if (num_comh >= max_comh) { + /* round up to next multiple of 4 */ + max_comh = (num_comh + 4) & ~(size_t)3; + tab_comh = erealloc(tab_comh, max_comh * sizeof(tab_comh[0])); + } + + /* create a new slot and populate it. */ + slot = emalloc_zero(sizeof(comhandles)); + LIB_GETBUF(slot->comName); + snprintf(slot->comName, LIB_BUFLENGTH, "\\\\.\\COM%d", unit); + slot->comName = estrdup(slot->comName); + slot->devCtx = DevCtxAlloc(); + slot->unit = unit; + + /* plug it into table and return it */ + tab_comh[num_comh++] = slot; + return slot; +} + +/* remove a COM slot from the table and destroy it. */ +static void +remove_com_slot( + comhandles * slot /* must be valid! */ + ) +{ + size_t tidx; + for (tidx = 0; tidx < num_comh; ++tidx) + if (tab_comh[tidx] == slot) { + tab_comh[tidx] = tab_comh[--num_comh]; + break; + } + + DevCtxDetach(slot->devCtx); + free(slot->comName); + free(slot); +} + +/* fetch the stored device context block. + * This does NOT step the reference counter! + */ +DevCtx_t* +serial_devctx( + HANDLE h + ) +{ + comhandles * slot = NULL; + if (INVALID_HANDLE_VALUE != h && NULL != h) + slot = lookup_com_handle(h, NULL); + return (NULL != slot) ? slot->devCtx : NULL; +} + /* * common_serial_open ensures duplicate opens of the same port @@ -30,16 +149,13 @@ size_t c_hnds; /* current array size */ HANDLE common_serial_open( const char * dev, - char ** pwindev + const char ** pwindev ) { - char * windev; HANDLE handle; size_t unit; - size_t prev_c_hnds; - size_t opens; const char * pch; - u_int uibuf; + comhandles * slot; /* * This is odd, but we'll take any unix device path @@ -55,147 +171,105 @@ common_serial_open( TRACE(1, ("common_serial_open given %s\n", dev)); + handle = INVALID_HANDLE_VALUE; + pch = NULL; if ('/' == dev[0]) { - pch = dev + strlen(dev) - 1; - - if (isdigit(pch[0])) { - while (isdigit(pch[0])) { - pch--; - } - pch++; - } + pch = dev + strlen(dev); + while (isdigit((u_char)pch[-1])) + --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])) { + } else if (0 == _strnicmp("COM", dev, 3)) { pch = dev + 3; TRACE(1, ("common_serial_open skipped COM leaving %s\n", pch)); } - if (!pch || !isdigit(pch[0])) { + if (!pch || !isdigit((u_char)pch[0])) { TRACE(1, ("not a digit: %s\n", pch ? pch : "[NULL]")); return INVALID_HANDLE_VALUE; } - if (1 != sscanf(pch, "%u", &uibuf) - || (unit = uibuf) > MAX_SERIAL) { - TRACE(1, ("sscanf failure of %s\n", pch)); + unit = strtoul(pch, (char**)&pch, 10); + if (*pch || unit > MAX_SERIAL) { + TRACE(1, ("conversion failure: unit=%u at '%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) { - INSIST(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, + /* Now.... find the COM slot, and either create a new file + * (if there is no handle yet) or duplicate one of the existing + * handles. Unless the dup table for one com port would overflow, + * but that's an indication of a programming error somewhere. + */ + slot = insert_com_unit(unit); + if (slot->nhnd == 0) { + TRACE(1, ("windows device %s\n", slot->comName)); + slot->htab[0] = CreateFileA( + slot->comName, 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; + if (INVALID_HANDLE_VALUE != slot->htab[0]) { + slot->nhnd = 1; + handle = slot->htab[0]; + *pwindev = slot->comName; + } + } else if (slot->nhnd >= MAX_COMDUP) { + SetLastError(ERROR_TOO_MANY_OPEN_FILES); + } else if (DuplicateHandle(GetCurrentProcess(), slot->htab[0], + GetCurrentProcess(), &slot->htab[slot->nhnd], + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + handle = slot->htab[slot->nhnd++]; + *pwindev = slot->comName; } - return INVALID_HANDLE_VALUE; + return handle; } - /* * closeserial() is used in place of close by ntpd refclock I/O for ttys */ int -closeserial(int fd) +closeserial( + int fd + ) { HANDLE h; - BOOL found; - size_t u; - size_t d; + size_t hidx; + comhandles * slot; h = (HANDLE)_get_osfhandle(fd); - if (INVALID_HANDLE_VALUE == h) { - errno = EBADF; - return -1; - } + if (INVALID_HANDLE_VALUE == h) + goto onerror; - 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; - } - } + slot = lookup_com_handle(h, &hidx); + if (NULL == slot) + goto onerror; + + slot->htab[hidx] = slot->htab[--slot->nhnd]; + if (slot->nhnd == 0) + remove_com_slot(slot); + + return close(fd); /* closes system handle, too! */ - return close(fd); +onerror: + errno = EBADF; + return -1; } /* * isserialhandle() -- check if a handle is a COM port handle */ -int isserialhandle( +int/*BOOL*/ +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; + if (INVALID_HANDLE_VALUE != h && NULL != h) + return lookup_com_handle(h, NULL) != NULL; return FALSE; } @@ -206,23 +280,21 @@ int isserialhandle( * This routine opens a serial port for and returns the * file descriptor if success and -1 if failure. */ -int tty_open( +int +tty_open( const char *dev, /* device name pointer */ int access, /* O_RDWR */ int mode /* unused */ ) { - HANDLE Handle; - char * windev; + HANDLE Handle; + const char * windev; /* * open communication port handle */ - windev = NULL; + windev = dev; 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); @@ -230,7 +302,7 @@ int tty_open( return -1; } - return (int)_open_osfhandle((intptr_t)Handle, _O_TEXT); + return _open_osfhandle((intptr_t)Handle, _O_TEXT); } @@ -247,7 +319,7 @@ refclock_open( u_int flags /* line discipline flags */ ) { - char * windev; + const char * windev; HANDLE h; COMMTIMEOUTS timeouts; DCB dcb; @@ -258,9 +330,8 @@ refclock_open( /* * open communication port handle */ - windev = NULL; + windev = dev; h = common_serial_open(dev, &windev); - windev = (windev) ? windev : dev; if (INVALID_HANDLE_VALUE == h) { SAVE_ERRNO( |