enum {
SOCK_RECV,
SOCK_SEND,
- CLOCK_READ,
- CLOCK_WRITE
+ SERIAL_WAIT,
+ SERIAL_READ,
+ SERIAL_WRITE
};
#define recv_buf buff_space.rbuf
#define trans_buf buff_space.tbuf
+#if !defined( _W64 )
+ /*
+ * if ULONG_PTR needs to be defined then the build environment
+ * is pure 32 bit Windows. Since ULONG_PTR and DWORD have
+ * the same size in 32 bit Windows we can safely define
+ * a replacement.
+ */
+ typedef DWORD ULONG_PTR;
+#endif
+
/*
* local function definitions
*/
-static int QueueIORead( struct refclockio *, recvbuf_t *buff, IoCompletionInfo *lpo);
+static int QueueSerialWait(struct refclockio *, recvbuf_t *buff, IoCompletionInfo *lpo, BOOL clear_timestamp);
-static int OnSocketRecv(DWORD, IoCompletionInfo *, DWORD, int);
-static int OnIoReadComplete(DWORD, IoCompletionInfo *, DWORD, int);
-static int OnWriteComplete(DWORD, IoCompletionInfo *, DWORD, int);
+static int OnSocketRecv(ULONG_PTR, IoCompletionInfo *, DWORD, int);
+static int OnSerialWaitComplete(ULONG_PTR, IoCompletionInfo *, DWORD, int);
+static int OnSerialReadComplete(ULONG_PTR, IoCompletionInfo *, DWORD, int);
+static int OnWriteComplete(ULONG_PTR, IoCompletionInfo *, DWORD, int);
+/* #define USE_HEAP */
+
+#ifdef USE_HEAP
static HANDLE hHeapHandle = NULL;
+#endif
static HANDLE hIoCompletionPort = NULL;
#define MAXHANDLES 3
HANDLE WaitHandles[MAXHANDLES] = { NULL, NULL, NULL };
-#define USE_HEAP
-
IoCompletionInfo *
GetHeapAlloc(char *fromfunc)
{
HEAP_ZERO_MEMORY,
sizeof(IoCompletionInfo));
#else
- lpo = (IoCompletionInfo *) calloc(1, sizeof(IoCompletionInfo));
-#endif
-#ifdef DEBUG
- if (debug > 3) {
- printf("Allocation %d memory for %s, ptr %x\n", sizeof(IoCompletionInfo), fromfunc, lpo);
- }
+ lpo = (IoCompletionInfo *) calloc(1, sizeof(*lpo));
#endif
+ DPRINTF(3, ("Allocation %d memory for %s, ptr %x\n", sizeof(IoCompletionInfo), fromfunc, lpo));
+
return (lpo);
}
void
FreeHeap(IoCompletionInfo *lpo, char *fromfunc)
{
-#ifdef DEBUG
- if (debug > 3)
- {
- printf("Freeing memory for %s, ptr %x\n", fromfunc, lpo);
- }
-#endif
+ DPRINTF(3, ("Freeing memory for %s, ptr %x\n", fromfunc, lpo));
#ifdef USE_HEAP
HeapFree(hHeapHandle, 0, lpo);
transmitbuf_t *
get_trans_buf()
{
- transmitbuf_t *tb = calloc(sizeof(transmitbuf_t), 1);
+ transmitbuf_t *tb = (transmitbuf_t *) emalloc(sizeof(transmitbuf_t));
tb->wsabuf.len = 0;
tb->wsabuf.buf = (char *) &tb->pkt;
return (tb);
signal_io_completion_port_exit()
{
if (!PostQueuedCompletionStatus(hIoCompletionPort, 0, 0, 0)) {
- msyslog(LOG_ERR, "Can't request service thread to exit: %m");
- exit(1);
+ DPRINTF(1, ("Can't request IO thread to exit: %d\n", GetLastError()));
}
}
-static void
+static unsigned WINAPI
iocompletionthread(void *NotUsed)
{
BOOL bSuccess = FALSE;
int errstatus = 0;
DWORD BytesTransferred = 0;
- DWORD Key = 0;
+ ULONG_PTR Key = 0;
IoCompletionInfo * lpo = NULL;
+ u_long time_next_ifscan_after_error = 0;
+
+ /* UNUSED_ARG(NotUsed); */
/* Set the thread priority high enough so I/O will
* preempt normal recv packet processing, but not
}
while (TRUE) {
- bSuccess = GetQueuedCompletionStatus(hIoCompletionPort,
- &BytesTransferred,
- &Key,
- & (LPOVERLAPPED) lpo,
- INFINITE);
+ bSuccess = GetQueuedCompletionStatus(
+ hIoCompletionPort,
+ &BytesTransferred,
+ &Key,
+ (LPOVERLAPPED *) &lpo,
+ INFINITE);
if (lpo == NULL)
{
-#ifdef DEBUG
- if (debug > 2) {
- printf("Overlapped IO Thread Exits: \n");
- }
-#endif
+ DPRINTF(2, ("Overlapped IO Thread Exiting\n"));
break; /* fail */
}
/*
* Deal with errors
*/
- errstatus = 0;
- if (!bSuccess)
+ if (bSuccess)
+ errstatus = 0;
+ else
{
errstatus = GetLastError();
- if (BytesTransferred == 0 && errstatus == WSA_OPERATION_ABORTED)
+ if (BytesTransferred == 0)
{
-#ifdef DEBUG
- if (debug > 2) {
- printf("Transfer Operation aborted\n");
+ if (WSA_OPERATION_ABORTED == errstatus) {
+ DPRINTF(4, ("Transfer Operation aborted\n"));
+ } else if (ERROR_UNEXP_NET_ERR == errstatus) {
+ /*
+ * We get this error when trying to send an the network
+ * interface is gone or has lost link. Rescan interfaces
+ * to catch on sooner, but no more than once per minute.
+ * Once ntp is able to detect changes without polling
+ * this should be unneccessary
+ */
+ if (time_next_ifscan_after_error < current_time) {
+ time_next_ifscan_after_error = current_time + 60;
+ timer_interfacetimeout(current_time);
+ }
+ DPRINTF(4, ("sendto unexpected network error, interface may be down\n"));
}
-#endif
}
else
{
- msyslog(LOG_ERR, "Error transferring packet after %d bytes: %m", BytesTransferred);
+ msyslog(LOG_ERR, "sendto error after %d bytes: %m", BytesTransferred);
}
}
*/
switch(lpo->request_type)
{
- case CLOCK_READ:
- OnIoReadComplete(Key, lpo, BytesTransferred, errstatus);
+ case SERIAL_WAIT:
+ OnSerialWaitComplete(Key, lpo, BytesTransferred, errstatus);
+ break;
+ case SERIAL_READ:
+ OnSerialReadComplete(Key, lpo, BytesTransferred, errstatus);
break;
case SOCK_RECV:
OnSocketRecv(Key, lpo, BytesTransferred, errstatus);
break;
case SOCK_SEND:
- case CLOCK_WRITE:
+ case SERIAL_WRITE:
OnWriteComplete(Key, lpo, BytesTransferred, errstatus);
break;
default:
-#if DEBUG
- if (debug > 2) {
- printf("Unknown request type %d found in completion port\n",
- lpo->request_type);
- }
-#endif
+ DPRINTF(1, ("Unknown request type %d found in completion port\n",
+ lpo->request_type));
break;
}
}
+
+ return 0;
}
/* Create/initialise the I/O creation port
void
)
{
+ unsigned tid;
+ HANDLE thread;
+#ifdef USE_HEAP
/*
* Create a handle to the Heap
*/
msyslog(LOG_ERR, "Can't initialize Heap: %m");
exit(1);
}
-
+#endif
/* Create the event used to signal an IO event
*/
- WaitableIoEventHandle = CreateEvent(NULL, FALSE, FALSE, "WaitableIoEventHandle");
- if (WaitableIoEventHandle == NULL) {
- msyslog(LOG_ERR,
- "Can't create I/O event handle: %m - another process may be running - EXITING");
- exit(1);
- }
+ WaitableIoEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
+
/* Create the event used to signal an exit event
*/
- WaitableExitEventHandle = CreateEvent(NULL, FALSE, FALSE, "WaitableExitEventHandle");
- if (WaitableExitEventHandle == NULL) {
- msyslog(LOG_ERR,
- "Can't create exit event handle: %m - another process may be running - EXITING");
- exit(1);
- }
+ WaitableExitEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
/* Create the IO completion port
*/
hIoCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
- if (hIoCompletionPort == NULL) {
- msyslog(LOG_ERR, "Can't create I/O completion port: %m");
+
+#ifdef DEBUG
+ if (NULL == WaitableExitEventHandle ||
+ NULL == WaitableIoEventHandle ||
+ NULL == hIoCompletionPort) {
+ DPRINTF(1, ("init_io_completion_port: Can't create event or port\n"));
exit(1);
}
-
+#endif
+
/*
* Initialize the Wait Handles
*/
/* Have one thread servicing I/O - there were 4, but this would
* somehow cause NTP to stop replying to ntpq requests; TODO
*/
- _beginthread(iocompletionthread, 0, NULL);
+ thread = (HANDLE)_beginthreadex(
+ NULL,
+ 0,
+ iocompletionthread,
+ NULL,
+ CREATE_SUSPENDED,
+ &tid);
+ ResumeThread(thread);
+ CloseHandle(thread);
}
}
-static int QueueIORead( struct refclockio *rio, recvbuf_t *buff, IoCompletionInfo *lpo) {
-
- lpo->request_type = CLOCK_READ;
+static int QueueSerialWait(struct refclockio *rio, recvbuf_t *buff, IoCompletionInfo *lpo, BOOL clear_timestamp)
+{
+ lpo->request_type = SERIAL_WAIT;
lpo->recv_buf = buff;
- buff->fd = rio->fd;
- if (!ReadFile((HANDLE) buff->fd, buff->wsabuff.buf, buff->wsabuff.len, NULL, (LPOVERLAPPED) lpo)) {
- DWORD Result = GetLastError();
- switch (Result) {
- case NO_ERROR :
- case ERROR_HANDLE_EOF :
- case ERROR_IO_PENDING :
- break ;
+ if (clear_timestamp)
+ memset(&buff->recv_time, 0, sizeof(buff->recv_time));
- /*
- * Something bad happened
- */
- default:
- msyslog(LOG_ERR, "Can't read from Refclock: %m");
+ buff->fd = _get_osfhandle(rio->fd);
+ if (!WaitCommEvent((HANDLE) buff->fd, (DWORD *)&buff->recv_buffer, (LPOVERLAPPED) lpo)) {
+ if (ERROR_IO_PENDING != GetLastError()) {
+ msyslog(LOG_ERR, "Can't wait on Refclock: %m");
freerecvbuf(buff);
return 0;
}
}
-
-/* Return 1 on Successful Read */
static int
-OnIoReadComplete(DWORD i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
+OnSerialWaitComplete(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
{
recvbuf_t *buff;
- recvbuf_t *newbuff;
struct refclockio * rio = (struct refclockio *) i;
+ struct peer *pp;
l_fp arrival_time;
+ DWORD comm_mask;
+ DWORD modem_status;
+ static const l_fp zero_time = { 0 };
+ DWORD dwBytesReturned;
+ BOOL rc;
get_systime(&arrival_time);
/*
* Get the recvbuf pointer from the overlapped buffer.
*/
- buff = (recvbuf_t *) lpo->recv_buf;
- /*
- * Get a new recv buffer for the next packet
- */
- newbuff = get_free_recv_buffer_alloc();
- if (newbuff == NULL) {
- /*
- * recv buffers not available so we drop the packet
- * and reuse the buffer.
- */
- newbuff = buff;
- }
- else
- {
- /*
- * ignore 0 bytes read due to timeout's and closure on fd
- */
- if (Bytes > 0 && errstatus != WSA_OPERATION_ABORTED) {
- memcpy(&buff->recv_time, &arrival_time, sizeof(arrival_time));
- buff->recv_length = (int) Bytes;
- buff->receiver = rio->clock_recv;
- buff->dstadr = NULL;
- buff->recv_srcclock = rio->srcclock;
- add_full_recv_buffer(buff);
- if( !SetEvent( WaitableIoEventHandle ) ) {
+ buff = lpo->recv_buf;
+ comm_mask = (*(DWORD *)&buff->recv_buffer);
#ifdef DEBUG
- if (debug > 3) {
- printf( "Error %d setting IoEventHandle\n", GetLastError() );
- }
+ if (errstatus || comm_mask & ~(EV_RXFLAG | EV_RLSD)) {
+ msyslog(LOG_ERR, "WaitCommEvent returned unexpected mask %x errstatus %d",
+ comm_mask, errstatus);
+ exit(-1);
+ }
#endif
+ if (comm_mask & EV_RLSD) {
+ modem_status = 0;
+ GetCommModemStatus((HANDLE)buff->fd, &modem_status);
+ if (modem_status & MS_RLSD_ON) {
+ /*
+ * Use the timestamp from this PPS CD not
+ * the later end of line.
+ */
+ buff->recv_time = arrival_time;
+ }
+
+ if (!(comm_mask & EV_RXFLAG)) {
+ /*
+ * if we didn't see an end of line yet
+ * issue another wait for it.
+ */
+ QueueSerialWait(rio, buff, lpo, FALSE);
+ return 1;
}
}
- else
- {
+
+ /*
+ * We've detected the end of line of serial input.
+ * Use this timestamp unless we already have a CD PPS
+ * timestamp in buff->recv_time.
+ */
+ if (memcmp(&buff->recv_time, &zero_time, sizeof buff->recv_time)) {
+ /*
+ * We will first see a user PPS timestamp here on either
+ * the first or second line of text. Log a one-time
+ * message while processing the second line.
+ */
+ if (1 == rio->recvcount) {
+ pp = (struct peer *)rio->srcclock;
+ msyslog(LOG_NOTICE, "Using user-mode PPS timestamp for %s",
+ refnumtoa(&pp->srcadr));
+ }
+ } else {
+ buff->recv_time = arrival_time;
+ }
+
+ /*
+ * Now that we have a complete line waiting, read it.
+ * There is still a race here, but we're likely to win.
+ */
+
+ lpo->request_type = SERIAL_READ;
+
+ rc = ReadFile(
+ (HANDLE)buff->fd,
+ buff->wsabuff.buf,
+ buff->wsabuff.len,
+ &buff->wsabuff.len,
+ (LPOVERLAPPED) lpo);
+
+ if (!rc && ERROR_IO_PENDING != GetLastError()) {
+ msyslog(LOG_ERR, "Can't read from Refclock: %m");
freerecvbuf(buff);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return 1 on Successful Read */
+static int
+OnSerialReadComplete(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
+{
+ recvbuf_t *buff;
+ struct refclockio * rio = (struct refclockio *) i;
+
+ /*
+ * Get the recvbuf pointer from the overlapped buffer.
+ */
+ buff = lpo->recv_buf;
+
+ /*
+ * ignore 0 bytes read due to timeout's and closure on fd
+ */
+ if (!errstatus && Bytes) {
+ buff->recv_length = (int) Bytes;
+ buff->receiver = rio->clock_recv;
+ buff->dstadr = NULL;
+ buff->recv_srcclock = rio->srcclock;
+ packets_received++;
+ /*
+ * Eat the first line of input as it's possibly
+ * partial and if a PPS is present, it may not
+ * have fired since the port was opened.
+ */
+ if (rio->recvcount++) {
+ add_full_recv_buffer(buff);
+ /*
+ * Now signal we have something to process
+ */
+ SetEvent(WaitableIoEventHandle);
+ buff = get_free_recv_buffer_alloc();
}
}
- QueueIORead( rio, newbuff, lpo );
+ QueueSerialWait(rio, buff, lpo, TRUE);
+
return 1;
}
IoCompletionInfo *lpo;
recvbuf_t *buff;
- if (NULL == CreateIoCompletionPort((HANDLE) rio->fd, hIoCompletionPort, (DWORD) rio, 0)) {
+ if (NULL == CreateIoCompletionPort((HANDLE)_get_osfhandle(rio->fd), hIoCompletionPort, (ULONG_PTR) rio, 0)) {
msyslog(LOG_ERR, "Can't add COM port to i/o completion port: %m");
return 1;
}
}
buff = get_free_recv_buffer_alloc();
-
- if (buff == NULL)
- {
- msyslog(LOG_ERR, "Can't allocate memory for clock socket: %m");
- FreeHeap(lpo, "io_completion_port_add_clock_io");
- return 1;
- }
- QueueIORead( rio, buff, lpo );
+ QueueSerialWait(rio, buff, lpo, TRUE);
return 0;
}
-/* Queue a receiver on a socket. Returns 0 if no buffer can be queued */
-
+/*
+ * Queue a receiver on a socket. Returns 0 if no buffer can be queued
+ *
+ * Note: As per the winsock documentation, we use WSARecvFrom. Using
+ * ReadFile() is less efficient.
+ */
static unsigned long QueueSocketRecv(SOCKET s, recvbuf_t *buff, IoCompletionInfo *lpo) {
int AddrLen;
DWORD Result = WSAGetLastError();
switch (Result) {
case NO_ERROR :
- case WSA_IO_INCOMPLETE :
- case WSA_WAIT_IO_COMPLETION :
case WSA_IO_PENDING :
break ;
/* Returns 0 if any Error */
static int
-OnSocketRecv(DWORD i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
+OnSocketRecv(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
{
struct recvbuf *buff = NULL;
recvbuf_t *newbuff;
l_fp arrival_time;
struct interface * inter = (struct interface *) i;
- get_systime(&arrival_time);
+ get_systime(&arrival_time);
/* Convert the overlapped pointer back to a recvbuf pointer.
*/
/*
- * Get a new recv buffer for the next packet
+ * Get a new recv buffer for the replacement socket receive
*/
newbuff = get_free_recv_buffer_alloc();
- if (newbuff == NULL) {
- /*
- * recv buffers not available so we drop the packet
- * and reuse the buffer.
- */
- newbuff = buff;
- }
- else
- {
- ignore_this = inter->ignore_packets;
+ QueueSocketRecv(inter->fd, newbuff, lpo);
- /*
- * If we keep it add some info to the structure
- */
- if (Bytes > 0 && ignore_this == ISC_FALSE) {
- memcpy(&buff->recv_time, &arrival_time, sizeof(arrival_time));
- buff->recv_length = (int) Bytes;
- buff->receiver = receive;
- buff->dstadr = inter;
-#ifdef DEBUG
- if (debug > 1)
- printf("Received %d bytes of fd %d in buffer %x from %s\n", Bytes, buff->fd, buff, stoa(&buff->recv_srcadr));
-#endif
- add_full_recv_buffer(buff);
- /*
- * Now signal we have something to process
- */
- if( !SetEvent( WaitableIoEventHandle ) ) {
+ ignore_this = inter->ignore_packets;
+
+ /*
+ * If we keep it add some info to the structure
+ */
+ if (Bytes > 0 && ignore_this == ISC_FALSE) {
+ memcpy(&buff->recv_time, &arrival_time, sizeof buff->recv_time);
+ buff->recv_length = (int) Bytes;
+ buff->receiver = receive;
+ buff->dstadr = inter;
#ifdef DEBUG
- if (debug > 1) {
- printf( "Error %d setting IoEventHandle\n", GetLastError() );
- }
+ if (debug > 1)
+ printf("Received %d bytes of fd %d in buffer %x from %s\n", Bytes, buff->fd, buff, stoa(&buff->recv_srcadr));
#endif
- }
- }
- else {
- freerecvbuf(buff);
- }
+ packets_received++;
+ inter->received++;
+ add_full_recv_buffer(buff);
+ /*
+ * Now signal we have something to process
+ */
+ SetEvent(WaitableIoEventHandle);
}
- if (newbuff != NULL)
- QueueSocketRecv(inter->fd, newbuff, lpo);
+ else
+ freerecvbuf(buff);
+
return 1;
}
-/* Add a socket handle to the I/O completion port, and send an I/O
- * read request to the kernel.
- *
- * Note: As per the winsock documentation, we use WSARecvFrom. Using
- * ReadFile() is less efficient.
+/* Add a socket handle to the I/O completion port, and send
+ * NTP_RECVS_PER_SOCKET recv requests to the kernel.
*/
extern int
io_completion_port_add_socket(SOCKET fd, struct interface *inter)
{
IoCompletionInfo *lpo;
recvbuf_t *buff;
+ int n;
if (fd != INVALID_SOCKET) {
if (NULL == CreateIoCompletionPort((HANDLE) fd, hIoCompletionPort,
}
}
- lpo = (IoCompletionInfo *) GetHeapAlloc("io_completion_port_add_socket");
- if (lpo == NULL)
- {
- msyslog(LOG_ERR, "Can't allocate heap for completion port: %m");
- return 1;
- }
+#define WINDOWS_RECVS_PER_SOCKET 4
- buff = get_free_recv_buffer_alloc();
+ for (n = 0; n < WINDOWS_RECVS_PER_SOCKET; n++) {
- if (buff == NULL)
- {
- msyslog(LOG_ERR, "Can't allocate memory for network socket: %m");
- FreeHeap(lpo, "io_completion_port_add_socket");
- return 1;
- }
+ buff = get_free_recv_buffer_alloc();
+ lpo = (IoCompletionInfo *) GetHeapAlloc("io_completion_port_add_socket");
+ if (lpo == NULL)
+ {
+ msyslog(LOG_ERR, "Can't allocate heap for completion port: %m");
+ return 1;
+ }
+
+ QueueSocketRecv(fd, buff, lpo);
- QueueSocketRecv(fd, buff, lpo);
+ }
return 0;
}
static int
-OnWriteComplete(DWORD Key, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
+OnWriteComplete(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
{
transmitbuf_t *buff;
- (void) Bytes;
- (void) Key;
+ struct interface *inter;
+
+ /* UNUSED_ARG(Bytes); */
buff = lpo->trans_buf;
free_trans_buf(buff);
+ if (SOCK_SEND == lpo->request_type) {
+ switch (errstatus) {
+ case WSA_OPERATION_ABORTED:
+ case NO_ERROR:
+ break;
+
+ default:
+ inter = (struct interface *)i;
+ packets_notsent++;
+ inter->notsent++;
+ break;
+ }
+ }
+
if (errstatus == WSA_OPERATION_ABORTED)
FreeHeap(lpo, "OnWriteComplete: Socket Closed");
else
}
-DWORD
+/*
+ * Return value is really GetLastError-style error code
+ * which is a DWORD but using int, which is large enough,
+ * decreases #ifdef forest in ntp_io.c harmlessly.
+ */
+int
io_completion_port_sendto(
struct interface *inter,
struct pkt *pkt,
switch (errval) {
case NO_ERROR :
- case WSA_IO_INCOMPLETE :
- case WSA_WAIT_IO_COMPLETION :
case WSA_IO_PENDING :
Result = ERROR_SUCCESS;
break ;
/*
- * Async IO Write
+ * async_write, clone of write(), used by some reflock drivers
*/
-DWORD
-io_completion_port_write(
- HANDLE fd,
- char *pkt,
- int len)
+int
+async_write(
+ int fd,
+ const void *data,
+ unsigned int count)
{
- DWORD errval;
- transmitbuf_t *buff = NULL;
- DWORD lpNumberOfBytesWritten;
- DWORD Result = ERROR_INSUFFICIENT_BUFFER;
+ transmitbuf_t *buff;
IoCompletionInfo *lpo;
+ DWORD BytesWritten;
- lpo = (IoCompletionInfo *) GetHeapAlloc("io_completion_port_write");
+ if (count > sizeof buff->pkt) {
+#ifdef DEBUG
+ if (debug) {
+ printf("async_write: %d bytes too large, limit is %d\n",
+ count, sizeof buff->pkt);
+ exit(-1);
+ }
+#endif
+ errno = ENOMEM;
+ return -1;
+ }
- if (lpo == NULL)
- return ERROR_OUTOFMEMORY;
+ buff = get_trans_buf();
+ lpo = (IoCompletionInfo *) GetHeapAlloc("async_write");
- if (len <= sizeof(buff->pkt)) {
- buff = get_trans_buf();
- if (buff == NULL) {
+ if (! buff || ! lpo) {
+ if (buff) {
+ free_trans_buf(buff);
+#ifdef DEBUG
+ if (debug)
+ printf("async_write: out of memory, \n");
+#endif
+ } else {
msyslog(LOG_ERR, "No more transmit buffers left - data discarded");
- FreeHeap(lpo, "io_completion_port_write");
}
- lpo->request_type = CLOCK_WRITE;
- lpo->trans_buf = buff;
- memcpy(&buff->pkt, pkt, len);
+ errno = ENOMEM;
+ return -1;
+ }
- Result = WriteFile(fd, buff->pkt, len, &lpNumberOfBytesWritten, (LPOVERLAPPED) lpo);
+ lpo->request_type = SERIAL_WRITE;
+ lpo->trans_buf = buff;
+ memcpy(&buff->pkt, data, count);
- if(Result == SOCKET_ERROR)
- {
- errval = WSAGetLastError();
- switch (errval) {
+ if (! WriteFile((HANDLE)_get_osfhandle(fd), buff->pkt, count, &BytesWritten, (LPOVERLAPPED) lpo) &&
+ ERROR_IO_PENDING != GetLastError()) {
- case NO_ERROR :
- case WSA_IO_INCOMPLETE :
- case WSA_WAIT_IO_COMPLETION :
- case WSA_IO_PENDING :
- Result = ERROR_SUCCESS;
- break ;
-
- default :
- netsyslog(LOG_ERR, "WriteFile - error sending message: %m");
- free_trans_buf(buff);
- FreeHeap(lpo, "io_completion_port_write");
- break;
- }
- }
-#ifdef DEBUG
- if (debug > 2) {
- printf("WriteFile - %d bytes %d\n", len, Result);
- }
-#endif
- if (Result) return len;
+ msyslog(LOG_ERR, "async_write - error %m");
+ free_trans_buf(buff);
+ FreeHeap(lpo, "async_write");
+ errno = EBADF;
+ return -1;
}
- else {
-#ifdef DEBUG
- if (debug) printf("Packet too large: %d Bytes\n", len);
-#endif
- }
- return Result;
+
+ return count;
}
+
/*
* GetReceivedBuffers
* Note that this is in effect the main loop for processing requests
-/* This file implementes system calls that are not compatible with UNIX */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
+/* This file implements system calls that are not compatible with UNIX */
+#include <config.h>
#include <stdio.h>
#include "ntp_machine.h"
#include "ntp_stdlib.h"
#include "ntp_syslog.h"
+#include "ntp_debug.h"
#include "ntp_fp.h"
#include "ntp.h"
#include "ntp_refclock.h"
#include "win32_io.h"
+#define MAX_SERIAL 16 /* COM1-COM16 */
+
int NT_set_process_priority(void)
{
return 1;
}
+
+/*
+ * 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
+ )
+{
+ static HANDLE SerialHandles[MAX_SERIAL+1] = {0};
+ HANDLE RetHandle;
+ int unit;
+
+ if (1 != sscanf(dev, "COM%d:", &unit) || unit > MAX_SERIAL)
+ return INVALID_HANDLE_VALUE;
+
+ if (!SerialHandles[unit])
+ SerialHandles[unit] = CreateFile(
+ dev,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, /* sharing prohibited */
+ NULL, /* default security */
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ if (INVALID_HANDLE_VALUE == SerialHandles[unit]) {
+ SerialHandles[unit] = 0;
+ return INVALID_HANDLE_VALUE;
+ }
+
+ DuplicateHandle(
+ GetCurrentProcess(),
+ SerialHandles[unit],
+ GetCurrentProcess(),
+ &RetHandle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS
+ );
+
+ return RetHandle;
+}
+
+/*
+ * pps_open - open serial port for PPS
+ *
+ * This routine opens a serial port for and returns the
+ * file descriptor if success and -1 if failure.
+ */
+int pps_open(
+ char *dev, /* device name pointer */
+ int access, /* O_RDWR */
+ int mode /* unused */
+ )
+{
+ HANDLE Handle;
+ char windev[3 + 20 + 1 + 1];
+ int unit;
+
+ if (1 != sscanf(dev, "/dev/pps%d", &unit)) {
+ errno = ENOENT;
+ return -1;
+ }
+ /*
+ * there never is a COM0: but this is the ntp convention
+ */
+ _snprintf(windev, sizeof(windev)-1, "COM%d:", unit);
+ windev[sizeof(windev)-1] = 0;
+
+ /*
+ * open communication port handle
+ */
+ Handle = common_serial_open(windev);
+
+ if (Handle == INVALID_HANDLE_VALUE) {
+ msyslog(LOG_ERR, "pps_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 if success and zero if failure.
*/
-int
-refclock_open(
+int refclock_open(
char *dev, /* device name pointer */
u_int speed, /* serial port speed (code) */
u_int flags /* line discipline flags */
{
HANDLE Handle = INVALID_HANDLE_VALUE;
COMMTIMEOUTS timeouts;
- DCB dcb = {0};
-
- //
- // open communication port handle
- //
- Handle = CreateFile(dev,
- GENERIC_READ | GENERIC_WRITE,
- 0, // no sharing
- NULL, // no security
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
- NULL); // not template
+ DCB dcb;
+
+ /*
+ * open communication port handle
+ */
+ Handle = common_serial_open(dev);
+
if (Handle == INVALID_HANDLE_VALUE) {
- msyslog(LOG_ERR, "NT_COM: Device %s: CreateFile error: %m", dev);
+ msyslog(LOG_ERR, "NT_COM: Device %s CreateFile error: %m", dev);
return -1;
}
/* Change the input/output buffers to be large.
*/
if (!SetupComm( Handle, 1024, 1024)) {
- msyslog(LOG_ERR, "NT_COM: Device %s: SetupComm error: %m", dev);
+ msyslog(LOG_ERR, "NT_COM: Device %s SetupComm error: %m", dev);
return -1;
}
dcb.DCBlength = sizeof(dcb);
if (!GetCommState(Handle, &dcb)) {
// Error getting current DCB settings
- msyslog(LOG_ERR, "NT_COM: Device %s: GetCommState error: %m", dev);
+ msyslog(LOG_ERR, "NT_COM: Device %s GetCommState error: %m", dev);
return -1;
}
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, "NT_COM: Device %s: unsupported baud rate", dev);
+ msyslog(LOG_ERR, "NT_COM: Device %s unsupported baud rate", dev);
return -1;
}
- dcb.ByteSize = 8;
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = 0;
- dcb.fTXContinueOnXoff = FALSE;
+ dcb.fTXContinueOnXoff = TRUE;
dcb.fOutX = 0;
dcb.fInX = 0;
dcb.fErrorChar = 0;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
dcb.ErrorChar = 0;
- dcb.EvtChar = 0;
+ dcb.EvtChar = 13; /* CR */
dcb.EofChar = 0;
if (!SetCommState(Handle, &dcb)) {
- msyslog(LOG_ERR, "NT_COM: Device %s: SetCommState error: %m", dev);
+ msyslog(LOG_ERR, "NT_COM: Device %s SetCommState error: %m", dev);
+ return -1;
+ }
+
+ /* watch out for CR (dcb.EvtChar) as well as the CD line */
+ if (!SetCommMask(Handle, EV_RXFLAG | EV_RLSD)) {
+ msyslog(LOG_ERR, "NT_COM: Device %s SetCommMask error: %m", dev);
return -1;
}
- timeouts.ReadIntervalTimeout = 20;
+ /* configure the handle to never block */
+ timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
- timeouts.ReadTotalTimeoutConstant = 5000;
+ timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
- timeouts.WriteTotalTimeoutConstant = 5000;
+ timeouts.WriteTotalTimeoutConstant = 0;
- // Error setting time-outs.
if (!SetCommTimeouts(Handle, &timeouts)) {
- msyslog(LOG_ERR, "NT_COM: Device %s: SetCommTimeouts error: %m", dev);
+ msyslog(LOG_ERR, "NT_COM: Device %s SetCommTimeouts error: %m", dev);
return -1;
}
- return (int) Handle;
+ return (int) _open_osfhandle((int)Handle, _O_TEXT);
}
-
int
ioctl(int fd,
- int cmd,
- int *x) {
+ int cmd,
+ int *x)
+{
+ HANDLE h = (HANDLE) _get_osfhandle(fd);
if ((cmd == TIOCMSET) && (*x & TIOCM_RTS)) {
- if (!EscapeCommFunction((HANDLE) fd, SETRTS))
+ if (!EscapeCommFunction(h, SETRTS))
return -1;
}
else if ((cmd == TIOCMSET) && !(*x & TIOCM_RTS)){
- if (!EscapeCommFunction((HANDLE) fd, CLRRTS))
+ if (!EscapeCommFunction(h, CLRRTS))
+ return -1;
+ }
+ if ((cmd == TIOCMSET) && (*x & TIOCM_DTR)) {
+ if (!EscapeCommFunction(h, SETDTR))
+ return -1;
+ }
+ else if ((cmd == TIOCMSET) && !(*x & TIOCM_DTR)){
+ if (!EscapeCommFunction(h, CLRDTR))
return -1;
}
const struct termios * s)
{
DCB dcb = { 0 };
- HANDLE Handle = (HANDLE) fd;
+ HANDLE Handle = (HANDLE) _get_osfhandle(fd);
dcb.DCBlength = sizeof(dcb);
if (!GetCommState(Handle, &dcb)) {
// Error getting current DCB settings
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, "NT_COM: unsupported baud rate");
return FALSE;
dcb.fParity = FALSE;
dcb.Parity = NOPARITY;
}
-
-
- dcb.fOutxCtsFlow = 0;
- dcb.fOutxDsrFlow = 0;
- dcb.fDtrControl = DTR_CONTROL_DISABLE;
- dcb.fDsrSensitivity = 0;
- dcb.fOutX = 0;
- dcb.fInX = 0;
- dcb.fErrorChar = 0;
- dcb.fNull = 0;
- dcb.fRtsControl = RTS_CONTROL_DISABLE;
- dcb.fAbortOnError = 0;
- dcb.ErrorChar = 0;
- dcb.EvtChar = 0;
- dcb.EofChar = 0;
+ if (s->c_cflag & CSTOPB ) {
+ dcb.StopBits = TWOSTOPBITS;
+ }
+ else {
+ dcb.StopBits = ONESTOPBIT;
+ }
if (!SetCommState(Handle, &dcb)) {
- msyslog(LOG_ERR, "NT_COM: SetCommState error: %m");
+ msyslog(LOG_ERR, "NT_COM: SetCommState error 2: %m");
return FALSE;
}
return TRUE;
tcgetattr(
int fd, struct termios *s)
{
- DCB dcb = { 0 };
- HANDLE Handle = (HANDLE) fd;
+ DCB dcb;
+ HANDLE Handle = (HANDLE) _get_osfhandle(fd);
+
dcb.DCBlength = sizeof(dcb);
if (!GetCommState(Handle, &dcb)) {
- // Error getting current DCB settings
msyslog(LOG_ERR, "NT_COM: GetCommState error: %m");
return FALSE;
}
case 9600 : s->c_ispeed = s->c_ospeed = B9600; break;
case 19200 : s->c_ispeed = s->c_ospeed = B19200; break;
case 38400 : s->c_ispeed = s->c_ospeed = B38400; break;
+ case 57600 : s->c_ispeed = s->c_ospeed = B57600; break;
+ case 115200 : s->c_ispeed = s->c_ospeed = B115200; break;
default : s->c_ispeed = s->c_ospeed = B9600;
}
case ODDPARITY : s->c_cflag |= PARODD; break;
case SPACEPARITY : break;
}
+ switch (dcb.StopBits) {
+ case ONESTOPBIT : break;
+ case ONE5STOPBITS : break;
+ case TWOSTOPBITS : s->c_cflag |= CSTOPB; break;
+ }
s->c_iflag = 0;
s->c_lflag = 0;
extern int tcflush(int fd, int mode) {
-
-
-
-
-return 0;
-
+ int Result = 0;
+ HANDLE h = (HANDLE) _get_osfhandle(fd);
+ switch ( mode ) {
+ case TCIFLUSH:
+ Result = PurgeComm(h, PURGE_RXCLEAR);
+ break;
+ case TCOFLUSH:
+ Result = PurgeComm(h, PURGE_TXABORT);
+ break;
+ case TCIOFLUSH:
+ Result = PurgeComm(h, PURGE_RXCLEAR | PURGE_TXABORT);
+ break;
+ }
+ if ( Result == 0 ) return -1; /* failed */
+ else return 0; /* successful */
}
+
extern int cfsetispeed(struct termios *tio, int speed) {
-
+
return 0;
};
extern int cfsetospeed(struct termios *tio, int speed) {
-
+
return 0;
};