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