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