/* This file implements system calls that are not compatible with UNIX */
#include <config.h>
#include <io.h>
#include <stdio.h>
#include "ntp_machine.h"
#include "ntp_stdlib.h"
#include "ntp_syslog.h"
#include "ntp_assert.h"
#include "ntp_debug.h"
#include "ntp_fp.h"
#include "ntp.h"
#include "ntp_refclock.h"
#include "win32_io.h"
#define MAX_SERIAL 255 /* COM1: - COM255: */
/*
* 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
)
{
static HANDLE * hnds = NULL; /* handle array */
static int c_hnd = 0; /* current array size */
static char windev[32]; /* return pointer into this */
HANDLE handle;
int unit;
int prev_c_hnd;
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.
*/
DPRINTF(1, ("common_serial_open given %s\n", dev));
pch = NULL;
if ('/' == dev[0]) {
pch = dev + strlen(dev) - 1;
//DPRINTF(1, ("common_serial_open initial %s\n", pch));
if (isdigit(pch[0])) {
while (isdigit(pch[0])) {
pch--;
//DPRINTF(1, ("common_serial_open backed up to %s\n", pch));
}
pch++;
}
DPRINTF(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;
DPRINTF(1, ("common_serial_open skipped COM leaving %s\n", pch));
}
if (!pch || !isdigit(pch[0])) {
DPRINTF(1, ("not a digit: %s\n", pch ? pch : "[NULL]"));
return INVALID_HANDLE_VALUE;
}
if (1 != sscanf(pch, "%d", &unit)
|| unit > MAX_SERIAL
|| unit < 0) {
DPRINTF(1, ("sscanf failure of %s\n", pch));
return INVALID_HANDLE_VALUE;
}
if (c_hnd < unit + 1) {
prev_c_hnd = c_hnd;
c_hnd = unit + 1;
/* round up to closest multiple of 4 to avoid churn */
c_hnd = (c_hnd + 3) & ~3;
hnds = erealloc(hnds, c_hnd * sizeof hnds[0]);
memset(&hnds[prev_c_hnd], 0,
(c_hnd - prev_c_hnd) * sizeof hnds[0]);
}
if (NULL == hnds[unit]) {
snprintf(windev, sizeof(windev), "\\\\.\\COM%d", unit);
DPRINTF(1, ("windows device %s\n", windev));
*pwindev = windev;
hnds[unit] = 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]) {
hnds[unit] = NULL;
handle = INVALID_HANDLE_VALUE;
} else
DuplicateHandle(
GetCurrentProcess(),
hnds[unit],
GetCurrentProcess(),
&handle,
0,
FALSE,
DUPLICATE_SAME_ACCESS
);
return handle;
}
/*
* 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 0 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;
int fd;
/*
* open communication port handle
*/
windev = NULL;
h = common_serial_open(dev, &windev);
windev = (windev) ? windev : dev;
if (INVALID_HANDLE_VALUE == h) {
msyslog(LOG_ERR, "Device %s CreateFile error: %m", windev);
return 0;
}
/* Change the input/output buffers to be large. */
if (!SetupComm(h, 1024, 1024)) {
msyslog(LOG_ERR, "Device %s SetupComm error: %m", windev);
return 0;
}
dcb.DCBlength = sizeof(dcb);
if (!GetCommState(h, &dcb)) {
msyslog(LOG_ERR, "Device %s GetCommState error: %m",
windev);
return 0;
}
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, "Device %s unsupported baud rate "
"code %u", windev, speed);
return 0;
}
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
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.EvtChar = 13; /* CR */
dcb.EofChar = 0;
if (!SetCommState(h, &dcb)) {
msyslog(LOG_ERR, "Device %s SetCommState error: %m", windev);
return 0;
}
/* watch out for CR (dcb.EvtChar) as well as the CD line */
if (!SetCommMask(h, EV_RXFLAG | EV_RLSD)) {
msyslog(LOG_ERR, "Device %s SetCommMask error: %m", windev);
return 0;
}
/* 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)) {
msyslog(LOG_ERR, "Device %s SetCommTimeouts error: %m", windev);
return 0;
}
fd = _open_osfhandle((int)h, _O_TEXT);
if (fd < 0)
return 0;
NTP_INSIST(fd != 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;
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;
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;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>