Audit combo control code.
[wine] / dlls / user / comm16.c
1 /*
2  * DEC 93 Erik Bos <erik@xs4all.nl>
3  *
4  * Copyright 1996 Marcus Meissner
5  *
6  * Copyright 2001 Mike McCormack
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * History:
23  *
24  * Mar 31, 1999. Ove Kåven <ovek@arcticnet.no>
25  * - Implemented buffers and EnableCommNotification.
26  *
27  * Apr 3, 1999.  Lawson Whitney <lawson_whitney@juno.com>
28  * - Fixed the modem control part of EscapeCommFunction16.
29  *
30  * Mar 3, 1999. Ove Kåven <ovek@arcticnet.no>
31  * - Use port indices instead of unixfds for win16
32  * - Moved things around (separated win16 and win32 routines)
33  * - Added some hints on how to implement buffers and EnableCommNotification.
34  *
35  * May 26, 1997.  Fixes and comments by Rick Richardson <rick@dgii.com> [RER]
36  * - ptr->fd wasn't getting cleared on close.
37  * - GetCommEventMask() and GetCommError() didn't do much of anything.
38  *   IMHO, they are still wrong, but they at least implement the RXCHAR
39  *   event and return I/O queue sizes, which makes the app I'm interested
40  *   in (analog devices EZKIT DSP development system) work.
41  *
42  * August 12, 1997.  Take a bash at SetCommEventMask - Lawson Whitney
43  *                                     <lawson_whitney@juno.com>
44  * July 6, 1998. Fixes and comments by Valentijn Sessink
45  *                                     <vsessink@ic.uva.nl> [V]
46  * Oktober 98, Rein Klazes [RHK]
47  * A program that wants to monitor the modem status line (RLSD/DCD) may
48  * poll the modem status register in the commMask structure. I update the bit
49  * in GetCommError, waiting for an implementation of communication events.
50  *
51  */
52
53 #include "config.h"
54 #include "wine/port.h"
55
56 #include <stdlib.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <errno.h>
61 #include <ctype.h>
62
63 #include "ntstatus.h"
64 #include "windef.h"
65 #include "winbase.h"
66 #include "wingdi.h"
67 #include "winreg.h"
68 #include "winuser.h"
69 #include "wine/winuser16.h"
70 #include "win.h"
71 #include "winerror.h"
72
73 #include "wine/debug.h"
74
75 WINE_DEFAULT_DEBUG_CHANNEL(comm);
76
77 /* window's semi documented modem status register */
78 #define COMM_MSR_OFFSET  35
79 #define MSR_CTS  0x10
80 #define MSR_DSR  0x20
81 #define MSR_RI   0x40
82 #define MSR_RLSD 0x80
83 #define MSR_MASK (MSR_CTS|MSR_DSR|MSR_RI|MSR_RLSD)
84
85 #define FLAG_LPT 0x80
86
87 #define MAX_PORTS   9
88
89 struct DosDeviceStruct {
90     HANDLE handle;
91     int suspended;
92     int unget,xmit;
93     int evtchar;
94     /* events */
95     int commerror, eventmask;
96     /* buffers */
97     char *inbuf,*outbuf;
98     unsigned ibuf_size,ibuf_head,ibuf_tail;
99     unsigned obuf_size,obuf_head,obuf_tail;
100     /* notifications */
101     HWND wnd;
102     int n_read, n_write;
103     OVERLAPPED read_ov, write_ov;
104     /* save terminal states */
105     DCB16 dcb;
106     /* pointer to unknown(==undocumented) comm structure */
107     SEGPTR seg_unknown;
108     BYTE unknown[40];
109 };
110
111 static struct DosDeviceStruct COM[MAX_PORTS];
112 static struct DosDeviceStruct LPT[MAX_PORTS];
113
114 /* update window's semi documented modem status register */
115 /* see knowledge base Q101417 */
116 static void COMM_MSRUpdate( HANDLE handle, UCHAR * pMsr )
117 {
118     UCHAR tmpmsr=0;
119     DWORD mstat=0;
120
121     if(!GetCommModemStatus(handle,&mstat))
122         return;
123
124     if(mstat & MS_CTS_ON) tmpmsr |= MSR_CTS;
125     if(mstat & MS_DSR_ON) tmpmsr |= MSR_DSR;
126     if(mstat & MS_RING_ON) tmpmsr |= MSR_RI;
127     if(mstat & MS_RLSD_ON) tmpmsr |= MSR_RLSD;
128     *pMsr = (*pMsr & ~MSR_MASK) | tmpmsr;
129 }
130
131 static struct DosDeviceStruct *GetDeviceStruct(int index)
132 {
133         if ((index&0x7F)<=MAX_PORTS) {
134             if (!(index&FLAG_LPT)) {
135                 if (COM[index].handle)
136                     return &COM[index];
137             } else {
138                 index &= 0x7f;
139                 if (LPT[index].handle)
140                     return &LPT[index];
141             }
142         }
143
144         return NULL;
145 }
146
147 static int    GetCommPort_ov(LPOVERLAPPED ov, int write)
148 {
149         int x;
150
151         for (x=0; x<MAX_PORTS; x++) {
152                 if (ov == (write?&COM[x].write_ov:&COM[x].read_ov))
153                         return x;
154         }
155
156         return -1;
157 }
158
159 static int WinError(void)
160 {
161         TRACE("errno = %d\n", errno);
162         switch (errno) {
163                 default:
164                         return CE_IOE;
165                 }
166 }
167
168 static unsigned comm_inbuf(struct DosDeviceStruct *ptr)
169 {
170   return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
171     + ptr->ibuf_head - ptr->ibuf_tail;
172 }
173
174 static unsigned comm_outbuf(struct DosDeviceStruct *ptr)
175 {
176   return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
177     + ptr->obuf_head - ptr->obuf_tail;
178 }
179
180 static void comm_waitread(struct DosDeviceStruct *ptr);
181 static void comm_waitwrite(struct DosDeviceStruct *ptr);
182
183 static VOID WINAPI COMM16_ReadComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
184 {
185         int prev ;
186         WORD mask = 0;
187         int cid = GetCommPort_ov(ov,0);
188         struct DosDeviceStruct *ptr;
189
190         if(cid<0) {
191                 ERR("async write with bad overlapped pointer\n");
192                 return;
193         }
194         ptr = &COM[cid];
195
196         /* we get cancelled when CloseComm is called */
197         if (status==STATUS_CANCELLED)
198         {
199                 TRACE("Cancelled\n");
200                 return;
201         }
202
203         /* read data from comm port */
204         if (status != STATUS_SUCCESS) {
205                 ERR("async read failed %08lx\n",status);
206                 COM[cid].commerror = CE_RXOVER;
207                 return;
208         }
209         TRACE("async read completed %ld bytes\n",len);
210
211         prev = comm_inbuf(ptr);
212
213         /* check for events */
214         if ((ptr->eventmask & EV_RXFLAG) &&
215             memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) {
216                 *(WORD*)(COM[cid].unknown) |= EV_RXFLAG;
217                 mask |= CN_EVENT;
218         }
219         if (ptr->eventmask & EV_RXCHAR) {
220                 *(WORD*)(COM[cid].unknown) |= EV_RXCHAR;
221                 mask |= CN_EVENT;
222         }
223
224         /* advance buffer position */
225         ptr->ibuf_head += len;
226         if (ptr->ibuf_head >= ptr->ibuf_size)
227                 ptr->ibuf_head = 0;
228
229         /* check for notification */
230         if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
231             (comm_inbuf(ptr)>=ptr->n_read)) {
232                 /* passed the receive notification threshold */
233                 mask |= CN_RECEIVE;
234         }
235
236         /* send notifications, if any */
237         if (ptr->wnd && mask) {
238                 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
239                 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
240         }
241
242         /* on real windows, this could cause problems, since it is recursive */
243         /* restart the receive */
244         comm_waitread(ptr);
245 }
246
247 /* this is meant to work like write() */
248 static INT COMM16_WriteFile(HANDLE hComm, LPCVOID buffer, DWORD len)
249 {
250         OVERLAPPED ov;
251         DWORD count= -1;
252
253         ZeroMemory(&ov,sizeof(ov));
254         ov.hEvent = CreateEventA(NULL,0,0,NULL);
255         if(ov.hEvent==INVALID_HANDLE_VALUE)
256                 return -1;
257
258         if(!WriteFile(hComm,buffer,len,&count,&ov))
259         {
260                 if(GetLastError()==ERROR_IO_PENDING)
261                 {
262                         GetOverlappedResult(hComm,&ov,&count,TRUE);
263                 }
264         }
265         CloseHandle(ov.hEvent);
266
267         return count;
268 }
269
270 static VOID WINAPI COMM16_WriteComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
271 {
272         int prev, bleft;
273         WORD mask = 0;
274         int cid = GetCommPort_ov(ov,1);
275         struct DosDeviceStruct *ptr;
276
277         if(cid<0) {
278                 ERR("async write with bad overlapped pointer\n");
279                 return;
280         }
281         ptr = &COM[cid];
282
283         /* read data from comm port */
284         if (status != STATUS_SUCCESS) {
285                 ERR("async write failed\n");
286                 COM[cid].commerror = CE_RXOVER;
287                 return;
288         }
289         TRACE("async write completed %ld bytes\n",len);
290
291         /* update the buffer pointers */
292         prev = comm_outbuf(&COM[cid]);
293         ptr->obuf_tail += len;
294         if (ptr->obuf_tail >= ptr->obuf_size)
295                 ptr->obuf_tail = 0;
296
297         /* write any TransmitCommChar character */
298         if (ptr->xmit>=0) {
299                 len = COMM16_WriteFile(ptr->handle, &(ptr->xmit), 1);
300                 if (len > 0) ptr->xmit = -1;
301         }
302
303         /* write from output queue */
304         bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
305                 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
306
307         /* check for notification */
308         if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
309           (comm_outbuf(ptr)<ptr->n_write)) {
310                 /* passed the transmit notification threshold */
311                 mask |= CN_TRANSMIT;
312         }
313
314         /* send notifications, if any */
315         if (ptr->wnd && mask) {
316                 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
317                 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
318         }
319
320         /* start again if necessary */
321         if(bleft)
322                 comm_waitwrite(ptr);
323 }
324
325 static void comm_waitread(struct DosDeviceStruct *ptr)
326 {
327         unsigned int bleft;
328         COMSTAT stat;
329
330         /* FIXME: get timeouts working properly so we can read bleft bytes */
331         bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ?
332                 (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
333
334         /* find out how many bytes are left in the buffer */
335         if(ClearCommError(ptr->handle,NULL,&stat))
336                 bleft = (bleft<stat.cbInQue) ? bleft : stat.cbInQue;
337         else
338                 bleft = 1;
339
340         /* always read at least one byte */
341         if(bleft==0)
342                 bleft++;
343
344         ReadFileEx(ptr->handle,
345                 ptr->inbuf + ptr->ibuf_head,
346                 bleft,
347                 &ptr->read_ov,
348                 COMM16_ReadComplete);
349 }
350
351 static void comm_waitwrite(struct DosDeviceStruct *ptr)
352 {
353         int bleft;
354
355         bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
356                 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
357         WriteFileEx(ptr->handle,
358                 ptr->outbuf + ptr->obuf_tail,
359                 bleft,
360                 &ptr->write_ov,
361                 COMM16_WriteComplete);
362 }
363
364 /*****************************************************************************
365  *      COMM16_DCBtoDCB16       (Internal)
366  */
367 INT16 COMM16_DCBtoDCB16(LPDCB lpdcb, LPDCB16 lpdcb16)
368 {
369         if(lpdcb->BaudRate<0x10000)
370                 lpdcb16->BaudRate = lpdcb->BaudRate;
371         else if(lpdcb->BaudRate==115200)
372                         lpdcb16->BaudRate = 57601;
373         else {
374                 WARN("Baud rate can't be converted\n");
375                 lpdcb16->BaudRate = 57601;
376         }
377         lpdcb16->ByteSize = lpdcb->ByteSize;
378         lpdcb16->fParity = lpdcb->fParity;
379         lpdcb16->Parity = lpdcb->Parity;
380         lpdcb16->StopBits = lpdcb->StopBits;
381
382         lpdcb16->RlsTimeout = 50;
383         lpdcb16->CtsTimeout = 50;
384         lpdcb16->DsrTimeout = 50;
385         lpdcb16->fNull = 0;
386         lpdcb16->fChEvt = 0;
387         lpdcb16->fBinary = 1;
388
389         lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_HANDSHAKE);
390         lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_HANDSHAKE);
391         lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow;
392         lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow;
393         lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE);
394
395         lpdcb16->fInX = lpdcb->fInX;
396
397         lpdcb16->fOutX = lpdcb->fOutX;
398 /*
399         lpdcb16->XonChar =
400         lpdcb16->XoffChar =
401  */
402         lpdcb16->XonLim = 10;
403         lpdcb16->XoffLim = 10;
404
405         return 0;
406 }
407
408
409 /**************************************************************************
410  *         BuildCommDCB         (USER.213)
411  *
412  * According to the ECMA-234 (368.3) the function will return FALSE on
413  * success, otherwise it will return -1.
414  */
415 INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
416 {
417         /* "COM1:96,n,8,1"      */
418         /*  012345              */
419         int port;
420         DCB dcb;
421
422         TRACE("(%s), ptr %p\n", device, lpdcb);
423
424         if (strncasecmp(device,"COM",3))
425                 return -1;
426         port = device[3] - '0';
427
428         if (port-- == 0) {
429                 ERR("BUG ! COM0 can't exist!\n");
430                 return -1;
431         }
432
433         memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
434
435         lpdcb->Id = port;
436         dcb.DCBlength = sizeof(DCB);
437
438         if (strchr(device,'=')) /* block new style */
439                 return -1;
440
441         if(!BuildCommDCBA(device,&dcb))
442                 return -1;
443
444         return COMM16_DCBtoDCB16(&dcb, lpdcb);
445 }
446
447 /*****************************************************************************
448  *      OpenComm                (USER.200)
449  */
450 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
451 {
452         int port;
453         HANDLE handle;
454
455         TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue);
456
457         if (strlen(device) < 4)
458            return IE_BADID;
459
460         port = device[3] - '0';
461
462         if (port-- == 0)
463                 ERR("BUG ! COM0 or LPT0 don't exist !\n");
464
465         if (!strncasecmp(device,"COM",3))
466         {
467                 if (COM[port].handle)
468                         return IE_OPEN;
469
470                 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
471                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
472                         FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, 0 );
473                 if (handle == INVALID_HANDLE_VALUE) {
474                         return IE_HARDWARE;
475                 } else {
476                         memset(COM[port].unknown, 0, sizeof(COM[port].unknown));
477                         COM[port].seg_unknown = 0;
478                         COM[port].handle = handle;
479                         COM[port].commerror = 0;
480                         COM[port].eventmask = 0;
481                         COM[port].evtchar = 0; /* FIXME: default? */
482                         /* save terminal state */
483                         GetCommState16(port,&COM[port].dcb);
484                         /* init priority characters */
485                         COM[port].unget = -1;
486                         COM[port].xmit = -1;
487                         /* allocate buffers */
488                         COM[port].ibuf_size = cbInQueue;
489                         COM[port].ibuf_head = COM[port].ibuf_tail = 0;
490                         COM[port].obuf_size = cbOutQueue;
491                         COM[port].obuf_head = COM[port].obuf_tail = 0;
492
493                         COM[port].inbuf = HeapAlloc(GetProcessHeap(), 0, cbInQueue);
494                         if (COM[port].inbuf) {
495                           COM[port].outbuf = HeapAlloc( GetProcessHeap(), 0, cbOutQueue);
496                           if (!COM[port].outbuf)
497                             HeapFree( GetProcessHeap(), 0, COM[port].inbuf);
498                         } else COM[port].outbuf = NULL;
499                         if (!COM[port].outbuf) {
500                           /* not enough memory */
501                           CloseHandle(COM[port].handle);
502                           ERR("out of memory\n");
503                           return IE_MEMORY;
504                         }
505
506                         ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED));
507                         ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED));
508
509                         comm_waitread( &COM[port] );
510                         USER16_AlertableWait++;
511
512                         return port;
513                 }
514         }
515         else
516         if (!strncasecmp(device,"LPT",3)) {
517
518                 if (LPT[port].handle)
519                         return IE_OPEN;
520
521                 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
522                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
523                 if (handle == INVALID_HANDLE_VALUE) {
524                         return IE_HARDWARE;
525                 } else {
526                         LPT[port].handle = handle;
527                         LPT[port].commerror = 0;
528                         LPT[port].eventmask = 0;
529                         return port|FLAG_LPT;
530                 }
531         }
532         return IE_BADID;
533 }
534
535 /*****************************************************************************
536  *      CloseComm               (USER.207)
537  */
538 INT16 WINAPI CloseComm16(INT16 cid)
539 {
540         struct DosDeviceStruct *ptr;
541
542         TRACE("cid=%d\n", cid);
543         if ((ptr = GetDeviceStruct(cid)) == NULL) {
544                 FIXME("no cid=%d found!\n", cid);
545                 return -1;
546         }
547         if (!(cid&FLAG_LPT)) {
548                 /* COM port */
549                 UnMapLS( COM[cid].seg_unknown );
550                 USER16_AlertableWait--;
551                 CancelIo(ptr->handle);
552
553                 /* free buffers */
554                 HeapFree( GetProcessHeap(), 0, ptr->outbuf);
555                 HeapFree( GetProcessHeap(), 0, ptr->inbuf);
556
557                 /* reset modem lines */
558                 SetCommState16(&COM[cid].dcb);
559         }
560
561         if (!CloseHandle(ptr->handle)) {
562                 ptr->commerror = WinError();
563                 /* FIXME: should we clear ptr->handle here? */
564                 return -1;
565         } else {
566                 ptr->commerror = 0;
567                 ptr->handle = 0;
568                 return 0;
569         }
570 }
571
572 /*****************************************************************************
573  *      SetCommBreak            (USER.210)
574  */
575 INT16 WINAPI SetCommBreak16(INT16 cid)
576 {
577         struct DosDeviceStruct *ptr;
578
579         TRACE("cid=%d\n", cid);
580         if ((ptr = GetDeviceStruct(cid)) == NULL) {
581                 FIXME("no cid=%d found!\n", cid);
582                 return -1;
583         }
584
585         ptr->suspended = 1;
586         ptr->commerror = 0;
587         return 0;
588 }
589
590 /*****************************************************************************
591  *      ClearCommBreak  (USER.211)
592  */
593 INT16 WINAPI ClearCommBreak16(INT16 cid)
594 {
595         struct DosDeviceStruct *ptr;
596
597         TRACE("cid=%d\n", cid);
598         if (!(ptr = GetDeviceStruct(cid))) {
599                 FIXME("no cid=%d found!\n", cid);
600                 return -1;
601         }
602         ptr->suspended = 0;
603         ptr->commerror = 0;
604         return 0;
605 }
606
607 /*****************************************************************************
608  *      EscapeCommFunction      (USER.214)
609  */
610 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
611 {
612         struct  DosDeviceStruct *ptr;
613
614         TRACE("cid=%d, function=%d\n", cid, nFunction);
615
616         switch(nFunction) {
617         case GETMAXCOM:
618                 TRACE("GETMAXCOM\n");
619                 return 4;  /* FIXME */
620
621         case GETMAXLPT:
622                 TRACE("GETMAXLPT\n");
623                 return FLAG_LPT + 3;  /* FIXME */
624
625         case GETBASEIRQ:
626                 TRACE("GETBASEIRQ\n");
627                 /* FIXME: use tables */
628                 /* just fake something for now */
629                 if (cid & FLAG_LPT) {
630                         /* LPT1: irq 7, LPT2: irq 5 */
631                         return (cid & 0x7f) ? 5 : 7;
632                 } else {
633                         /* COM1: irq 4, COM2: irq 3,
634                            COM3: irq 4, COM4: irq 3 */
635                         return 4 - (cid & 1);
636                 }
637         }
638
639         if ((ptr = GetDeviceStruct(cid)) == NULL) {
640                 FIXME("no cid=%d found!\n", cid);
641                 return -1;
642         }
643
644         switch (nFunction) {
645         case RESETDEV:
646         case CLRDTR:
647         case CLRRTS:
648         case SETDTR:
649         case SETRTS:
650         case SETXOFF:
651         case SETXON:
652                 if(EscapeCommFunction(ptr->handle,nFunction))
653                         return 0;
654                 else {
655                         ptr->commerror = WinError();
656                         return -1;
657                 }
658
659         case CLRBREAK:
660         case SETBREAK:
661         default:
662                 WARN("(cid=%d,nFunction=%d): Unknown function\n",
663                         cid, nFunction);
664         }
665         return -1;
666 }
667
668 /*****************************************************************************
669  *      FlushComm       (USER.215)
670  */
671 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
672 {
673         DWORD queue;
674         struct DosDeviceStruct *ptr;
675
676         TRACE("cid=%d, queue=%d\n", cid, fnQueue);
677         if ((ptr = GetDeviceStruct(cid)) == NULL) {
678                 FIXME("no cid=%d found!\n", cid);
679                 return -1;
680         }
681         switch (fnQueue) {
682         case 0:
683                 queue = PURGE_TXABORT;
684                 ptr->obuf_tail = ptr->obuf_head;
685                 break;
686         case 1:
687                 queue = PURGE_RXABORT;
688                 ptr->ibuf_head = ptr->ibuf_tail;
689                 break;
690         default:
691                 WARN("(cid=%d,fnQueue=%d):Unknown queue\n",
692                             cid, fnQueue);
693                 return -1;
694         }
695
696         if (!PurgeComm(ptr->handle,queue)) {
697                 ptr->commerror = WinError();
698                 return -1;
699         } else {
700                 ptr->commerror = 0;
701                 return 0;
702         }
703 }
704
705 /********************************************************************
706  *      GetCommError    (USER.203)
707  */
708 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
709 {
710         int             temperror;
711         struct DosDeviceStruct *ptr;
712         unsigned char *stol;
713
714         if ((ptr = GetDeviceStruct(cid)) == NULL) {
715                 FIXME("no handle for cid = %0x!\n",cid);
716                 return -1;
717         }
718         if (cid&FLAG_LPT) {
719             WARN(" cid %d not comm port\n",cid);
720             return CE_MODE;
721         }
722         stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
723         COMM_MSRUpdate( ptr->handle, stol );
724
725         if (lpStat) {
726                 lpStat->status = 0;
727
728                 SleepEx(1,TRUE);
729
730                 lpStat->cbOutQue = comm_outbuf(ptr);
731                 lpStat->cbInQue = comm_inbuf(ptr);
732
733                 TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n",
734                              cid, ptr->commerror, lpStat->status, lpStat->cbInQue,
735                              lpStat->cbOutQue, *stol);
736         }
737         else
738                 TRACE("cid %d, error %d, lpStat NULL stol %x\n",
739                              cid, ptr->commerror, *stol);
740
741         /* Return any errors and clear it */
742         temperror = ptr->commerror;
743         ptr->commerror = 0;
744         return(temperror);
745 }
746
747 /*****************************************************************************
748  *      SetCommEventMask        (USER.208)
749  */
750 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
751 {
752         struct DosDeviceStruct *ptr;
753         unsigned char *stol;
754
755         TRACE("cid %d,mask %d\n",cid,fuEvtMask);
756         if ((ptr = GetDeviceStruct(cid)) == NULL) {
757                 FIXME("no handle for cid = %0x!\n",cid);
758             return (SEGPTR)NULL;
759         }
760
761         ptr->eventmask = fuEvtMask;
762
763         if (cid&FLAG_LPT) {
764             WARN(" cid %d not comm port\n",cid);
765             return (SEGPTR)NULL;
766         }
767         /* it's a COM port ? -> modify flags */
768         stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
769         COMM_MSRUpdate( ptr->handle, stol );
770
771         TRACE(" modem dcd construct %x\n",*stol);
772         if (!COM[cid].seg_unknown) COM[cid].seg_unknown = MapLS( COM[cid].unknown );
773         return COM[cid].seg_unknown;
774 }
775
776 /*****************************************************************************
777  *      GetCommEventMask        (USER.209)
778  */
779 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
780 {
781         struct DosDeviceStruct *ptr;
782         WORD events;
783
784         TRACE("cid %d, mask %d\n", cid, fnEvtClear);
785         if ((ptr = GetDeviceStruct(cid)) == NULL) {
786                 FIXME("no handle for cid = %0x!\n",cid);
787             return 0;
788         }
789
790         if (cid&FLAG_LPT) {
791             WARN(" cid %d not comm port\n",cid);
792             return 0;
793         }
794
795         events = *(WORD*)(COM[cid].unknown) & fnEvtClear;
796         *(WORD*)(COM[cid].unknown) &= ~fnEvtClear;
797         return events;
798 }
799
800 /*****************************************************************************
801  *      SetCommState    (USER.201)
802  */
803 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
804 {
805         struct DosDeviceStruct *ptr;
806         DCB dcb;
807
808         TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb);
809         if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
810                 FIXME("no handle for cid = %0x!\n",lpdcb->Id);
811                 return -1;
812         }
813
814         memset(&dcb,0,sizeof(dcb));
815         dcb.DCBlength = sizeof(dcb);
816
817         /*
818          * according to MSDN, we should first interpret lpdcb->BaudRate as follows:
819          * 1. if the baud rate is a CBR constant, interpret it.
820          * 2. if it is greater than 57600, the baud rate is 115200
821          * 3. use the actual baudrate
822          * steps 2 and 3 are equivilent to 16550 baudrate divisor = 115200/BaudRate
823          * see http://support.microsoft.com/support/kb/articles/q108/9/28.asp
824          */
825         switch(lpdcb->BaudRate)
826         {
827         case CBR_110:    dcb.BaudRate = 110;    break;
828         case CBR_300:    dcb.BaudRate = 300;    break;
829         case CBR_600:    dcb.BaudRate = 600;    break;
830         case CBR_1200:   dcb.BaudRate = 1200;   break;
831         case CBR_2400:   dcb.BaudRate = 2400;   break;
832         case CBR_4800:   dcb.BaudRate = 4800;   break;
833         case CBR_9600:   dcb.BaudRate = 9600;   break;
834         case CBR_14400:  dcb.BaudRate = 14400;  break;
835         case CBR_19200:  dcb.BaudRate = 19200;  break;
836         case CBR_38400:  dcb.BaudRate = 38400;  break;
837         case CBR_56000:  dcb.BaudRate = 56000;  break;
838         case CBR_128000: dcb.BaudRate = 128000; break;
839         case CBR_256000: dcb.BaudRate = 256000; break;
840         default:
841                 if(lpdcb->BaudRate>57600)
842                 dcb.BaudRate = 115200;
843         else
844                 dcb.BaudRate = lpdcb->BaudRate;
845         }
846
847         dcb.ByteSize=lpdcb->ByteSize;
848         dcb.StopBits=lpdcb->StopBits;
849
850         dcb.fParity=lpdcb->fParity;
851         dcb.Parity=lpdcb->Parity;
852
853         dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow;
854
855         if (lpdcb->fDtrflow || lpdcb->fRtsflow)
856                 dcb.fRtsControl = TRUE;
857
858         if (lpdcb->fDtrDisable)
859                 dcb.fDtrControl = TRUE;
860
861         ptr->evtchar = lpdcb->EvtChar;
862
863         dcb.fInX = lpdcb->fInX;
864         dcb.fOutX = lpdcb->fOutX;
865
866         if (!SetCommState(ptr->handle,&dcb)) {
867                 ptr->commerror = WinError();
868                 return -1;
869         } else {
870                 ptr->commerror = 0;
871                 return 0;
872         }
873 }
874
875 /*****************************************************************************
876  *      GetCommState    (USER.202)
877  */
878 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
879 {
880         struct DosDeviceStruct *ptr;
881         DCB dcb;
882
883         TRACE("cid %d, ptr %p\n", cid, lpdcb);
884         if ((ptr = GetDeviceStruct(cid)) == NULL) {
885                 FIXME("no handle for cid = %0x!\n",cid);
886                 return -1;
887         }
888         if (!GetCommState(ptr->handle,&dcb)) {
889                 ptr->commerror = WinError();
890                 return -1;
891         }
892
893         lpdcb->Id = cid;
894
895         COMM16_DCBtoDCB16(&dcb,lpdcb);
896
897         lpdcb->EvtChar = ptr->evtchar;
898
899         ptr->commerror = 0;
900         return 0;
901 }
902
903 /*****************************************************************************
904  *      TransmitCommChar        (USER.206)
905  */
906 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
907 {
908         struct DosDeviceStruct *ptr;
909
910         TRACE("cid %d, data %d \n", cid, chTransmit);
911         if ((ptr = GetDeviceStruct(cid)) == NULL) {
912                 FIXME("no handle for cid = %0x!\n",cid);
913                 return -1;
914         }
915
916         if (ptr->suspended) {
917                 ptr->commerror = IE_HARDWARE;
918                 return -1;
919         }
920
921         if (ptr->xmit >= 0) {
922           /* character already queued */
923           /* FIXME: which error would Windows return? */
924           ptr->commerror = CE_TXFULL;
925           return -1;
926         }
927
928         if (ptr->obuf_head == ptr->obuf_tail) {
929           /* transmit queue empty, try to transmit directly */
930           if(1!=COMM16_WriteFile(ptr->handle, &chTransmit, 1))
931           {
932             /* didn't work, queue it */
933             ptr->xmit = chTransmit;
934             comm_waitwrite(ptr);
935           }
936         } else {
937           /* data in queue, let this char be transmitted next */
938           ptr->xmit = chTransmit;
939           comm_waitwrite(ptr);
940         }
941
942         ptr->commerror = 0;
943         return 0;
944 }
945
946 /*****************************************************************************
947  *      UngetCommChar   (USER.212)
948  */
949 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
950 {
951         struct DosDeviceStruct *ptr;
952
953         TRACE("cid %d (char %d)\n", cid, chUnget);
954         if ((ptr = GetDeviceStruct(cid)) == NULL) {
955                 FIXME("no handle for cid = %0x!\n",cid);
956                 return -1;
957         }
958
959         if (ptr->suspended) {
960                 ptr->commerror = IE_HARDWARE;
961                 return -1;
962         }
963
964         if (ptr->unget>=0) {
965           /* character already queued */
966           /* FIXME: which error would Windows return? */
967           ptr->commerror = CE_RXOVER;
968           return -1;
969         }
970
971         ptr->unget = chUnget;
972
973         ptr->commerror = 0;
974         return 0;
975 }
976
977 /*****************************************************************************
978  *      ReadComm        (USER.204)
979  */
980 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
981 {
982         int status, length;
983         struct DosDeviceStruct *ptr;
984         LPSTR orgBuf = lpvBuf;
985
986         TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
987         if ((ptr = GetDeviceStruct(cid)) == NULL) {
988                 FIXME("no handle for cid = %0x!\n",cid);
989                 return -1;
990         }
991
992         if (ptr->suspended) {
993                 ptr->commerror = IE_HARDWARE;
994                 return -1;
995         }
996
997         if(0==comm_inbuf(ptr))
998                 SleepEx(1,TRUE);
999
1000         /* read unget character */
1001         if (ptr->unget>=0) {
1002                 *lpvBuf++ = ptr->unget;
1003                 ptr->unget = -1;
1004
1005                 length = 1;
1006         } else
1007                 length = 0;
1008
1009         /* read from receive buffer */
1010         while (length < cbRead) {
1011           status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1012                     ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1013           if (!status) break;
1014           if ((cbRead - length) < status)
1015             status = cbRead - length;
1016
1017           memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1018           ptr->ibuf_tail += status;
1019           if (ptr->ibuf_tail >= ptr->ibuf_size)
1020             ptr->ibuf_tail = 0;
1021           lpvBuf += status;
1022           length += status;
1023         }
1024
1025         TRACE("%s\n", debugstr_an( orgBuf, length ));
1026         ptr->commerror = 0;
1027         return length;
1028 }
1029
1030 /*****************************************************************************
1031  *      WriteComm       (USER.205)
1032  */
1033 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1034 {
1035         int status, length;
1036         struct DosDeviceStruct *ptr;
1037
1038         TRACE("cid %d, ptr %p, length %d\n",
1039                 cid, lpvBuf, cbWrite);
1040         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1041                 FIXME("no handle for cid = %0x!\n",cid);
1042                 return -1;
1043         }
1044
1045         if (ptr->suspended) {
1046                 ptr->commerror = IE_HARDWARE;
1047                 return -1;
1048         }
1049
1050         TRACE("%s\n", debugstr_an( lpvBuf, cbWrite ));
1051
1052         length = 0;
1053         while (length < cbWrite) {
1054           if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1055             /* no data queued, try to write directly */
1056             status = COMM16_WriteFile(ptr->handle, lpvBuf, cbWrite - length);
1057             if (status > 0) {
1058               lpvBuf += status;
1059               length += status;
1060               continue;
1061             }
1062           }
1063           /* can't write directly, put into transmit buffer */
1064           status = ((ptr->obuf_tail > ptr->obuf_head) ?
1065                     (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1066           if (!status) break;
1067           if ((cbWrite - length) < status)
1068             status = cbWrite - length;
1069           memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1070           ptr->obuf_head += status;
1071           if (ptr->obuf_head >= ptr->obuf_size)
1072             ptr->obuf_head = 0;
1073           lpvBuf += status;
1074           length += status;
1075           comm_waitwrite(ptr);
1076         }
1077
1078         ptr->commerror = 0;
1079         return length;
1080 }
1081
1082 /***********************************************************************
1083  *           EnableCommNotification   (USER.245)
1084  */
1085 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1086                                       INT16 cbWriteNotify, INT16 cbOutQueue )
1087 {
1088         struct DosDeviceStruct *ptr;
1089
1090         TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1091         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1092                 FIXME("no handle for cid = %0x!\n",cid);
1093                 return -1;
1094         }
1095         ptr->wnd = WIN_Handle32( hwnd );
1096         ptr->n_read = cbWriteNotify;
1097         ptr->n_write = cbOutQueue;
1098         return TRUE;
1099 }