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