Move 16-bit COMM code to USER dll.
[wine.git] / dlls / user / comm16.c
blobf2da71bcccbc21eaafccf9044e887976afa0374e
1 /*
2 * DEC 93 Erik Bos <erik@xs4all.nl>
4 * Copyright 1996 Marcus Meissner
6 * Copyright 2001 Mike McCormack
8 * Mar 31, 1999. Ove Kåven <ovek@arcticnet.no>
9 * - Implemented buffers and EnableCommNotification.
11 * Apr 3, 1999. Lawson Whitney <lawson_whitney@juno.com>
12 * - Fixed the modem control part of EscapeCommFunction16.
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.
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.
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.
37 #include "config.h"
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <ctype.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wingdi.h"
48 #include "winuser.h"
49 #include "wine/winuser16.h"
50 #include "wine/port.h"
51 #include "heap.h"
52 #include "options.h"
53 #include "winerror.h"
55 #include "debugtools.h"
57 DEFAULT_DEBUG_CHANNEL(comm);
59 /* window's semi documented modem status register */
60 #define COMM_MSR_OFFSET 35
61 #define MSR_CTS 0x10
62 #define MSR_DSR 0x20
63 #define MSR_RI 0x40
64 #define MSR_RLSD 0x80
65 #define MSR_MASK (MSR_CTS|MSR_DSR|MSR_RI|MSR_RLSD)
67 #define FLAG_LPT 0x80
69 #define MAX_PORTS 9
71 struct DosDeviceStruct {
72 char *devicename; /* /dev/ttyS0 */
73 HANDLE handle;
74 int suspended;
75 int unget,xmit;
76 int baudrate;
77 int evtchar;
78 /* events */
79 int commerror, eventmask;
80 /* buffers */
81 char *inbuf,*outbuf;
82 unsigned ibuf_size,ibuf_head,ibuf_tail;
83 unsigned obuf_size,obuf_head,obuf_tail;
84 /* notifications */
85 int wnd, n_read, n_write;
86 OVERLAPPED read_ov, write_ov;
87 /* save terminal states */
88 DCB16 dcb;
89 /* pointer to unknown(==undocumented) comm structure */
90 LPCVOID *unknown;
93 static struct DosDeviceStruct COM[MAX_PORTS];
94 static struct DosDeviceStruct LPT[MAX_PORTS];
96 /* update window's semi documented modem status register */
97 /* see knowledge base Q101417 */
98 static void COMM_MSRUpdate( HANDLE handle, UCHAR * pMsr )
100 UCHAR tmpmsr=0;
101 DWORD mstat=0;
103 if(!GetCommModemStatus(handle,&mstat))
104 return;
106 if(mstat & MS_CTS_ON) tmpmsr |= MSR_CTS;
107 if(mstat & MS_DSR_ON) tmpmsr |= MSR_DSR;
108 if(mstat & MS_RING_ON) tmpmsr |= MSR_RI;
109 if(mstat & MS_RLSD_ON) tmpmsr |= MSR_RLSD;
110 *pMsr = (*pMsr & ~MSR_MASK) | tmpmsr;
113 void COMM_Init(void)
115 int x;
116 char option[10], temp[256], *btemp;
118 for (x=0; x!=MAX_PORTS; x++) {
119 strcpy(option,"COMx");
120 option[3] = '1' + x;
121 option[4] = '\0';
123 PROFILE_GetWineIniString( "serialports", option, "*",
124 temp, sizeof(temp) );
125 if (!strcmp(temp, "*") || *temp == '\0')
126 COM[x].devicename = NULL;
127 else {
128 btemp = strchr(temp,',');
129 if (btemp != NULL) {
130 *btemp++ = '\0';
131 COM[x].baudrate = atoi(btemp);
132 } else {
133 COM[x].baudrate = -1;
135 if ((COM[x].devicename = malloc(strlen(temp)+1)) == NULL)
136 WARN("Can't malloc for device info!\n");
137 else {
138 COM[x].handle = 0;
139 strcpy(COM[x].devicename, temp);
141 TRACE("%s = %s\n", option, COM[x].devicename);
144 strcpy(option, "LPTx");
145 option[3] = '1' + x;
146 option[4] = '\0';
148 PROFILE_GetWineIniString( "parallelports", option, "*",
149 temp, sizeof(temp) );
150 if (!strcmp(temp, "*") || *temp == '\0')
151 LPT[x].devicename = NULL;
152 else {
153 if ((LPT[x].devicename = malloc(strlen(temp)+1)) == NULL)
154 WARN("Can't malloc for device info!\n");
155 else {
156 LPT[x].handle = 0;
157 strcpy(LPT[x].devicename, temp);
159 TRACE("%s = %s\n", option, LPT[x].devicename);
166 static struct DosDeviceStruct *GetDeviceStruct(int index)
168 if ((index&0x7F)<=MAX_PORTS) {
169 if (!(index&FLAG_LPT)) {
170 if (COM[index].handle)
171 return &COM[index];
172 } else {
173 index &= 0x7f;
174 if (LPT[index].handle)
175 return &LPT[index];
179 return NULL;
182 static int GetCommPort_ov(LPOVERLAPPED ov, int write)
184 int x;
186 for (x=0; x<MAX_PORTS; x++) {
187 if (ov == (write?&COM[x].write_ov:&COM[x].read_ov))
188 return x;
191 return -1;
194 static int ValidCOMPort(int x)
196 return(x < MAX_PORTS ? (int) COM[x].devicename : 0);
199 static int ValidLPTPort(int x)
201 return(x < MAX_PORTS ? (int) LPT[x].devicename : 0);
204 static int WinError(void)
206 TRACE("errno = %d\n", errno);
207 switch (errno) {
208 default:
209 return CE_IOE;
213 static unsigned comm_inbuf(struct DosDeviceStruct *ptr)
215 return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
216 + ptr->ibuf_head - ptr->ibuf_tail;
219 static unsigned comm_outbuf(struct DosDeviceStruct *ptr)
221 return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
222 + ptr->obuf_head - ptr->obuf_tail;
225 static void comm_waitread(struct DosDeviceStruct *ptr);
226 static void comm_waitwrite(struct DosDeviceStruct *ptr);
228 static VOID WINAPI COMM16_ReadComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
230 int prev ;
231 WORD mask = 0;
232 int cid = GetCommPort_ov(ov,0);
233 struct DosDeviceStruct *ptr;
235 if(cid<0) {
236 ERR("async write with bad overlapped pointer\n");
237 return;
239 ptr = &COM[cid];
241 /* read data from comm port */
242 if (status != STATUS_SUCCESS) {
243 ERR("async read failed\n");
244 COM[cid].commerror = CE_RXOVER;
245 return;
247 TRACE("async read completed %ld bytes\n",len);
249 prev = comm_inbuf(ptr);
251 /* check for events */
252 if ((ptr->eventmask & EV_RXFLAG) &&
253 memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) {
254 *(WORD*)(COM[cid].unknown) |= EV_RXFLAG;
255 mask |= CN_EVENT;
257 if (ptr->eventmask & EV_RXCHAR) {
258 *(WORD*)(COM[cid].unknown) |= EV_RXCHAR;
259 mask |= CN_EVENT;
262 /* advance buffer position */
263 ptr->ibuf_head += len;
264 if (ptr->ibuf_head >= ptr->ibuf_size)
265 ptr->ibuf_head = 0;
267 /* check for notification */
268 if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
269 (comm_inbuf(ptr)>=ptr->n_read)) {
270 /* passed the receive notification threshold */
271 mask |= CN_RECEIVE;
274 /* send notifications, if any */
275 if (ptr->wnd && mask) {
276 TRACE("notifying %04x: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
277 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
280 /* on real windows, this could cause problems, since it is recursive */
281 /* restart the receive */
282 comm_waitread(ptr);
285 static VOID WINAPI COMM16_WriteComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
287 int prev, bleft;
288 WORD mask = 0;
289 int cid = GetCommPort_ov(ov,1);
290 struct DosDeviceStruct *ptr;
292 if(cid<0) {
293 ERR("async write with bad overlapped pointer\n");
294 return;
296 ptr = &COM[cid];
298 /* read data from comm port */
299 if (status != STATUS_SUCCESS) {
300 ERR("async write failed\n");
301 COM[cid].commerror = CE_RXOVER;
302 return;
304 TRACE("async write completed %ld bytes\n",len);
306 /* update the buffer pointers */
307 prev = comm_outbuf(&COM[cid]);
308 ptr->obuf_tail += len;
309 if (ptr->obuf_tail >= ptr->obuf_size)
310 ptr->obuf_tail = 0;
312 /* write any TransmitCommChar character */
313 if (ptr->xmit>=0) {
314 if(!WriteFile(ptr->handle, &(ptr->xmit), 1, &len, NULL))
315 len = -1;
316 if (len > 0) ptr->xmit = -1;
319 /* write from output queue */
320 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
321 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
323 /* check for notification */
324 if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
325 (comm_outbuf(ptr)<ptr->n_write)) {
326 /* passed the transmit notification threshold */
327 mask |= CN_TRANSMIT;
330 /* send notifications, if any */
331 if (ptr->wnd && mask) {
332 TRACE("notifying %04x: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
333 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
336 /* start again if necessary */
337 if(bleft)
338 comm_waitwrite(ptr);
341 static void comm_waitread(struct DosDeviceStruct *ptr)
343 int bleft;
345 bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ?
346 (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
347 /* FIXME: get timeouts working properly so we can read bleft bytes */
348 ReadFileEx(ptr->handle,
349 ptr->inbuf + ptr->ibuf_head,
351 &ptr->read_ov,
352 COMM16_ReadComplete);
355 static void comm_waitwrite(struct DosDeviceStruct *ptr)
357 int bleft;
359 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
360 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
361 WriteFileEx(ptr->handle,
362 ptr->outbuf + ptr->obuf_tail,
363 bleft,
364 &ptr->write_ov,
365 COMM16_WriteComplete);
368 /*****************************************************************************
369 * COMM16_DCBtoDCB16 (Internal)
371 INT16 COMM16_DCBtoDCB16(LPDCB lpdcb, LPDCB16 lpdcb16)
373 if(lpdcb->BaudRate<0x10000)
374 lpdcb16->BaudRate = lpdcb->BaudRate;
375 else if(lpdcb->BaudRate==115200)
376 lpdcb16->BaudRate = 57601;
377 else {
378 WARN("Baud rate can't be converted\n");
379 lpdcb16->BaudRate = 57601;
381 lpdcb16->ByteSize = lpdcb->ByteSize;
382 lpdcb16->fParity = lpdcb->fParity;
383 lpdcb16->Parity = lpdcb->Parity;
384 lpdcb16->StopBits = lpdcb->StopBits;
386 lpdcb16->RlsTimeout = 50;
387 lpdcb16->CtsTimeout = 50;
388 lpdcb16->DsrTimeout = 50;
389 lpdcb16->fNull = 0;
390 lpdcb16->fChEvt = 0;
391 lpdcb16->fBinary = 1;
392 lpdcb16->fDtrDisable = 0;
394 lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_ENABLE);
395 lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_ENABLE);
396 lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow;
397 lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow;
398 lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE);
400 lpdcb16->fInX = lpdcb->fInX;
402 lpdcb16->fOutX = lpdcb->fOutX;
404 lpdcb16->XonChar =
405 lpdcb16->XoffChar =
407 lpdcb16->XonLim = 10;
408 lpdcb16->XoffLim = 10;
410 return 0;
414 /**************************************************************************
415 * BuildCommDCB (USER.213)
417 * According to the ECMA-234 (368.3) the function will return FALSE on
418 * success, otherwise it will return -1.
420 INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
422 /* "COM1:96,n,8,1" */
423 /* 012345 */
424 int port;
425 DCB dcb;
427 TRACE("(%s), ptr %p\n", device, lpdcb);
429 if (strncasecmp(device,"COM",3))
430 return -1;
431 port = device[3] - '0';
433 if (port-- == 0) {
434 ERR("BUG ! COM0 can't exist!\n");
435 return -1;
438 if (!ValidCOMPort(port)) {
439 FIXME("invalid COM port %d?\n",port);
440 return -1;
443 memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
445 lpdcb->Id = port;
446 dcb.DCBlength = sizeof(DCB);
448 if (strchr(device,'=')) /* block new style */
449 return -1;
451 if(!BuildCommDCBA(device,&dcb))
452 return -1;
454 return COMM16_DCBtoDCB16(&dcb, lpdcb);
457 /*****************************************************************************
458 * OpenComm (USER.200)
460 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
462 int port;
463 HANDLE handle;
465 TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue);
467 if (strlen(device) < 4)
468 return IE_BADID;
470 port = device[3] - '0';
472 if (port-- == 0)
473 ERR("BUG ! COM0 or LPT0 don't exist !\n");
475 if (!strncasecmp(device,"COM",3)) {
477 TRACE("%s = %s\n", device, COM[port].devicename);
479 if (!ValidCOMPort(port))
480 return IE_BADID;
482 if (COM[port].handle)
483 return IE_OPEN;
485 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
486 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
487 if (handle == INVALID_HANDLE_VALUE) {
488 ERR("Couldn't open %s ! (%s)\n", COM[port].devicename, strerror(errno));
489 return IE_HARDWARE;
490 } else {
491 COM[port].unknown = SEGPTR_ALLOC(40);
492 memset(COM[port].unknown, 0, 40);
493 COM[port].handle = handle;
494 COM[port].commerror = 0;
495 COM[port].eventmask = 0;
496 COM[port].evtchar = 0; /* FIXME: default? */
497 /* save terminal state */
498 GetCommState16(port,&COM[port].dcb);
499 /* set default parameters */
500 if(COM[port].baudrate>-1){
501 DCB16 dcb;
502 memcpy(&dcb,&COM[port].dcb,sizeof dcb);
503 dcb.BaudRate=COM[port].baudrate;
504 /* more defaults:
505 * databits, parity, stopbits
507 SetCommState16( &dcb);
509 /* init priority characters */
510 COM[port].unget = -1;
511 COM[port].xmit = -1;
512 /* allocate buffers */
513 COM[port].ibuf_size = cbInQueue;
514 COM[port].ibuf_head = COM[port].ibuf_tail = 0;
515 COM[port].obuf_size = cbOutQueue;
516 COM[port].obuf_head = COM[port].obuf_tail = 0;
518 COM[port].inbuf = malloc(cbInQueue);
519 if (COM[port].inbuf) {
520 COM[port].outbuf = malloc(cbOutQueue);
521 if (!COM[port].outbuf)
522 free(COM[port].inbuf);
523 } else COM[port].outbuf = NULL;
524 if (!COM[port].outbuf) {
525 /* not enough memory */
526 SetCommState16(&COM[port].dcb);
527 CloseHandle(COM[port].handle);
528 ERR("out of memory\n");
529 return IE_MEMORY;
532 ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED));
533 ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED));
534 COM[port].read_ov.hEvent = CreateEventA(NULL,0,0,NULL);
535 COM[port].write_ov.hEvent = CreateEventA(NULL,0,0,NULL);
537 comm_waitread( &COM[port] );
539 return port;
542 else
543 if (!strncasecmp(device,"LPT",3)) {
545 if (!ValidLPTPort(port))
546 return IE_BADID;
548 if (LPT[port].handle)
549 return IE_OPEN;
551 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
552 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
553 if (handle == INVALID_HANDLE_VALUE) {
554 return IE_HARDWARE;
555 } else {
556 LPT[port].handle = handle;
557 LPT[port].commerror = 0;
558 LPT[port].eventmask = 0;
559 return port|FLAG_LPT;
562 return IE_BADID;
565 /*****************************************************************************
566 * CloseComm (USER.207)
568 INT16 WINAPI CloseComm16(INT16 cid)
570 struct DosDeviceStruct *ptr;
572 TRACE("cid=%d\n", cid);
573 if ((ptr = GetDeviceStruct(cid)) == NULL) {
574 FIXME("no cid=%d found!\n", cid);
575 return -1;
577 if (!(cid&FLAG_LPT)) {
578 /* COM port */
579 SEGPTR_FREE(COM[cid].unknown); /* [LW] */
581 CloseHandle(COM[cid].read_ov.hEvent);
582 CloseHandle(COM[cid].write_ov.hEvent);
584 /* free buffers */
585 free(ptr->outbuf);
586 free(ptr->inbuf);
588 /* reset modem lines */
589 SetCommState16(&COM[cid].dcb);
592 if (!CloseHandle(ptr->handle)) {
593 ptr->commerror = WinError();
594 /* FIXME: should we clear ptr->handle here? */
595 return -1;
596 } else {
597 ptr->commerror = 0;
598 ptr->handle = 0;
599 return 0;
603 /*****************************************************************************
604 * SetCommBreak (USER.210)
606 INT16 WINAPI SetCommBreak16(INT16 cid)
608 struct DosDeviceStruct *ptr;
610 TRACE("cid=%d\n", cid);
611 if ((ptr = GetDeviceStruct(cid)) == NULL) {
612 FIXME("no cid=%d found!\n", cid);
613 return -1;
616 ptr->suspended = 1;
617 ptr->commerror = 0;
618 return 0;
621 /*****************************************************************************
622 * ClearCommBreak (USER.211)
624 INT16 WINAPI ClearCommBreak16(INT16 cid)
626 struct DosDeviceStruct *ptr;
628 TRACE("cid=%d\n", cid);
629 if (!(ptr = GetDeviceStruct(cid))) {
630 FIXME("no cid=%d found!\n", cid);
631 return -1;
633 ptr->suspended = 0;
634 ptr->commerror = 0;
635 return 0;
638 /*****************************************************************************
639 * EscapeCommFunction (USER.214)
641 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
643 struct DosDeviceStruct *ptr;
644 int max;
646 TRACE("cid=%d, function=%d\n", cid, nFunction);
648 switch(nFunction) {
649 case GETMAXCOM:
650 TRACE("GETMAXCOM\n");
651 for (max = MAX_PORTS;!COM[max].devicename;max--)
653 return max;
655 case GETMAXLPT:
656 TRACE("GETMAXLPT\n");
657 for (max = MAX_PORTS;!LPT[max].devicename;max--)
659 return FLAG_LPT + max;
661 case GETBASEIRQ:
662 TRACE("GETBASEIRQ\n");
663 /* FIXME: use tables */
664 /* just fake something for now */
665 if (cid & FLAG_LPT) {
666 /* LPT1: irq 7, LPT2: irq 5 */
667 return (cid & 0x7f) ? 5 : 7;
668 } else {
669 /* COM1: irq 4, COM2: irq 3,
670 COM3: irq 4, COM4: irq 3 */
671 return 4 - (cid & 1);
675 if ((ptr = GetDeviceStruct(cid)) == NULL) {
676 FIXME("no cid=%d found!\n", cid);
677 return -1;
680 switch (nFunction) {
681 case RESETDEV:
682 case CLRDTR:
683 case CLRRTS:
684 case SETDTR:
685 case SETRTS:
686 case SETXOFF:
687 case SETXON:
688 if(EscapeCommFunction(ptr->handle,nFunction))
689 return 0;
690 else {
691 ptr->commerror = WinError();
692 return -1;
695 case CLRBREAK:
696 case SETBREAK:
697 default:
698 WARN("(cid=%d,nFunction=%d): Unknown function\n",
699 cid, nFunction);
701 return -1;
704 /*****************************************************************************
705 * FlushComm (USER.215)
707 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
709 DWORD queue;
710 struct DosDeviceStruct *ptr;
712 TRACE("cid=%d, queue=%d\n", cid, fnQueue);
713 if ((ptr = GetDeviceStruct(cid)) == NULL) {
714 FIXME("no cid=%d found!\n", cid);
715 return -1;
717 switch (fnQueue) {
718 case 0:
719 queue = PURGE_TXABORT;
720 ptr->obuf_tail = ptr->obuf_head;
721 break;
722 case 1:
723 queue = PURGE_RXABORT;
724 ptr->ibuf_head = ptr->ibuf_tail;
725 break;
726 default:
727 WARN("(cid=%d,fnQueue=%d):Unknown queue\n",
728 cid, fnQueue);
729 return -1;
732 if (!PurgeComm(ptr->handle,queue)) {
733 ptr->commerror = WinError();
734 return -1;
735 } else {
736 ptr->commerror = 0;
737 return 0;
741 /********************************************************************
742 * GetCommError (USER.203)
744 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
746 int temperror;
747 struct DosDeviceStruct *ptr;
748 unsigned char *stol;
750 if ((ptr = GetDeviceStruct(cid)) == NULL) {
751 FIXME("no handle for cid = %0x!\n",cid);
752 return -1;
754 if (cid&FLAG_LPT) {
755 WARN(" cid %d not comm port\n",cid);
756 return CE_MODE;
758 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
759 COMM_MSRUpdate( ptr->handle, stol );
761 if (lpStat) {
762 HANDLE rw_events[2];
764 lpStat->status = 0;
766 rw_events[0] = COM[cid].read_ov.hEvent;
767 rw_events[1] = COM[cid].write_ov.hEvent;
769 WaitForMultipleObjectsEx(2,&rw_events[0],FALSE,1,TRUE);
771 lpStat->cbOutQue = comm_outbuf(ptr);
772 lpStat->cbInQue = comm_inbuf(ptr);
774 TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n",
775 cid, ptr->commerror, lpStat->status, lpStat->cbInQue,
776 lpStat->cbOutQue, *stol);
778 else
779 TRACE("cid %d, error %d, lpStat NULL stol %x\n",
780 cid, ptr->commerror, *stol);
782 /* Return any errors and clear it */
783 temperror = ptr->commerror;
784 ptr->commerror = 0;
785 return(temperror);
788 /*****************************************************************************
789 * SetCommEventMask (USER.208)
791 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
793 struct DosDeviceStruct *ptr;
794 unsigned char *stol;
796 TRACE("cid %d,mask %d\n",cid,fuEvtMask);
797 if ((ptr = GetDeviceStruct(cid)) == NULL) {
798 FIXME("no handle for cid = %0x!\n",cid);
799 return (SEGPTR)NULL;
802 ptr->eventmask = fuEvtMask;
804 if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
805 WARN(" cid %d not comm port\n",cid);
806 return (SEGPTR)NULL;
808 /* it's a COM port ? -> modify flags */
809 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
810 COMM_MSRUpdate( ptr->handle, stol );
812 TRACE(" modem dcd construct %x\n",*stol);
813 return SEGPTR_GET(COM[cid].unknown);
816 /*****************************************************************************
817 * GetCommEventMask (USER.209)
819 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
821 struct DosDeviceStruct *ptr;
822 WORD events;
824 TRACE("cid %d, mask %d\n", cid, fnEvtClear);
825 if ((ptr = GetDeviceStruct(cid)) == NULL) {
826 FIXME("no handle for cid = %0x!\n",cid);
827 return 0;
830 if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
831 WARN(" cid %d not comm port\n",cid);
832 return 0;
835 events = *(WORD*)(COM[cid].unknown) & fnEvtClear;
836 *(WORD*)(COM[cid].unknown) &= ~fnEvtClear;
837 return events;
840 /*****************************************************************************
841 * SetCommState (USER.201)
843 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
845 struct DosDeviceStruct *ptr;
846 DCB dcb;
848 TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb);
849 if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
850 FIXME("no handle for cid = %0x!\n",lpdcb->Id);
851 return -1;
854 memset(&dcb,0,sizeof dcb);
855 dcb.DCBlength = sizeof dcb;
856 if(lpdcb->BaudRate==57601)
857 dcb.BaudRate = 115200;
858 else
859 dcb.BaudRate = lpdcb->BaudRate;
861 dcb.ByteSize=lpdcb->ByteSize;
862 dcb.StopBits=lpdcb->StopBits;
864 dcb.fParity=lpdcb->fParity;
865 dcb.Parity=lpdcb->Parity;
867 dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow;
869 if (lpdcb->fDtrflow || lpdcb->fRtsflow)
870 dcb.fRtsControl = TRUE;
872 if (lpdcb->fDtrDisable)
873 dcb.fDtrControl = TRUE;
875 ptr->evtchar = lpdcb->EvtChar;
877 dcb.fInX = lpdcb->fInX;
878 dcb.fOutX = lpdcb->fOutX;
880 if (!SetCommState(ptr->handle,&dcb)) {
881 ptr->commerror = WinError();
882 return -1;
883 } else {
884 ptr->commerror = 0;
885 return 0;
889 /*****************************************************************************
890 * GetCommState (USER.202)
892 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
894 struct DosDeviceStruct *ptr;
895 DCB dcb;
897 TRACE("cid %d, ptr %p\n", cid, lpdcb);
898 if ((ptr = GetDeviceStruct(cid)) == NULL) {
899 FIXME("no handle for cid = %0x!\n",cid);
900 return -1;
902 if (!GetCommState(ptr->handle,&dcb)) {
903 ptr->commerror = WinError();
904 return -1;
907 lpdcb->Id = cid;
909 COMM16_DCBtoDCB16(&dcb,lpdcb);
911 lpdcb->EvtChar = ptr->evtchar;
913 ptr->commerror = 0;
914 return 0;
917 /*****************************************************************************
918 * TransmitCommChar (USER.206)
920 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
922 struct DosDeviceStruct *ptr;
924 TRACE("cid %d, data %d \n", cid, chTransmit);
925 if ((ptr = GetDeviceStruct(cid)) == NULL) {
926 FIXME("no handle for cid = %0x!\n",cid);
927 return -1;
930 if (ptr->suspended) {
931 ptr->commerror = IE_HARDWARE;
932 return -1;
935 if (ptr->xmit >= 0) {
936 /* character already queued */
937 /* FIXME: which error would Windows return? */
938 ptr->commerror = CE_TXFULL;
939 return -1;
942 if (ptr->obuf_head == ptr->obuf_tail) {
943 /* transmit queue empty, try to transmit directly */
944 DWORD len;
945 if(!WriteFile(ptr->handle, &chTransmit, 1, &len, NULL)) {
946 /* didn't work, queue it */
947 ptr->xmit = chTransmit;
948 comm_waitwrite(ptr);
950 } else {
951 /* data in queue, let this char be transmitted next */
952 ptr->xmit = chTransmit;
953 comm_waitwrite(ptr);
956 ptr->commerror = 0;
957 return 0;
960 /*****************************************************************************
961 * UngetCommChar (USER.212)
963 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
965 struct DosDeviceStruct *ptr;
967 TRACE("cid %d (char %d)\n", cid, chUnget);
968 if ((ptr = GetDeviceStruct(cid)) == NULL) {
969 FIXME("no handle for cid = %0x!\n",cid);
970 return -1;
973 if (ptr->suspended) {
974 ptr->commerror = IE_HARDWARE;
975 return -1;
978 if (ptr->unget>=0) {
979 /* character already queued */
980 /* FIXME: which error would Windows return? */
981 ptr->commerror = CE_RXOVER;
982 return -1;
985 ptr->unget = chUnget;
987 ptr->commerror = 0;
988 return 0;
991 /*****************************************************************************
992 * ReadComm (USER.204)
994 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
996 int status, length;
997 struct DosDeviceStruct *ptr;
998 LPSTR orgBuf = lpvBuf;
1000 TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
1001 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1002 FIXME("no handle for cid = %0x!\n",cid);
1003 return -1;
1006 if (ptr->suspended) {
1007 ptr->commerror = IE_HARDWARE;
1008 return -1;
1011 /* read unget character */
1012 if (ptr->unget>=0) {
1013 *lpvBuf++ = ptr->unget;
1014 ptr->unget = -1;
1016 length = 1;
1017 } else
1018 length = 0;
1020 /* read from receive buffer */
1021 while (length < cbRead) {
1022 status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1023 ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1024 if (!status) break;
1025 if ((cbRead - length) < status)
1026 status = cbRead - length;
1028 memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1029 ptr->ibuf_tail += status;
1030 if (ptr->ibuf_tail >= ptr->ibuf_size)
1031 ptr->ibuf_tail = 0;
1032 lpvBuf += status;
1033 length += status;
1036 TRACE("%.*s\n", length, orgBuf);
1037 ptr->commerror = 0;
1038 return length;
1041 /*****************************************************************************
1042 * WriteComm (USER.205)
1044 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1046 int status, length;
1047 struct DosDeviceStruct *ptr;
1049 TRACE("cid %d, ptr %p, length %d\n",
1050 cid, lpvBuf, cbWrite);
1051 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1052 FIXME("no handle for cid = %0x!\n",cid);
1053 return -1;
1056 if (ptr->suspended) {
1057 ptr->commerror = IE_HARDWARE;
1058 return -1;
1061 TRACE("%.*s\n", cbWrite, lpvBuf );
1063 length = 0;
1064 while (length < cbWrite) {
1065 if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1066 /* no data queued, try to write directly */
1067 if(!WriteFile(ptr->handle, lpvBuf, cbWrite - length, (LPDWORD)&status, NULL))
1068 status = -1;
1069 if (status > 0) {
1070 lpvBuf += status;
1071 length += status;
1072 continue;
1075 /* can't write directly, put into transmit buffer */
1076 status = ((ptr->obuf_tail > ptr->obuf_head) ?
1077 (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1078 if (!status) break;
1079 if ((cbWrite - length) < status)
1080 status = cbWrite - length;
1081 memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1082 ptr->obuf_head += status;
1083 if (ptr->obuf_head >= ptr->obuf_size)
1084 ptr->obuf_head = 0;
1085 lpvBuf += status;
1086 length += status;
1087 comm_waitwrite(ptr);
1090 ptr->commerror = 0;
1091 return length;
1094 /***********************************************************************
1095 * EnableCommNotification (USER.245)
1097 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1098 INT16 cbWriteNotify, INT16 cbOutQueue )
1100 struct DosDeviceStruct *ptr;
1102 TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1103 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1104 FIXME("no handle for cid = %0x!\n",cid);
1105 return -1;
1107 ptr->wnd = hwnd;
1108 ptr->n_read = cbWriteNotify;
1109 ptr->n_write = cbOutQueue;
1110 return TRUE;