ntdll/kernel32: GetCommState & IOCTL_SERIAL_GET_HANDFLOW
[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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 #ifdef HAVE_TERMIOS_H
43 #include <termios.h>
44 #endif
45 #include <fcntl.h>
46 #ifdef HAVE_SYS_STAT_H
47 # include <sys/stat.h>
48 #endif
49 #include <sys/types.h>
50 #ifdef HAVE_SYS_FILIO_H
51 # include <sys/filio.h>
52 #endif
53 #ifdef HAVE_SYS_IOCTL_H
54 #include <sys/ioctl.h>
55 #endif
56 #ifdef HAVE_SYS_POLL_H
57 # include <sys/poll.h>
58 #endif
59 #ifdef HAVE_SYS_MODEM_H
60 # include <sys/modem.h>
61 #endif
62 #ifdef HAVE_SYS_STRTIO_H
63 # include <sys/strtio.h>
64 #endif
65
66 #define NONAMELESSUNION
67 #define NONAMELESSSTRUCT
68 #include "ntstatus.h"
69 #define WIN32_NO_STATUS
70 #include "windef.h"
71 #include "winternl.h"
72 #include "winioctl.h"
73 #include "ddk/ntddser.h"
74 #include "ntdll_misc.h"
75 #include "wine/server.h"
76 #include "wine/library.h"
77 #include "wine/debug.h"
78
79 #ifdef HAVE_LINUX_SERIAL_H
80 #include <linux/serial.h>
81 #endif
82
83 WINE_DEFAULT_DEBUG_CHANNEL(comm);
84
85 static const char* iocode2str(DWORD ioc)
86 {
87     switch (ioc)
88     {
89 #define X(x)    case (x): return #x;
90         X(IOCTL_SERIAL_CLEAR_STATS);
91         X(IOCTL_SERIAL_CLR_DTR);
92         X(IOCTL_SERIAL_CLR_RTS);
93         X(IOCTL_SERIAL_CONFIG_SIZE);
94         X(IOCTL_SERIAL_GET_BAUD_RATE);
95         X(IOCTL_SERIAL_GET_CHARS);
96         X(IOCTL_SERIAL_GET_COMMSTATUS);
97         X(IOCTL_SERIAL_GET_DTRRTS);
98         X(IOCTL_SERIAL_GET_HANDFLOW);
99         X(IOCTL_SERIAL_GET_LINE_CONTROL);
100         X(IOCTL_SERIAL_GET_MODEM_CONTROL);
101         X(IOCTL_SERIAL_GET_MODEMSTATUS);
102         X(IOCTL_SERIAL_GET_PROPERTIES);
103         X(IOCTL_SERIAL_GET_STATS);
104         X(IOCTL_SERIAL_GET_TIMEOUTS);
105         X(IOCTL_SERIAL_GET_WAIT_MASK);
106         X(IOCTL_SERIAL_IMMEDIATE_CHAR);
107         X(IOCTL_SERIAL_LSRMST_INSERT);
108         X(IOCTL_SERIAL_PURGE);
109         X(IOCTL_SERIAL_RESET_DEVICE);
110         X(IOCTL_SERIAL_SET_BAUD_RATE);
111         X(IOCTL_SERIAL_SET_BREAK_ON);
112         X(IOCTL_SERIAL_SET_BREAK_OFF);
113         X(IOCTL_SERIAL_SET_CHARS);
114         X(IOCTL_SERIAL_SET_DTR);
115         X(IOCTL_SERIAL_SET_FIFO_CONTROL);
116         X(IOCTL_SERIAL_SET_HANDFLOW);
117         X(IOCTL_SERIAL_SET_LINE_CONTROL);
118         X(IOCTL_SERIAL_SET_MODEM_CONTROL);
119         X(IOCTL_SERIAL_SET_QUEUE_SIZE);
120         X(IOCTL_SERIAL_SET_RTS);
121         X(IOCTL_SERIAL_SET_TIMEOUTS);
122         X(IOCTL_SERIAL_SET_WAIT_MASK);
123         X(IOCTL_SERIAL_SET_XOFF);
124         X(IOCTL_SERIAL_SET_XON);
125         X(IOCTL_SERIAL_WAIT_ON_MASK);
126         X(IOCTL_SERIAL_XOFF_COUNTER);
127 #undef X
128     default: { static char tmp[32]; sprintf(tmp, "IOCTL_SERIAL_%ld\n", ioc); return tmp; }
129     }
130 }
131
132 static NTSTATUS get_baud_rate(int fd, SERIAL_BAUD_RATE* sbr)
133 {
134     struct termios port;
135     int speed;
136     
137     if (tcgetattr(fd, &port) == -1)
138     {
139         ERR("tcgetattr error '%s'\n", strerror(errno));
140         return FILE_GetNtStatus();
141     }
142 #ifndef __EMX__
143 #ifdef CBAUD
144     speed = port.c_cflag & CBAUD;
145 #else
146     speed = cfgetospeed(&port);
147 #endif
148     switch (speed)
149     {
150     case B0:            sbr->BaudRate = 0;      break;
151     case B50:           sbr->BaudRate = 50;     break;
152     case B75:           sbr->BaudRate = 75;     break;
153     case B110:          sbr->BaudRate = 110;    break;
154     case B134:          sbr->BaudRate = 134;    break;
155     case B150:          sbr->BaudRate = 150;    break;
156     case B200:          sbr->BaudRate = 200;    break;
157     case B300:          sbr->BaudRate = 300;    break;
158     case B600:          sbr->BaudRate = 600;    break;
159     case B1200:         sbr->BaudRate = 1200;   break;
160     case B1800:         sbr->BaudRate = 1800;   break;
161     case B2400:         sbr->BaudRate = 2400;   break;
162     case B4800:         sbr->BaudRate = 4800;   break;
163     case B9600:         sbr->BaudRate = 9600;   break;
164     case B19200:        sbr->BaudRate = 19200;  break;
165     case B38400:        sbr->BaudRate = 38400;  break;
166 #ifdef B57600
167     case B57600:        sbr->BaudRate = 57600;  break;
168 #endif
169 #ifdef B115200
170     case B115200:       sbr->BaudRate = 115200; break;
171 #endif
172 #ifdef B230400
173     case B230400:       sbr->BaudRate = 230400; break;
174 #endif
175 #ifdef B460800
176     case B460800:       sbr->BaudRate = 460800; break;
177 #endif
178     default:
179         ERR("unknown speed %x\n", speed);
180         return STATUS_INVALID_PARAMETER;
181     }
182 #else
183     return STATUS_INVALID_PARAMETER;
184 #endif
185     return STATUS_SUCCESS;
186 }
187
188 static NTSTATUS get_hand_flow(int fd, SERIAL_HANDFLOW* shf)
189 {
190     int stat;
191     struct termios port;
192     
193     if (tcgetattr(fd, &port) == -1)
194     {
195         ERR("tcgetattr error '%s'\n", strerror(errno));
196         return FILE_GetNtStatus();
197     }
198 #ifdef TIOCMGET
199     if (ioctl(fd, TIOCMGET, &stat) == -1)
200     {
201         WARN("ioctl error '%s'\n", strerror(errno));
202         stat = DTR_CONTROL_ENABLE | RTS_CONTROL_ENABLE;
203     }
204 #endif
205     /* termios does not support DTR/DSR flow control */
206     shf->ControlHandShake = 0;
207     shf->FlowReplace = 0;
208 #ifdef TIOCM_DTR
209     if (stat & TIOCM_DTR)
210 #endif
211         shf->ControlHandShake |= SERIAL_DTR_CONTROL;
212 #ifdef CRTSCTS
213     if (port.c_cflag & CRTSCTS)
214     {
215         shf->ControlHandShake |= SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE;
216         shf->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
217     }
218     else
219 #endif
220     {
221 #ifdef TIOCM_RTS
222         if (stat & TIOCM_RTS)
223 #endif
224             shf->ControlHandShake |= SERIAL_RTS_CONTROL;
225     }
226     if (port.c_iflag & IXON)
227         shf->FlowReplace |= SERIAL_AUTO_RECEIVE;
228     if (port.c_iflag & IXOFF)
229         shf->FlowReplace |= SERIAL_AUTO_TRANSMIT;
230
231     shf->XonLimit = 10;
232     shf->XoffLimit = 10;
233     return STATUS_SUCCESS;
234 }
235
236 static NTSTATUS get_line_control(int fd, SERIAL_LINE_CONTROL* slc)
237 {
238     struct termios port;
239     
240     if (tcgetattr(fd, &port) == -1)
241     {
242         ERR("tcgetattr error '%s'\n", strerror(errno));
243         return FILE_GetNtStatus();
244     }
245     
246 #ifdef CMSPAR
247     switch (port.c_cflag & (PARENB | PARODD | CMSPAR))
248 #else
249     switch (port.c_cflag & (PARENB | PARODD))
250 #endif
251     {
252     case 0:                     slc->Parity = NOPARITY;         break;
253     case PARENB:                slc->Parity = EVENPARITY;       break;
254     case PARENB|PARODD:         slc->Parity = ODDPARITY;        break;
255 #ifdef CMSPAR
256     case PARENB|CMSPAR:         slc->Parity = MARKPARITY;       break;
257     case PARENB|PARODD|CMSPAR:  slc->Parity = SPACEPARITY;      break;
258         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", 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_SUCCESS;
286     int         mstat;
287
288 #ifdef TIOCMGET
289     if (ioctl(fd, TIOCMGET, &mstat) == -1)
290     {
291         WARN("ioctl failed\n");
292         status = FILE_GetNtStatus();
293     }
294     else
295     {
296         *lpModemStat = 0;
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     }
316 #else
317     status = STATUS_NOT_SUPPORTED;
318 #endif
319     return status;
320 }
321
322 static NTSTATUS get_status(int fd, SERIAL_STATUS* ss)
323 {
324     NTSTATUS    status = STATUS_SUCCESS;
325
326     ss->Errors = 0;
327     ss->HoldReasons = 0;
328     ss->EofReceived = FALSE;
329     ss->WaitForImmediate = FALSE;
330 #ifdef TIOCOUTQ
331     if (ioctl(fd, TIOCOUTQ, &ss->AmountInOutQueue) == -1)
332     {
333         WARN("ioctl returned error\n");
334         status = FILE_GetNtStatus();
335     }
336 #else
337     ss->AmountInOutQueue = 0; /* FIXME: find a different way to find out */
338 #endif
339
340 #ifdef TIOCINQ
341     if (ioctl(fd, TIOCINQ, &ss->AmountInInQueue))
342     {
343         WARN("ioctl returned error\n");
344         status = FILE_GetNtStatus();
345     }
346 #else
347     ss->AmountInInQueue = 0; /* FIXME: find a different way to find out */
348 #endif
349     return status;
350 }
351
352 static NTSTATUS get_wait_mask(HANDLE hDevice, DWORD* mask)
353 {
354     NTSTATUS    status;
355
356     SERVER_START_REQ( get_serial_info )
357     {
358         req->handle = hDevice;
359         if (!(status = wine_server_call( req )))
360             *mask = reply->eventmask;
361     }
362     SERVER_END_REQ;
363     return status;
364 }
365
366 static NTSTATUS purge(int fd, DWORD flags)
367 {
368     /*
369     ** not exactly sure how these are different
370     ** Perhaps if we had our own internal queues, one flushes them
371     ** and the other flushes the kernel's buffers.
372     */
373     if (flags & PURGE_TXABORT) tcflush(fd, TCOFLUSH);
374     if (flags & PURGE_RXABORT) tcflush(fd, TCIFLUSH);
375     if (flags & PURGE_TXCLEAR) tcflush(fd, TCOFLUSH);
376     if (flags & PURGE_RXCLEAR) tcflush(fd, TCIFLUSH);
377     return STATUS_SUCCESS;
378 }
379
380 static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr)
381 {
382     struct termios port;
383
384     if (tcgetattr(fd, &port) == -1)
385     {
386         ERR("tcgetattr error '%s'\n", strerror(errno));
387         return FILE_GetNtStatus();
388     }
389
390 #ifdef CBAUD
391     port.c_cflag &= ~CBAUD;
392     switch (sbr->BaudRate)
393     {
394     case 0:             port.c_cflag |= B0;     break;
395     case 50:            port.c_cflag |= B50;    break;
396     case 75:            port.c_cflag |= B75;    break;
397     case 110:   
398     case CBR_110:       port.c_cflag |= B110;   break;
399     case 134:           port.c_cflag |= B134;   break;
400     case 150:           port.c_cflag |= B150;   break;
401     case 200:           port.c_cflag |= B200;   break;
402     case 300:           
403     case CBR_300:       port.c_cflag |= B300;   break;
404     case 600:
405     case CBR_600:       port.c_cflag |= B600;   break;
406     case 1200:
407     case CBR_1200:      port.c_cflag |= B1200;  break;
408     case 1800:          port.c_cflag |= B1800;  break;
409     case 2400:
410     case CBR_2400:      port.c_cflag |= B2400;  break;
411     case 4800:
412     case CBR_4800:      port.c_cflag |= B4800;  break;
413     case 9600:
414     case CBR_9600:      port.c_cflag |= B9600;  break;
415     case 19200:
416     case CBR_19200:     port.c_cflag |= B19200; break;
417     case 38400:
418     case CBR_38400:     port.c_cflag |= B38400; break;
419 #ifdef B57600
420     case 57600:         port.c_cflag |= B57600; break;
421 #endif
422 #ifdef B115200
423     case 115200:        port.c_cflag |= B115200;break;
424 #endif
425 #ifdef B230400
426     case 230400:        port.c_cflag |= B230400;break;
427 #endif
428 #ifdef B460800
429     case 460800:        port.c_cflag |= B460800;break;
430 #endif
431     default:
432 #if defined (HAVE_LINUX_SERIAL_H) && defined (TIOCSSERIAL)
433         {
434             struct serial_struct nuts;
435             int arby;
436         
437             ioctl(fd, TIOCGSERIAL, &nuts);
438             nuts.custom_divisor = nuts.baud_base / sbr->BaudRate;
439             if (!(nuts.custom_divisor)) nuts.custom_divisor = 1;
440             arby = nuts.baud_base / nuts.custom_divisor;
441             nuts.flags &= ~ASYNC_SPD_MASK;
442             nuts.flags |= ASYNC_SPD_CUST;
443             WARN("You (or a program acting at your behest) have specified\n"
444                  "a non-standard baud rate %ld.  Wine will set the rate to %d,\n"
445                  "which is as close as we can get by our present understanding of your\n"
446                  "hardware. I hope you know what you are doing.  Any disruption Wine\n"
447                  "has caused to your linux system can be undone with setserial \n"
448                  "(see man setserial). If you have incapacitated a Hayes type modem,\n"
449                  "reset it and it will probably recover.\n", sbr->BaudRate, arby);
450             ioctl(fd, TIOCSSERIAL, &nuts);
451             port.c_cflag |= B38400;
452         }
453         break;
454 #endif    /* Don't have linux/serial.h or lack TIOCSSERIAL */
455         ERR("baudrate %ld\n", sbr->BaudRate);
456         return STATUS_NOT_SUPPORTED;
457     }
458 #elif !defined(__EMX__)
459     switch (sbr->BaudRate)
460     {
461     case 0:             port.c_ospeed = B0;     break;
462     case 50:            port.c_ospeed = B50;    break;
463     case 75:            port.c_ospeed = B75;    break;
464     case 110:
465     case CBR_110:       port.c_ospeed = B110;   break;
466     case 134:           port.c_ospeed = B134;   break;
467     case 150:           port.c_ospeed = B150;   break;
468     case 200:           port.c_ospeed = B200;   break;
469     case 300:
470     case CBR_300:       port.c_ospeed = B300;   break;
471     case 600:
472     case CBR_600:       port.c_ospeed = B600;   break;
473     case 1200:
474     case CBR_1200:      port.c_ospeed = B1200;  break;
475     case 1800:          port.c_ospeed = B1800;  break;
476     case 2400:
477     case CBR_2400:      port.c_ospeed = B2400;  break;
478     case 4800:
479     case CBR_4800:      port.c_ospeed = B4800;  break;
480     case 9600:
481     case CBR_9600:      port.c_ospeed = B9600;  break;
482     case 19200:
483     case CBR_19200:     port.c_ospeed = B19200; break;
484     case 38400:
485     case CBR_38400:     port.c_ospeed = B38400; break;
486 #ifdef B57600
487     case 57600:
488     case CBR_57600:     port.c_cflag |= B57600; break;
489 #endif
490 #ifdef B115200
491     case 115200:
492     case CBR_115200:    port.c_cflag |= B115200;break;
493 #endif
494 #ifdef B230400
495     case 230400:        port.c_cflag |= B230400;break;
496 #endif
497 #ifdef B460800
498     case 460800:        port.c_cflag |= B460800;break;
499 #endif
500     default:
501         ERR("baudrate %ld\n", sbr->BaudRate);
502         return STATUS_NOT_SUPPORTED;
503     }
504     port.c_ispeed = port.c_ospeed;
505 #endif
506     if (tcsetattr(fd, TCSANOW, &port) == -1)
507     {
508         ERR("tcsetattr error '%s'\n", strerror(errno));
509         return FILE_GetNtStatus();
510     }
511     return STATUS_SUCCESS;
512 }
513
514 static int whack_modem(int fd, unsigned int andy, unsigned int orrie)
515 {
516 #ifdef TIOCMGET
517     unsigned int mstat, okay;
518     okay = ioctl(fd, TIOCMGET, &mstat);
519     if (okay) return okay;
520     if (andy) mstat &= andy;
521     mstat |= orrie;
522     return ioctl(fd, TIOCMSET, &mstat);
523 #else
524     return 0;
525 #endif
526 }
527
528 static NTSTATUS set_handflow(int fd, const SERIAL_HANDFLOW* shf)
529 {
530     struct termios port;
531
532     if ((shf->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE)) == 
533         (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE))
534         return STATUS_NOT_SUPPORTED;
535
536     if (tcgetattr(fd, &port) == -1)
537     {
538         ERR("tcgetattr error '%s'\n", strerror(errno));
539         return FILE_GetNtStatus();
540     }
541     
542 #ifdef CRTSCTS
543     if ((shf->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
544         (shf->FlowReplace & SERIAL_RTS_HANDSHAKE))
545     {
546         port.c_cflag |= CRTSCTS;
547         TRACE("CRTSCTS\n");
548     }
549     else
550         port.c_cflag &= ~CRTSCTS;
551 #endif
552 #ifdef TIOCM_DTR
553     if (shf->ControlHandShake & SERIAL_DTR_HANDSHAKE)
554     {
555         WARN("DSR/DTR flow control not supported\n");
556     } else if (shf->ControlHandShake & SERIAL_DTR_CONTROL)
557         whack_modem(fd, ~TIOCM_DTR, 0);
558     else    
559         whack_modem(fd, 0, TIOCM_DTR);
560 #endif
561 #ifdef TIOCM_RTS
562     if (!(shf->ControlHandShake & SERIAL_DSR_HANDSHAKE))
563     {
564         if ((shf->FlowReplace & (SERIAL_RTS_CONTROL|SERIAL_RTS_HANDSHAKE)) == 0)
565             whack_modem(fd, ~TIOCM_RTS, 0);
566         else    
567             whack_modem(fd, 0, TIOCM_RTS);
568     }
569 #endif
570
571     if (shf->FlowReplace & SERIAL_AUTO_RECEIVE)
572         port.c_iflag |= IXON;
573     else
574         port.c_iflag &= ~IXON;
575     if (shf->FlowReplace & SERIAL_AUTO_TRANSMIT)
576         port.c_iflag |= IXOFF;
577     else
578         port.c_iflag &= ~IXOFF;
579     if (tcsetattr(fd, TCSANOW, &port) == -1)
580     {
581         ERR("tcsetattr error '%s'\n", strerror(errno));
582         return FILE_GetNtStatus();
583     }
584
585     return STATUS_SUCCESS;
586 }
587
588 static NTSTATUS set_line_control(int fd, const SERIAL_LINE_CONTROL* slc)
589 {
590     struct termios port;
591     unsigned bytesize, stopbits;
592     
593     if (tcgetattr(fd, &port) == -1)
594     {
595         ERR("tcgetattr error '%s'\n", strerror(errno));
596         return FILE_GetNtStatus();
597     }
598     
599 #ifdef IMAXBEL
600     port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|PARMRK|IMAXBEL);
601 #else
602     port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|PARMRK);
603 #endif
604     port.c_iflag |= IGNBRK | INPCK;
605     
606     port.c_oflag &= ~(OPOST);
607     
608     port.c_cflag &= ~(HUPCL);
609     port.c_cflag |= CLOCAL | CREAD;
610     
611     port.c_lflag &= ~(ICANON|ECHO|ISIG);
612     port.c_lflag |= NOFLSH;
613     
614     bytesize = slc->WordLength;
615     stopbits = slc->StopBits;
616     
617 #ifdef CMSPAR
618     port.c_cflag &= ~(PARENB | PARODD | CMSPAR);
619 #else
620     port.c_cflag &= ~(PARENB | PARODD);
621 #endif
622
623     switch (slc->Parity)
624     {
625     case NOPARITY:      port.c_iflag &= ~INPCK;                         break;
626     case ODDPARITY:     port.c_cflag |= PARENB | PARODD;                break;
627     case EVENPARITY:    port.c_cflag |= PARENB;                         break;
628 #ifdef CMSPAR
629         /* Linux defines mark/space (stick) parity */
630     case MARKPARITY:    port.c_cflag |= PARENB | CMSPAR;                break;
631     case SPACEPARITY:   port.c_cflag |= PARENB | PARODD |  CMSPAR;      break;
632 #else
633         /* try the POSIX way */
634     case MARKPARITY:
635         if (slc->StopBits == ONESTOPBIT)
636         {
637             stopbits = TWOSTOPBITS;
638             port.c_iflag &= ~INPCK;
639         }
640         else
641         {
642             ERR("Cannot set MARK Parity\n");
643             return STATUS_NOT_SUPPORTED;
644         }
645         break;
646     case SPACEPARITY:
647         if (slc->WordLength < 8)
648         {
649             bytesize +=1;
650             port.c_iflag &= ~INPCK;
651         }
652         else
653         {
654             ERR("Cannot set SPACE Parity\n");
655             return STATUS_NOT_SUPPORTED;
656         }
657         break;
658 #endif
659     default:
660         ERR("Parity\n");
661         return STATUS_NOT_SUPPORTED;
662     }
663     
664     port.c_cflag &= ~CSIZE;
665     switch (bytesize)
666     {
667     case 5:     port.c_cflag |= CS5;    break;
668     case 6:     port.c_cflag |= CS6;    break;
669     case 7:     port.c_cflag |= CS7;    break;
670     case 8:     port.c_cflag |= CS8;    break;
671     default:
672         ERR("ByteSize\n");
673         return STATUS_NOT_SUPPORTED;
674     }
675     
676     switch (stopbits)
677     {
678     case ONESTOPBIT:    port.c_cflag &= ~CSTOPB;        break;
679     case ONE5STOPBITS: /* will be selected if bytesize is 5 */
680     case TWOSTOPBITS:   port.c_cflag |= CSTOPB;         break;
681     default:
682         ERR("StopBits\n");
683         return STATUS_NOT_SUPPORTED;
684     }
685     /* otherwise it hangs with pending input*/
686     if (tcsetattr(fd, TCSANOW, &port) == -1)
687     {
688         ERR("tcsetattr error '%s'\n", strerror(errno));
689         return FILE_GetNtStatus();
690     }
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[VMIN  ] = 0;
705     port.c_cc[VTIME ] = 1;
706     
707     port.c_cc[VEOF  ] = sc->EofChar;
708     /* FIXME: sc->ErrorChar is not supported */
709     /* FIXME: sc->BreakChar is not supported */
710     /* FIXME: sc->EventChar is not supported */
711     port.c_cc[VSTART] = sc->XonChar;
712     port.c_cc[VSTOP ] = sc->XoffChar;
713     
714     if (tcsetattr(fd, TCSANOW, &port) == -1)
715     {
716         ERR("tcsetattr error '%s'\n", strerror(errno));
717         return FILE_GetNtStatus();
718     }
719     return STATUS_SUCCESS;
720 }
721
722 static NTSTATUS set_wait_mask(HANDLE hDevice, DWORD mask)
723 {
724     NTSTATUS status;
725
726     SERVER_START_REQ( set_serial_info )
727     {
728         req->handle    = hDevice;
729         req->flags     = SERIALINFO_SET_MASK;
730         req->eventmask = mask;
731         status = wine_server_call( req );
732     }
733     SERVER_END_REQ;
734     return status;
735 }
736
737 static NTSTATUS xmit_immediate(HANDLE hDevice, int fd, char* ptr)
738 {
739     /* FIXME: not perfect as it should bypass the in-queue */
740     WARN("(%p,'%c') not perfect!\n", hDevice, *ptr);
741     if (write(fd, ptr, 1) != 1)
742         return FILE_GetNtStatus();
743     return STATUS_SUCCESS;
744 }
745
746 /******************************************************************
747  *              COMM_DeviceIoControl
748  *
749  *
750  */
751 NTSTATUS COMM_DeviceIoControl(HANDLE hDevice, 
752                               HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
753                               PVOID UserApcContext, 
754                               PIO_STATUS_BLOCK piosb, 
755                               ULONG dwIoControlCode,
756                               LPVOID lpInBuffer, DWORD nInBufferSize,
757                               LPVOID lpOutBuffer, DWORD nOutBufferSize)
758 {
759     DWORD       sz = 0, access = FILE_READ_DATA;
760     NTSTATUS    status = STATUS_SUCCESS;
761     int         fd;
762
763     TRACE("%p %s %p %ld %p %ld %p\n",
764           hDevice, iocode2str(dwIoControlCode), lpInBuffer, nInBufferSize,
765           lpOutBuffer, nOutBufferSize, piosb);
766
767     piosb->Information = 0;
768
769     if ((status = wine_server_handle_to_fd( hDevice, access, &fd, NULL ))) goto error;
770
771     switch (dwIoControlCode)
772     {
773     case IOCTL_SERIAL_GET_BAUD_RATE:
774         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_BAUD_RATE))
775         {
776             if (!(status = get_baud_rate(fd, (SERIAL_BAUD_RATE*)lpOutBuffer)))
777                 sz = sizeof(SERIAL_BAUD_RATE);
778         }
779         else
780             status = STATUS_INVALID_PARAMETER;
781         break;
782      case IOCTL_SERIAL_GET_COMMSTATUS:
783         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_STATUS))
784         {
785             if (!(status = get_status(fd, (SERIAL_STATUS*)lpOutBuffer)))
786                 sz = sizeof(SERIAL_STATUS);
787         }
788         else status = STATUS_INVALID_PARAMETER;
789         break;
790     case IOCTL_SERIAL_GET_HANDFLOW:
791         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_HANDFLOW))
792         {
793             if (!(status = get_hand_flow(fd, (SERIAL_HANDFLOW*)lpOutBuffer)))
794                 sz = sizeof(SERIAL_HANDFLOW);
795         }
796         else
797             status = STATUS_INVALID_PARAMETER;
798         break;
799     case IOCTL_SERIAL_GET_LINE_CONTROL:
800         if (lpOutBuffer && nOutBufferSize == sizeof(SERIAL_LINE_CONTROL))
801         {
802             if (!(status = get_line_control(fd, (SERIAL_LINE_CONTROL*)lpOutBuffer)))
803                 sz = sizeof(SERIAL_LINE_CONTROL);
804         }
805         else
806             status = STATUS_INVALID_PARAMETER;
807         break;
808     case IOCTL_SERIAL_GET_MODEMSTATUS:
809         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
810         {
811             if (!(status = get_modem_status(fd, (DWORD*)lpOutBuffer)))
812                 sz = sizeof(DWORD);
813         }
814         else status = STATUS_INVALID_PARAMETER;
815         break;
816     case IOCTL_SERIAL_GET_WAIT_MASK:
817         if (lpOutBuffer && nOutBufferSize == sizeof(DWORD))
818         {
819             if (!(status = get_wait_mask(hDevice, (DWORD*)lpOutBuffer)))
820                 sz = sizeof(DWORD);
821         }
822         else
823             status = STATUS_INVALID_PARAMETER;
824         break;
825     case IOCTL_SERIAL_IMMEDIATE_CHAR:
826         if (lpInBuffer && nInBufferSize == sizeof(CHAR))
827             status = xmit_immediate(hDevice, fd, lpInBuffer);
828         else
829             status = STATUS_INVALID_PARAMETER;
830         break;
831     case IOCTL_SERIAL_PURGE:
832         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
833             status = purge(fd, *(DWORD*)lpInBuffer);
834         else
835             status = STATUS_INVALID_PARAMETER;
836         break;
837     case IOCTL_SERIAL_SET_BAUD_RATE:
838         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_BAUD_RATE))
839             status = set_baud_rate(fd, (const SERIAL_BAUD_RATE*)lpInBuffer);
840         else
841             status = STATUS_INVALID_PARAMETER;
842         break;
843     case IOCTL_SERIAL_SET_BREAK_OFF:
844 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
845         if (ioctl(fd, TIOCCBRK, 0) == -1)
846         {
847             TRACE("ioctl failed\n");
848             status = FILE_GetNtStatus();
849         }
850 #else
851         FIXME("ioctl not available\n");
852         status = STATUS_NOT_SUPPORTED;
853 #endif
854         break;
855     case IOCTL_SERIAL_SET_BREAK_ON:
856 #if defined(TIOCSBRK) && defined(TIOCCBRK) /* check if available for compilation */
857         if (ioctl(fd, TIOCSBRK, 0) == -1)
858         {
859             TRACE("ioctl failed\n");
860             status = FILE_GetNtStatus();
861         }
862 #else
863         FIXME("ioctl not available\n");
864         status = STATUS_NOT_SUPPORTED;
865 #endif
866         break;
867     case IOCTL_SERIAL_SET_CHARS:
868         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_CHARS))
869             status = set_special_chars(fd, (const SERIAL_CHARS*)lpInBuffer);
870         else
871             status = STATUS_INVALID_PARAMETER;
872         break;
873     case IOCTL_SERIAL_SET_HANDFLOW:
874         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_HANDFLOW))
875             status = set_handflow(fd, (const SERIAL_HANDFLOW*)lpInBuffer);
876         else
877             status = STATUS_INVALID_PARAMETER;
878         break;
879     case IOCTL_SERIAL_SET_LINE_CONTROL:
880         if (lpInBuffer && nInBufferSize == sizeof(SERIAL_LINE_CONTROL))
881             status = set_line_control(fd, (const SERIAL_LINE_CONTROL*)lpInBuffer);
882         else
883             status = STATUS_INVALID_PARAMETER;
884         break;
885     case IOCTL_SERIAL_SET_WAIT_MASK:
886         if (lpInBuffer && nInBufferSize == sizeof(DWORD))
887         {
888             status = set_wait_mask(hDevice, *(DWORD*)lpInBuffer);
889         }
890         else status = STATUS_INVALID_PARAMETER;
891         break;
892     default:
893         FIXME("Unsupported IOCTL %lx (type=%lx access=%lx func=%lx meth=%lx)\n", 
894               dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
895               (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
896         sz = 0;
897         status = STATUS_INVALID_PARAMETER;
898         break;
899     }
900     wine_server_release_fd( hDevice, fd );
901  error:
902     piosb->u.Status = status;
903     piosb->Information = sz;
904     if (hEvent) NtSetEvent(hEvent, NULL);
905     return status;
906 }