1 /* This file implements system calls that are not compatible with UNIX */
2 /* Moved to libntp/termios.c */
10 #include "lib_strbuf.h"
11 #include "ntp_assert.h"
14 #define MAX_SERIAL 255 /* COM1: - COM255: */
16 typedef struct comhandles_tag {
22 comhandles * hnds; /* handle/dupes array */
23 size_t c_hnds; /* current array size */
26 * common_serial_open ensures duplicate opens of the same port
27 * work by duplicating the handle for the 2nd open, allowing
28 * refclock_atom to share a GPS refclock's comm port.
45 * This is odd, but we'll take any unix device path
46 * by looking for the initial '/' and strip off everything
47 * before the final digits, then translate that to COM__:
48 * maintaining backward compatibility with NTP practice of
49 * mapping unit 0 to the nonfunctional COM0:
51 * To ease the job of taking the windows COMx: device names
52 * out of reference clocks, we'll also work with those
56 TRACE(1, ("common_serial_open given %s\n", dev));
60 pch = dev + strlen(dev) - 1;
62 if (isdigit(pch[0])) {
63 while (isdigit(pch[0])) {
68 TRACE(1, ("common_serial_open skipped to ending digits leaving %s\n", pch));
69 } else if ('c' == tolower(dev[0])
70 && 'o' == tolower(dev[1])
71 && 'm' == tolower(dev[2])) {
73 TRACE(1, ("common_serial_open skipped COM leaving %s\n", pch));
76 if (!pch || !isdigit(pch[0])) {
77 TRACE(1, ("not a digit: %s\n", pch ? pch : "[NULL]"));
78 return INVALID_HANDLE_VALUE;
81 if (1 != sscanf(pch, "%u", &uibuf)
82 || (unit = uibuf) > MAX_SERIAL) {
83 TRACE(1, ("sscanf failure of %s\n", pch));
84 return INVALID_HANDLE_VALUE;
88 if (c_hnds < unit + 1) {
91 /* round up to closest multiple of 4 to avoid churn */
92 c_hnds = (c_hnds + 3) & ~3;
93 hnds = erealloc_zero(hnds, c_hnds * sizeof(hnds[0]),
94 prev_c_hnds * sizeof(hnds[0]));
97 if (NULL == hnds[unit].h) {
98 INSIST(0 == hnds[unit].opens);
100 snprintf(windev, LIB_BUFLENGTH, "\\\\.\\COM%d", unit);
101 TRACE(1, ("windows device %s\n", windev));
106 GENERIC_READ | GENERIC_WRITE,
107 0, /* sharing prohibited */
108 NULL, /* default security */
110 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
112 if (INVALID_HANDLE_VALUE == hnds[unit].h)
116 if (NULL != hnds[unit].h) {
117 /* think handle = dup(hnds[unit].h); */
125 DUPLICATE_SAME_ACCESS
128 opens = hnds[unit].opens;
129 hnds[unit].dupes = erealloc(hnds[unit].dupes, opens *
130 sizeof(hnds[unit].dupes[0]));
131 hnds[unit].dupes[opens - 1] = handle;
135 return INVALID_HANDLE_VALUE;
140 * closeserial() is used in place of close by ntpd refclock I/O for ttys
150 h = (HANDLE)_get_osfhandle(fd);
151 if (INVALID_HANDLE_VALUE == h) {
156 d = 0; /* silence potent. uninit. warning */
158 for (u = 0; u < c_hnds; u++) {
159 for (d = 0; d < hnds[u].opens; d++) {
160 if (hnds[u].dupes[d] == h) {
170 if (d < hnds[u].opens)
171 memmove(&hnds[u].dupes[d],
172 &hnds[u].dupes[d + 1],
174 sizeof(hnds[u].dupes[d]));
175 if (0 == hnds[u].opens) {
176 CloseHandle(hnds[u].h);
185 * isserialhandle() -- check if a handle is a COM port handle
195 for (u = 0; u < c_hnds; u++)
196 for (d = 0; d < hnds[u].opens; d++)
197 if (hnds[u].dupes[d] == h)
204 * tty_open - open serial port for refclock special uses
206 * This routine opens a serial port for and returns the
207 * file descriptor if success and -1 if failure.
210 const char *dev, /* device name pointer */
211 int access, /* O_RDWR */
212 int mode /* unused */
219 * open communication port handle
222 Handle = common_serial_open(dev, &windev);
227 if (Handle == INVALID_HANDLE_VALUE) {
228 msyslog(LOG_ERR, "tty_open: device %s CreateFile error: %m", windev);
229 errno = EMFILE; /* lie, lacking conversion from GetLastError() */
233 return (int)_open_osfhandle((intptr_t)Handle, _O_TEXT);
238 * refclock_open - open serial port for reference clock
240 * This routine opens a serial port for I/O and sets default options. It
241 * returns the file descriptor or -1 indicating failure.
245 const char * dev, /* device name pointer */
246 u_int speed, /* serial port speed (code) */
247 u_int flags /* line discipline flags */
252 COMMTIMEOUTS timeouts;
259 * open communication port handle
262 h = common_serial_open(dev, &windev);
263 windev = (windev) ? windev : dev;
265 if (INVALID_HANDLE_VALUE == h) {
267 msyslog(LOG_ERR, "CreateFile(%s) error: %m",
273 /* Change the input/output buffers to be large. */
274 if (!SetupComm(h, 1024, 1024)) {
276 msyslog(LOG_ERR, "SetupComm(%s) error: %m",
282 dcb.DCBlength = sizeof(dcb);
284 if (!GetCommState(h, &dcb)) {
287 "GetCommState(%s) error: %m",
316 dcb.BaudRate = 19200;
320 dcb.BaudRate = 38400;
324 dcb.BaudRate = 57600;
328 dcb.BaudRate = 115200;
332 msyslog(LOG_ERR, "%s unsupported bps code %u", windev,
334 SetLastError(ERROR_INVALID_PARAMETER);
340 dcb.fOutxCtsFlow = 0;
341 dcb.fOutxDsrFlow = 0;
342 dcb.fDtrControl = DTR_CONTROL_ENABLE;
343 dcb.fDsrSensitivity = 0;
344 dcb.fTXContinueOnXoff = TRUE;
349 dcb.fRtsControl = RTS_CONTROL_DISABLE;
350 dcb.fAbortOnError = 0;
352 dcb.StopBits = ONESTOPBIT;
353 dcb.Parity = NOPARITY;
356 if (LDISC_RAW & flags)
361 if (!SetCommState(h, &dcb)) {
363 msyslog(LOG_ERR, "SetCommState(%s) error: %m",
369 /* watch out for CR (dcb.EvtChar) as well as the CD line */
371 if (LDISC_RAW & flags)
372 dwEvtMask |= EV_RXCHAR;
374 dwEvtMask |= EV_RXFLAG;
375 if (!SetCommMask(h, dwEvtMask)) {
377 msyslog(LOG_ERR, "SetCommMask(%s) error: %m",
383 /* configure the handle to never block */
384 timeouts.ReadIntervalTimeout = MAXDWORD;
385 timeouts.ReadTotalTimeoutMultiplier = 0;
386 timeouts.ReadTotalTimeoutConstant = 0;
387 timeouts.WriteTotalTimeoutMultiplier = 0;
388 timeouts.WriteTotalTimeoutConstant = 0;
390 if (!SetCommTimeouts(h, &timeouts)) {
393 "Device %s SetCommTimeouts error: %m",
399 translate = (LDISC_RAW & flags)
402 fd = _open_osfhandle((intptr_t)h, translate);
403 /* refclock_open() long returned 0 on failure, avoid it. */
421 if (!GetCommModemStatus(h, &dw)) {
426 *pi = ((dw & MS_CTS_ON) ? TIOCM_CTS : 0)
427 | ((dw & MS_DSR_ON) ? TIOCM_DSR : 0)
428 | ((dw & MS_RLSD_ON) ? TIOCM_CAR : 0)
429 | ((dw & MS_RING_ON) ? TIOCM_RI : 0);
444 failed = !EscapeCommFunction(
452 failed = !EscapeCommFunction(
479 int *pi = (int *) pv;
481 h = (HANDLE)_get_osfhandle(fd);
483 if (INVALID_HANDLE_VALUE == h) {
484 /* errno already set */
491 result = ioctl_tiocmget(h, pi);
495 result = ioctl_tiocmset(h, pi);
499 result = ioctl_tiocmget(h, &modctl);
503 result = ioctl_tiocmset(h, &modctl);
507 result = ioctl_tiocmget(h, &modctl);
511 result = ioctl_tiocmset(h, &modctl);
526 int optional_actions,
527 const struct termios * tios
533 UNUSED_ARG(optional_actions);
535 h = (HANDLE)_get_osfhandle(fd);
537 if (INVALID_HANDLE_VALUE == h) {
538 /* errno already set */
542 dcb.DCBlength = sizeof(dcb);
543 if (!GetCommState(h, &dcb)) {
548 switch (max(tios->c_ospeed, tios->c_ispeed)) {
571 dcb.BaudRate = 19200;
575 dcb.BaudRate = 38400;
579 dcb.BaudRate = 57600;
583 dcb.BaudRate = 115200;
587 msyslog(LOG_ERR, "unsupported serial baud rate");
592 switch (tios->c_cflag & CSIZE) {
611 msyslog(LOG_ERR, "unsupported serial word size");
616 if (PARENB & tios->c_cflag) {
618 dcb.Parity = (tios->c_cflag & PARODD)
623 dcb.Parity = NOPARITY;
626 dcb.StopBits = (CSTOPB & tios->c_cflag)
630 if (!SetCommState(h, &dcb)) {
648 h = (HANDLE)_get_osfhandle(fd);
650 if (INVALID_HANDLE_VALUE == h) {
651 /* errno already set */
655 dcb.DCBlength = sizeof(dcb);
657 if (!GetCommState(h, &dcb)) {
662 /* Set c_ispeed & c_ospeed */
664 switch (dcb.BaudRate) {
667 tios->c_ispeed = tios->c_ospeed = B300;
671 tios->c_ispeed = tios->c_ospeed = B1200;
675 tios->c_ispeed = tios->c_ospeed = B2400;
679 tios->c_ispeed = tios->c_ospeed = B4800;
683 tios->c_ispeed = tios->c_ospeed = B9600;
687 tios->c_ispeed = tios->c_ospeed = B19200;
691 tios->c_ispeed = tios->c_ospeed = B38400;
695 tios->c_ispeed = tios->c_ospeed = B57600;
699 tios->c_ispeed = tios->c_ospeed = B115200;
703 tios->c_ispeed = tios->c_ospeed = B9600;
707 switch (dcb.ByteSize) {
726 tios->c_cflag |= PARENB;
728 if (ODDPARITY == dcb.Parity)
729 tios->c_cflag |= PARODD;
732 if (TWOSTOPBITS == dcb.StopBits)
733 tios->c_cflag |= CSTOPB;
755 h = (HANDLE)_get_osfhandle(fd);
757 if (INVALID_HANDLE_VALUE == h) {
758 /* errno already set */
765 flags = PURGE_RXCLEAR;
769 flags = PURGE_TXABORT;
773 flags = PURGE_RXCLEAR | PURGE_TXABORT;
781 success = PurgeComm(h, flags);