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