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