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