]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - ports/winnt/libntp/termios.c
Vendor import of ntp-4.2.8p8.
[FreeBSD/FreeBSD.git] / ports / winnt / libntp / termios.c
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 #define MAX_SERIAL 255  /* COM1: - COM255: */
15
16 typedef struct comhandles_tag {
17         HANDLE          h;
18         size_t          opens;
19         HANDLE *        dupes;
20 } comhandles;
21
22 comhandles *    hnds;   /* handle/dupes array */
23 size_t          c_hnds; /* current array size */
24
25 /*
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.
29  */
30 HANDLE
31 common_serial_open(
32         const char *    dev,
33         char **         pwindev
34         )
35 {
36         char *          windev;
37         HANDLE          handle;
38         size_t          unit;
39         size_t          prev_c_hnds;
40         size_t          opens;
41         const char *    pch;
42         u_int           uibuf;
43
44         /*
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:
50          *
51          * To ease the job of taking the windows COMx: device names
52          * out of reference clocks, we'll also work with those
53          * equanimously.
54          */
55
56         TRACE(1, ("common_serial_open given %s\n", dev));
57
58         pch = NULL;
59         if ('/' == dev[0]) {
60                 pch = dev + strlen(dev) - 1;
61
62                 if (isdigit(pch[0])) {
63                         while (isdigit(pch[0])) {
64                                 pch--;
65                         }
66                         pch++;
67                 }
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])) {
72                 pch = dev + 3;
73                 TRACE(1, ("common_serial_open skipped COM leaving %s\n", pch));
74         }
75
76         if (!pch || !isdigit(pch[0])) {
77                 TRACE(1, ("not a digit: %s\n", pch ? pch : "[NULL]"));
78                 return INVALID_HANDLE_VALUE;
79         }
80
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;
85         }
86
87
88         if (c_hnds < unit + 1) {
89                 prev_c_hnds = c_hnds;
90                 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]));
95         }
96
97         if (NULL == hnds[unit].h) {
98                 INSIST(0 == hnds[unit].opens);
99                 LIB_GETBUF(windev);
100                 snprintf(windev, LIB_BUFLENGTH, "\\\\.\\COM%d", unit);
101                 TRACE(1, ("windows device %s\n", windev));
102                 *pwindev = windev;
103                 hnds[unit].h =
104                     CreateFile(
105                         windev,
106                         GENERIC_READ | GENERIC_WRITE,
107                         0, /* sharing prohibited */
108                         NULL, /* default security */
109                         OPEN_EXISTING,
110                         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
111                         NULL);
112                 if (INVALID_HANDLE_VALUE == hnds[unit].h)
113                         hnds[unit].h = NULL;
114         }
115
116         if (NULL != hnds[unit].h) {
117                 /* think handle = dup(hnds[unit].h); */
118                 DuplicateHandle(
119                         GetCurrentProcess(),
120                         hnds[unit].h,
121                         GetCurrentProcess(),
122                         &handle,
123                         0,
124                         FALSE,
125                         DUPLICATE_SAME_ACCESS
126                         );
127                 hnds[unit].opens++;
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;
132                 return handle;
133         }
134
135         return INVALID_HANDLE_VALUE;
136 }
137
138
139 /*
140  * closeserial() is used in place of close by ntpd refclock I/O for ttys
141  */
142 int
143 closeserial(int fd)
144 {
145         HANDLE  h;
146         BOOL    found;
147         size_t  u;
148         size_t  d;
149
150         h = (HANDLE)_get_osfhandle(fd);
151         if (INVALID_HANDLE_VALUE == h) {
152                 errno = EBADF;
153                 return -1;
154         }
155
156         d = 0;          /* silence potent. uninit. warning */
157         found = FALSE;
158         for (u = 0; u < c_hnds; u++) {
159                 for (d = 0; d < hnds[u].opens; d++) {
160                         if (hnds[u].dupes[d] == h) {
161                                 found = TRUE;
162                                 break;
163                         }
164                 }
165                 if (found)
166                         break;
167         }
168         if (found) {
169                 hnds[u].opens--;
170                 if (d < hnds[u].opens)
171                         memmove(&hnds[u].dupes[d],
172                                 &hnds[u].dupes[d + 1],
173                                 hnds[u].opens - d *
174                                     sizeof(hnds[u].dupes[d]));
175                 if (0 == hnds[u].opens) {
176                         CloseHandle(hnds[u].h);
177                         hnds[u].h = NULL;
178                 }
179         }
180
181         return close(fd);
182 }
183
184 /*
185  * isserialhandle() -- check if a handle is a COM port handle
186  */
187 int isserialhandle(
188         HANDLE h
189         )
190 {
191         size_t  u;
192         size_t  d;
193
194
195         for (u = 0; u < c_hnds; u++)
196                 for (d = 0; d < hnds[u].opens; d++)
197                         if (hnds[u].dupes[d] == h)
198                                 return TRUE;
199         return FALSE;
200 }
201
202
203 /*
204  * tty_open - open serial port for refclock special uses
205  *
206  * This routine opens a serial port for and returns the 
207  * file descriptor if success and -1 if failure.
208  */
209 int tty_open(
210         const char *dev,        /* device name pointer */
211         int access,             /* O_RDWR */
212         int mode                /* unused */
213         )
214 {
215         HANDLE  Handle;
216         char *  windev;
217
218         /*
219          * open communication port handle
220          */
221         windev = NULL;
222         Handle = common_serial_open(dev, &windev);
223         windev = (windev)
224                      ? windev
225                      : dev;
226
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() */
230                 return -1;
231         }
232
233         return (int)_open_osfhandle((intptr_t)Handle, _O_TEXT);
234 }
235
236
237 /*
238  * refclock_open - open serial port for reference clock
239  *
240  * This routine opens a serial port for I/O and sets default options. It
241  * returns the file descriptor or -1 indicating failure.
242  */
243 int
244 refclock_open(
245         const char *    dev,    /* device name pointer */
246         u_int           speed,  /* serial port speed (code) */
247         u_int           flags   /* line discipline flags */
248         )
249 {
250         char *          windev;
251         HANDLE          h;
252         COMMTIMEOUTS    timeouts;
253         DCB             dcb;
254         DWORD           dwEvtMask;
255         int             fd;
256         int             translate;
257
258         /*
259          * open communication port handle
260          */
261         windev = NULL;
262         h = common_serial_open(dev, &windev);
263         windev = (windev) ? windev : dev;
264
265         if (INVALID_HANDLE_VALUE == h) {
266                 SAVE_ERRNO(
267                         msyslog(LOG_ERR, "CreateFile(%s) error: %m",
268                                 windev);
269                 )
270                 return -1;
271         }
272
273         /* Change the input/output buffers to be large. */
274         if (!SetupComm(h, 1024, 1024)) {
275                 SAVE_ERRNO(
276                         msyslog(LOG_ERR, "SetupComm(%s) error: %m",
277                                 windev);
278                 )
279                 return -1;
280         }
281
282         dcb.DCBlength = sizeof(dcb);
283
284         if (!GetCommState(h, &dcb)) {
285                 SAVE_ERRNO(
286                         msyslog(LOG_ERR,
287                                 "GetCommState(%s) error: %m",
288                                 windev);
289                 )
290                 return -1;
291         }
292
293         switch (speed) {
294
295         case B300:
296                 dcb.BaudRate = 300;
297                 break;
298
299         case B1200:  
300                 dcb.BaudRate = 1200;
301                 break;
302
303         case B2400:
304                 dcb.BaudRate = 2400;
305                 break;
306
307         case B4800:
308                 dcb.BaudRate = 4800;
309                 break;
310
311         case B9600:
312                 dcb.BaudRate = 9600;
313                 break;
314
315         case B19200:
316                 dcb.BaudRate = 19200;
317                 break;
318
319         case B38400:
320                 dcb.BaudRate = 38400;
321                 break;
322
323         case B57600:
324                 dcb.BaudRate = 57600;
325                 break;
326
327         case B115200:
328                 dcb.BaudRate = 115200;
329                 break;
330
331         default:
332                 msyslog(LOG_ERR, "%s unsupported bps code %u", windev,
333                         speed);
334                 SetLastError(ERROR_INVALID_PARAMETER);
335                 return -1;
336         }
337
338         dcb.fBinary = TRUE;
339         dcb.fParity = TRUE;
340         dcb.fOutxCtsFlow = 0;
341         dcb.fOutxDsrFlow = 0;
342         dcb.fDtrControl = DTR_CONTROL_ENABLE;
343         dcb.fDsrSensitivity = 0;
344         dcb.fTXContinueOnXoff = TRUE;
345         dcb.fOutX = 0; 
346         dcb.fInX = 0;
347         dcb.fErrorChar = 0;
348         dcb.fNull = 0;
349         dcb.fRtsControl = RTS_CONTROL_DISABLE;
350         dcb.fAbortOnError = 0;
351         dcb.ByteSize = 8;
352         dcb.StopBits = ONESTOPBIT;
353         dcb.Parity = NOPARITY;
354         dcb.ErrorChar = 0;
355         dcb.EofChar = 0;
356         if (LDISC_RAW & flags)
357                 dcb.EvtChar = 0;
358         else
359                 dcb.EvtChar = '\r';
360
361         if (!SetCommState(h, &dcb)) {
362                 SAVE_ERRNO(
363                         msyslog(LOG_ERR, "SetCommState(%s) error: %m",
364                                 windev);
365                 )
366                 return -1;
367         }
368
369         /* watch out for CR (dcb.EvtChar) as well as the CD line */
370         dwEvtMask = EV_RLSD;
371         if (LDISC_RAW & flags)
372                 dwEvtMask |= EV_RXCHAR;
373         else
374                 dwEvtMask |= EV_RXFLAG;
375         if (!SetCommMask(h, dwEvtMask)) {
376                 SAVE_ERRNO(
377                         msyslog(LOG_ERR, "SetCommMask(%s) error: %m",
378                                 windev);
379                 )
380                 return -1;
381         }
382
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;
389
390         if (!SetCommTimeouts(h, &timeouts)) {
391                 SAVE_ERRNO(
392                         msyslog(LOG_ERR,
393                                 "Device %s SetCommTimeouts error: %m",
394                                 windev);
395                 )
396                 return -1;
397         }
398
399         translate = (LDISC_RAW & flags)
400                         ? 0
401                         : _O_TEXT;
402         fd = _open_osfhandle((intptr_t)h, translate);
403         /* refclock_open() long returned 0 on failure, avoid it. */
404         if (0 == fd) {
405                 fd = _dup(0);
406                 _close(0);
407         }
408
409         return fd;
410 }
411
412
413 int
414 ioctl_tiocmget(
415         HANDLE h,
416         int *pi
417         )
418 {
419         DWORD   dw;
420
421         if (!GetCommModemStatus(h, &dw)) {
422                 errno = ENOTTY;
423                 return -1;
424         }
425
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);
430
431         return 0;
432 }
433
434
435 int
436 ioctl_tiocmset(
437         HANDLE h,
438         int *pi
439         )
440 {
441         BOOL    failed;
442         int     result;
443         
444         failed = !EscapeCommFunction(
445                         h, 
446                         (*pi & TIOCM_RTS) 
447                             ? SETRTS
448                             : CLRRTS
449                         );
450
451         if (!failed)
452                 failed = !EscapeCommFunction(
453                                 h, 
454                                 (*pi & TIOCM_DTR) 
455                                     ? SETDTR
456                                     : CLRDTR
457                                 );
458
459         if (failed) {
460                 errno = ENOTTY;
461                 result = -1;
462         } else
463                 result = 0;
464
465         return result;
466 }
467
468
469 int 
470 ioctl(
471         int fd,
472         int op,
473         void *pv
474         )
475 {
476         HANDLE  h;
477         int     result;
478         int     modctl;
479         int *pi = (int *) pv;
480         
481         h = (HANDLE)_get_osfhandle(fd);
482
483         if (INVALID_HANDLE_VALUE == h) {
484                 /* errno already set */
485                 return -1;
486         }
487
488         switch (op) {
489
490         case TIOCMGET:
491                 result = ioctl_tiocmget(h, pi);
492                 break;
493
494         case TIOCMSET:
495                 result = ioctl_tiocmset(h, pi);
496                 break;
497
498         case TIOCMBIC:
499                 result = ioctl_tiocmget(h, &modctl);
500                 if (result < 0)
501                         return result;
502                 modctl &= ~(*pi);
503                 result = ioctl_tiocmset(h, &modctl);
504                 break;
505
506         case TIOCMBIS:
507                 result = ioctl_tiocmget(h, &modctl);
508                 if (result < 0)
509                         return result;
510                 modctl |= *pi;
511                 result = ioctl_tiocmset(h, &modctl);
512                 break;
513
514         default:
515                 errno = EINVAL;
516                 result = -1;
517         }
518
519         return result;
520 }
521
522
523 int     
524 tcsetattr(
525         int                     fd, 
526         int                     optional_actions, 
527         const struct termios *  tios
528         )
529 {
530         DCB dcb;
531         HANDLE h;
532
533         UNUSED_ARG(optional_actions);
534
535         h = (HANDLE)_get_osfhandle(fd);
536
537         if (INVALID_HANDLE_VALUE == h) {
538                 /* errno already set */
539                 return -1;
540         }
541
542         dcb.DCBlength = sizeof(dcb);
543         if (!GetCommState(h, &dcb)) {
544                 errno = ENOTTY;
545                 return -1;
546         }
547
548         switch (max(tios->c_ospeed, tios->c_ispeed)) {
549
550         case B300:
551                 dcb.BaudRate = 300;
552                 break;
553
554         case B1200:
555                 dcb.BaudRate = 1200;
556                 break;
557
558         case B2400:
559                 dcb.BaudRate = 2400;
560                 break;
561
562         case B4800:
563                 dcb.BaudRate = 4800;
564                 break;
565
566         case B9600:
567                 dcb.BaudRate = 9600;
568                 break;
569
570         case B19200:
571                 dcb.BaudRate = 19200;
572                 break;
573
574         case B38400:
575                 dcb.BaudRate = 38400;
576                 break;
577
578         case B57600:
579                 dcb.BaudRate = 57600;
580                 break;
581
582         case B115200:
583                 dcb.BaudRate = 115200;
584                 break;
585
586         default:
587                 msyslog(LOG_ERR, "unsupported serial baud rate");
588                 errno = EINVAL;
589                 return -1;
590         }
591
592         switch (tios->c_cflag & CSIZE) {
593
594         case CS5:
595                 dcb.ByteSize = 5;
596                 break;
597
598         case CS6:
599                 dcb.ByteSize = 6;
600                 break;
601
602         case CS7:
603                 dcb.ByteSize = 7;
604                 break;
605
606         case CS8:
607                 dcb.ByteSize = 8;
608                 break;
609
610         default:
611                 msyslog(LOG_ERR, "unsupported serial word size");
612                 errno = EINVAL;
613                 return FALSE;
614         }
615
616         if (PARENB & tios->c_cflag) {
617                 dcb.fParity = TRUE;
618                 dcb.Parity = (tios->c_cflag & PARODD)
619                                 ? ODDPARITY
620                                 : EVENPARITY;
621         } else {
622                 dcb.fParity = FALSE;
623                 dcb.Parity = NOPARITY;
624         }
625
626         dcb.StopBits = (CSTOPB & tios->c_cflag)
627                         ? TWOSTOPBITS
628                         : ONESTOPBIT;
629
630         if (!SetCommState(h, &dcb)) {
631                 errno = ENOTTY;
632                 return -1;
633         }
634
635         return 0;
636 }
637
638
639 int
640 tcgetattr(
641         int             fd,
642         struct termios *tios
643         )
644 {
645         DCB     dcb;
646         HANDLE  h;
647
648         h = (HANDLE)_get_osfhandle(fd);
649
650         if (INVALID_HANDLE_VALUE == h) {
651                 /* errno already set */
652                 return -1;
653         }
654
655         dcb.DCBlength = sizeof(dcb);
656
657         if (!GetCommState(h, &dcb)) {
658                 errno = ENOTTY;
659                 return -1;
660         }
661
662         /*  Set c_ispeed & c_ospeed */
663
664         switch (dcb.BaudRate) {
665
666         case 300:
667                 tios->c_ispeed = tios->c_ospeed = B300;
668                 break;
669
670         case 1200: 
671                 tios->c_ispeed = tios->c_ospeed = B1200;
672                 break;
673
674         case 2400:
675                 tios->c_ispeed = tios->c_ospeed = B2400;
676                 break;
677
678         case 4800: 
679                 tios->c_ispeed = tios->c_ospeed = B4800;
680                 break;
681
682         case 9600:
683                 tios->c_ispeed = tios->c_ospeed = B9600;
684                 break;
685
686         case 19200:
687                 tios->c_ispeed = tios->c_ospeed = B19200;
688                 break;
689
690         case 38400:
691                 tios->c_ispeed = tios->c_ospeed = B38400;
692                 break;
693
694         case 57600:
695                 tios->c_ispeed = tios->c_ospeed = B57600;
696                 break;
697
698         case 115200:
699                 tios->c_ispeed = tios->c_ospeed = B115200;
700                 break;
701
702         default:
703                 tios->c_ispeed = tios->c_ospeed = B9600;
704         }
705         
706
707         switch (dcb.ByteSize) {
708                 case 5:
709                         tios->c_cflag = CS5;
710                         break;
711
712                 case 6:
713                         tios->c_cflag = CS6;
714                         break;
715
716                 case 7: 
717                         tios->c_cflag = CS7; 
718                         break;
719
720                 case 8:
721                 default:
722                         tios->c_cflag = CS8;
723         }
724
725         if (dcb.fParity) {
726                 tios->c_cflag |= PARENB;
727
728                 if (ODDPARITY == dcb.Parity)
729                         tios->c_cflag |= PARODD;
730         }
731
732         if (TWOSTOPBITS == dcb.StopBits)
733                 tios->c_cflag |= CSTOPB;
734
735         tios->c_iflag = 0;
736         tios->c_lflag = 0;
737         tios->c_line = 0;
738         tios->c_oflag = 0;
739
740         return 0;
741 }
742
743
744 int
745 tcflush(
746         int fd,
747         int mode
748         )
749 {
750         HANDLE  h;
751         BOOL    success;
752         DWORD   flags;
753         int     result;
754
755         h = (HANDLE)_get_osfhandle(fd);
756
757         if (INVALID_HANDLE_VALUE == h) {
758                 /* errno already set */
759                 return -1;
760         }
761
762         switch (mode) {
763
764         case TCIFLUSH:
765                 flags = PURGE_RXCLEAR;
766                 break;
767
768         case TCOFLUSH:
769                 flags = PURGE_TXABORT;
770                 break;
771
772         case TCIOFLUSH:
773                 flags = PURGE_RXCLEAR | PURGE_TXABORT;
774                 break;
775
776         default:
777                 errno = EINVAL;
778                 return -1;
779         }
780
781         success = PurgeComm(h, flags);
782
783         if (success)
784                 result = 0;
785         else {
786                 errno = ENOTTY;
787                 result = -1;
788         }
789
790         return result;
791 }
792