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