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