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