1 /* This file implements system calls that are not compatible with UNIX */
2 /* Moved to libntp/termios.c */
3
4 #include <config.h>
5 #include <io.h>
6 #include <stdio.h>
7
8 #include "ntp.h"
9 #include "ntp_tty.h"
10 #include "lib_strbuf.h"
11 #include "ntp_assert.h"
12 #include "win32_io.h"
13
14 #include "ntp_iocplmem.h"
15 #include "ntp_iocpltypes.h"
16
17 /* -------------------------------------------------------------------
18 * COM port management
19 *
20 * com port handling needs some special functionality, especially for
21 * PPS support. There are things that are shared by the Windows Kernel
22 * on device level, not handle level. These include IOCPL membership,
23 * event wait slot, ... It's also no so simple to open a device a
24 * second time, and so we must manage the handles on open com ports
25 * in userland. Well, partially.
26 */
27 #define MAX_SERIAL 255 /* COM1: - COM255: */
28 #define MAX_COMDUP 8 /* max. allowed number of dupes per device */
29
30 typedef struct comhandles_tag {
31 uint16_t unit; /* COMPORT number */
32 uint16_t nhnd; /* number of open handles */
33 char * comName;/* windows device name */
34 DevCtx_t * devCtx; /* shared device context */
35 HANDLE htab[MAX_COMDUP]; /* OS handles */
36 } comhandles;
37
38 comhandles ** tab_comh; /* device data table */
39 size_t num_comh; /* current used array size */
40 size_t max_comh; /* current allocated array size */
41
42 /* lookup a COM unit by a handle
43 * Scans all used units for a matching handle. Returns the slot
44 * or NULL on failure.
45 *
46 * If 'phidx' is given, the index in the slots handle table that
47 * holds the handle is also returned.
48 *
49 * This a simple 2d table scan. But since we don't expect to have
50 * hundreds of com ports open, this should be no problem.
51 */
52 static comhandles*
lookup_com_handle(HANDLE h,size_t * phidx)53 lookup_com_handle(
54 HANDLE h,
55 size_t * phidx
56 )
57 {
58 size_t tidx, hidx;
59 comhandles * slot;
60 for (tidx = 0; tidx < num_comh; ++tidx) {
61 slot = tab_comh[tidx];
62 for (hidx = 0; hidx < slot->nhnd; ++hidx) {
63 if (slot->htab[hidx] == h) {
64 if (phidx != NULL)
65 *phidx = hidx;
66 return slot;
67 }
68 }
69 }
70 return NULL;
71 }
72
73 /* lookup the list of COM units by unit number. This will always return
74 * a valid location -- eventually the table gets expanded, and a new
75 * entry is returned. In that case, the structure is set up with all
76 * entries valid and *no* file handles yet.
77 */
78 static comhandles*
insert_com_unit(uint16_t unit)79 insert_com_unit(
80 uint16_t unit
81 )
82 {
83 size_t tidx;
84 comhandles * slot;
85
86 /* search for matching entry and return if found */
87 for (tidx = 0; tidx < num_comh; ++tidx)
88 if (tab_comh[tidx]->unit == unit)
89 return tab_comh[tidx];
90
91 /* search failed. make sure we can add a new slot */
92 if (num_comh >= max_comh) {
93 /* round up to next multiple of 4 */
94 max_comh = (num_comh + 4) & ~(size_t)3;
95 tab_comh = erealloc(tab_comh, max_comh * sizeof(tab_comh[0]));
96 }
97
98 /* create a new slot and populate it. */
99 slot = emalloc_zero(sizeof(comhandles));
100 LIB_GETBUF(slot->comName);
101 snprintf(slot->comName, LIB_BUFLENGTH, "\\\\.\\COM%d", unit);
102 slot->comName = estrdup(slot->comName);
103 slot->devCtx = DevCtxAlloc();
104 slot->unit = unit;
105
106 /* plug it into table and return it */
107 tab_comh[num_comh++] = slot;
108 return slot;
109 }
110
111 /* remove a COM slot from the table and destroy it. */
112 static void
remove_com_slot(comhandles * slot)113 remove_com_slot(
114 comhandles * slot /* must be valid! */
115 )
116 {
117 size_t tidx;
118 for (tidx = 0; tidx < num_comh; ++tidx)
119 if (tab_comh[tidx] == slot) {
120 tab_comh[tidx] = tab_comh[--num_comh];
121 break;
122 }
123
124 DevCtxDetach(slot->devCtx);
125 free(slot->comName);
126 free(slot);
127 }
128
129 /* fetch the stored device context block.
130 * This does NOT step the reference counter!
131 */
132 DevCtx_t*
serial_devctx(HANDLE h)133 serial_devctx(
134 HANDLE h
135 )
136 {
137 comhandles * slot = NULL;
138 if (INVALID_HANDLE_VALUE != h && NULL != h)
139 slot = lookup_com_handle(h, NULL);
140 return (NULL != slot) ? slot->devCtx : NULL;
141 }
142
143
144 /*
145 * common_serial_open ensures duplicate opens of the same port
146 * work by duplicating the handle for the 2nd open, allowing
147 * refclock_atom to share a GPS refclock's comm port.
148 */
149 HANDLE
common_serial_open(const char * dev,const char ** pwindev)150 common_serial_open(
151 const char * dev,
152 const char ** pwindev
153 )
154 {
155 HANDLE handle;
156 size_t unit;
157 const char * pch;
158 comhandles * slot;
159
160 /*
161 * This is odd, but we'll take any unix device path
162 * by looking for the initial '/' and strip off everything
163 * before the final digits, then translate that to COM__:
164 * maintaining backward compatibility with NTP practice of
165 * mapping unit 0 to the nonfunctional COM0:
166 *
167 * To ease the job of taking the windows COMx: device names
168 * out of reference clocks, we'll also work with those
169 * equanimously.
170 */
171
172 TRACE(1, ("common_serial_open given %s\n", dev));
173
174 handle = INVALID_HANDLE_VALUE;
175
176 pch = NULL;
177 if ('/' == dev[0]) {
178 pch = dev + strlen(dev);
179 while (isdigit((u_char)pch[-1]))
180 --pch;
181 TRACE(1, ("common_serial_open skipped to ending digits leaving %s\n", pch));
182 } else if (0 == _strnicmp("COM", dev, 3)) {
183 pch = dev + 3;
184 TRACE(1, ("common_serial_open skipped COM leaving %s\n", pch));
185 }
186
187 if (!pch || !isdigit((u_char)pch[0])) {
188 TRACE(1, ("not a digit: %s\n", pch ? pch : "[NULL]"));
189 return INVALID_HANDLE_VALUE;
190 }
191
192 unit = strtoul(pch, (char**)&pch, 10);
193 if (*pch || unit > MAX_SERIAL) {
194 TRACE(1, ("conversion failure: unit=%u at '%s'\n", pch));
195 return INVALID_HANDLE_VALUE;
196 }
197
198 /* Now.... find the COM slot, and either create a new file
199 * (if there is no handle yet) or duplicate one of the existing
200 * handles. Unless the dup table for one com port would overflow,
201 * but that's an indication of a programming error somewhere.
202 */
203 slot = insert_com_unit(unit);
204 if (slot->nhnd == 0) {
205 TRACE(1, ("windows device %s\n", slot->comName));
206 slot->htab[0] = CreateFileA(
207 slot->comName,
208 GENERIC_READ | GENERIC_WRITE,
209 0, /* sharing prohibited */
210 NULL, /* default security */
211 OPEN_EXISTING,
212 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
213 NULL);
214 if (INVALID_HANDLE_VALUE != slot->htab[0]) {
215 slot->nhnd = 1;
216 handle = slot->htab[0];
217 *pwindev = slot->comName;
218 }
219 } else if (slot->nhnd >= MAX_COMDUP) {
220 SetLastError(ERROR_TOO_MANY_OPEN_FILES);
221 } else if (DuplicateHandle(GetCurrentProcess(), slot->htab[0],
222 GetCurrentProcess(), &slot->htab[slot->nhnd],
223 0, FALSE, DUPLICATE_SAME_ACCESS))
224 {
225 handle = slot->htab[slot->nhnd++];
226 *pwindev = slot->comName;
227 }
228
229 return handle;
230 }
231
232 /*
233 * closeserial() is used in place of close by ntpd refclock I/O for ttys
234 */
235 int
closeserial(int fd)236 closeserial(
237 int fd
238 )
239 {
240 HANDLE h;
241 size_t hidx;
242 comhandles * slot;
243
244 h = (HANDLE)_get_osfhandle(fd);
245 if (INVALID_HANDLE_VALUE == h)
246 goto onerror;
247
248 slot = lookup_com_handle(h, &hidx);
249 if (NULL == slot)
250 goto onerror;
251
252 slot->htab[hidx] = slot->htab[--slot->nhnd];
253 if (slot->nhnd == 0)
254 remove_com_slot(slot);
255
256 return close(fd); /* closes system handle, too! */
257
258 onerror:
259 errno = EBADF;
260 return -1;
261 }
262
263 /*
264 * isserialhandle() -- check if a handle is a COM port handle
265 */
266 int/*BOOL*/
isserialhandle(HANDLE h)267 isserialhandle(
268 HANDLE h
269 )
270 {
271 if (INVALID_HANDLE_VALUE != h && NULL != h)
272 return lookup_com_handle(h, NULL) != NULL;
273 return FALSE;
274 }
275
276
277 /*
278 * tty_open - open serial port for refclock special uses
279 *
280 * This routine opens a serial port for and returns the
281 * file descriptor if success and -1 if failure.
282 */
283 int
tty_open(const char * dev,int access,int mode)284 tty_open(
285 const char *dev, /* device name pointer */
286 int access, /* O_RDWR */
287 int mode /* unused */
288 )
289 {
290 HANDLE Handle;
291 const char * windev;
292
293 /*
294 * open communication port handle
295 */
296 windev = dev;
297 Handle = common_serial_open(dev, &windev);
298
299 if (Handle == INVALID_HANDLE_VALUE) {
300 msyslog(LOG_ERR, "tty_open: device %s CreateFile error: %m", windev);
301 errno = EMFILE; /* lie, lacking conversion from GetLastError() */
302 return -1;
303 }
304
305 return _open_osfhandle((intptr_t)Handle, _O_TEXT);
306 }
307
308
309 /*
310 * refclock_open - open serial port for reference clock
311 *
312 * This routine opens a serial port for I/O and sets default options. It
313 * returns the file descriptor or -1 indicating failure.
314 */
315 int
refclock_open(const char * dev,u_int speed,u_int flags)316 refclock_open(
317 const char * dev, /* device name pointer */
318 u_int speed, /* serial port speed (code) */
319 u_int flags /* line discipline flags */
320 )
321 {
322 const char * windev;
323 HANDLE h;
324 COMMTIMEOUTS timeouts;
325 DCB dcb;
326 DWORD dwEvtMask;
327 int fd;
328 int translate;
329
330 /*
331 * open communication port handle
332 */
333 windev = dev;
334 h = common_serial_open(dev, &windev);
335
336 if (INVALID_HANDLE_VALUE == h) {
337 SAVE_ERRNO(
338 msyslog(LOG_ERR, "CreateFile(%s) error: %m",
339 windev);
340 )
341 return -1;
342 }
343
344 /* Change the input/output buffers to be large. */
345 if (!SetupComm(h, 1024, 1024)) {
346 SAVE_ERRNO(
347 msyslog(LOG_ERR, "SetupComm(%s) error: %m",
348 windev);
349 )
350 return -1;
351 }
352
353 dcb.DCBlength = sizeof(dcb);
354
355 if (!GetCommState(h, &dcb)) {
356 SAVE_ERRNO(
357 msyslog(LOG_ERR,
358 "GetCommState(%s) error: %m",
359 windev);
360 )
361 return -1;
362 }
363
364 switch (speed) {
365
366 case B300:
367 dcb.BaudRate = 300;
368 break;
369
370 case B1200:
371 dcb.BaudRate = 1200;
372 break;
373
374 case B2400:
375 dcb.BaudRate = 2400;
376 break;
377
378 case B4800:
379 dcb.BaudRate = 4800;
380 break;
381
382 case B9600:
383 dcb.BaudRate = 9600;
384 break;
385
386 case B19200:
387 dcb.BaudRate = 19200;
388 break;
389
390 case B38400:
391 dcb.BaudRate = 38400;
392 break;
393
394 case B57600:
395 dcb.BaudRate = 57600;
396 break;
397
398 case B115200:
399 dcb.BaudRate = 115200;
400 break;
401
402 default:
403 msyslog(LOG_ERR, "%s unsupported bps code %u", windev,
404 speed);
405 SetLastError(ERROR_INVALID_PARAMETER);
406 return -1;
407 }
408
409 dcb.fBinary = TRUE;
410 dcb.fParity = TRUE;
411 dcb.fOutxCtsFlow = 0;
412 dcb.fOutxDsrFlow = 0;
413 dcb.fDtrControl = DTR_CONTROL_ENABLE;
414 dcb.fDsrSensitivity = 0;
415 dcb.fTXContinueOnXoff = TRUE;
416 dcb.fOutX = 0;
417 dcb.fInX = 0;
418 dcb.fErrorChar = 0;
419 dcb.fNull = 0;
420 dcb.fRtsControl = RTS_CONTROL_DISABLE;
421 dcb.fAbortOnError = 0;
422 dcb.ByteSize = 8;
423 dcb.StopBits = ONESTOPBIT;
424 dcb.Parity = NOPARITY;
425 dcb.ErrorChar = 0;
426 dcb.EofChar = 0;
427 if (LDISC_RAW & flags)
428 dcb.EvtChar = 0;
429 else
430 dcb.EvtChar = '\r';
431
432 if (!SetCommState(h, &dcb)) {
433 SAVE_ERRNO(
434 msyslog(LOG_ERR, "SetCommState(%s) error: %m",
435 windev);
436 )
437 return -1;
438 }
439
440 /* watch out for CR (dcb.EvtChar) as well as the CD line */
441 dwEvtMask = EV_RLSD;
442 if (LDISC_RAW & flags)
443 dwEvtMask |= EV_RXCHAR;
444 else
445 dwEvtMask |= EV_RXFLAG;
446 if (!SetCommMask(h, dwEvtMask)) {
447 SAVE_ERRNO(
448 msyslog(LOG_ERR, "SetCommMask(%s) error: %m",
449 windev);
450 )
451 return -1;
452 }
453
454 /* configure the handle to never block */
455 timeouts.ReadIntervalTimeout = MAXDWORD;
456 timeouts.ReadTotalTimeoutMultiplier = 0;
457 timeouts.ReadTotalTimeoutConstant = 0;
458 timeouts.WriteTotalTimeoutMultiplier = 0;
459 timeouts.WriteTotalTimeoutConstant = 0;
460
461 if (!SetCommTimeouts(h, &timeouts)) {
462 SAVE_ERRNO(
463 msyslog(LOG_ERR,
464 "Device %s SetCommTimeouts error: %m",
465 windev);
466 )
467 return -1;
468 }
469
470 translate = (LDISC_RAW & flags)
471 ? 0
472 : _O_TEXT;
473 fd = _open_osfhandle((intptr_t)h, translate);
474 /* refclock_open() long returned 0 on failure, avoid it. */
475 if (0 == fd) {
476 fd = _dup(0);
477 _close(0);
478 }
479
480 return fd;
481 }
482
483
484 int
ioctl_tiocmget(HANDLE h,int * pi)485 ioctl_tiocmget(
486 HANDLE h,
487 int *pi
488 )
489 {
490 DWORD dw;
491
492 if (!GetCommModemStatus(h, &dw)) {
493 errno = ENOTTY;
494 return -1;
495 }
496
497 *pi = ((dw & MS_CTS_ON) ? TIOCM_CTS : 0)
498 | ((dw & MS_DSR_ON) ? TIOCM_DSR : 0)
499 | ((dw & MS_RLSD_ON) ? TIOCM_CAR : 0)
500 | ((dw & MS_RING_ON) ? TIOCM_RI : 0);
501
502 return 0;
503 }
504
505
506 int
ioctl_tiocmset(HANDLE h,int * pi)507 ioctl_tiocmset(
508 HANDLE h,
509 int *pi
510 )
511 {
512 BOOL failed;
513 int result;
514
515 failed = !EscapeCommFunction(
516 h,
517 (*pi & TIOCM_RTS)
518 ? SETRTS
519 : CLRRTS
520 );
521
522 if (!failed)
523 failed = !EscapeCommFunction(
524 h,
525 (*pi & TIOCM_DTR)
526 ? SETDTR
527 : CLRDTR
528 );
529
530 if (failed) {
531 errno = ENOTTY;
532 result = -1;
533 } else
534 result = 0;
535
536 return result;
537 }
538
539
540 int
ioctl(int fd,int op,void * pv)541 ioctl(
542 int fd,
543 int op,
544 void *pv
545 )
546 {
547 HANDLE h;
548 int result;
549 int modctl;
550 int *pi = (int *) pv;
551
552 h = (HANDLE)_get_osfhandle(fd);
553
554 if (INVALID_HANDLE_VALUE == h) {
555 /* errno already set */
556 return -1;
557 }
558
559 switch (op) {
560
561 case TIOCMGET:
562 result = ioctl_tiocmget(h, pi);
563 break;
564
565 case TIOCMSET:
566 result = ioctl_tiocmset(h, pi);
567 break;
568
569 case TIOCMBIC:
570 result = ioctl_tiocmget(h, &modctl);
571 if (result < 0)
572 return result;
573 modctl &= ~(*pi);
574 result = ioctl_tiocmset(h, &modctl);
575 break;
576
577 case TIOCMBIS:
578 result = ioctl_tiocmget(h, &modctl);
579 if (result < 0)
580 return result;
581 modctl |= *pi;
582 result = ioctl_tiocmset(h, &modctl);
583 break;
584
585 default:
586 errno = EINVAL;
587 result = -1;
588 }
589
590 return result;
591 }
592
593
594 int
tcsetattr(int fd,int optional_actions,const struct termios * tios)595 tcsetattr(
596 int fd,
597 int optional_actions,
598 const struct termios * tios
599 )
600 {
601 DCB dcb;
602 HANDLE h;
603
604 UNUSED_ARG(optional_actions);
605
606 h = (HANDLE)_get_osfhandle(fd);
607
608 if (INVALID_HANDLE_VALUE == h) {
609 /* errno already set */
610 return -1;
611 }
612
613 dcb.DCBlength = sizeof(dcb);
614 if (!GetCommState(h, &dcb)) {
615 errno = ENOTTY;
616 return -1;
617 }
618
619 switch (max(tios->c_ospeed, tios->c_ispeed)) {
620
621 case B300:
622 dcb.BaudRate = 300;
623 break;
624
625 case B1200:
626 dcb.BaudRate = 1200;
627 break;
628
629 case B2400:
630 dcb.BaudRate = 2400;
631 break;
632
633 case B4800:
634 dcb.BaudRate = 4800;
635 break;
636
637 case B9600:
638 dcb.BaudRate = 9600;
639 break;
640
641 case B19200:
642 dcb.BaudRate = 19200;
643 break;
644
645 case B38400:
646 dcb.BaudRate = 38400;
647 break;
648
649 case B57600:
650 dcb.BaudRate = 57600;
651 break;
652
653 case B115200:
654 dcb.BaudRate = 115200;
655 break;
656
657 default:
658 msyslog(LOG_ERR, "unsupported serial baud rate");
659 errno = EINVAL;
660 return -1;
661 }
662
663 switch (tios->c_cflag & CSIZE) {
664
665 case CS5:
666 dcb.ByteSize = 5;
667 break;
668
669 case CS6:
670 dcb.ByteSize = 6;
671 break;
672
673 case CS7:
674 dcb.ByteSize = 7;
675 break;
676
677 case CS8:
678 dcb.ByteSize = 8;
679 break;
680
681 default:
682 msyslog(LOG_ERR, "unsupported serial word size");
683 errno = EINVAL;
684 return FALSE;
685 }
686
687 if (PARENB & tios->c_cflag) {
688 dcb.fParity = TRUE;
689 dcb.Parity = (tios->c_cflag & PARODD)
690 ? ODDPARITY
691 : EVENPARITY;
692 } else {
693 dcb.fParity = FALSE;
694 dcb.Parity = NOPARITY;
695 }
696
697 dcb.StopBits = (CSTOPB & tios->c_cflag)
698 ? TWOSTOPBITS
699 : ONESTOPBIT;
700
701 if (!SetCommState(h, &dcb)) {
702 errno = ENOTTY;
703 return -1;
704 }
705
706 return 0;
707 }
708
709
710 int
tcgetattr(int fd,struct termios * tios)711 tcgetattr(
712 int fd,
713 struct termios *tios
714 )
715 {
716 DCB dcb;
717 HANDLE h;
718
719 h = (HANDLE)_get_osfhandle(fd);
720
721 if (INVALID_HANDLE_VALUE == h) {
722 /* errno already set */
723 return -1;
724 }
725
726 dcb.DCBlength = sizeof(dcb);
727
728 if (!GetCommState(h, &dcb)) {
729 errno = ENOTTY;
730 return -1;
731 }
732
733 /* Set c_ispeed & c_ospeed */
734
735 switch (dcb.BaudRate) {
736
737 case 300:
738 tios->c_ispeed = tios->c_ospeed = B300;
739 break;
740
741 case 1200:
742 tios->c_ispeed = tios->c_ospeed = B1200;
743 break;
744
745 case 2400:
746 tios->c_ispeed = tios->c_ospeed = B2400;
747 break;
748
749 case 4800:
750 tios->c_ispeed = tios->c_ospeed = B4800;
751 break;
752
753 case 9600:
754 tios->c_ispeed = tios->c_ospeed = B9600;
755 break;
756
757 case 19200:
758 tios->c_ispeed = tios->c_ospeed = B19200;
759 break;
760
761 case 38400:
762 tios->c_ispeed = tios->c_ospeed = B38400;
763 break;
764
765 case 57600:
766 tios->c_ispeed = tios->c_ospeed = B57600;
767 break;
768
769 case 115200:
770 tios->c_ispeed = tios->c_ospeed = B115200;
771 break;
772
773 default:
774 tios->c_ispeed = tios->c_ospeed = B9600;
775 }
776
777
778 switch (dcb.ByteSize) {
779 case 5:
780 tios->c_cflag = CS5;
781 break;
782
783 case 6:
784 tios->c_cflag = CS6;
785 break;
786
787 case 7:
788 tios->c_cflag = CS7;
789 break;
790
791 case 8:
792 default:
793 tios->c_cflag = CS8;
794 }
795
796 if (dcb.fParity) {
797 tios->c_cflag |= PARENB;
798
799 if (ODDPARITY == dcb.Parity)
800 tios->c_cflag |= PARODD;
801 }
802
803 if (TWOSTOPBITS == dcb.StopBits)
804 tios->c_cflag |= CSTOPB;
805
806 tios->c_iflag = 0;
807 tios->c_lflag = 0;
808 tios->c_line = 0;
809 tios->c_oflag = 0;
810
811 return 0;
812 }
813
814
815 int
tcflush(int fd,int mode)816 tcflush(
817 int fd,
818 int mode
819 )
820 {
821 HANDLE h;
822 BOOL success;
823 DWORD flags;
824 int result;
825
826 h = (HANDLE)_get_osfhandle(fd);
827
828 if (INVALID_HANDLE_VALUE == h) {
829 /* errno already set */
830 return -1;
831 }
832
833 switch (mode) {
834
835 case TCIFLUSH:
836 flags = PURGE_RXCLEAR;
837 break;
838
839 case TCOFLUSH:
840 flags = PURGE_TXABORT;
841 break;
842
843 case TCIOFLUSH:
844 flags = PURGE_RXCLEAR | PURGE_TXABORT;
845 break;
846
847 default:
848 errno = EINVAL;
849 return -1;
850 }
851
852 success = PurgeComm(h, flags);
853
854 if (success)
855 result = 0;
856 else {
857 errno = ENOTTY;
858 result = -1;
859 }
860
861 return result;
862 }
863
864