Don't print NULL strings (crashes on Solaris).
[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 "heap.h"
53 #include "win.h"
54 #include "winerror.h"
55
56 #include "debugtools.h"
57
58 DEFAULT_DEBUG_CHANNEL(comm);
59
60 /* window's semi documented modem status register */
61 #define COMM_MSR_OFFSET  35
62 #define MSR_CTS  0x10
63 #define MSR_DSR  0x20
64 #define MSR_RI   0x40
65 #define MSR_RLSD 0x80
66 #define MSR_MASK (MSR_CTS|MSR_DSR|MSR_RI|MSR_RLSD)
67
68 #define FLAG_LPT 0x80
69
70 #define MAX_PORTS   9
71
72 struct DosDeviceStruct {
73     char *devicename;   /* /dev/ttyS0 */
74     HANDLE handle;
75     int suspended;
76     int unget,xmit;
77     int baudrate;
78     int evtchar;
79     /* events */
80     int commerror, eventmask;
81     /* buffers */
82     char *inbuf,*outbuf;
83     unsigned ibuf_size,ibuf_head,ibuf_tail;
84     unsigned obuf_size,obuf_head,obuf_tail;
85     /* notifications */
86     HWND wnd;
87     int n_read, n_write;
88     OVERLAPPED read_ov, write_ov;
89     /* save terminal states */
90     DCB16 dcb;
91     /* pointer to unknown(==undocumented) comm structure */ 
92     LPCVOID *unknown;
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         /* read data from comm port */
263         if (status != STATUS_SUCCESS) {
264                 ERR("async read failed\n");
265                 COM[cid].commerror = CE_RXOVER;
266                 return;
267         }
268         TRACE("async read completed %ld bytes\n",len);
269
270         prev = comm_inbuf(ptr);
271
272         /* check for events */
273         if ((ptr->eventmask & EV_RXFLAG) &&
274             memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) {
275                 *(WORD*)(COM[cid].unknown) |= EV_RXFLAG;
276                 mask |= CN_EVENT;
277         }
278         if (ptr->eventmask & EV_RXCHAR) {
279                 *(WORD*)(COM[cid].unknown) |= EV_RXCHAR;
280                 mask |= CN_EVENT;
281         }
282
283         /* advance buffer position */
284         ptr->ibuf_head += len;
285         if (ptr->ibuf_head >= ptr->ibuf_size)
286                 ptr->ibuf_head = 0;
287
288         /* check for notification */
289         if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
290             (comm_inbuf(ptr)>=ptr->n_read)) {
291                 /* passed the receive notification threshold */
292                 mask |= CN_RECEIVE;
293         }
294
295         /* send notifications, if any */
296         if (ptr->wnd && mask) {
297                 TRACE("notifying %04x: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
298                 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
299         }
300
301         /* on real windows, this could cause problems, since it is recursive */
302         /* restart the receive */
303         comm_waitread(ptr);
304 }
305
306 static VOID WINAPI COMM16_WriteComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
307 {
308         int prev, bleft;
309         WORD mask = 0;
310         int cid = GetCommPort_ov(ov,1);
311         struct DosDeviceStruct *ptr;
312
313         if(cid<0) {
314                 ERR("async write with bad overlapped pointer\n");
315                 return;
316         }
317         ptr = &COM[cid];
318
319         /* read data from comm port */
320         if (status != STATUS_SUCCESS) {
321                 ERR("async write failed\n");
322                 COM[cid].commerror = CE_RXOVER;
323                 return;
324         }
325         TRACE("async write completed %ld bytes\n",len);
326
327         /* update the buffer pointers */
328         prev = comm_outbuf(&COM[cid]);
329         ptr->obuf_tail += len;
330         if (ptr->obuf_tail >= ptr->obuf_size)
331                 ptr->obuf_tail = 0;
332
333         /* write any TransmitCommChar character */
334         if (ptr->xmit>=0) {
335                 if(!WriteFile(ptr->handle, &(ptr->xmit), 1, &len, NULL))
336                         len = -1;
337                 if (len > 0) ptr->xmit = -1;
338         }
339
340         /* write from output queue */
341         bleft = ((ptr->obuf_tail <= ptr->obuf_head) ? 
342                 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
343
344         /* check for notification */
345         if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
346           (comm_outbuf(ptr)<ptr->n_write)) {
347                 /* passed the transmit notification threshold */
348                 mask |= CN_TRANSMIT;
349         }
350
351         /* send notifications, if any */
352         if (ptr->wnd && mask) {
353                 TRACE("notifying %04x: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
354                 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
355         }
356
357         /* start again if necessary */
358         if(bleft)
359                 comm_waitwrite(ptr);
360 }
361
362 static void comm_waitread(struct DosDeviceStruct *ptr)
363 {
364         int bleft;
365
366         bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ? 
367                 (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
368         /* FIXME: get timeouts working properly so we can read bleft bytes */
369         ReadFileEx(ptr->handle,
370                 ptr->inbuf + ptr->ibuf_head,
371                 1,
372                 &ptr->read_ov,
373                 COMM16_ReadComplete);
374 }
375
376 static void comm_waitwrite(struct DosDeviceStruct *ptr)
377 {
378         int bleft;
379
380         bleft = ((ptr->obuf_tail <= ptr->obuf_head) ? 
381                 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
382         WriteFileEx(ptr->handle,
383                 ptr->outbuf + ptr->obuf_tail,
384                 bleft,
385                 &ptr->write_ov,
386                 COMM16_WriteComplete);
387 }
388
389 /*****************************************************************************
390  *      COMM16_DCBtoDCB16       (Internal)
391  */
392 INT16 COMM16_DCBtoDCB16(LPDCB lpdcb, LPDCB16 lpdcb16)
393 {
394         if(lpdcb->BaudRate<0x10000)
395                 lpdcb16->BaudRate = lpdcb->BaudRate;
396         else if(lpdcb->BaudRate==115200)
397                         lpdcb16->BaudRate = 57601;
398         else {
399                 WARN("Baud rate can't be converted\n");
400                 lpdcb16->BaudRate = 57601;
401         }
402         lpdcb16->ByteSize = lpdcb->ByteSize;
403         lpdcb16->fParity = lpdcb->fParity;
404         lpdcb16->Parity = lpdcb->Parity;
405         lpdcb16->StopBits = lpdcb->StopBits;
406
407         lpdcb16->RlsTimeout = 50;
408         lpdcb16->CtsTimeout = 50; 
409         lpdcb16->DsrTimeout = 50;
410         lpdcb16->fNull = 0;
411         lpdcb16->fChEvt = 0;
412         lpdcb16->fBinary = 1;
413         lpdcb16->fDtrDisable = 0;
414
415         lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_ENABLE);
416         lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_ENABLE);
417         lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow;
418         lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow;
419         lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE);
420
421         lpdcb16->fInX = lpdcb->fInX;
422
423         lpdcb16->fOutX = lpdcb->fOutX;
424 /*
425         lpdcb16->XonChar = 
426         lpdcb16->XoffChar = 
427  */
428         lpdcb16->XonLim = 10;
429         lpdcb16->XoffLim = 10;
430
431         return 0;
432 }
433
434
435 /**************************************************************************
436  *         BuildCommDCB         (USER.213)
437  *
438  * According to the ECMA-234 (368.3) the function will return FALSE on 
439  * success, otherwise it will return -1. 
440  */
441 INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
442 {
443         /* "COM1:96,n,8,1"      */
444         /*  012345              */
445         int port;
446         DCB dcb;
447
448         TRACE("(%s), ptr %p\n", device, lpdcb);
449
450         if (strncasecmp(device,"COM",3))
451                 return -1;
452         port = device[3] - '0';
453
454         if (port-- == 0) {
455                 ERR("BUG ! COM0 can't exist!\n");
456                 return -1;
457         }
458
459         if (!ValidCOMPort(port)) {
460                 FIXME("invalid COM port %d?\n",port);
461                 return -1;
462         }
463                 
464         memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
465
466         lpdcb->Id = port;
467         dcb.DCBlength = sizeof(DCB);
468
469         if (strchr(device,'=')) /* block new style */
470                 return -1;
471
472         if(!BuildCommDCBA(device,&dcb))
473                 return -1;
474
475         return COMM16_DCBtoDCB16(&dcb, lpdcb);
476 }
477
478 /*****************************************************************************
479  *      OpenComm                (USER.200)
480  */
481 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
482 {
483         int port;
484         HANDLE handle;
485
486         TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue);
487
488         if (strlen(device) < 4)
489            return IE_BADID;
490
491         port = device[3] - '0';
492
493         if (port-- == 0)
494                 ERR("BUG ! COM0 or LPT0 don't exist !\n");
495
496         if (!strncasecmp(device,"COM",3)) {
497                 
498                 TRACE("%s = %s\n", device, COM[port].devicename);
499
500                 if (!ValidCOMPort(port))
501                         return IE_BADID;
502
503                 if (COM[port].handle)
504                         return IE_OPEN;
505
506                 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
507                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
508                 if (handle == INVALID_HANDLE_VALUE) {
509                         ERR("Couldn't open %s ! (%s)\n", COM[port].devicename, strerror(errno));
510                         return IE_HARDWARE;
511                 } else {
512                         COM[port].unknown = SEGPTR_ALLOC(40);
513                         memset(COM[port].unknown, 0, 40);
514                         COM[port].handle = handle;
515                         COM[port].commerror = 0;
516                         COM[port].eventmask = 0;
517                         COM[port].evtchar = 0; /* FIXME: default? */
518                         /* save terminal state */
519                         GetCommState16(port,&COM[port].dcb);
520                         /* set default parameters */
521                         if(COM[port].baudrate>-1){
522                             DCB16 dcb;
523                             memcpy(&dcb,&COM[port].dcb,sizeof dcb);
524                             dcb.BaudRate=COM[port].baudrate;
525                             /* more defaults:
526                              * databits, parity, stopbits
527                              */
528                             SetCommState16( &dcb);
529                         }
530                         /* init priority characters */
531                         COM[port].unget = -1;
532                         COM[port].xmit = -1;
533                         /* allocate buffers */
534                         COM[port].ibuf_size = cbInQueue;
535                         COM[port].ibuf_head = COM[port].ibuf_tail = 0;
536                         COM[port].obuf_size = cbOutQueue;
537                         COM[port].obuf_head = COM[port].obuf_tail = 0;
538
539                         COM[port].inbuf = malloc(cbInQueue);
540                         if (COM[port].inbuf) {
541                           COM[port].outbuf = malloc(cbOutQueue);
542                           if (!COM[port].outbuf)
543                             free(COM[port].inbuf);
544                         } else COM[port].outbuf = NULL;
545                         if (!COM[port].outbuf) {
546                           /* not enough memory */
547                           SetCommState16(&COM[port].dcb);
548                           CloseHandle(COM[port].handle);
549                           ERR("out of memory\n");
550                           return IE_MEMORY;
551                         }
552
553                         ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED));
554                         ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED));
555                         COM[port].read_ov.hEvent = CreateEventA(NULL,0,0,NULL);
556                         COM[port].write_ov.hEvent = CreateEventA(NULL,0,0,NULL);
557
558                         comm_waitread( &COM[port] );
559
560                         return port;
561                 }
562         } 
563         else 
564         if (!strncasecmp(device,"LPT",3)) {
565         
566                 if (!ValidLPTPort(port))
567                         return IE_BADID;
568
569                 if (LPT[port].handle)
570                         return IE_OPEN;
571
572                 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
573                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
574                 if (handle == INVALID_HANDLE_VALUE) {
575                         return IE_HARDWARE;
576                 } else {
577                         LPT[port].handle = handle;
578                         LPT[port].commerror = 0;
579                         LPT[port].eventmask = 0;
580                         return port|FLAG_LPT;
581                 }
582         }
583         return IE_BADID;
584 }
585
586 /*****************************************************************************
587  *      CloseComm               (USER.207)
588  */
589 INT16 WINAPI CloseComm16(INT16 cid)
590 {
591         struct DosDeviceStruct *ptr;
592         
593         TRACE("cid=%d\n", cid);
594         if ((ptr = GetDeviceStruct(cid)) == NULL) {
595                 FIXME("no cid=%d found!\n", cid);
596                 return -1;
597         }
598         if (!(cid&FLAG_LPT)) {
599                 /* COM port */
600                 SEGPTR_FREE(COM[cid].unknown); /* [LW] */
601
602                 CloseHandle(COM[cid].read_ov.hEvent);
603                 CloseHandle(COM[cid].write_ov.hEvent);
604
605                 /* free buffers */
606                 free(ptr->outbuf);
607                 free(ptr->inbuf);
608
609                 /* reset modem lines */
610                 SetCommState16(&COM[cid].dcb);
611         }
612
613         if (!CloseHandle(ptr->handle)) {
614                 ptr->commerror = WinError();
615                 /* FIXME: should we clear ptr->handle here? */
616                 return -1;
617         } else {
618                 ptr->commerror = 0;
619                 ptr->handle = 0;
620                 return 0;
621         }
622 }
623
624 /*****************************************************************************
625  *      SetCommBreak            (USER.210)
626  */
627 INT16 WINAPI SetCommBreak16(INT16 cid)
628 {
629         struct DosDeviceStruct *ptr;
630
631         TRACE("cid=%d\n", cid);
632         if ((ptr = GetDeviceStruct(cid)) == NULL) {
633                 FIXME("no cid=%d found!\n", cid);
634                 return -1;
635         }
636
637         ptr->suspended = 1;
638         ptr->commerror = 0;
639         return 0;
640 }
641
642 /*****************************************************************************
643  *      ClearCommBreak  (USER.211)
644  */
645 INT16 WINAPI ClearCommBreak16(INT16 cid)
646 {
647         struct DosDeviceStruct *ptr;
648
649         TRACE("cid=%d\n", cid);
650         if (!(ptr = GetDeviceStruct(cid))) {
651                 FIXME("no cid=%d found!\n", cid);
652                 return -1;
653         }
654         ptr->suspended = 0;
655         ptr->commerror = 0;
656         return 0;
657 }
658
659 /*****************************************************************************
660  *      EscapeCommFunction      (USER.214)
661  */
662 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
663 {
664         struct  DosDeviceStruct *ptr;
665         int     max;
666
667         TRACE("cid=%d, function=%d\n", cid, nFunction);
668
669         switch(nFunction) {
670         case GETMAXCOM:
671                 TRACE("GETMAXCOM\n"); 
672                 for (max = MAX_PORTS;!COM[max].devicename;max--)
673                         ;
674                 return max;
675
676         case GETMAXLPT:
677                 TRACE("GETMAXLPT\n"); 
678                 for (max = MAX_PORTS;!LPT[max].devicename;max--)
679                         ;
680                 return FLAG_LPT + max;
681
682         case GETBASEIRQ:
683                 TRACE("GETBASEIRQ\n"); 
684                 /* FIXME: use tables */
685                 /* just fake something for now */
686                 if (cid & FLAG_LPT) {
687                         /* LPT1: irq 7, LPT2: irq 5 */
688                         return (cid & 0x7f) ? 5 : 7;
689                 } else {
690                         /* COM1: irq 4, COM2: irq 3,
691                            COM3: irq 4, COM4: irq 3 */
692                         return 4 - (cid & 1);
693                 }
694         }
695
696         if ((ptr = GetDeviceStruct(cid)) == NULL) {
697                 FIXME("no cid=%d found!\n", cid);
698                 return -1;
699         }
700
701         switch (nFunction) {
702         case RESETDEV:
703         case CLRDTR:
704         case CLRRTS:
705         case SETDTR:
706         case SETRTS:
707         case SETXOFF:
708         case SETXON:
709                 if(EscapeCommFunction(ptr->handle,nFunction))
710                         return 0;
711                 else {
712                         ptr->commerror = WinError();
713                         return -1;
714                 }
715
716         case CLRBREAK:
717         case SETBREAK:
718         default:
719                 WARN("(cid=%d,nFunction=%d): Unknown function\n", 
720                         cid, nFunction);
721         }
722         return -1;
723 }
724
725 /*****************************************************************************
726  *      FlushComm       (USER.215)
727  */
728 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
729 {
730         DWORD queue;
731         struct DosDeviceStruct *ptr;
732
733         TRACE("cid=%d, queue=%d\n", cid, fnQueue);
734         if ((ptr = GetDeviceStruct(cid)) == NULL) {
735                 FIXME("no cid=%d found!\n", cid);
736                 return -1;
737         }
738         switch (fnQueue) {
739         case 0:
740                 queue = PURGE_TXABORT;
741                 ptr->obuf_tail = ptr->obuf_head;
742                 break;
743         case 1:
744                 queue = PURGE_RXABORT;
745                 ptr->ibuf_head = ptr->ibuf_tail;
746                 break;
747         default:
748                 WARN("(cid=%d,fnQueue=%d):Unknown queue\n", 
749                             cid, fnQueue);
750                 return -1;
751         }
752
753         if (!PurgeComm(ptr->handle,queue)) {
754                 ptr->commerror = WinError();
755                 return -1;      
756         } else {
757                 ptr->commerror = 0;
758                 return 0;
759         }
760 }  
761
762 /********************************************************************
763  *      GetCommError    (USER.203)
764  */
765 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
766 {
767         int             temperror;
768         struct DosDeviceStruct *ptr;
769         unsigned char *stol;
770
771         if ((ptr = GetDeviceStruct(cid)) == NULL) {
772                 FIXME("no handle for cid = %0x!\n",cid);
773                 return -1;
774         }
775         if (cid&FLAG_LPT) {
776             WARN(" cid %d not comm port\n",cid);
777             return CE_MODE;
778         }
779         stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
780         COMM_MSRUpdate( ptr->handle, stol );
781
782         if (lpStat) {
783                 HANDLE rw_events[2];
784
785                 lpStat->status = 0;
786
787                 rw_events[0] = COM[cid].read_ov.hEvent;
788                 rw_events[1] = COM[cid].write_ov.hEvent;
789
790                 WaitForMultipleObjectsEx(2,&rw_events[0],FALSE,1,TRUE);
791
792                 lpStat->cbOutQue = comm_outbuf(ptr);
793                 lpStat->cbInQue = comm_inbuf(ptr);
794
795                 TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n",
796                              cid, ptr->commerror, lpStat->status, lpStat->cbInQue, 
797                              lpStat->cbOutQue, *stol);
798         }
799         else
800                 TRACE("cid %d, error %d, lpStat NULL stol %x\n",
801                              cid, ptr->commerror, *stol);
802
803         /* Return any errors and clear it */
804         temperror = ptr->commerror;
805         ptr->commerror = 0;
806         return(temperror);
807 }
808
809 /*****************************************************************************
810  *      SetCommEventMask        (USER.208)
811  */
812 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
813 {
814         struct DosDeviceStruct *ptr;
815         unsigned char *stol;
816
817         TRACE("cid %d,mask %d\n",cid,fuEvtMask);
818         if ((ptr = GetDeviceStruct(cid)) == NULL) {
819                 FIXME("no handle for cid = %0x!\n",cid);
820             return (SEGPTR)NULL;
821         }
822
823         ptr->eventmask = fuEvtMask;
824
825         if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
826             WARN(" cid %d not comm port\n",cid);
827             return (SEGPTR)NULL;
828         }
829         /* it's a COM port ? -> modify flags */
830         stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
831         COMM_MSRUpdate( ptr->handle, stol );
832
833         TRACE(" modem dcd construct %x\n",*stol);
834         return SEGPTR_GET(COM[cid].unknown);
835 }
836
837 /*****************************************************************************
838  *      GetCommEventMask        (USER.209)
839  */
840 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
841 {
842         struct DosDeviceStruct *ptr;
843         WORD events;
844
845         TRACE("cid %d, mask %d\n", cid, fnEvtClear);
846         if ((ptr = GetDeviceStruct(cid)) == NULL) {
847                 FIXME("no handle for cid = %0x!\n",cid);
848             return 0;
849         }
850
851         if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
852             WARN(" cid %d not comm port\n",cid);
853             return 0;
854         }
855
856         events = *(WORD*)(COM[cid].unknown) & fnEvtClear;
857         *(WORD*)(COM[cid].unknown) &= ~fnEvtClear;
858         return events;
859 }
860
861 /*****************************************************************************
862  *      SetCommState    (USER.201)
863  */
864 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
865 {
866         struct DosDeviceStruct *ptr;
867         DCB dcb;
868
869         TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb);
870         if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
871                 FIXME("no handle for cid = %0x!\n",lpdcb->Id);
872                 return -1;
873         }
874
875         memset(&dcb,0,sizeof dcb);
876         dcb.DCBlength = sizeof dcb;
877         if(lpdcb->BaudRate==57601)
878                 dcb.BaudRate = 115200;
879         else
880                 dcb.BaudRate = lpdcb->BaudRate;
881         
882         dcb.ByteSize=lpdcb->ByteSize;
883         dcb.StopBits=lpdcb->StopBits;
884
885         dcb.fParity=lpdcb->fParity;
886         dcb.Parity=lpdcb->Parity;
887
888         dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow;
889
890         if (lpdcb->fDtrflow || lpdcb->fRtsflow)
891                 dcb.fRtsControl = TRUE;
892
893         if (lpdcb->fDtrDisable) 
894                 dcb.fDtrControl = TRUE;
895
896         ptr->evtchar = lpdcb->EvtChar;
897
898         dcb.fInX = lpdcb->fInX;
899         dcb.fOutX = lpdcb->fOutX;
900         
901         if (!SetCommState(ptr->handle,&dcb)) {
902                 ptr->commerror = WinError();    
903                 return -1;
904         } else {
905                 ptr->commerror = 0;
906                 return 0;
907         }
908 }
909
910 /*****************************************************************************
911  *      GetCommState    (USER.202)
912  */
913 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
914 {
915         struct DosDeviceStruct *ptr;
916         DCB dcb;
917
918         TRACE("cid %d, ptr %p\n", cid, lpdcb);
919         if ((ptr = GetDeviceStruct(cid)) == NULL) {
920                 FIXME("no handle for cid = %0x!\n",cid);
921                 return -1;
922         }
923         if (!GetCommState(ptr->handle,&dcb)) {
924                 ptr->commerror = WinError();    
925                 return -1;
926         }
927
928         lpdcb->Id = cid;
929
930         COMM16_DCBtoDCB16(&dcb,lpdcb);
931
932         lpdcb->EvtChar = ptr->evtchar;
933
934         ptr->commerror = 0;
935         return 0;
936 }
937
938 /*****************************************************************************
939  *      TransmitCommChar        (USER.206)
940  */
941 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
942 {
943         struct DosDeviceStruct *ptr;
944
945         TRACE("cid %d, data %d \n", cid, chTransmit);
946         if ((ptr = GetDeviceStruct(cid)) == NULL) {
947                 FIXME("no handle for cid = %0x!\n",cid);
948                 return -1;
949         }
950
951         if (ptr->suspended) {
952                 ptr->commerror = IE_HARDWARE;
953                 return -1;
954         }       
955
956         if (ptr->xmit >= 0) {
957           /* character already queued */
958           /* FIXME: which error would Windows return? */
959           ptr->commerror = CE_TXFULL;
960           return -1;
961         }
962
963         if (ptr->obuf_head == ptr->obuf_tail) {
964           /* transmit queue empty, try to transmit directly */
965           DWORD len;
966           if(!WriteFile(ptr->handle, &chTransmit, 1, &len, NULL)) {
967             /* didn't work, queue it */
968             ptr->xmit = chTransmit;
969             comm_waitwrite(ptr);
970           }
971         } else {
972           /* data in queue, let this char be transmitted next */
973           ptr->xmit = chTransmit;
974           comm_waitwrite(ptr);
975         }
976
977         ptr->commerror = 0;
978         return 0;
979 }
980
981 /*****************************************************************************
982  *      UngetCommChar   (USER.212)
983  */
984 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
985 {
986         struct DosDeviceStruct *ptr;
987
988         TRACE("cid %d (char %d)\n", cid, chUnget);
989         if ((ptr = GetDeviceStruct(cid)) == NULL) {
990                 FIXME("no handle for cid = %0x!\n",cid);
991                 return -1;
992         }
993
994         if (ptr->suspended) {
995                 ptr->commerror = IE_HARDWARE;
996                 return -1;
997         }       
998
999         if (ptr->unget>=0) {
1000           /* character already queued */
1001           /* FIXME: which error would Windows return? */
1002           ptr->commerror = CE_RXOVER;
1003           return -1;
1004         }
1005
1006         ptr->unget = chUnget;
1007
1008         ptr->commerror = 0;
1009         return 0;
1010 }
1011
1012 /*****************************************************************************
1013  *      ReadComm        (USER.204)
1014  */
1015 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
1016 {
1017         int status, length;
1018         struct DosDeviceStruct *ptr;
1019         LPSTR orgBuf = lpvBuf;
1020
1021         TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
1022         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1023                 FIXME("no handle for cid = %0x!\n",cid);
1024                 return -1;
1025         }
1026
1027         if (ptr->suspended) {
1028                 ptr->commerror = IE_HARDWARE;
1029                 return -1;
1030         }       
1031
1032         /* read unget character */
1033         if (ptr->unget>=0) {
1034                 *lpvBuf++ = ptr->unget;
1035                 ptr->unget = -1;
1036
1037                 length = 1;
1038         } else
1039                 length = 0;
1040
1041         /* read from receive buffer */
1042         while (length < cbRead) {
1043           status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1044                     ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1045           if (!status) break;
1046           if ((cbRead - length) < status)
1047             status = cbRead - length;
1048
1049           memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1050           ptr->ibuf_tail += status;
1051           if (ptr->ibuf_tail >= ptr->ibuf_size)
1052             ptr->ibuf_tail = 0;
1053           lpvBuf += status;
1054           length += status;
1055         }
1056
1057         TRACE("%.*s\n", length, orgBuf);
1058         ptr->commerror = 0;
1059         return length;
1060 }
1061
1062 /*****************************************************************************
1063  *      WriteComm       (USER.205)
1064  */
1065 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1066 {
1067         int status, length;
1068         struct DosDeviceStruct *ptr;
1069
1070         TRACE("cid %d, ptr %p, length %d\n", 
1071                 cid, lpvBuf, cbWrite);
1072         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1073                 FIXME("no handle for cid = %0x!\n",cid);
1074                 return -1;
1075         }
1076
1077         if (ptr->suspended) {
1078                 ptr->commerror = IE_HARDWARE;
1079                 return -1;
1080         }       
1081         
1082         TRACE("%.*s\n", cbWrite, lpvBuf );
1083
1084         length = 0;
1085         while (length < cbWrite) {
1086           if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1087             /* no data queued, try to write directly */
1088             if(!WriteFile(ptr->handle, lpvBuf, cbWrite - length, (LPDWORD)&status, NULL))
1089               status = -1;
1090             if (status > 0) {
1091               lpvBuf += status;
1092               length += status;
1093               continue;
1094             }
1095           }
1096           /* can't write directly, put into transmit buffer */
1097           status = ((ptr->obuf_tail > ptr->obuf_head) ?
1098                     (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1099           if (!status) break;
1100           if ((cbWrite - length) < status)
1101             status = cbWrite - length;
1102           memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1103           ptr->obuf_head += status;
1104           if (ptr->obuf_head >= ptr->obuf_size)
1105             ptr->obuf_head = 0;
1106           lpvBuf += status;
1107           length += status;
1108           comm_waitwrite(ptr);
1109         }
1110
1111         ptr->commerror = 0;     
1112         return length;
1113 }
1114
1115 /***********************************************************************
1116  *           EnableCommNotification   (USER.245)
1117  */
1118 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1119                                       INT16 cbWriteNotify, INT16 cbOutQueue )
1120 {
1121         struct DosDeviceStruct *ptr;
1122
1123         TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1124         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1125                 FIXME("no handle for cid = %0x!\n",cid);
1126                 return -1;
1127         }
1128         ptr->wnd = WIN_Handle32( hwnd );
1129         ptr->n_read = cbWriteNotify;
1130         ptr->n_write = cbOutQueue;
1131         return TRUE;
1132 }
1133