dinput: Add effect gain support.
[wine] / dlls / ntdll / serial.c
1 /* Main file for COMM support
2  *
3  * DEC 93 Erik Bos <erik@xs4all.nl>
4  * Copyright 1996 Marcus Meissner
5  * Copyright 2005,2006 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <errno.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #ifdef HAVE_STRINGS_H
31 # include <strings.h>
32 #endif
33 #ifdef HAVE_TERMIOS_H
34 #include <termios.h>
35 #endif
36 #ifdef HAVE_IO_H
37 # include <io.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <fcntl.h>
43 #ifdef HAVE_SYS_STAT_H
44 # include <sys/stat.h>
45 #endif
46 #include <sys/types.h>
47 #ifdef HAVE_SYS_FILIO_H
48 # include <sys/filio.h>
49 #endif
50 #ifdef HAVE_SYS_IOCTL_H
51 #include <sys/ioctl.h>
52 #endif
53 #ifdef HAVE_SYS_POLL_H
54 # include <sys/poll.h>
55 #endif
56 #ifdef HAVE_SYS_MODEM_H
57 # include <sys/modem.h>
58 #endif
59 #ifdef HAVE_SYS_STRTIO_H
60 # include <sys/strtio.h>
61 #endif
62
63 #define NONAMELESSUNION
64 #define NONAMELESSSTRUCT
65 #include "ntstatus.h"
66 #define WIN32_NO_STATUS
67 #include "windef.h"
68 #include "winternl.h"
69 #include "winioctl.h"
70 #include "ddk/ntddser.h"
71 #include "ntdll_misc.h"
72 #include "wine/server.h"
73 #include "wine/library.h"
74 #include "wine/debug.h"
75
76 #ifdef HAVE_LINUX_SERIAL_H
77 #ifdef HAVE_ASM_TYPES_H
78 #include <asm/types.h>
79 #endif
80 #include <linux/serial.h>
81 #endif
82
83 #if !defined(TIOCINQ) && defined(FIONREAD)
84 #define TIOCINQ FIONREAD
85 #endif
86
87 WINE_DEFAULT_DEBUG_CHANNEL(comm);
88
89 static const char* iocode2str(DWORD ioc)
90 {
91     switch (ioc)
92     {
93 #define X(x)    case (x): return #x
94         X(IOCTL_SERIAL_CLEAR_STATS);
95         X(IOCTL_SERIAL_CLR_DTR);
96         X(IOCTL_SERIAL_CLR_RTS);
97         X(IOCTL_SERIAL_CONFIG_SIZE);
98         X(IOCTL_SERIAL_GET_BAUD_RATE);
99         X(IOCTL_SERIAL_GET_CHARS);
100         X(IOCTL_SERIAL_GET_COMMSTATUS);
101         X(IOCTL_SERIAL_GET_DTRRTS);
102         X(IOCTL_SERIAL_GET_HANDFLOW);
103         X(IOCTL_SERIAL_GET_LINE_CONTROL);
104         X(IOCTL_SERIAL_GET_MODEM_CONTROL);
105         X(IOCTL_SERIAL_GET_MODEMSTATUS);
106         X(IOCTL_SERIAL_GET_PROPERTIES);
107         X(IOCTL_SERIAL_GET_STATS);
108         X(IOCTL_SERIAL_GET_TIMEOUTS);
109         X(IOCTL_SERIAL_GET_WAIT_MASK);
110         X(IOCTL_SERIAL_IMMEDIATE_CHAR);
111         X(IOCTL_SERIAL_LSRMST_INSERT);
112         X(IOCTL_SERIAL_PURGE);
113         X(IOCTL_SERIAL_RESET_DEVICE);
114         X(IOCTL_SERIAL_SET_BAUD_RATE);
115         X(IOCTL_SERIAL_SET_BREAK_ON);
116         X(IOCTL_SERIAL_SET_BREAK_OFF);
117         X(IOCTL_SERIAL_SET_CHARS);
118         X(IOCTL_SERIAL_SET_DTR);
119         X(IOCTL_SERIAL_SET_FIFO_CONTROL);
120         X(IOCTL_SERIAL_SET_HANDFLOW);
121         X(IOCTL_SERIAL_SET_LINE_CONTROL);
122         X(IOCTL_SERIAL_SET_MODEM_CONTROL);
123         X(IOCTL_SERIAL_SET_QUEUE_SIZE);
124         X(IOCTL_SERIAL_SET_RTS);
125         X(IOCTL_SERIAL_SET_TIMEOUTS);
126         X(IOCTL_SERIAL_SET_WAIT_MASK);
127         X(IOCTL_SERIAL_SET_XOFF);
128         X(IOCTL_SERIAL_SET_XON);
129         X(IOCTL_SERIAL_WAIT_ON_MASK);
130         X(IOCTL_SERIAL_XOFF_COUNTER);
131 #undef X
132     default: { static char tmp[32]; sprintf(tmp, "IOCTL_SERIAL_%d\n", ioc); return tmp; }
133     }
134 }
135
136 static NTSTATUS get_baud_rate(int fd, SERIAL_BAUD_RATE* sbr)
137 {
138     struct termios port;
139     int speed;
140     
141     if (tcgetattr(fd, &port) == -1)
142     {
143         ERR("tcgetattr error '%s'\n", strerror(errno));
144         return FILE_GetNtStatus();
145     }
146 #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 = 0;
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     /* termios does not support DTR/DSR flow control */
203     shf->ControlHandShake = 0;
204     shf->FlowReplace = 0;
205 #ifdef TIOCMGET
206     if (ioctl(fd, TIOCMGET, &stat) == -1)
207     {
208         WARN("ioctl error '%s'\n", strerror(errno));
209         shf->ControlHandShake |= SERIAL_DTR_CONTROL;
210         shf->FlowReplace |= SERIAL_RTS_CONTROL;
211     }
212 #else
213     WARN("Setting DTR/RTS to enabled by default\n");
214     shf->ControlHandShake |= SERIAL_DTR_CONTROL;
215     shf->FlowReplace |= SERIAL_RTS_CONTROL;
216 #endif
217 #ifdef TIOCM_DTR
218     if (stat & TIOCM_DTR)
219 #endif
220         shf->ControlHandShake |= SERIAL_DTR_CONTROL;
221 #ifdef CRTSCTS
222     if (port.c_cflag & CRTSCTS)
223     {
224         shf->FlowReplace |= SERIAL_RTS_CONTROL;
225         shf->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
226     }
227     else
228 #endif
229     {
230 #ifdef TIOCM_RTS
231         if (stat & TIOCM_RTS)
232 #endif
233             shf->FlowReplace |= SERIAL_RTS_CONTROL;
234     }
235     if (port.c_iflag & IXOFF)
236         shf->FlowReplace |= SERIAL_AUTO_RECEIVE;
237     if (port.c_iflag & IXON)
238         shf->FlowReplace |= SERIAL_AUTO_TRANSMIT;
239
240     shf->XonLimit = 10;
241     shf->XoffLimit = 10;
242     return STATUS_SUCCESS;
243 }
244
245 static NTSTATUS get_line_control(int fd, SERIAL_LINE_CONTROL* slc)
246 {
247     struct termios port;
248     
249     if (tcgetattr(fd, &port) == -1)
250     {
251         ERR("tcgetattr error '%s'\n", strerror(errno));
252         return FILE_GetNtStatus();
253     }
254     
255 #ifdef CMSPAR
256     switch (port.c_cflag & (PARENB | PARODD | CMSPAR))
257 #else
258     switch (port.c_cflag & (PARENB | PARODD))
259 #endif
260     {
261     case 0:                     slc->Parity = NOPARITY;         break;
262     case PARENB:                slc->Parity = EVENPARITY;       break;
263     case PARENB|PARODD:         slc->Parity = ODDPARITY;        break;
264 #ifdef CMSPAR
265     case PARENB|CMSPAR:         slc->Parity = MARKPARITY;       break;
266     case PARENB|PARODD|CMSPAR:  slc->Parity = SPACEPARITY;      break;
267 #endif
268     }
269     switch (port.c_cflag & CSIZE)
270     {
271     case CS5:   slc->WordLength = 5;    break;
272     case CS6:   slc->WordLength = 6;    break;
273     case CS7:   slc->WordLength = 7;    break;
274     case CS8:   slc->WordLength = 8;    break;
275     default: ERR("unknown size %x\n", (UINT)(port.c_cflag & CSIZE));
276     }
277
278     if (port.c_cflag & CSTOPB)
279     {
280         if (slc->WordLength == 5)
281             slc->StopBits = ONE5STOPBITS;
282         else
283             slc->StopBits = TWOSTOPBITS;
284     }
285     else
286         slc->StopBits = ONESTOPBIT;
287
288     return STATUS_SUCCESS;
289 }
290
291 static NTSTATUS get_modem_status(int fd, DWORD* lpModemStat)
292 {
293     NTSTATUS    status = STATUS_NOT_SUPPORTED;
294     int         mstat;
295
296     *lpModemStat = 0;
297 #ifdef TIOCMGET
298     if (!ioctl(fd, TIOCMGET, &mstat))
299     {
300 #ifdef TIOCM_CTS
301         if (mstat & TIOCM_CTS)  *lpModemStat |= MS_CTS_ON;
302 #endif
303 #ifdef TIOCM_DSR
304         if (mstat & TIOCM_DSR)  *lpModemStat |= MS_DSR_ON;
305 #endif
306 #ifdef TIOCM_RNG
307         if (mstat & TIOCM_RNG)  *lpModemStat |= MS_RING_ON;
308 #endif
309 #ifdef TIOCM_CAR
310         /* FIXME: Not really sure about RLSD UB 990810 */
311         if (mstat & TIOCM_CAR)  *lpModemStat |= MS_RLSD_ON;
312 #endif
313         TRACE("%04x -> %s%s%s%s\n", mstat,
314               (*lpModemStat & MS_RLSD_ON) ? "MS_RLSD_ON " : "",
315               (*lpModemStat & MS_RING_ON) ? "MS_RING_ON " : "",
316               (*lpModemStat & MS_DSR_ON)  ? "MS_DSR_ON  " : "",
317               (*lpModemStat & MS_CTS_ON)  ? "MS_CTS_ON  " : "");
318         return STATUS_SUCCESS;
319     }
320     WARN("ioctl failed\n");
321     status = FILE_GetNtStatus();
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 = wine_server_obj_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 = wine_server_obj_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 |= IXOFF;
616     else
617         port.c_iflag &= ~IXOFF;
618     if (shf->FlowReplace & SERIAL_AUTO_TRANSMIT)
619         port.c_iflag |= IXON;
620     else
621         port.c_iflag &= ~IXON;
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     /*
655      * on FreeBSD, turning off ICANON does not disable IEXTEN,
656      * so we must turn it off explicitly. No harm done on Linux.
657      */
658     port.c_lflag &= ~(ICANON|ECHO|ISIG|IEXTEN);
659     port.c_lflag |= NOFLSH;
660     
661     bytesize = slc->WordLength;
662     stopbits = slc->StopBits;
663     
664 #ifdef CMSPAR
665     port.c_cflag &= ~(PARENB | PARODD | CMSPAR);
666 #else
667     port.c_cflag &= ~(PARENB | PARODD);
668 #endif
669
670     /* make sure that reads don't block */
671     port.c_cc[VMIN] = 0;
672     port.c_cc[VTIME] = 0;
673
674     switch (slc->Parity)
675     {
676     case NOPARITY:      port.c_iflag &= ~INPCK;                         break;
677     case ODDPARITY:     port.c_cflag |= PARENB | PARODD;                break;
678     case EVENPARITY:    port.c_cflag |= PARENB;                         break;
679 #ifdef CMSPAR
680         /* Linux defines mark/space (stick) parity */
681     case MARKPARITY:    port.c_cflag |= PARENB | CMSPAR;                break;
682     case SPACEPARITY:   port.c_cflag |= PARENB | PARODD |  CMSPAR;      break;
683 #else
684         /* try the POSIX way */
685     case MARKPARITY:
686         if (slc->StopBits == ONESTOPBIT)
687         {
688             stopbits = TWOSTOPBITS;
689             port.c_iflag &= ~INPCK;
690         }
691         else
692         {
693             ERR("Cannot set MARK Parity\n");
694             return STATUS_NOT_SUPPORTED;
695         }
696         break;
697     case SPACEPARITY:
698         if (slc->WordLength < 8)
699         {
700             bytesize +=1;
701             port.c_iflag &= ~INPCK;
702         }
703         else
704         {
705             ERR("Cannot set SPACE Parity\n");
706             return STATUS_NOT_SUPPORTED;
707         }
708         break;
709 #endif
710     default:
711         ERR("Parity\n");
712         return STATUS_NOT_SUPPORTED;
713     }
714     
715     port.c_cflag &= ~CSIZE;
716     switch (bytesize)
717     {
718     case 5:     port.c_cflag |= CS5;    break;
719     case 6:     port.c_cflag |= CS6;    break;
720     case 7:     port.c_cflag |= CS7;    break;
721     case 8:     port.c_cflag |= CS8;    break;
722     default:
723         ERR("ByteSize\n");
724         return STATUS_NOT_SUPPORTED;
725     }
726     
727     switch (stopbits)
728     {
729     case ONESTOPBIT:    port.c_cflag &= ~CSTOPB;        break;
730     case ONE5STOPBITS: /* will be selected if bytesize is 5 */
731     case TWOSTOPBITS:   port.c_cflag |= CSTOPB;         break;
732     default:
733         ERR("StopBits\n");
734         return STATUS_NOT_SUPPORTED;
735     }
736     /* otherwise it hangs with pending input*/
737     if (tcsetattr(fd, TCSANOW, &port) == -1)
738     {
739         ERR("tcsetattr error '%s'\n", strerror(errno));
740         return FILE_GetNtStatus();
741     }
742     return STATUS_SUCCESS;
743 }
744
745 static NTSTATUS set_queue_size(int fd, const SERIAL_QUEUE_SIZE* sqs)
746 {
747     FIXME("insize %d outsize %d unimplemented stub\n", sqs->InSize, sqs->OutSize);
748     return STATUS_SUCCESS;
749 }
750
751 static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc)
752 {
753     struct termios port;
754     
755     if (tcgetattr(fd, &port) == -1)
756     {
757         ERR("tcgetattr error '%s'\n", strerror(errno));
758         return FILE_GetNtStatus();
759     }
760     
761     port.c_cc[VEOF  ] = sc->EofChar;
762     /* FIXME: sc->ErrorChar is not supported */
763     /* FIXME: sc->BreakChar is not supported */
764     /* FIXME: sc->EventChar is not supported */
765     port.c_cc[VSTART] = sc->XonChar;
766     port.c_cc[VSTOP ] = sc->XoffChar;
767     
768     if (tcsetattr(fd, TCSANOW, &port) == -1)
769     {
770         ERR("tcsetattr error '%s'\n", strerror(errno));
771         return FILE_GetNtStatus();
772     }
773     return STATUS_SUCCESS;
774 }
775
776 static NTSTATUS set_timeouts(HANDLE handle, const SERIAL_TIMEOUTS* st)
777 {
778     NTSTATUS            status;
779
780     SERVER_START_REQ( set_serial_info )
781     {
782         req->handle       = wine_server_obj_handle( handle );
783         req->flags        = SERIALINFO_SET_TIMEOUTS;
784         req->readinterval = st->ReadIntervalTimeout ;
785         req->readmult     = st->ReadTotalTimeoutMultiplier ;
786         req->readconst    = st->ReadTotalTimeoutConstant ;
787         req->writemult    = st->WriteTotalTimeoutMultiplier ;
788         req->writeconst   = st->WriteTotalTimeoutConstant ;
789         status = wine_server_call( req );
790     }
791     SERVER_END_REQ;
792     return status;
793 }
794
795 static NTSTATUS set_wait_mask(HANDLE hDevice, DWORD mask)
796 {
797     NTSTATUS status;
798
799     SERVER_START_REQ( set_serial_info )
800     {
801         req->handle    = wine_server_obj_handle( hDevice );
802         req->flags     = SERIALINFO_SET_MASK;
803         req->eventmask = mask;
804         status = wine_server_call( req );
805     }
806     SERVER_END_REQ;
807     return status;
808 }
809
810 /*
811  * does not change IXOFF but simulates that IXOFF has been received:
812  */
813 static NTSTATUS set_XOff(int fd)
814 {
815     if (tcflow(fd, TCOOFF))
816     {
817         return FILE_GetNtStatus();
818     }
819     return STATUS_SUCCESS;
820 }
821
822 /*
823  * does not change IXON but simulates that IXON has been received:
824  */
825 static NTSTATUS set_XOn(int fd)
826 {
827     if (tcflow(fd, TCOON))
828     {
829         return FILE_GetNtStatus();
830     }
831     return STATUS_SUCCESS;
832 }
833
834 /*             serial_irq_info
835  * local structure holding the irq values we need for WaitCommEvent()
836  *
837  * Stripped down from struct serial_icounter_struct, which may not be available on some systems
838  * As the modem line interrupts (cts, dsr, rng, dcd) only get updated with TIOCMIWAIT active,
839  * no need to carry them in the internal structure
840  *
841  */
842 typedef struct serial_irq_info
843 {
844     int rx , tx, frame, overrun, parity, brk, buf_overrun;
845 }serial_irq_info;
846
847 /***********************************************************************
848  * Data needed by the thread polling for the changing CommEvent
849  */
850 typedef struct async_commio
851 {
852     HANDLE              hDevice;
853     DWORD*              events;
854     HANDLE              hEvent;
855     DWORD               evtmask;
856     DWORD               mstat;
857     serial_irq_info     irq_info;
858 } async_commio;
859
860 /***********************************************************************
861  *   Get extended interrupt count info, needed for wait_on
862  */
863 static NTSTATUS get_irq_info(int fd, serial_irq_info *irq_info)
864 {
865     NTSTATUS status = STATUS_NOT_IMPLEMENTED;
866 #ifdef TIOCGICOUNT
867     struct serial_icounter_struct einfo;
868     if (!ioctl(fd, TIOCGICOUNT, &einfo))
869     {
870         irq_info->rx          = einfo.rx;
871         irq_info->tx          = einfo.tx;
872         irq_info->frame       = einfo.frame;
873         irq_info->overrun     = einfo.overrun;
874         irq_info->parity      = einfo.parity;
875         irq_info->brk         = einfo.brk;
876         irq_info->buf_overrun = einfo.buf_overrun;
877         return STATUS_SUCCESS;
878     }
879     TRACE("TIOCGICOUNT err %s\n", strerror(errno));
880     status = FILE_GetNtStatus();
881 #endif
882     memset(irq_info,0, sizeof(serial_irq_info));
883     return status;
884 }
885
886
887 static DWORD check_events(int fd, DWORD mask,
888                           const serial_irq_info *new,
889                           const serial_irq_info *old,
890                           DWORD new_mstat, DWORD old_mstat)
891 {
892     DWORD ret = 0, queue;
893
894     TRACE("mask 0x%08x\n", mask);
895     TRACE("old->rx          0x%08x vs. new->rx          0x%08x\n", old->rx, new->rx);
896     TRACE("old->tx          0x%08x vs. new->tx          0x%08x\n", old->tx, new->tx);
897     TRACE("old->frame       0x%08x vs. new->frame       0x%08x\n", old->frame, new->frame);
898     TRACE("old->overrun     0x%08x vs. new->overrun     0x%08x\n", old->overrun, new->overrun);
899     TRACE("old->parity      0x%08x vs. new->parity      0x%08x\n", old->parity, new->parity);
900     TRACE("old->brk         0x%08x vs. new->brk         0x%08x\n", old->brk, new->brk);
901     TRACE("old->buf_overrun 0x%08x vs. new->buf_overrun 0x%08x\n", old->buf_overrun, new->buf_overrun);
902
903     if (old->brk != new->brk) ret |= EV_BREAK;
904     if ((old_mstat & MS_CTS_ON ) != (new_mstat & MS_CTS_ON )) ret |= EV_CTS;
905     if ((old_mstat & MS_DSR_ON ) != (new_mstat & MS_DSR_ON )) ret |= EV_DSR;
906     if ((old_mstat & MS_RING_ON) != (new_mstat & MS_RING_ON)) ret |= EV_RING;
907     if ((old_mstat & MS_RLSD_ON) != (new_mstat & MS_RLSD_ON)) ret |= EV_RLSD;
908     if (old->frame != new->frame || old->overrun != new->overrun || old->parity != new->parity) ret |= EV_ERR;
909     if (mask & EV_RXCHAR)
910     {
911         queue = 0;
912 #ifdef TIOCINQ
913         if (ioctl(fd, TIOCINQ, &queue))
914             WARN("TIOCINQ returned error\n");
915 #endif
916         if (queue)
917             ret |= EV_RXCHAR;
918     }
919     if (mask & EV_TXEMPTY)
920     {
921         queue = 0;
922 /* We really want to know when all characters have gone out of the transmitter */
923 #if defined(TIOCSERGETLSR) 
924         if (ioctl(fd, TIOCSERGETLSR, &queue))
925             WARN("TIOCSERGETLSR returned error\n");
926         if (queue)
927 /* TIOCOUTQ only checks for an empty buffer */
928 #elif defined(TIOCOUTQ)
929         if (ioctl(fd, TIOCOUTQ, &queue))
930             WARN("TIOCOUTQ returned error\n");
931         if (!queue)
932 #endif
933            ret |= EV_TXEMPTY;
934         TRACE("OUTQUEUE %d, Transmitter %sempty\n",
935               queue, (ret & EV_TXEMPTY) ? "" : "not ");
936     }
937     return ret & mask;
938 }
939
940 /***********************************************************************
941  *             wait_for_event      (INTERNAL)
942  *
943  *  We need to poll for what is interesting
944  *  TIOCMIWAIT only checks modem status line and may not be aborted by a changing mask
945  *
946  */
947 static DWORD CALLBACK wait_for_event(LPVOID arg)
948 {
949     async_commio *commio = (async_commio*) arg;
950     int fd, needs_close;
951
952     if (!server_get_unix_fd( commio->hDevice, FILE_READ_DATA | FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))
953     {
954         serial_irq_info new_irq_info;
955         DWORD new_mstat, new_evtmask;
956         LARGE_INTEGER time;
957         
958         TRACE("device=%p fd=0x%08x mask=0x%08x buffer=%p event=%p irq_info=%p\n", 
959               commio->hDevice, fd, commio->evtmask, commio->events, commio->hEvent, &commio->irq_info);
960
961         time.QuadPart = (ULONGLONG)10000;
962         time.QuadPart = -time.QuadPart;
963         for (;;)
964         {
965             /*
966              * TIOCMIWAIT is not adequate
967              *
968              * FIXME:
969              * We don't handle the EV_RXFLAG (the eventchar)
970              */
971             NtDelayExecution(FALSE, &time);
972             get_irq_info(fd, &new_irq_info);
973             if (get_modem_status(fd, &new_mstat))
974                 TRACE("get_modem_status failed\n");
975             *commio->events = check_events(fd, commio->evtmask,
976                                            &new_irq_info, &commio->irq_info,
977                                            new_mstat, commio->mstat);
978             if (*commio->events) break;
979             get_wait_mask(commio->hDevice, &new_evtmask);
980             if (commio->evtmask != new_evtmask)
981             {
982                 *commio->events = 0;
983                 break;
984             }
985         }
986         if (needs_close) close( fd );
987     }
988     if (commio->hEvent) NtSetEvent(commio->hEvent, NULL);
989     RtlFreeHeap(GetProcessHeap(), 0, commio);
990     return 0;
991 }
992
993 static NTSTATUS wait_on(HANDLE hDevice, int fd, HANDLE hEvent, DWORD* events)
994 {
995     async_commio*       commio;
996     NTSTATUS            status;
997
998     if ((status = NtResetEvent(hEvent, NULL)))
999         return status;
1000
1001     commio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof (async_commio));
1002     if (!commio) return STATUS_NO_MEMORY;
1003
1004     commio->hDevice = hDevice;
1005     commio->events  = events;
1006     commio->hEvent  = hEvent;
1007     get_wait_mask(commio->hDevice, &commio->evtmask);
1008
1009 /* We may never return, if some capabilities miss
1010  * Return error in that case
1011  */
1012 #if !defined(TIOCINQ)
1013     if (commio->evtmask & EV_RXCHAR)
1014         goto error_caps;
1015 #endif
1016 #if !(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)
1017     if (commio->evtmask & EV_TXEMPTY)
1018         goto error_caps;
1019 #endif
1020 #if !defined(TIOCMGET)
1021     if (commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD))
1022         goto error_caps;
1023 #endif
1024 #if !defined(TIOCM_CTS)
1025     if (commio->evtmask & EV_CTS)
1026         goto error_caps;
1027 #endif
1028 #if !defined(TIOCM_DSR)
1029     if (commio->evtmask & EV_DSR)
1030         goto error_caps;
1031 #endif
1032 #if !defined(TIOCM_RNG)
1033     if (commio->evtmask & EV_RING)
1034         goto error_caps;
1035 #endif
1036 #if !defined(TIOCM_CAR)
1037     if (commio->evtmask & EV_RLSD)
1038         goto error_caps;
1039 #endif
1040     if (commio->evtmask & EV_RXFLAG)
1041         FIXME("EV_RXFLAG not handled\n");
1042
1043     if ((status = get_irq_info(fd, &commio->irq_info)) &&
1044         (commio->evtmask & (EV_BREAK | EV_ERR)))
1045         goto out_now;
1046
1047     if ((status = get_modem_status(fd, &commio->mstat)) &&
1048         (commio->evtmask & (EV_CTS | EV_DSR| EV_RING| EV_RLSD)))
1049         goto out_now;
1050
1051     /* We might have received something or the TX buffer is delivered */
1052     *events = check_events(fd, commio->evtmask,
1053                                &commio->irq_info, &commio->irq_info,
1054                                commio->mstat, commio->mstat);
1055     if (*events)
1056     {
1057         status = STATUS_SUCCESS;
1058         goto out_now;
1059     }
1060
1061     /* create the worker for the task */
1062     status = RtlQueueWorkItem(wait_for_event, commio, 0 /* FIXME */);
1063     if (status != STATUS_SUCCESS) goto out_now;
1064     return STATUS_PENDING;
1065
1066 #if !defined(TIOCINQ) || (!(defined(TIOCSERGETLSR) && defined(TIOCSER_TEMT)) || !defined(TIOCINQ)) || !defined(TIOCMGET) || !defined(TIOCM_CTS) ||!defined(TIOCM_DSR) || !defined(TIOCM_RNG) || !defined(TIOCM_CAR)
1067 error_caps:
1068     FIXME("Returning error because of missing capabilities\n");
1069     status = STATUS_INVALID_PARAMETER;
1070 #endif
1071 out_now:
1072     RtlFreeHeap(GetProcessHeap(), 0, commio);
1073     return status;
1074 }
1075
1076 static NTSTATUS xmit_immediate(HANDLE hDevice, int fd, const char* ptr)
1077 {
1078     /* FIXME: not perfect as it should bypass the in-queue */
1079     WARN("(%p,'%c') not perfect!\n", hDevice, *ptr);
1080     if (write(fd, ptr, 1) != 1)
1081         return FILE_GetNtStatus();
1082     return STATUS_SUCCESS;
1083 }
1084
1085 /******************************************************************
1086  *              COMM_DeviceIoControl
1087  *
1088  *
1089  */
1090 static inline NTSTATUS io_control(HANDLE hDevice, 
1091                                   HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1092                                   PVOID UserApcContext, 
1093                                   PIO_STATUS_BLOCK piosb, 
1094                                   ULONG dwIoControlCode,
1095                                   LPVOID lpInBuffer, DWORD nInBufferSize,
1096                                   LPVOID lpOutBuffer, DWORD nOutBufferSize)
1097 {
1098     DWORD       sz = 0, access = FILE_READ_DATA;
1099     NTSTATUS    status = STATUS_SUCCESS;
1100     int         fd = -1, needs_close = 0;
1101
1102     TRACE("%p %s %p %d %p %d %p\n",
1103           hDevice, iocode2str(dwIoControlCode), lpInBuffer, nInBufferSize,
1104           lpOutBuffer, nOutBufferSize, piosb);
1105
1106     piosb->Information = 0;
1107
1108     if (dwIoControlCode != IOCTL_SERIAL_GET_TIMEOUTS &&
1109         dwIoControlCode != IOCTL_SERIAL_SET_TIMEOUTS)
1110         if ((status = server_get_unix_fd( hDevice, access, &fd, &needs_close, NULL, NULL )))
1111             goto error;
1112
1113     switch (dwIoControlCode)
1114     {
1115     case IOCTL_SERIAL_CLR_DTR:
1116 #ifdef TIOCM_DTR
1117         if (whack_modem(fd, ~TIOCM_DTR, 0) == -1) status = FILE_GetNtStatus();
1118 #else
1119         status = STATUS_NOT_SUPPORTED;
1120 #endif
1121         break;
1122     case IOCTL_SERIAL_CLR_RTS:
1123 #ifdef TIOCM_RTS
1124         if (whack_modem(fd, ~TIOCM_RTS, 0) == -1) status = FILE_GetNtStatus();
1125 #else
1126         status = STATUS_NOT_SUPPORTED;
1127 #endif
1128         break;
1129     case IOCTL_SERIAL_GET_BAUD_RATE:
1130         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_BAUD_RATE))
1131         {
1132             if (!(status = get_baud_rate(fd, (SERIAL_BAUD_RATE*)lpOutBuffer)))
1133                 sz = sizeof(SERIAL_BAUD_RATE);
1134         }
1135         else
1136             status = STATUS_INVALID_PARAMETER;
1137         break;
1138     case IOCTL_SERIAL_GET_CHARS:
1139         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_CHARS))
1140         {
1141             if (!(status = get_special_chars(fd, (SERIAL_CHARS*)lpOutBuffer)))
1142                 sz = sizeof(SERIAL_CHARS);
1143         }
1144         else
1145             status = STATUS_INVALID_PARAMETER;
1146         break;
1147      case IOCTL_SERIAL_GET_COMMSTATUS:
1148         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_STATUS))
1149         {
1150             if (!(status = get_status(fd, (SERIAL_STATUS*)lpOutBuffer)))
1151                 sz = sizeof(SERIAL_STATUS);
1152         }
1153         else status = STATUS_INVALID_PARAMETER;
1154         break;
1155     case IOCTL_SERIAL_GET_HANDFLOW:
1156         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_HANDFLOW))
1157         {
1158             if (!(status = get_hand_flow(fd, (SERIAL_HANDFLOW*)lpOutBuffer)))
1159                 sz = sizeof(SERIAL_HANDFLOW);
1160         }
1161         else
1162             status = STATUS_INVALID_PARAMETER;
1163         break;
1164     case IOCTL_SERIAL_GET_LINE_CONTROL:
1165         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_LINE_CONTROL))
1166         {
1167             if (!(status = get_line_control(fd, (SERIAL_LINE_CONTROL*)lpOutBuffer)))
1168                 sz = sizeof(SERIAL_LINE_CONTROL);
1169         }
1170         else
1171             status = STATUS_INVALID_PARAMETER;
1172         break;
1173     case IOCTL_SERIAL_GET_MODEMSTATUS:
1174         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1175         {
1176             if (!(status = get_modem_status(fd, (DWORD*)lpOutBuffer)))
1177                 sz = sizeof(DWORD);
1178         }
1179         else status = STATUS_INVALID_PARAMETER;
1180         break;
1181     case IOCTL_SERIAL_GET_TIMEOUTS:
1182         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_TIMEOUTS))
1183         {
1184             if (!(status = get_timeouts(hDevice, (SERIAL_TIMEOUTS*)lpOutBuffer)))
1185                 sz = sizeof(SERIAL_TIMEOUTS);
1186         }
1187         else
1188             status = STATUS_INVALID_PARAMETER;
1189         break;
1190     case IOCTL_SERIAL_GET_WAIT_MASK:
1191         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1192         {
1193             if (!(status = get_wait_mask(hDevice, (DWORD*)lpOutBuffer)))
1194                 sz = sizeof(DWORD);
1195         }
1196         else
1197             status = STATUS_INVALID_PARAMETER;
1198         break;
1199     case IOCTL_SERIAL_IMMEDIATE_CHAR:
1200         if (lpInBuffer && nInBufferSize == sizeof(CHAR))
1201             status = xmit_immediate(hDevice, fd, lpInBuffer);
1202         else
1203             status = STATUS_INVALID_PARAMETER;
1204         break;
1205     case IOCTL_SERIAL_PURGE:
1206         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
1207             status = purge(fd, *(DWORD*)lpInBuffer);
1208         else
1209             status = STATUS_INVALID_PARAMETER;
1210         break;
1211     case IOCTL_SERIAL_RESET_DEVICE:
1212         FIXME("Unsupported\n");
1213         break;
1214     case IOCTL_SERIAL_SET_BAUD_RATE:
1215         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_BAUD_RATE))
1216             status = set_baud_rate(fd, (const SERIAL_BAUD_RATE*)lpInBuffer);
1217         else
1218             status = STATUS_INVALID_PARAMETER;
1219         break;
1220     case IOCTL_SERIAL_SET_BREAK_OFF:
1221 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
1222         if (ioctl(fd, TIOCCBRK, 0) == -1)
1223         {
1224             TRACE("ioctl failed\n");
1225             status = FILE_GetNtStatus();
1226         }
1227 #else
1228         FIXME("ioctl not available\n");
1229         status = STATUS_NOT_SUPPORTED;
1230 #endif
1231         break;
1232     case IOCTL_SERIAL_SET_BREAK_ON:
1233 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
1234         if (ioctl(fd, TIOCSBRK, 0) == -1)
1235         {
1236             TRACE("ioctl failed\n");
1237             status = FILE_GetNtStatus();
1238         }
1239 #else
1240         FIXME("ioctl not available\n");
1241         status = STATUS_NOT_SUPPORTED;
1242 #endif
1243         break;
1244     case IOCTL_SERIAL_SET_CHARS:
1245         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_CHARS))
1246             status = set_special_chars(fd, (const SERIAL_CHARS*)lpInBuffer);
1247         else
1248             status = STATUS_INVALID_PARAMETER;
1249         break;
1250     case IOCTL_SERIAL_SET_DTR:
1251 #ifdef TIOCM_DTR
1252         if (whack_modem(fd, 0, TIOCM_DTR) == -1) status = FILE_GetNtStatus();
1253 #else
1254         status = STATUS_NOT_SUPPORTED;
1255 #endif
1256         break;
1257     case IOCTL_SERIAL_SET_HANDFLOW:
1258         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_HANDFLOW))
1259             status = set_handflow(fd, (const SERIAL_HANDFLOW*)lpInBuffer);
1260         else
1261             status = STATUS_INVALID_PARAMETER;
1262         break;
1263     case IOCTL_SERIAL_SET_LINE_CONTROL:
1264         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_LINE_CONTROL))
1265             status = set_line_control(fd, (const SERIAL_LINE_CONTROL*)lpInBuffer);
1266         else
1267             status = STATUS_INVALID_PARAMETER;
1268         break;
1269     case IOCTL_SERIAL_SET_QUEUE_SIZE:
1270         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_QUEUE_SIZE))
1271             status = set_queue_size(fd, (const SERIAL_QUEUE_SIZE*)lpInBuffer);
1272         else
1273             status = STATUS_INVALID_PARAMETER;
1274         break;
1275     case IOCTL_SERIAL_SET_RTS:
1276 #ifdef TIOCM_RTS
1277         if (whack_modem(fd, 0, TIOCM_RTS) == -1) status = FILE_GetNtStatus();
1278 #else
1279         status = STATUS_NOT_SUPPORTED;
1280 #endif
1281         break;
1282     case IOCTL_SERIAL_SET_TIMEOUTS:
1283         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_TIMEOUTS))
1284             status = set_timeouts(hDevice, (const SERIAL_TIMEOUTS*)lpInBuffer);
1285         else
1286             status = STATUS_INVALID_PARAMETER;
1287         break;
1288     case IOCTL_SERIAL_SET_WAIT_MASK:
1289         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
1290         {
1291             status = set_wait_mask(hDevice, *(DWORD*)lpInBuffer);
1292         }
1293         else status = STATUS_INVALID_PARAMETER;
1294         break;
1295     case IOCTL_SERIAL_SET_XOFF:
1296         status = set_XOff(fd);
1297         break;
1298     case IOCTL_SERIAL_SET_XON:
1299         status = set_XOn(fd);
1300         break;
1301     case IOCTL_SERIAL_WAIT_ON_MASK:
1302         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
1303         {
1304             if (!(status = wait_on(hDevice, fd, hEvent, (DWORD*)lpOutBuffer)))
1305                 sz = sizeof(DWORD);
1306         }
1307         else
1308             status = STATUS_INVALID_PARAMETER;
1309         break;
1310     default:
1311         FIXME("Unsupported IOCTL %x (type=%x access=%x func=%x meth=%x)\n", 
1312               dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
1313               (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
1314         sz = 0;
1315         status = STATUS_INVALID_PARAMETER;
1316         break;
1317     }
1318     if (needs_close) close( fd );
1319  error:
1320     piosb->u.Status = status;
1321     piosb->Information = sz;
1322     if (hEvent && status != STATUS_PENDING) NtSetEvent(hEvent, NULL);
1323     return status;
1324 }
1325
1326 NTSTATUS COMM_DeviceIoControl(HANDLE hDevice, 
1327                               HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1328                               PVOID UserApcContext, 
1329                               PIO_STATUS_BLOCK piosb, 
1330                               ULONG dwIoControlCode,
1331                               LPVOID lpInBuffer, DWORD nInBufferSize,
1332                               LPVOID lpOutBuffer, DWORD nOutBufferSize)
1333 {
1334     NTSTATUS    status;
1335
1336     if (dwIoControlCode == IOCTL_SERIAL_WAIT_ON_MASK)
1337     {
1338         HANDLE          hev = hEvent;
1339
1340         /* this is an ioctl we implement in a non blocking way if hEvent is not
1341          * null
1342          * so we have to explicitly wait if no hEvent is provided
1343          */
1344         if (!hev)
1345         {
1346             OBJECT_ATTRIBUTES   attr;
1347             
1348             attr.Length                   = sizeof(attr);
1349             attr.RootDirectory            = 0;
1350             attr.ObjectName               = NULL;
1351             attr.Attributes               = OBJ_CASE_INSENSITIVE | OBJ_OPENIF;
1352             attr.SecurityDescriptor       = NULL;
1353             attr.SecurityQualityOfService = NULL;
1354             status = NtCreateEvent(&hev, EVENT_ALL_ACCESS, &attr, FALSE, FALSE);
1355
1356             if (status) goto done;
1357         }
1358         status = io_control(hDevice, hev, UserApcRoutine, UserApcContext,
1359                             piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
1360                             lpOutBuffer, nOutBufferSize);
1361         if (hev != hEvent)
1362         {
1363             if (status == STATUS_PENDING)
1364             {
1365                 NtWaitForSingleObject(hev, FALSE, NULL);
1366                 status = STATUS_SUCCESS;
1367             }
1368             NtClose(hev);
1369         }
1370     }
1371     else status = io_control(hDevice, hEvent, UserApcRoutine, UserApcContext,
1372                              piosb, dwIoControlCode, lpInBuffer, nInBufferSize,
1373                              lpOutBuffer, nOutBufferSize);
1374 done:
1375     return status;
1376 }