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