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