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