mshtml: Use the correct variable in the FIXME.
[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 #endif    /* Don't have linux/serial.h or lack TIOCSSERIAL */
497         ERR("baudrate %d\n", sbr->BaudRate);
498         return STATUS_NOT_SUPPORTED;
499     }
500 #elif !defined(__EMX__)
501     switch (sbr->BaudRate)
502     {
503     case 0:             port.c_ospeed = B0;     break;
504     case 50:            port.c_ospeed = B50;    break;
505     case 75:            port.c_ospeed = B75;    break;
506     case 110:
507     case CBR_110:       port.c_ospeed = B110;   break;
508     case 134:           port.c_ospeed = B134;   break;
509     case 150:           port.c_ospeed = B150;   break;
510     case 200:           port.c_ospeed = B200;   break;
511     case 300:
512     case CBR_300:       port.c_ospeed = B300;   break;
513     case 600:
514     case CBR_600:       port.c_ospeed = B600;   break;
515     case 1200:
516     case CBR_1200:      port.c_ospeed = B1200;  break;
517     case 1800:          port.c_ospeed = B1800;  break;
518     case 2400:
519     case CBR_2400:      port.c_ospeed = B2400;  break;
520     case 4800:
521     case CBR_4800:      port.c_ospeed = B4800;  break;
522     case 9600:
523     case CBR_9600:      port.c_ospeed = B9600;  break;
524     case 19200:
525     case CBR_19200:     port.c_ospeed = B19200; break;
526     case 38400:
527     case CBR_38400:     port.c_ospeed = B38400; break;
528 #ifdef B57600
529     case 57600:
530     case CBR_57600:     port.c_cflag |= B57600; break;
531 #endif
532 #ifdef B115200
533     case 115200:
534     case CBR_115200:    port.c_cflag |= B115200;break;
535 #endif
536 #ifdef B230400
537     case 230400:        port.c_cflag |= B230400;break;
538 #endif
539 #ifdef B460800
540     case 460800:        port.c_cflag |= B460800;break;
541 #endif
542     default:
543         ERR("baudrate %d\n", sbr->BaudRate);
544         return STATUS_NOT_SUPPORTED;
545     }
546     port.c_ispeed = port.c_ospeed;
547 #endif
548     if (tcsetattr(fd, TCSANOW, &port) == -1)
549     {
550         ERR("tcsetattr error '%s'\n", strerror(errno));
551         return FILE_GetNtStatus();
552     }
553     return STATUS_SUCCESS;
554 }
555
556 static int whack_modem(int fd, unsigned int andy, unsigned int orrie)
557 {
558 #ifdef TIOCMGET
559     unsigned int mstat, okay;
560     okay = ioctl(fd, TIOCMGET, &mstat);
561     if (okay) return okay;
562     if (andy) mstat &= andy;
563     mstat |= orrie;
564     return ioctl(fd, TIOCMSET, &mstat);
565 #else
566     return 0;
567 #endif
568 }
569
570 static NTSTATUS set_handflow(int fd, const SERIAL_HANDFLOW* shf)
571 {
572     struct termios port;
573
574     if ((shf->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE)) == 
575         (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE))
576         return STATUS_NOT_SUPPORTED;
577
578     if (tcgetattr(fd, &port) == -1)
579     {
580         ERR("tcgetattr error '%s'\n", strerror(errno));
581         return FILE_GetNtStatus();
582     }
583     
584 #ifdef CRTSCTS
585     if ((shf->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
586         (shf->FlowReplace & SERIAL_RTS_HANDSHAKE))
587     {
588         port.c_cflag |= CRTSCTS;
589         TRACE("CRTSCTS\n");
590     }
591     else
592         port.c_cflag &= ~CRTSCTS;
593 #endif
594 #ifdef TIOCM_DTR
595     if (shf->ControlHandShake & SERIAL_DTR_HANDSHAKE)
596     {
597         WARN("DSR/DTR flow control not supported\n");
598     } else if (!(shf->ControlHandShake & SERIAL_DTR_CONTROL))
599         whack_modem(fd, ~TIOCM_DTR, 0);
600     else
601         whack_modem(fd, 0, TIOCM_DTR);
602 #endif
603 #ifdef TIOCM_RTS
604     if (!(shf->ControlHandShake & SERIAL_CTS_HANDSHAKE))
605     {
606         if ((shf->FlowReplace & (SERIAL_RTS_CONTROL|SERIAL_RTS_HANDSHAKE)) == 0)
607             whack_modem(fd, ~TIOCM_RTS, 0);
608         else    
609             whack_modem(fd, 0, TIOCM_RTS);
610     }
611 #endif
612
613     if (shf->FlowReplace & SERIAL_AUTO_RECEIVE)
614         port.c_iflag |= IXON;
615     else
616         port.c_iflag &= ~IXON;
617     if (shf->FlowReplace & SERIAL_AUTO_TRANSMIT)
618         port.c_iflag |= IXOFF;
619     else
620         port.c_iflag &= ~IXOFF;
621     if (tcsetattr(fd, TCSANOW, &port) == -1)
622     {
623         ERR("tcsetattr error '%s'\n", strerror(errno));
624         return FILE_GetNtStatus();
625     }
626
627     return STATUS_SUCCESS;
628 }
629
630 static NTSTATUS set_line_control(int fd, const SERIAL_LINE_CONTROL* slc)
631 {
632     struct termios port;
633     unsigned bytesize, stopbits;
634     
635     if (tcgetattr(fd, &port) == -1)
636     {
637         ERR("tcgetattr error '%s'\n", strerror(errno));
638         return FILE_GetNtStatus();
639     }
640     
641 #ifdef IMAXBEL
642     port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|PARMRK|IMAXBEL);
643 #else
644     port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|PARMRK);
645 #endif
646     port.c_iflag |= IGNBRK | INPCK;
647     
648     port.c_oflag &= ~(OPOST);
649     
650     port.c_cflag &= ~(HUPCL);
651     port.c_cflag |= CLOCAL | CREAD;
652     
653     port.c_lflag &= ~(ICANON|ECHO|ISIG);
654     port.c_lflag |= NOFLSH;
655     
656     bytesize = slc->WordLength;
657     stopbits = slc->StopBits;
658     
659 #ifdef CMSPAR
660     port.c_cflag &= ~(PARENB | PARODD | CMSPAR);
661 #else
662     port.c_cflag &= ~(PARENB | PARODD);
663 #endif
664
665     switch (slc->Parity)
666     {
667     case NOPARITY:      port.c_iflag &= ~INPCK;                         break;
668     case ODDPARITY:     port.c_cflag |= PARENB | PARODD;                break;
669     case EVENPARITY:    port.c_cflag |= PARENB;                         break;
670 #ifdef CMSPAR
671         /* Linux defines mark/space (stick) parity */
672     case MARKPARITY:    port.c_cflag |= PARENB | CMSPAR;                break;
673     case SPACEPARITY:   port.c_cflag |= PARENB | PARODD |  CMSPAR;      break;
674 #else
675         /* try the POSIX way */
676     case MARKPARITY:
677         if (slc->StopBits == ONESTOPBIT)
678         {
679             stopbits = TWOSTOPBITS;
680             port.c_iflag &= ~INPCK;
681         }
682         else
683         {
684             ERR("Cannot set MARK Parity\n");
685             return STATUS_NOT_SUPPORTED;
686         }
687         break;
688     case SPACEPARITY:
689         if (slc->WordLength < 8)
690         {
691             bytesize +=1;
692             port.c_iflag &= ~INPCK;
693         }
694         else
695         {
696             ERR("Cannot set SPACE Parity\n");
697             return STATUS_NOT_SUPPORTED;
698         }
699         break;
700 #endif
701     default:
702         ERR("Parity\n");
703         return STATUS_NOT_SUPPORTED;
704     }
705     
706     port.c_cflag &= ~CSIZE;
707     switch (bytesize)
708     {
709     case 5:     port.c_cflag |= CS5;    break;
710     case 6:     port.c_cflag |= CS6;    break;
711     case 7:     port.c_cflag |= CS7;    break;
712     case 8:     port.c_cflag |= CS8;    break;
713     default:
714         ERR("ByteSize\n");
715         return STATUS_NOT_SUPPORTED;
716     }
717     
718     switch (stopbits)
719     {
720     case ONESTOPBIT:    port.c_cflag &= ~CSTOPB;        break;
721     case ONE5STOPBITS: /* will be selected if bytesize is 5 */
722     case TWOSTOPBITS:   port.c_cflag |= CSTOPB;         break;
723     default:
724         ERR("StopBits\n");
725         return STATUS_NOT_SUPPORTED;
726     }
727     /* otherwise it hangs with pending input*/
728     if (tcsetattr(fd, TCSANOW, &port) == -1)
729     {
730         ERR("tcsetattr error '%s'\n", strerror(errno));
731         return FILE_GetNtStatus();
732     }
733     return STATUS_SUCCESS;
734 }
735
736 static NTSTATUS set_queue_size(int fd, const SERIAL_QUEUE_SIZE* sqs)
737 {
738     FIXME("insize %d outsize %d unimplemented stub\n", sqs->InSize, sqs->OutSize);
739     return STATUS_SUCCESS;
740 }
741
742 static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc)
743 {
744     struct termios port;
745     
746     if (tcgetattr(fd, &port) == -1)
747     {
748         ERR("tcgetattr error '%s'\n", strerror(errno));
749         return FILE_GetNtStatus();
750     }
751     
752     port.c_cc[VMIN  ] = 0;
753     port.c_cc[VTIME ] = 1;
754     
755     port.c_cc[VEOF  ] = sc->EofChar;
756     /* FIXME: sc->ErrorChar is not supported */
757     /* FIXME: sc->BreakChar is not supported */
758     /* FIXME: sc->EventChar is not supported */
759     port.c_cc[VSTART] = sc->XonChar;
760     port.c_cc[VSTOP ] = sc->XoffChar;
761     
762     if (tcsetattr(fd, TCSANOW, &port) == -1)
763     {
764         ERR("tcsetattr error '%s'\n", strerror(errno));
765         return FILE_GetNtStatus();
766     }
767     return STATUS_SUCCESS;
768 }
769
770 static NTSTATUS set_timeouts(HANDLE handle, int fd, const SERIAL_TIMEOUTS* st)
771 {
772     NTSTATUS            status;
773     struct termios      port;
774     unsigned int        ux_timeout;
775
776     SERVER_START_REQ( set_serial_info )
777     {
778         req->handle       = handle;
779         req->flags        = SERIALINFO_SET_TIMEOUTS;
780         req->readinterval = st->ReadIntervalTimeout ;
781         req->readmult     = st->ReadTotalTimeoutMultiplier ;
782         req->readconst    = st->ReadTotalTimeoutConstant ;
783         req->writemult    = st->WriteTotalTimeoutMultiplier ;
784         req->writeconst   = st->WriteTotalTimeoutConstant ;
785         status = wine_server_call( req );
786     }
787     SERVER_END_REQ;
788     if (status) return status;
789
790     if (tcgetattr(fd, &port) == -1)
791     {
792         FIXME("tcgetattr on fd %d failed (%s)!\n", fd, strerror(errno));
793         return FILE_GetNtStatus();
794     }
795
796     /* VTIME is in 1/10 seconds */
797     if (st->ReadIntervalTimeout == 0) /* 0 means no timeout */
798         ux_timeout = 0;
799     else
800     {
801         ux_timeout = (st->ReadIntervalTimeout + 99) / 100;
802         if (ux_timeout == 0)
803             ux_timeout = 1; /* must be at least some timeout */
804     }
805     port.c_cc[VTIME] = ux_timeout;
806
807     if (tcsetattr(fd, 0, &port) == -1)
808     {
809         FIXME("tcsetattr on fd %d failed (%s)!\n", fd, strerror(errno));
810         return FILE_GetNtStatus();
811     }
812     return STATUS_SUCCESS;
813 }
814
815 static NTSTATUS set_wait_mask(HANDLE hDevice, DWORD mask)
816 {
817     NTSTATUS status;
818
819     SERVER_START_REQ( set_serial_info )
820     {
821         req->handle    = hDevice;
822         req->flags     = SERIALINFO_SET_MASK;
823         req->eventmask = mask;
824         status = wine_server_call( req );
825     }
826     SERVER_END_REQ;
827     return status;
828 }
829
830 static NTSTATUS set_XOff(int fd)
831 {
832     struct termios      port;
833
834     if (tcgetattr(fd,&port) == -1)
835     {
836         FIXME("tcgetattr on fd %d failed (%s)!\n", fd, strerror(errno));
837         return FILE_GetNtStatus();
838
839
840     }
841     port.c_iflag |= IXOFF;
842     if (tcsetattr(fd, TCSADRAIN, &port) == -1)
843     {
844         FIXME("tcsetattr on fd %d failed (%s)!\n", fd, strerror(errno));
845         return FILE_GetNtStatus();
846     }
847     return STATUS_SUCCESS;
848 }
849
850 static NTSTATUS set_XOn(int fd)
851 {
852     struct termios      port;
853
854     if (tcgetattr(fd,&port) == -1)
855     {
856         FIXME("tcgetattr on fd %d failed (%s)!\n", fd, strerror(errno));
857         return FILE_GetNtStatus();
858     }
859     port.c_iflag |= IXON;
860     if (tcsetattr(fd, TCSADRAIN, &port) == -1)
861     {
862         FIXME("tcsetattr on fd %d failed (%s)!\n", fd, strerror(errno));
863         return FILE_GetNtStatus();
864     }
865     return STATUS_SUCCESS;
866 }
867
868 /*             serial_irq_info
869  * local structure holding the irq values we need for WaitCommEvent()
870  *
871  * Stripped down from struct serial_icounter_struct, which may not be available on some systems
872  * As the modem line interrupts (cts, dsr, rng, dcd) only get updated with TIOCMIWAIT active,
873  * no need to carry them in the internal structure
874  *
875  */
876 typedef struct serial_irq_info
877 {
878     int rx , tx, frame, overrun, parity, brk, buf_overrun;
879 }serial_irq_info;
880
881 /***********************************************************************
882  * Data needed by the thread polling for the changing CommEvent
883  */
884 typedef struct async_commio
885 {
886     HANDLE              hDevice;
887     DWORD*              events;
888     HANDLE              hEvent;
889     DWORD               evtmask;
890     DWORD               mstat;
891     serial_irq_info     irq_info;
892 } async_commio;
893
894 /***********************************************************************
895  *   Get extended interrupt count info, needed for wait_on
896  */
897 static NTSTATUS get_irq_info(int fd, serial_irq_info *irq_info)
898 {
899 #ifdef TIOCGICOUNT
900     struct serial_icounter_struct einfo;
901     if (!ioctl(fd, TIOCGICOUNT, &einfo))
902     {
903         irq_info->rx          = einfo.rx;
904         irq_info->tx          = einfo.tx;
905         irq_info->frame       = einfo.frame;
906         irq_info->overrun     = einfo.overrun;
907         irq_info->parity      = einfo.parity;
908         irq_info->brk         = einfo.brk;
909         irq_info->buf_overrun = einfo.buf_overrun;
910         return STATUS_SUCCESS;
911     }
912     TRACE("TIOCGICOUNT err %s\n", strerror(errno));
913     return FILE_GetNtStatus();
914 #endif
915     memset(irq_info,0, sizeof(serial_irq_info));
916     return STATUS_NOT_IMPLEMENTED;
917 }
918
919
920 static DWORD WINAPI check_events(int fd, DWORD mask, 
921                                  const serial_irq_info *new, 
922                                  const serial_irq_info *old,
923                                  DWORD new_mstat, DWORD old_mstat)
924 {
925     DWORD ret = 0, queue;
926
927     TRACE("mask 0x%08x\n", mask);
928     TRACE("old->rx          0x%08x vs. new->rx          0x%08x\n", old->rx, new->rx);
929     TRACE("old->tx          0x%08x vs. new->tx          0x%08x\n", old->tx, new->tx);
930     TRACE("old->frame       0x%08x vs. new->frame       0x%08x\n", old->frame, new->frame);
931     TRACE("old->overrun     0x%08x vs. new->overrun     0x%08x\n", old->overrun, new->overrun);
932     TRACE("old->parity      0x%08x vs. new->parity      0x%08x\n", old->parity, new->parity);
933     TRACE("old->brk         0x%08x vs. new->brk         0x%08x\n", old->brk, new->brk);
934     TRACE("old->buf_overrun 0x%08x vs. new->buf_overrun 0x%08x\n", old->buf_overrun, new->buf_overrun);
935
936     if (old->brk != new->brk) ret |= EV_BREAK;
937     if ((old_mstat & MS_CTS_ON ) != (new_mstat & MS_CTS_ON )) ret |= EV_CTS;
938     if ((old_mstat & MS_DSR_ON ) != (new_mstat & MS_DSR_ON )) ret |= EV_DSR;
939     if ((old_mstat & MS_RING_ON) != (new_mstat & MS_RING_ON)) ret |= EV_RING;
940     if ((old_mstat & MS_RLSD_ON) != (new_mstat & MS_RLSD_ON)) ret |= EV_RLSD;
941     if (old->frame != new->frame || old->overrun != new->overrun || old->parity != new->parity) ret |= EV_ERR;
942     if (mask & EV_RXCHAR)
943     {
944         queue = 0;
945 #ifdef TIOCINQ
946         if (ioctl(fd, TIOCINQ, &queue))
947             WARN("TIOCINQ returned error\n");
948 #endif
949         if (queue)
950             ret |= EV_RXCHAR;
951     }
952     if (mask & EV_TXEMPTY)
953     {
954         queue = 0;
955 /* We really want to know when all characters have gone out of the transmitter */
956 #if defined(TIOCSERGETLSR) 
957         if (ioctl(fd, TIOCSERGETLSR, &queue))
958             WARN("TIOCSERGETLSR returned error\n");
959         if (queue)
960 /* TIOCOUTQ only checks for an empty buffer */
961 #elif defined(TIOCOUTQ)
962         if (ioctl(fd, TIOCOUTQ, &queue))
963             WARN("TIOCOUTQ returned error\n");
964         if (!queue)
965 #endif
966            ret |= EV_TXEMPTY;
967         TRACE("OUTQUEUE %d, Transmitter %sempty\n",
968               queue, (ret & EV_TXEMPTY) ? "" : "not ");
969     }
970     return ret & mask;
971 }
972
973 /***********************************************************************
974  *             wait_for_event      (INTERNAL)
975  *
976  *  We need to poll for what is interesting
977  *  TIOCMIWAIT only checks modem status line and may not be aborted by a changing mask
978  *
979  */
980 static DWORD CALLBACK wait_for_event(LPVOID arg)
981 {
982     async_commio *commio = (async_commio*) arg;
983     int fd, needs_close;
984
985     if (!server_get_unix_fd( commio->hDevice, FILE_READ_DATA | FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))
986     {
987         serial_irq_info new_irq_info;
988         DWORD new_mstat, new_evtmask;
989         LARGE_INTEGER time;
990         
991         TRACE("device=%p fd=0x%08x mask=0x%08x buffer=%p event=%p irq_info=%p\n", 
992               commio->hDevice, fd, commio->evtmask, commio->events, commio->hEvent, &commio->irq_info);
993
994         time.QuadPart = (ULONGLONG)10000;
995         time.QuadPart = -time.QuadPart;
996         for (;;)
997         {
998             /*
999              * TIOCMIWAIT is not adequate
1000              *
1001              * FIXME:
1002              * We don't handle the EV_RXFLAG (the eventchar)
1003              */
1004             NtDelayExecution(FALSE, &time);
1005             get_irq_info(fd, &new_irq_info);
1006             if (get_modem_status(fd, &new_mstat))
1007                 TRACE("get_modem_status failed\n");
1008             *commio->events = check_events(fd, commio->evtmask,
1009                                            &new_irq_info, &commio->irq_info,
1010                                            new_mstat, commio->mstat);
1011             if (*commio->events) break;
1012             get_wait_mask(commio->hDevice, &new_evtmask);
1013             if (commio->evtmask != new_evtmask)
1014             {
1015                 *commio->events = 0;
1016                 break;
1017             }
1018         }
1019         if (needs_close) close( fd );
1020     }
1021     if (commio->hEvent) NtSetEvent(commio->hEvent, NULL);
1022     RtlFreeHeap(GetProcessHeap(), 0, commio);
1023     return 0;
1024 }
1025
1026 static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, DWORD* events)
1027 {
1028     async_commio*       commio;
1029     NTSTATUS            status;
1030
1031     if ((status = NtResetEvent(hEvent, NULL)))
1032         return status;
1033
1034     commio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof (async_commio));
1035     if (!commio) return STATUS_NO_MEMORY;
1036
1037     commio->hDevice = hDevice;
1038     commio->events  = events;
1039     commio->hEvent  = hEvent;
1040     get_wait_mask(commio->hDevice, &commio->evtmask);
1041
1042 /* We may never return, if some capabilities miss
1043  * Return error in that case
1044  */
1045 #if !defined(TIOCINQ)
1046     if (commio->evtmask & EV_RXCHAR)
1047         goto error_caps;
1048 #endif
1049 #if !(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)
1050     if (commio->evtmask & EV_TXEMPTY)
1051         goto error_caps;
1052 #endif
1053 #if !defined(TIOCMGET)
1054     if (commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD))
1055         goto error_caps;
1056 #endif
1057 #if !defined(TIOCM_CTS)
1058     if (commio->evtmask & EV_CTS)
1059         goto error_caps;
1060 #endif
1061 #if !defined(TIOCM_DSR)
1062     if (commio->evtmask & EV_DSR)
1063         goto error_caps;
1064 #endif
1065 #if !defined(TIOCM_RNG)
1066     if (commio->evtmask & EV_RING)
1067         goto error_caps;
1068 #endif
1069 #if !defined(TIOCM_CAR)
1070     if (commio->evtmask & EV_RLSD)
1071         goto error_caps;
1072 #endif
1073     if (commio->evtmask & EV_RXFLAG)
1074         FIXME("EV_RXFLAG not handled\n");
1075     if ((status = get_irq_info(fd, &commio->irq_info)) ||
1076         (status = get_modem_status(fd, &commio->mstat)))
1077         goto out_now;
1078
1079     /* We might have received something or the TX bufffer is delivered */
1080     *events = check_events(fd, commio->evtmask,
1081                                &commio->irq_info, &commio->irq_info,
1082                                commio->mstat, commio->mstat);
1083     if (*events) goto out_now;
1084
1085     /* create the worker for the task */
1086     status = RtlQueueWorkItem(wait_for_event, commio, 0 /* FIXME */);
1087     if (status != STATUS_SUCCESS) goto out_now;
1088     return STATUS_PENDING;
1089
1090 #if !defined(TIOCINQ) || (!(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)) || !defined(TIOCMGET) || !defined(TIOCM_CTS) ||!defined(TIOCM_DSR) || !defined(TIOCM_RNG) || !defined(TIOCM_CAR)
1091 error_caps:
1092     FIXME("Returning error because of missing capabilities\n");
1093     status = STATUS_INVALID_PARAMETER;
1094 #endif
1095 out_now:
1096     RtlFreeHeap(GetProcessHeap(), 0, commio);
1097     return status;
1098 }
1099
1100 static NTSTATUS xmit_immediate(HANDLE hDevice, int fd, char* ptr)
1101 {
1102     /* FIXME: not perfect as it should bypass the in-queue */
1103     WARN("(%p,'%c') not perfect!\n", hDevice, *ptr);
1104     if (write(fd, ptr, 1) != 1)
1105         return FILE_GetNtStatus();
1106     return STATUS_SUCCESS;
1107 }
1108
1109 /******************************************************************
1110  *              COMM_DeviceIoControl
1111  *
1112  *
1113  */
1114 static inline NTSTATUS io_control(HANDLE hDevice, 
1115                                   HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1116                                   PVOID UserApcContext, 
1117                                   PIO_STATUS_BLOCK piosb, 
1118                                   ULONG dwIoControlCode,
1119                                   LPVOID lpInBuffer, DWORD nInBufferSize,
1120                                   LPVOID lpOutBuffer, DWORD nOutBufferSize)
1121 {
1122     DWORD       sz = 0, access = FILE_READ_DATA;
1123     NTSTATUS    status = STATUS_SUCCESS;
1124     int         fd = -1, needs_close = 0;
1125
1126     TRACE("%p %s %p %d %p %d %p\n",
1127           hDevice, iocode2str(dwIoControlCode), lpInBuffer, nInBufferSize,
1128           lpOutBuffer, nOutBufferSize, piosb);
1129
1130     piosb->Information = 0;
1131
1132     if (dwIoControlCode != IOCTL_SERIAL_GET_TIMEOUTS)
1133         if ((status = server_get_unix_fd( hDevice, access, &fd, &needs_close, NULL, NULL )))
1134             goto error;
1135
1136     switch (dwIoControlCode)
1137     {
1138     case IOCTL_SERIAL_CLR_DTR:
1139 #ifdef TIOCM_DTR
1140         if (whack_modem(fd, ~TIOCM_DTR, 0) == -1) status = FILE_GetNtStatus();
1141 #else
1142         status = STATUS_NOT_SUPPORTED;
1143 #endif
1144         break;
1145     case IOCTL_SERIAL_CLR_RTS:
1146 #ifdef TIOCM_RTS
1147         if (whack_modem(fd, ~TIOCM_RTS, 0) == -1) status = FILE_GetNtStatus();
1148 #else
1149         status = STATUS_NOT_SUPPORTED;
1150 #endif
1151         break;
1152     case IOCTL_SERIAL_GET_BAUD_RATE:
1153         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_BAUD_RATE))
1154         {
1155             if (!(status = get_baud_rate(fd, (SERIAL_BAUD_RATE*)lpOutBuffer)))
1156                 sz = sizeof(SERIAL_BAUD_RATE);
1157         }
1158         else
1159             status = STATUS_INVALID_PARAMETER;
1160         break;
1161     case IOCTL_SERIAL_GET_CHARS:
1162         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_CHARS))
1163         {
1164             if (!(status = get_special_chars(fd, (SERIAL_CHARS*)lpOutBuffer)))
1165                 sz = sizeof(SERIAL_CHARS);
1166         }
1167         else
1168             status = STATUS_INVALID_PARAMETER;
1169         break;
1170      case IOCTL_SERIAL_GET_COMMSTATUS:
1171         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_STATUS))
1172         {
1173             if (!(status = get_status(fd, (SERIAL_STATUS*)lpOutBuffer)))
1174                 sz = sizeof(SERIAL_STATUS);
1175         }
1176         else status = STATUS_INVALID_PARAMETER;
1177         break;
1178     case IOCTL_SERIAL_GET_HANDFLOW:
1179         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_HANDFLOW))
1180         {
1181             if (!(status = get_hand_flow(fd, (SERIAL_HANDFLOW*)lpOutBuffer)))
1182                 sz = sizeof(SERIAL_HANDFLOW);
1183         }
1184         else
1185             status = STATUS_INVALID_PARAMETER;
1186         break;
1187     case IOCTL_SERIAL_GET_LINE_CONTROL:
1188         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_LINE_CONTROL))
1189         {
1190             if (!(status = get_line_control(fd, (SERIAL_LINE_CONTROL*)lpOutBuffer)))
1191                 sz = sizeof(SERIAL_LINE_CONTROL);
1192         }
1193         else
1194             status = STATUS_INVALID_PARAMETER;
1195         break;
1196     case IOCTL_SERIAL_GET_MODEMSTATUS:
1197         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1198         {
1199             if (!(status = get_modem_status(fd, (DWORD*)lpOutBuffer)))
1200                 sz = sizeof(DWORD);
1201         }
1202         else status = STATUS_INVALID_PARAMETER;
1203         break;
1204     case IOCTL_SERIAL_GET_TIMEOUTS:
1205         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_TIMEOUTS))
1206         {
1207             if (!(status = get_timeouts(hDevice, (SERIAL_TIMEOUTS*)lpOutBuffer)))
1208                 sz = sizeof(SERIAL_TIMEOUTS);
1209         }
1210         else
1211             status = STATUS_INVALID_PARAMETER;
1212         break;
1213     case IOCTL_SERIAL_GET_WAIT_MASK:
1214         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1215         {
1216             if (!(status = get_wait_mask(hDevice, (DWORD*)lpOutBuffer)))
1217                 sz = sizeof(DWORD);
1218         }
1219         else
1220             status = STATUS_INVALID_PARAMETER;
1221         break;
1222     case IOCTL_SERIAL_IMMEDIATE_CHAR:
1223         if (lpInBuffer && nInBufferSize == sizeof(CHAR))
1224             status = xmit_immediate(hDevice, fd, lpInBuffer);
1225         else
1226             status = STATUS_INVALID_PARAMETER;
1227         break;
1228     case IOCTL_SERIAL_PURGE:
1229         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
1230             status = purge(fd, *(DWORD*)lpInBuffer);
1231         else
1232             status = STATUS_INVALID_PARAMETER;
1233         break;
1234     case IOCTL_SERIAL_RESET_DEVICE:
1235         FIXME("Unsupported\n");
1236         break;
1237     case IOCTL_SERIAL_SET_BAUD_RATE:
1238         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_BAUD_RATE))
1239             status = set_baud_rate(fd, (const SERIAL_BAUD_RATE*)lpInBuffer);
1240         else
1241             status = STATUS_INVALID_PARAMETER;
1242         break;
1243     case IOCTL_SERIAL_SET_BREAK_OFF:
1244 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
1245         if (ioctl(fd, TIOCCBRK, 0) == -1)
1246         {
1247             TRACE("ioctl failed\n");
1248             status = FILE_GetNtStatus();
1249         }
1250 #else
1251         FIXME("ioctl not available\n");
1252         status = STATUS_NOT_SUPPORTED;
1253 #endif
1254         break;
1255     case IOCTL_SERIAL_SET_BREAK_ON:
1256 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
1257         if (ioctl(fd, TIOCSBRK, 0) == -1)
1258         {
1259             TRACE("ioctl failed\n");
1260             status = FILE_GetNtStatus();
1261         }
1262 #else
1263         FIXME("ioctl not available\n");
1264         status = STATUS_NOT_SUPPORTED;
1265 #endif
1266         break;
1267     case IOCTL_SERIAL_SET_CHARS:
1268         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_CHARS))
1269             status = set_special_chars(fd, (const SERIAL_CHARS*)lpInBuffer);
1270         else
1271             status = STATUS_INVALID_PARAMETER;
1272         break;
1273     case IOCTL_SERIAL_SET_DTR:
1274 #ifdef TIOCM_DTR
1275         if (whack_modem(fd, 0, TIOCM_DTR) == -1) status = FILE_GetNtStatus();
1276 #else
1277         status = STATUS_NOT_SUPPORTED;
1278 #endif
1279         break;
1280     case IOCTL_SERIAL_SET_HANDFLOW:
1281         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_HANDFLOW))
1282             status = set_handflow(fd, (const SERIAL_HANDFLOW*)lpInBuffer);
1283         else
1284             status = STATUS_INVALID_PARAMETER;
1285         break;
1286     case IOCTL_SERIAL_SET_LINE_CONTROL:
1287         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_LINE_CONTROL))
1288             status = set_line_control(fd, (const SERIAL_LINE_CONTROL*)lpInBuffer);
1289         else
1290             status = STATUS_INVALID_PARAMETER;
1291         break;
1292     case IOCTL_SERIAL_SET_QUEUE_SIZE:
1293         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_QUEUE_SIZE))
1294             status = set_queue_size(fd, (const SERIAL_QUEUE_SIZE*)lpInBuffer);
1295         else
1296             status = STATUS_INVALID_PARAMETER;
1297         break;
1298     case IOCTL_SERIAL_SET_RTS:
1299 #ifdef TIOCM_RTS
1300         if (whack_modem(fd, 0, TIOCM_RTS) == -1) status = FILE_GetNtStatus();
1301 #else
1302         status = STATUS_NOT_SUPPORTED;
1303 #endif
1304         break;
1305     case IOCTL_SERIAL_SET_TIMEOUTS:
1306         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_TIMEOUTS))
1307             status = set_timeouts(hDevice, fd, (const SERIAL_TIMEOUTS*)lpInBuffer);
1308         else
1309             status = STATUS_INVALID_PARAMETER;
1310         break;
1311     case IOCTL_SERIAL_SET_WAIT_MASK:
1312         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
1313         {
1314             status = set_wait_mask(hDevice, *(DWORD*)lpInBuffer);
1315         }
1316         else status = STATUS_INVALID_PARAMETER;
1317         break;
1318     case IOCTL_SERIAL_SET_XOFF:
1319         status = set_XOff(fd);
1320         break;
1321     case IOCTL_SERIAL_SET_XON:
1322         status = set_XOn(fd);
1323         break;
1324     case IOCTL_SERIAL_WAIT_ON_MASK:
1325         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1326         {
1327             if (!(status = wait_on(hDevice, fd, hEvent, (DWORD*)lpOutBuffer)))
1328                 sz = sizeof(DWORD);
1329         }
1330         else
1331             status = STATUS_INVALID_PARAMETER;
1332         break;
1333     default:
1334         FIXME("Unsupported IOCTL %x (type=%x access=%x func=%x meth=%x)\n", 
1335               dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
1336               (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
1337         sz = 0;
1338         status = STATUS_INVALID_PARAMETER;
1339         break;
1340     }
1341     if (needs_close) close( fd );
1342  error:
1343     piosb->u.Status = status;
1344     piosb->Information = sz;
1345     if (hEvent && status != STATUS_PENDING) NtSetEvent(hEvent, NULL);
1346     return status;
1347 }
1348
1349 NTSTATUS COMM_DeviceIoControl(HANDLE hDevice, 
1350                               HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1351                               PVOID UserApcContext, 
1352                               PIO_STATUS_BLOCK piosb, 
1353                               ULONG dwIoControlCode,
1354                               LPVOID lpInBuffer, DWORD nInBufferSize,
1355                               LPVOID lpOutBuffer, DWORD nOutBufferSize)
1356 {
1357     NTSTATUS    status;
1358
1359     if (dwIoControlCode == IOCTL_SERIAL_WAIT_ON_MASK)
1360     {
1361         HANDLE          hev = hEvent;
1362
1363         /* this is an ioctl we implement in a non blocking way if hEvent is not
1364          * null
1365          * so we have to explicitely wait if no hEvent is provided
1366          */
1367         if (!hev)
1368         {
1369             OBJECT_ATTRIBUTES   attr;
1370             
1371             attr.Length                   = sizeof(attr);
1372             attr.RootDirectory            = 0;
1373             attr.ObjectName               = NULL;
1374             attr.Attributes               = OBJ_CASE_INSENSITIVE | OBJ_OPENIF;
1375             attr.SecurityDescriptor       = NULL;
1376             attr.SecurityQualityOfService = NULL;
1377             status = NtCreateEvent(&hev, EVENT_ALL_ACCESS, &attr, FALSE, FALSE);
1378
1379             if (status) goto done;
1380         }
1381         status = io_control(hDevice, hev, UserApcRoutine, UserApcContext,
1382                             piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
1383                             lpOutBuffer, nOutBufferSize);
1384         if (hev != hEvent)
1385         {
1386             if (status == STATUS_PENDING)
1387             {
1388                 NtWaitForSingleObject(hev, FALSE, NULL);
1389                 status = STATUS_SUCCESS;
1390             }
1391             NtClose(hev);
1392         }
1393     }
1394     else status = io_control(hDevice, hEvent, UserApcRoutine, UserApcContext,
1395                              piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
1396                              lpOutBuffer, nOutBufferSize);
1397 done:
1398     return status;
1399 }