ntdll: ETIME is not defined on FreeBSD.
[wine] / dlls / ntdll / serial.c
1 /* Main file for COMM support
2  *
3  * DEC 93 Erik Bos <erik@xs4all.nl>
4  * Copyright 1996 Marcus Meissner
5  * Copyright 2005,2006 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <errno.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #ifdef HAVE_STRINGS_H
31 # include <strings.h>
32 #endif
33 #ifdef HAVE_TERMIOS_H
34 #include <termios.h>
35 #endif
36 #ifdef HAVE_IO_H
37 # include <io.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <fcntl.h>
43 #ifdef HAVE_SYS_STAT_H
44 # include <sys/stat.h>
45 #endif
46 #include <sys/types.h>
47 #ifdef HAVE_SYS_FILIO_H
48 # include <sys/filio.h>
49 #endif
50 #ifdef HAVE_SYS_IOCTL_H
51 #include <sys/ioctl.h>
52 #endif
53 #ifdef HAVE_SYS_POLL_H
54 # include <sys/poll.h>
55 #endif
56 #ifdef HAVE_SYS_MODEM_H
57 # include <sys/modem.h>
58 #endif
59 #ifdef HAVE_SYS_STRTIO_H
60 # include <sys/strtio.h>
61 #endif
62
63 #define NONAMELESSUNION
64 #define NONAMELESSSTRUCT
65 #include "ntstatus.h"
66 #define WIN32_NO_STATUS
67 #include "windef.h"
68 #include "winternl.h"
69 #include "winioctl.h"
70 #include "ddk/ntddser.h"
71 #include "ntdll_misc.h"
72 #include "wine/server.h"
73 #include "wine/library.h"
74 #include "wine/debug.h"
75
76 #ifdef HAVE_LINUX_SERIAL_H
77 #ifdef HAVE_ASM_TYPES_H
78 #include <asm/types.h>
79 #endif
80 #include <linux/serial.h>
81 #endif
82
83 #if !defined(TIOCINQ) && defined(FIONREAD)
84 #define TIOCINQ FIONREAD
85 #endif
86
87 WINE_DEFAULT_DEBUG_CHANNEL(comm);
88
89 static const char* iocode2str(DWORD ioc)
90 {
91     switch (ioc)
92     {
93 #define X(x)    case (x): return #x
94         X(IOCTL_SERIAL_CLEAR_STATS);
95         X(IOCTL_SERIAL_CLR_DTR);
96         X(IOCTL_SERIAL_CLR_RTS);
97         X(IOCTL_SERIAL_CONFIG_SIZE);
98         X(IOCTL_SERIAL_GET_BAUD_RATE);
99         X(IOCTL_SERIAL_GET_CHARS);
100         X(IOCTL_SERIAL_GET_COMMSTATUS);
101         X(IOCTL_SERIAL_GET_DTRRTS);
102         X(IOCTL_SERIAL_GET_HANDFLOW);
103         X(IOCTL_SERIAL_GET_LINE_CONTROL);
104         X(IOCTL_SERIAL_GET_MODEM_CONTROL);
105         X(IOCTL_SERIAL_GET_MODEMSTATUS);
106         X(IOCTL_SERIAL_GET_PROPERTIES);
107         X(IOCTL_SERIAL_GET_STATS);
108         X(IOCTL_SERIAL_GET_TIMEOUTS);
109         X(IOCTL_SERIAL_GET_WAIT_MASK);
110         X(IOCTL_SERIAL_IMMEDIATE_CHAR);
111         X(IOCTL_SERIAL_LSRMST_INSERT);
112         X(IOCTL_SERIAL_PURGE);
113         X(IOCTL_SERIAL_RESET_DEVICE);
114         X(IOCTL_SERIAL_SET_BAUD_RATE);
115         X(IOCTL_SERIAL_SET_BREAK_ON);
116         X(IOCTL_SERIAL_SET_BREAK_OFF);
117         X(IOCTL_SERIAL_SET_CHARS);
118         X(IOCTL_SERIAL_SET_DTR);
119         X(IOCTL_SERIAL_SET_FIFO_CONTROL);
120         X(IOCTL_SERIAL_SET_HANDFLOW);
121         X(IOCTL_SERIAL_SET_LINE_CONTROL);
122         X(IOCTL_SERIAL_SET_MODEM_CONTROL);
123         X(IOCTL_SERIAL_SET_QUEUE_SIZE);
124         X(IOCTL_SERIAL_SET_RTS);
125         X(IOCTL_SERIAL_SET_TIMEOUTS);
126         X(IOCTL_SERIAL_SET_WAIT_MASK);
127         X(IOCTL_SERIAL_SET_XOFF);
128         X(IOCTL_SERIAL_SET_XON);
129         X(IOCTL_SERIAL_WAIT_ON_MASK);
130         X(IOCTL_SERIAL_XOFF_COUNTER);
131 #undef X
132     default: { static char tmp[32]; sprintf(tmp, "IOCTL_SERIAL_%d\n", ioc); return tmp; }
133     }
134 }
135
136 static NTSTATUS get_baud_rate(int fd, SERIAL_BAUD_RATE* sbr)
137 {
138     struct termios port;
139     int speed;
140     
141     if (tcgetattr(fd, &port) == -1)
142     {
143         ERR("tcgetattr error '%s'\n", strerror(errno));
144         return FILE_GetNtStatus();
145     }
146     speed = cfgetospeed(&port);
147     switch (speed)
148     {
149     case B0:            sbr->BaudRate = 0;      break;
150     case B50:           sbr->BaudRate = 50;     break;
151     case B75:           sbr->BaudRate = 75;     break;
152     case B110:          sbr->BaudRate = 110;    break;
153     case B134:          sbr->BaudRate = 134;    break;
154     case B150:          sbr->BaudRate = 150;    break;
155     case B200:          sbr->BaudRate = 200;    break;
156     case B300:          sbr->BaudRate = 300;    break;
157     case B600:          sbr->BaudRate = 600;    break;
158     case B1200:         sbr->BaudRate = 1200;   break;
159     case B1800:         sbr->BaudRate = 1800;   break;
160     case B2400:         sbr->BaudRate = 2400;   break;
161     case B4800:         sbr->BaudRate = 4800;   break;
162     case B9600:         sbr->BaudRate = 9600;   break;
163     case B19200:        sbr->BaudRate = 19200;  break;
164     case B38400:        sbr->BaudRate = 38400;  break;
165 #ifdef B57600
166     case B57600:        sbr->BaudRate = 57600;  break;
167 #endif
168 #ifdef B115200
169     case B115200:       sbr->BaudRate = 115200; break;
170 #endif
171 #ifdef B230400
172     case B230400:       sbr->BaudRate = 230400; break;
173 #endif
174 #ifdef B460800
175     case B460800:       sbr->BaudRate = 460800; break;
176 #endif
177     default:
178         ERR("unknown speed %x\n", speed);
179         return STATUS_INVALID_PARAMETER;
180     }
181     return STATUS_SUCCESS;
182 }
183
184 static NTSTATUS get_hand_flow(int fd, SERIAL_HANDFLOW* shf)
185 {
186     int stat = 0;
187     struct termios port;
188
189     if (tcgetattr(fd, &port) == -1)
190     {
191         ERR("tcgetattr error '%s'\n", strerror(errno));
192         return FILE_GetNtStatus();
193     }
194     /* termios does not support DTR/DSR flow control */
195     shf->ControlHandShake = 0;
196     shf->FlowReplace = 0;
197 #ifdef TIOCMGET
198     if (ioctl(fd, TIOCMGET, &stat) == -1)
199     {
200         WARN("ioctl error '%s'\n", strerror(errno));
201         shf->ControlHandShake |= SERIAL_DTR_CONTROL;
202         shf->FlowReplace |= SERIAL_RTS_CONTROL;
203     }
204 #else
205     WARN("Setting DTR/RTS to enabled by default\n");
206     shf->ControlHandShake |= SERIAL_DTR_CONTROL;
207     shf->FlowReplace |= SERIAL_RTS_CONTROL;
208 #endif
209 #ifdef TIOCM_DTR
210     if (stat & TIOCM_DTR)
211 #endif
212         shf->ControlHandShake |= SERIAL_DTR_CONTROL;
213 #ifdef CRTSCTS
214     if (port.c_cflag & CRTSCTS)
215     {
216         shf->FlowReplace |= SERIAL_RTS_CONTROL;
217         shf->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
218     }
219     else
220 #endif
221     {
222 #ifdef TIOCM_RTS
223         if (stat & TIOCM_RTS)
224 #endif
225             shf->FlowReplace |= SERIAL_RTS_CONTROL;
226     }
227     if (port.c_iflag & IXOFF)
228         shf->FlowReplace |= SERIAL_AUTO_RECEIVE;
229     if (port.c_iflag & IXON)
230         shf->FlowReplace |= SERIAL_AUTO_TRANSMIT;
231
232     shf->XonLimit = 10;
233     shf->XoffLimit = 10;
234     return STATUS_SUCCESS;
235 }
236
237 static NTSTATUS get_line_control(int fd, SERIAL_LINE_CONTROL* slc)
238 {
239     struct termios port;
240     
241     if (tcgetattr(fd, &port) == -1)
242     {
243         ERR("tcgetattr error '%s'\n", strerror(errno));
244         return FILE_GetNtStatus();
245     }
246     
247 #ifdef CMSPAR
248     switch (port.c_cflag & (PARENB | PARODD | CMSPAR))
249 #else
250     switch (port.c_cflag & (PARENB | PARODD))
251 #endif
252     {
253     case 0:                     slc->Parity = NOPARITY;         break;
254     case PARENB:                slc->Parity = EVENPARITY;       break;
255     case PARENB|PARODD:         slc->Parity = ODDPARITY;        break;
256 #ifdef CMSPAR
257     case PARENB|CMSPAR:         slc->Parity = MARKPARITY;       break;
258     case PARENB|PARODD|CMSPAR:  slc->Parity = SPACEPARITY;      break;
259 #endif
260     }
261     switch (port.c_cflag & CSIZE)
262     {
263     case CS5:   slc->WordLength = 5;    break;
264     case CS6:   slc->WordLength = 6;    break;
265     case CS7:   slc->WordLength = 7;    break;
266     case CS8:   slc->WordLength = 8;    break;
267     default: ERR("unknown size %x\n", (UINT)(port.c_cflag & CSIZE));
268     }
269
270     if (port.c_cflag & CSTOPB)
271     {
272         if (slc->WordLength == 5)
273             slc->StopBits = ONE5STOPBITS;
274         else
275             slc->StopBits = TWOSTOPBITS;
276     }
277     else
278         slc->StopBits = ONESTOPBIT;
279
280     return STATUS_SUCCESS;
281 }
282
283 static NTSTATUS get_modem_status(int fd, DWORD* lpModemStat)
284 {
285     NTSTATUS    status = STATUS_NOT_SUPPORTED;
286     int         mstat;
287
288     *lpModemStat = 0;
289 #ifdef TIOCMGET
290     if (!ioctl(fd, TIOCMGET, &mstat))
291     {
292 #ifdef TIOCM_CTS
293         if (mstat & TIOCM_CTS)  *lpModemStat |= MS_CTS_ON;
294 #endif
295 #ifdef TIOCM_DSR
296         if (mstat & TIOCM_DSR)  *lpModemStat |= MS_DSR_ON;
297 #endif
298 #ifdef TIOCM_RNG
299         if (mstat & TIOCM_RNG)  *lpModemStat |= MS_RING_ON;
300 #endif
301 #ifdef TIOCM_CAR
302         /* FIXME: Not really sure about RLSD UB 990810 */
303         if (mstat & TIOCM_CAR)  *lpModemStat |= MS_RLSD_ON;
304 #endif
305         TRACE("%04x -> %s%s%s%s\n", mstat,
306               (*lpModemStat & MS_RLSD_ON) ? "MS_RLSD_ON " : "",
307               (*lpModemStat & MS_RING_ON) ? "MS_RING_ON " : "",
308               (*lpModemStat & MS_DSR_ON)  ? "MS_DSR_ON  " : "",
309               (*lpModemStat & MS_CTS_ON)  ? "MS_CTS_ON  " : "");
310         return STATUS_SUCCESS;
311     }
312     WARN("ioctl failed\n");
313     status = FILE_GetNtStatus();
314 #endif
315     return status;
316 }
317
318 static NTSTATUS get_special_chars(int fd, SERIAL_CHARS* sc)
319 {
320     struct termios port;
321     
322     if (tcgetattr(fd, &port) == -1)
323     {
324         ERR("tcgetattr error '%s'\n", strerror(errno));
325         return FILE_GetNtStatus();
326     }
327     sc->EofChar   = port.c_cc[VEOF];
328     sc->ErrorChar = 0xFF;
329     sc->BreakChar = 0; /* FIXME */
330     sc->EventChar = 0; /* FIXME */
331     sc->XonChar   = port.c_cc[VSTART];
332     sc->XoffChar  = port.c_cc[VSTOP];
333
334     return STATUS_SUCCESS;
335 }
336
337 static NTSTATUS get_status(int fd, SERIAL_STATUS* ss)
338 {
339     NTSTATUS    status = STATUS_SUCCESS;
340
341     ss->Errors = 0;
342     ss->HoldReasons = 0;
343     ss->EofReceived = FALSE;
344     ss->WaitForImmediate = FALSE;
345 #ifdef TIOCOUTQ
346     if (ioctl(fd, TIOCOUTQ, &ss->AmountInOutQueue) == -1)
347     {
348         WARN("ioctl returned error\n");
349         status = FILE_GetNtStatus();
350     }
351 #else
352     ss->AmountInOutQueue = 0; /* FIXME: find a different way to find out */
353 #endif
354
355 #ifdef TIOCINQ
356     if (ioctl(fd, TIOCINQ, &ss->AmountInInQueue))
357     {
358         WARN("ioctl returned error\n");
359         status = FILE_GetNtStatus();
360     }
361 #else
362     ss->AmountInInQueue = 0; /* FIXME: find a different way to find out */
363 #endif
364     return status;
365 }
366
367 static NTSTATUS get_timeouts(HANDLE handle, SERIAL_TIMEOUTS* st)
368 {
369     NTSTATUS    status;
370     SERVER_START_REQ( get_serial_info )
371     {
372         req->handle = wine_server_obj_handle( handle );
373         if (!(status = wine_server_call( req )))
374         {
375             st->ReadIntervalTimeout         = reply->readinterval;
376             st->ReadTotalTimeoutMultiplier  = reply->readmult;
377             st->ReadTotalTimeoutConstant    = reply->readconst;
378             st->WriteTotalTimeoutMultiplier = reply->writemult;
379             st->WriteTotalTimeoutConstant   = reply->writeconst;
380         }
381     }
382     SERVER_END_REQ;
383     return status;
384 }
385
386 static NTSTATUS get_wait_mask(HANDLE hDevice, DWORD* mask)
387 {
388     NTSTATUS    status;
389
390     SERVER_START_REQ( get_serial_info )
391     {
392         req->handle = wine_server_obj_handle( hDevice );
393         if (!(status = wine_server_call( req )))
394             *mask = reply->eventmask;
395     }
396     SERVER_END_REQ;
397     return status;
398 }
399
400 static NTSTATUS purge(int fd, DWORD flags)
401 {
402     /*
403     ** not exactly sure how these are different
404     ** Perhaps if we had our own internal queues, one flushes them
405     ** and the other flushes the kernel's buffers.
406     */
407     if (flags & PURGE_TXABORT) tcflush(fd, TCOFLUSH);
408     if (flags & PURGE_RXABORT) tcflush(fd, TCIFLUSH);
409     if (flags & PURGE_TXCLEAR) tcflush(fd, TCOFLUSH);
410     if (flags & PURGE_RXCLEAR) tcflush(fd, TCIFLUSH);
411     return STATUS_SUCCESS;
412 }
413
414 static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr)
415 {
416     struct termios port;
417
418     if (tcgetattr(fd, &port) == -1)
419     {
420         ERR("tcgetattr error '%s'\n", strerror(errno));
421         return FILE_GetNtStatus();
422     }
423
424     switch (sbr->BaudRate)
425     {
426     case 0:         cfsetospeed( &port, B0 ); break;
427     case 50:        cfsetospeed( &port, B50 ); break;
428     case 75:        cfsetospeed( &port, B75 ); break;
429     case 110:
430     case CBR_110:   cfsetospeed( &port, B110 ); break;
431     case 134:       cfsetospeed( &port, B134 ); break;
432     case 150:       cfsetospeed( &port, B150 ); break;
433     case 200:       cfsetospeed( &port, B200 ); break;
434     case 300:
435     case CBR_300:   cfsetospeed( &port, B300 ); break;
436     case 600:
437     case CBR_600:   cfsetospeed( &port, B600 ); break;
438     case 1200:
439     case CBR_1200:  cfsetospeed( &port, B1200 ); break;
440     case 1800:      cfsetospeed( &port, B1800 ); break;
441     case 2400:
442     case CBR_2400:  cfsetospeed( &port, B2400 ); break;
443     case 4800:
444     case CBR_4800:  cfsetospeed( &port, B4800 ); break;
445     case 9600:
446     case CBR_9600:  cfsetospeed( &port, B9600 ); break;
447     case 19200:
448     case CBR_19200: cfsetospeed( &port, B19200 ); break;
449     case 38400:
450     case CBR_38400: cfsetospeed( &port, B38400 ); break;
451 #ifdef B57600
452     case 57600: cfsetospeed( &port, B57600 ); break;
453 #endif
454 #ifdef B115200
455     case 115200: cfsetospeed( &port, B115200 ); break;
456 #endif
457 #ifdef B230400
458     case 230400: cfsetospeed( &port, B230400 ); break;
459 #endif
460 #ifdef B460800
461     case 460800: cfsetospeed( &port, B460800 ); break;
462 #endif
463     default:
464 #if defined (HAVE_LINUX_SERIAL_H) && defined (TIOCSSERIAL)
465         {
466             struct serial_struct nuts;
467             int arby;
468         
469             ioctl(fd, TIOCGSERIAL, &nuts);
470             nuts.custom_divisor = nuts.baud_base / sbr->BaudRate;
471             if (!(nuts.custom_divisor)) nuts.custom_divisor = 1;
472             arby = nuts.baud_base / nuts.custom_divisor;
473             nuts.flags &= ~ASYNC_SPD_MASK;
474             nuts.flags |= ASYNC_SPD_CUST;
475             WARN("You (or a program acting at your behest) have specified\n"
476                  "a non-standard baud rate %d.  Wine will set the rate to %d,\n"
477                  "which is as close as we can get by our present understanding of your\n"
478                  "hardware. I hope you know what you are doing.  Any disruption Wine\n"
479                  "has caused to your linux system can be undone with setserial\n"
480                  "(see man setserial). If you have incapacitated a Hayes type modem,\n"
481                  "reset it and it will probably recover.\n", sbr->BaudRate, arby);
482             ioctl(fd, TIOCSSERIAL, &nuts);
483             cfsetospeed( &port, B38400 );
484         }
485         break;
486 #else     /* Don't have linux/serial.h or lack TIOCSSERIAL */
487         ERR("baudrate %d\n", sbr->BaudRate);
488         return STATUS_NOT_SUPPORTED;
489 #endif    /* Don't have linux/serial.h or lack TIOCSSERIAL */
490     }
491     cfsetispeed( &port, cfgetospeed(&port) );
492     if (tcsetattr(fd, TCSANOW, &port) == -1)
493     {
494         ERR("tcsetattr error '%s'\n", strerror(errno));
495         return FILE_GetNtStatus();
496     }
497     return STATUS_SUCCESS;
498 }
499
500 static int whack_modem(int fd, unsigned int andy, unsigned int orrie)
501 {
502 #ifdef TIOCMGET
503     unsigned int mstat, okay;
504     okay = ioctl(fd, TIOCMGET, &mstat);
505     if (okay) return okay;
506     if (andy) mstat &= andy;
507     mstat |= orrie;
508     return ioctl(fd, TIOCMSET, &mstat);
509 #else
510     return 0;
511 #endif
512 }
513
514 static NTSTATUS set_handflow(int fd, const SERIAL_HANDFLOW* shf)
515 {
516     struct termios port;
517
518     if ((shf->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE)) == 
519         (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE))
520         return STATUS_NOT_SUPPORTED;
521
522     if (tcgetattr(fd, &port) == -1)
523     {
524         ERR("tcgetattr error '%s'\n", strerror(errno));
525         return FILE_GetNtStatus();
526     }
527     
528 #ifdef CRTSCTS
529     if ((shf->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
530         (shf->FlowReplace & SERIAL_RTS_HANDSHAKE))
531     {
532         port.c_cflag |= CRTSCTS;
533         TRACE("CRTSCTS\n");
534     }
535     else
536         port.c_cflag &= ~CRTSCTS;
537 #endif
538 #ifdef TIOCM_DTR
539     if (shf->ControlHandShake & SERIAL_DTR_HANDSHAKE)
540     {
541         WARN("DSR/DTR flow control not supported\n");
542     } else if (!(shf->ControlHandShake & SERIAL_DTR_CONTROL))
543         whack_modem(fd, ~TIOCM_DTR, 0);
544     else
545         whack_modem(fd, 0, TIOCM_DTR);
546 #endif
547 #ifdef TIOCM_RTS
548     if (!(shf->ControlHandShake & SERIAL_CTS_HANDSHAKE))
549     {
550         if ((shf->FlowReplace & (SERIAL_RTS_CONTROL|SERIAL_RTS_HANDSHAKE)) == 0)
551             whack_modem(fd, ~TIOCM_RTS, 0);
552         else    
553             whack_modem(fd, 0, TIOCM_RTS);
554     }
555 #endif
556
557     if (shf->FlowReplace & SERIAL_AUTO_RECEIVE)
558         port.c_iflag |= IXOFF;
559     else
560         port.c_iflag &= ~IXOFF;
561     if (shf->FlowReplace & SERIAL_AUTO_TRANSMIT)
562         port.c_iflag |= IXON;
563     else
564         port.c_iflag &= ~IXON;
565     if (tcsetattr(fd, TCSANOW, &port) == -1)
566     {
567         ERR("tcsetattr error '%s'\n", strerror(errno));
568         return FILE_GetNtStatus();
569     }
570
571     return STATUS_SUCCESS;
572 }
573
574 static NTSTATUS set_line_control(int fd, const SERIAL_LINE_CONTROL* slc)
575 {
576     struct termios port;
577     unsigned bytesize, stopbits;
578     
579     if (tcgetattr(fd, &port) == -1)
580     {
581         ERR("tcgetattr error '%s'\n", strerror(errno));
582         return FILE_GetNtStatus();
583     }
584     
585 #ifdef IMAXBEL
586     port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|PARMRK|IMAXBEL);
587 #else
588     port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|PARMRK);
589 #endif
590     port.c_iflag |= IGNBRK | INPCK;
591     
592     port.c_oflag &= ~(OPOST);
593     
594     port.c_cflag &= ~(HUPCL);
595     port.c_cflag |= CLOCAL | CREAD;
596     
597     /*
598      * on FreeBSD, turning off ICANON does not disable IEXTEN,
599      * so we must turn it off explicitly. No harm done on Linux.
600      */
601     port.c_lflag &= ~(ICANON|ECHO|ISIG|IEXTEN);
602     port.c_lflag |= NOFLSH;
603     
604     bytesize = slc->WordLength;
605     stopbits = slc->StopBits;
606     
607 #ifdef CMSPAR
608     port.c_cflag &= ~(PARENB | PARODD | CMSPAR);
609 #else
610     port.c_cflag &= ~(PARENB | PARODD);
611 #endif
612
613     /* make sure that reads don't block */
614     port.c_cc[VMIN] = 0;
615     port.c_cc[VTIME] = 0;
616
617     switch (slc->Parity)
618     {
619     case NOPARITY:      port.c_iflag &= ~INPCK;                         break;
620     case ODDPARITY:     port.c_cflag |= PARENB | PARODD;                break;
621     case EVENPARITY:    port.c_cflag |= PARENB;                         break;
622 #ifdef CMSPAR
623         /* Linux defines mark/space (stick) parity */
624     case MARKPARITY:    port.c_cflag |= PARENB | CMSPAR;                break;
625     case SPACEPARITY:   port.c_cflag |= PARENB | PARODD |  CMSPAR;      break;
626 #else
627         /* try the POSIX way */
628     case MARKPARITY:
629         if (slc->StopBits == ONESTOPBIT)
630         {
631             stopbits = TWOSTOPBITS;
632             port.c_iflag &= ~INPCK;
633         }
634         else
635         {
636             ERR("Cannot set MARK Parity\n");
637             return STATUS_NOT_SUPPORTED;
638         }
639         break;
640     case SPACEPARITY:
641         if (slc->WordLength < 8)
642         {
643             bytesize +=1;
644             port.c_iflag &= ~INPCK;
645         }
646         else
647         {
648             ERR("Cannot set SPACE Parity\n");
649             return STATUS_NOT_SUPPORTED;
650         }
651         break;
652 #endif
653     default:
654         ERR("Parity\n");
655         return STATUS_NOT_SUPPORTED;
656     }
657     
658     port.c_cflag &= ~CSIZE;
659     switch (bytesize)
660     {
661     case 5:     port.c_cflag |= CS5;    break;
662     case 6:     port.c_cflag |= CS6;    break;
663     case 7:     port.c_cflag |= CS7;    break;
664     case 8:     port.c_cflag |= CS8;    break;
665     default:
666         ERR("ByteSize\n");
667         return STATUS_NOT_SUPPORTED;
668     }
669     
670     switch (stopbits)
671     {
672     case ONESTOPBIT:    port.c_cflag &= ~CSTOPB;        break;
673     case ONE5STOPBITS: /* will be selected if bytesize is 5 */
674     case TWOSTOPBITS:   port.c_cflag |= CSTOPB;         break;
675     default:
676         ERR("StopBits\n");
677         return STATUS_NOT_SUPPORTED;
678     }
679     /* otherwise it hangs with pending input*/
680     if (tcsetattr(fd, TCSANOW, &port) == -1)
681     {
682         ERR("tcsetattr error '%s'\n", strerror(errno));
683         return FILE_GetNtStatus();
684     }
685     return STATUS_SUCCESS;
686 }
687
688 static NTSTATUS set_queue_size(int fd, const SERIAL_QUEUE_SIZE* sqs)
689 {
690     FIXME("insize %d outsize %d unimplemented stub\n", sqs->InSize, sqs->OutSize);
691     return STATUS_SUCCESS;
692 }
693
694 static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc)
695 {
696     struct termios port;
697     
698     if (tcgetattr(fd, &port) == -1)
699     {
700         ERR("tcgetattr error '%s'\n", strerror(errno));
701         return FILE_GetNtStatus();
702     }
703     
704     port.c_cc[VEOF  ] = sc->EofChar;
705     /* FIXME: sc->ErrorChar is not supported */
706     /* FIXME: sc->BreakChar is not supported */
707     /* FIXME: sc->EventChar is not supported */
708     port.c_cc[VSTART] = sc->XonChar;
709     port.c_cc[VSTOP ] = sc->XoffChar;
710     
711     if (tcsetattr(fd, TCSANOW, &port) == -1)
712     {
713         ERR("tcsetattr error '%s'\n", strerror(errno));
714         return FILE_GetNtStatus();
715     }
716     return STATUS_SUCCESS;
717 }
718
719 static NTSTATUS set_timeouts(HANDLE handle, const SERIAL_TIMEOUTS* st)
720 {
721     NTSTATUS            status;
722
723     SERVER_START_REQ( set_serial_info )
724     {
725         req->handle       = wine_server_obj_handle( handle );
726         req->flags        = SERIALINFO_SET_TIMEOUTS;
727         req->readinterval = st->ReadIntervalTimeout ;
728         req->readmult     = st->ReadTotalTimeoutMultiplier ;
729         req->readconst    = st->ReadTotalTimeoutConstant ;
730         req->writemult    = st->WriteTotalTimeoutMultiplier ;
731         req->writeconst   = st->WriteTotalTimeoutConstant ;
732         status = wine_server_call( req );
733     }
734     SERVER_END_REQ;
735     return status;
736 }
737
738 static NTSTATUS set_wait_mask(HANDLE hDevice, DWORD mask)
739 {
740     NTSTATUS status;
741
742     SERVER_START_REQ( set_serial_info )
743     {
744         req->handle    = wine_server_obj_handle( hDevice );
745         req->flags     = SERIALINFO_SET_MASK;
746         req->eventmask = mask;
747         status = wine_server_call( req );
748     }
749     SERVER_END_REQ;
750     return status;
751 }
752
753 /*
754  * does not change IXOFF but simulates that IXOFF has been received:
755  */
756 static NTSTATUS set_XOff(int fd)
757 {
758     if (tcflow(fd, TCOOFF))
759     {
760         return FILE_GetNtStatus();
761     }
762     return STATUS_SUCCESS;
763 }
764
765 /*
766  * does not change IXON but simulates that IXON has been received:
767  */
768 static NTSTATUS set_XOn(int fd)
769 {
770     if (tcflow(fd, TCOON))
771     {
772         return FILE_GetNtStatus();
773     }
774     return STATUS_SUCCESS;
775 }
776
777 /*             serial_irq_info
778  * local structure holding the irq values we need for WaitCommEvent()
779  *
780  * Stripped down from struct serial_icounter_struct, which may not be available on some systems
781  * As the modem line interrupts (cts, dsr, rng, dcd) only get updated with TIOCMIWAIT active,
782  * no need to carry them in the internal structure
783  *
784  */
785 typedef struct serial_irq_info
786 {
787     int rx , tx, frame, overrun, parity, brk, buf_overrun;
788 }serial_irq_info;
789
790 /***********************************************************************
791  * Data needed by the thread polling for the changing CommEvent
792  */
793 typedef struct async_commio
794 {
795     HANDLE              hDevice;
796     DWORD*              events;
797     IO_STATUS_BLOCK*    iosb;
798     HANDLE              hEvent;
799     DWORD               evtmask;
800     DWORD               mstat;
801     serial_irq_info     irq_info;
802 } async_commio;
803
804 /***********************************************************************
805  *   Get extended interrupt count info, needed for wait_on
806  */
807 static NTSTATUS get_irq_info(int fd, serial_irq_info *irq_info)
808 {
809     NTSTATUS status = STATUS_NOT_IMPLEMENTED;
810 #ifdef TIOCGICOUNT
811     struct serial_icounter_struct einfo;
812     if (!ioctl(fd, TIOCGICOUNT, &einfo))
813     {
814         irq_info->rx          = einfo.rx;
815         irq_info->tx          = einfo.tx;
816         irq_info->frame       = einfo.frame;
817         irq_info->overrun     = einfo.overrun;
818         irq_info->parity      = einfo.parity;
819         irq_info->brk         = einfo.brk;
820         irq_info->buf_overrun = einfo.buf_overrun;
821         return STATUS_SUCCESS;
822     }
823     TRACE("TIOCGICOUNT err %s\n", strerror(errno));
824     status = FILE_GetNtStatus();
825 #endif
826     memset(irq_info,0, sizeof(serial_irq_info));
827     return status;
828 }
829
830
831 static DWORD check_events(int fd, DWORD mask,
832                           const serial_irq_info *new,
833                           const serial_irq_info *old,
834                           DWORD new_mstat, DWORD old_mstat)
835 {
836     DWORD ret = 0, queue;
837
838     TRACE("mask 0x%08x\n", mask);
839     TRACE("old->rx          0x%08x vs. new->rx          0x%08x\n", old->rx, new->rx);
840     TRACE("old->tx          0x%08x vs. new->tx          0x%08x\n", old->tx, new->tx);
841     TRACE("old->frame       0x%08x vs. new->frame       0x%08x\n", old->frame, new->frame);
842     TRACE("old->overrun     0x%08x vs. new->overrun     0x%08x\n", old->overrun, new->overrun);
843     TRACE("old->parity      0x%08x vs. new->parity      0x%08x\n", old->parity, new->parity);
844     TRACE("old->brk         0x%08x vs. new->brk         0x%08x\n", old->brk, new->brk);
845     TRACE("old->buf_overrun 0x%08x vs. new->buf_overrun 0x%08x\n", old->buf_overrun, new->buf_overrun);
846
847     if (old->brk != new->brk) ret |= EV_BREAK;
848     if ((old_mstat & MS_CTS_ON ) != (new_mstat & MS_CTS_ON )) ret |= EV_CTS;
849     if ((old_mstat & MS_DSR_ON ) != (new_mstat & MS_DSR_ON )) ret |= EV_DSR;
850     if ((old_mstat & MS_RING_ON) != (new_mstat & MS_RING_ON)) ret |= EV_RING;
851     if ((old_mstat & MS_RLSD_ON) != (new_mstat & MS_RLSD_ON)) ret |= EV_RLSD;
852     if (old->frame != new->frame || old->overrun != new->overrun || old->parity != new->parity) ret |= EV_ERR;
853     if (mask & EV_RXCHAR)
854     {
855         queue = 0;
856 #ifdef TIOCINQ
857         if (ioctl(fd, TIOCINQ, &queue))
858             WARN("TIOCINQ returned error\n");
859 #endif
860         if (queue)
861             ret |= EV_RXCHAR;
862     }
863     if (mask & EV_TXEMPTY)
864     {
865         queue = 0;
866 /* We really want to know when all characters have gone out of the transmitter */
867 #if defined(TIOCSERGETLSR) 
868         if (ioctl(fd, TIOCSERGETLSR, &queue))
869             WARN("TIOCSERGETLSR returned error\n");
870         if (queue)
871 /* TIOCOUTQ only checks for an empty buffer */
872 #elif defined(TIOCOUTQ)
873         if (ioctl(fd, TIOCOUTQ, &queue))
874             WARN("TIOCOUTQ returned error\n");
875         if (!queue)
876 #endif
877            ret |= EV_TXEMPTY;
878         TRACE("OUTQUEUE %d, Transmitter %sempty\n",
879               queue, (ret & EV_TXEMPTY) ? "" : "not ");
880     }
881     return ret & mask;
882 }
883
884 /***********************************************************************
885  *             wait_for_event      (INTERNAL)
886  *
887  *  We need to poll for what is interesting
888  *  TIOCMIWAIT only checks modem status line and may not be aborted by a changing mask
889  *
890  */
891 static DWORD CALLBACK wait_for_event(LPVOID arg)
892 {
893     async_commio *commio = arg;
894     int fd, needs_close;
895
896     if (!server_get_unix_fd( commio->hDevice, FILE_READ_DATA | FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))
897     {
898         serial_irq_info new_irq_info;
899         DWORD new_mstat, new_evtmask;
900         LARGE_INTEGER time;
901         
902         TRACE("device=%p fd=0x%08x mask=0x%08x buffer=%p event=%p irq_info=%p\n", 
903               commio->hDevice, fd, commio->evtmask, commio->events, commio->hEvent, &commio->irq_info);
904
905         time.QuadPart = (ULONGLONG)10000;
906         time.QuadPart = -time.QuadPart;
907         for (;;)
908         {
909             /*
910              * TIOCMIWAIT is not adequate
911              *
912              * FIXME:
913              * We don't handle the EV_RXFLAG (the eventchar)
914              */
915             NtDelayExecution(FALSE, &time);
916             get_irq_info(fd, &new_irq_info);
917             if (get_modem_status(fd, &new_mstat))
918                 TRACE("get_modem_status failed\n");
919             *commio->events = check_events(fd, commio->evtmask,
920                                            &new_irq_info, &commio->irq_info,
921                                            new_mstat, commio->mstat);
922             if (*commio->events) break;
923             get_wait_mask(commio->hDevice, &new_evtmask);
924             if (commio->evtmask != new_evtmask)
925             {
926                 *commio->events = 0;
927                 break;
928             }
929         }
930         if (needs_close) close( fd );
931     }
932     if (commio->iosb) commio->iosb->u.Status = *commio->events ? STATUS_SUCCESS : STATUS_CANCELLED;
933     if (commio->hEvent) NtSetEvent(commio->hEvent, NULL);
934     RtlFreeHeap(GetProcessHeap(), 0, commio);
935     return 0;
936 }
937
938 static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, PIO_STATUS_BLOCK piosb, DWORD* events)
939 {
940     async_commio*       commio;
941     NTSTATUS            status;
942
943     if ((status = NtResetEvent(hEvent, NULL)))
944         return status;
945
946     commio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof (async_commio));
947     if (!commio) return STATUS_NO_MEMORY;
948
949     commio->hDevice = hDevice;
950     commio->events  = events;
951     commio->iosb    = piosb;
952     commio->hEvent  = hEvent;
953     get_wait_mask(commio->hDevice, &commio->evtmask);
954
955 /* We may never return, if some capabilities miss
956  * Return error in that case
957  */
958 #if !defined(TIOCINQ)
959     if (commio->evtmask & EV_RXCHAR)
960         goto error_caps;
961 #endif
962 #if !(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)
963     if (commio->evtmask & EV_TXEMPTY)
964         goto error_caps;
965 #endif
966 #if !defined(TIOCMGET)
967     if (commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD))
968         goto error_caps;
969 #endif
970 #if !defined(TIOCM_CTS)
971     if (commio->evtmask & EV_CTS)
972         goto error_caps;
973 #endif
974 #if !defined(TIOCM_DSR)
975     if (commio->evtmask & EV_DSR)
976         goto error_caps;
977 #endif
978 #if !defined(TIOCM_RNG)
979     if (commio->evtmask & EV_RING)
980         goto error_caps;
981 #endif
982 #if !defined(TIOCM_CAR)
983     if (commio->evtmask & EV_RLSD)
984         goto error_caps;
985 #endif
986     if (commio->evtmask & EV_RXFLAG)
987         FIXME("EV_RXFLAG not handled\n");
988
989     if ((status = get_irq_info(fd, &commio->irq_info)) &&
990         (commio->evtmask & (EV_BREAK | EV_ERR)))
991         goto out_now;
992
993     if ((status = get_modem_status(fd, &commio->mstat)) &&
994         (commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD)))
995         goto out_now;
996
997     /* We might have received something or the TX buffer is delivered */
998     *events = check_events(fd, commio->evtmask,
999                                &commio->irq_info, &commio->irq_info,
1000                                commio->mstat, commio->mstat);
1001     if (*events)
1002     {
1003         status = STATUS_SUCCESS;
1004         goto out_now;
1005     }
1006
1007     /* create the worker for the task */
1008     status = RtlQueueWorkItem(wait_for_event, commio, 0 /* FIXME */);
1009     if (status != STATUS_SUCCESS) goto out_now;
1010     return STATUS_PENDING;
1011
1012 #if !defined(TIOCINQ) || (!(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)) || !defined(TIOCMGET) || !defined(TIOCM_CTS) ||!defined(TIOCM_DSR) || !defined(TIOCM_RNG) || !defined(TIOCM_CAR)
1013 error_caps:
1014     FIXME("Returning error because of missing capabilities\n");
1015     status = STATUS_INVALID_PARAMETER;
1016 #endif
1017 out_now:
1018     RtlFreeHeap(GetProcessHeap(), 0, commio);
1019     return status;
1020 }
1021
1022 static NTSTATUS xmit_immediate(HANDLE hDevice, int fd, const char* ptr)
1023 {
1024     /* FIXME: not perfect as it should bypass the in-queue */
1025     WARN("(%p,'%c') not perfect!\n", hDevice, *ptr);
1026     if (write(fd, ptr, 1) != 1)
1027         return FILE_GetNtStatus();
1028     return STATUS_SUCCESS;
1029 }
1030
1031 /******************************************************************
1032  *              COMM_DeviceIoControl
1033  *
1034  *
1035  */
1036 static inline NTSTATUS io_control(HANDLE hDevice, 
1037                                   HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1038                                   PVOID UserApcContext, 
1039                                   PIO_STATUS_BLOCK piosb, 
1040                                   ULONG dwIoControlCode,
1041                                   LPVOID lpInBuffer, DWORD nInBufferSize,
1042                                   LPVOID lpOutBuffer, DWORD nOutBufferSize)
1043 {
1044     DWORD       sz = 0, access = FILE_READ_DATA;
1045     NTSTATUS    status = STATUS_SUCCESS;
1046     int         fd = -1, needs_close = 0;
1047
1048     TRACE("%p %s %p %d %p %d %p\n",
1049           hDevice, iocode2str(dwIoControlCode), lpInBuffer, nInBufferSize,
1050           lpOutBuffer, nOutBufferSize, piosb);
1051
1052     piosb->Information = 0;
1053
1054     if (dwIoControlCode != IOCTL_SERIAL_GET_TIMEOUTS &&
1055         dwIoControlCode != IOCTL_SERIAL_SET_TIMEOUTS)
1056     {
1057         enum server_fd_type type;
1058         if ((status = server_get_unix_fd( hDevice, access, &fd, &needs_close, &type, NULL )))
1059             goto error;
1060         if (type != FD_TYPE_SERIAL)
1061         {
1062             if (needs_close) close( fd );
1063             status = STATUS_OBJECT_TYPE_MISMATCH;
1064             goto error;
1065         }
1066     }
1067
1068     switch (dwIoControlCode)
1069     {
1070     case IOCTL_SERIAL_CLR_DTR:
1071 #ifdef TIOCM_DTR
1072         if (whack_modem(fd, ~TIOCM_DTR, 0) == -1) status = FILE_GetNtStatus();
1073 #else
1074         status = STATUS_NOT_SUPPORTED;
1075 #endif
1076         break;
1077     case IOCTL_SERIAL_CLR_RTS:
1078 #ifdef TIOCM_RTS
1079         if (whack_modem(fd, ~TIOCM_RTS, 0) == -1) status = FILE_GetNtStatus();
1080 #else
1081         status = STATUS_NOT_SUPPORTED;
1082 #endif
1083         break;
1084     case IOCTL_SERIAL_GET_BAUD_RATE:
1085         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_BAUD_RATE))
1086         {
1087             if (!(status = get_baud_rate(fd, lpOutBuffer)))
1088                 sz = sizeof(SERIAL_BAUD_RATE);
1089         }
1090         else
1091             status = STATUS_INVALID_PARAMETER;
1092         break;
1093     case IOCTL_SERIAL_GET_CHARS:
1094         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_CHARS))
1095         {
1096             if (!(status = get_special_chars(fd, lpOutBuffer)))
1097                 sz = sizeof(SERIAL_CHARS);
1098         }
1099         else
1100             status = STATUS_INVALID_PARAMETER;
1101         break;
1102      case IOCTL_SERIAL_GET_COMMSTATUS:
1103         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_STATUS))
1104         {
1105             if (!(status = get_status(fd, lpOutBuffer)))
1106                 sz = sizeof(SERIAL_STATUS);
1107         }
1108         else status = STATUS_INVALID_PARAMETER;
1109         break;
1110     case IOCTL_SERIAL_GET_HANDFLOW:
1111         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_HANDFLOW))
1112         {
1113             if (!(status = get_hand_flow(fd, lpOutBuffer)))
1114                 sz = sizeof(SERIAL_HANDFLOW);
1115         }
1116         else
1117             status = STATUS_INVALID_PARAMETER;
1118         break;
1119     case IOCTL_SERIAL_GET_LINE_CONTROL:
1120         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_LINE_CONTROL))
1121         {
1122             if (!(status = get_line_control(fd, lpOutBuffer)))
1123                 sz = sizeof(SERIAL_LINE_CONTROL);
1124         }
1125         else
1126             status = STATUS_INVALID_PARAMETER;
1127         break;
1128     case IOCTL_SERIAL_GET_MODEMSTATUS:
1129         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1130         {
1131             if (!(status = get_modem_status(fd, lpOutBuffer)))
1132                 sz = sizeof(DWORD);
1133         }
1134         else status = STATUS_INVALID_PARAMETER;
1135         break;
1136     case IOCTL_SERIAL_GET_TIMEOUTS:
1137         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_TIMEOUTS))
1138         {
1139             if (!(status = get_timeouts(hDevice, lpOutBuffer)))
1140                 sz = sizeof(SERIAL_TIMEOUTS);
1141         }
1142         else
1143             status = STATUS_INVALID_PARAMETER;
1144         break;
1145     case IOCTL_SERIAL_GET_WAIT_MASK:
1146         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1147         {
1148             if (!(status = get_wait_mask(hDevice, lpOutBuffer)))
1149                 sz = sizeof(DWORD);
1150         }
1151         else
1152             status = STATUS_INVALID_PARAMETER;
1153         break;
1154     case IOCTL_SERIAL_IMMEDIATE_CHAR:
1155         if (lpInBuffer && nInBufferSize == sizeof(CHAR))
1156             status = xmit_immediate(hDevice, fd, lpInBuffer);
1157         else
1158             status = STATUS_INVALID_PARAMETER;
1159         break;
1160     case IOCTL_SERIAL_PURGE:
1161         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
1162             status = purge(fd, *(DWORD*)lpInBuffer);
1163         else
1164             status = STATUS_INVALID_PARAMETER;
1165         break;
1166     case IOCTL_SERIAL_RESET_DEVICE:
1167         FIXME("Unsupported\n");
1168         break;
1169     case IOCTL_SERIAL_SET_BAUD_RATE:
1170         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_BAUD_RATE))
1171             status = set_baud_rate(fd, lpInBuffer);
1172         else
1173             status = STATUS_INVALID_PARAMETER;
1174         break;
1175     case IOCTL_SERIAL_SET_BREAK_OFF:
1176 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
1177         if (ioctl(fd, TIOCCBRK, 0) == -1)
1178         {
1179             TRACE("ioctl failed\n");
1180             status = FILE_GetNtStatus();
1181         }
1182 #else
1183         FIXME("ioctl not available\n");
1184         status = STATUS_NOT_SUPPORTED;
1185 #endif
1186         break;
1187     case IOCTL_SERIAL_SET_BREAK_ON:
1188 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
1189         if (ioctl(fd, TIOCSBRK, 0) == -1)
1190         {
1191             TRACE("ioctl failed\n");
1192             status = FILE_GetNtStatus();
1193         }
1194 #else
1195         FIXME("ioctl not available\n");
1196         status = STATUS_NOT_SUPPORTED;
1197 #endif
1198         break;
1199     case IOCTL_SERIAL_SET_CHARS:
1200         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_CHARS))
1201             status = set_special_chars(fd, lpInBuffer);
1202         else
1203             status = STATUS_INVALID_PARAMETER;
1204         break;
1205     case IOCTL_SERIAL_SET_DTR:
1206 #ifdef TIOCM_DTR
1207         if (whack_modem(fd, 0, TIOCM_DTR) == -1) status = FILE_GetNtStatus();
1208 #else
1209         status = STATUS_NOT_SUPPORTED;
1210 #endif
1211         break;
1212     case IOCTL_SERIAL_SET_HANDFLOW:
1213         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_HANDFLOW))
1214             status = set_handflow(fd, lpInBuffer);
1215         else
1216             status = STATUS_INVALID_PARAMETER;
1217         break;
1218     case IOCTL_SERIAL_SET_LINE_CONTROL:
1219         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_LINE_CONTROL))
1220             status = set_line_control(fd, lpInBuffer);
1221         else
1222             status = STATUS_INVALID_PARAMETER;
1223         break;
1224     case IOCTL_SERIAL_SET_QUEUE_SIZE:
1225         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_QUEUE_SIZE))
1226             status = set_queue_size(fd, lpInBuffer);
1227         else
1228             status = STATUS_INVALID_PARAMETER;
1229         break;
1230     case IOCTL_SERIAL_SET_RTS:
1231 #ifdef TIOCM_RTS
1232         if (whack_modem(fd, 0, TIOCM_RTS) == -1) status = FILE_GetNtStatus();
1233 #else
1234         status = STATUS_NOT_SUPPORTED;
1235 #endif
1236         break;
1237     case IOCTL_SERIAL_SET_TIMEOUTS:
1238         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_TIMEOUTS))
1239             status = set_timeouts(hDevice, lpInBuffer);
1240         else
1241             status = STATUS_INVALID_PARAMETER;
1242         break;
1243     case IOCTL_SERIAL_SET_WAIT_MASK:
1244         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
1245         {
1246             status = set_wait_mask(hDevice, *(DWORD*)lpInBuffer);
1247         }
1248         else status = STATUS_INVALID_PARAMETER;
1249         break;
1250     case IOCTL_SERIAL_SET_XOFF:
1251         status = set_XOff(fd);
1252         break;
1253     case IOCTL_SERIAL_SET_XON:
1254         status = set_XOn(fd);
1255         break;
1256     case IOCTL_SERIAL_WAIT_ON_MASK:
1257         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1258         {
1259             if (!(status = wait_on(hDevice, fd, hEvent, piosb, lpOutBuffer)))
1260                 sz = sizeof(DWORD);
1261         }
1262         else
1263             status = STATUS_INVALID_PARAMETER;
1264         break;
1265     default:
1266         FIXME("Unsupported IOCTL %x (type=%x access=%x func=%x meth=%x)\n", 
1267               dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
1268               (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
1269         sz = 0;
1270         status = STATUS_INVALID_PARAMETER;
1271         break;
1272     }
1273     if (needs_close) close( fd );
1274  error:
1275     piosb->u.Status = status;
1276     piosb->Information = sz;
1277     if (hEvent && status != STATUS_PENDING) NtSetEvent(hEvent, NULL);
1278     return status;
1279 }
1280
1281 NTSTATUS COMM_DeviceIoControl(HANDLE hDevice, 
1282                               HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1283                               PVOID UserApcContext, 
1284                               PIO_STATUS_BLOCK piosb, 
1285                               ULONG dwIoControlCode,
1286                               LPVOID lpInBuffer, DWORD nInBufferSize,
1287                               LPVOID lpOutBuffer, DWORD nOutBufferSize)
1288 {
1289     NTSTATUS    status;
1290
1291     if (dwIoControlCode == IOCTL_SERIAL_WAIT_ON_MASK)
1292     {
1293         HANDLE          hev = hEvent;
1294
1295         /* this is an ioctl we implement in a non blocking way if hEvent is not
1296          * null
1297          * so we have to explicitly wait if no hEvent is provided
1298          */
1299         if (!hev)
1300         {
1301             OBJECT_ATTRIBUTES   attr;
1302             
1303             attr.Length                   = sizeof(attr);
1304             attr.RootDirectory            = 0;
1305             attr.ObjectName               = NULL;
1306             attr.Attributes               = OBJ_CASE_INSENSITIVE | OBJ_OPENIF;
1307             attr.SecurityDescriptor       = NULL;
1308             attr.SecurityQualityOfService = NULL;
1309             status = NtCreateEvent(&hev, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, FALSE);
1310
1311             if (status) goto done;
1312         }
1313         status = io_control(hDevice, hev, UserApcRoutine, UserApcContext,
1314                             piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
1315                             lpOutBuffer, nOutBufferSize);
1316         if (hev != hEvent)
1317         {
1318             if (status == STATUS_PENDING)
1319             {
1320                 NtWaitForSingleObject(hev, FALSE, NULL);
1321                 status = STATUS_SUCCESS;
1322             }
1323             NtClose(hev);
1324         }
1325     }
1326     else status = io_control(hDevice, hEvent, UserApcRoutine, UserApcContext,
1327                              piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
1328                              lpOutBuffer, nOutBufferSize);
1329 done:
1330     return status;
1331 }