Get rid of the registry lookups, rely entirely on the kernel devices
[wine.git] / dlls / user / comm16.c
blob66f9dd5ae7582d01b4421f5b9641a341a5283911
1 /*
2 * DEC 93 Erik Bos <erik@xs4all.nl>
4 * Copyright 1996 Marcus Meissner
6 * Copyright 2001 Mike McCormack
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.
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.
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
22 * History:
24 * Mar 31, 1999. Ove Kåven <ovek@arcticnet.no>
25 * - Implemented buffers and EnableCommNotification.
27 * Apr 3, 1999. Lawson Whitney <lawson_whitney@juno.com>
28 * - Fixed the modem control part of EscapeCommFunction16.
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.
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.
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.
53 #include "config.h"
55 #include <stdlib.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <errno.h>
60 #include <ctype.h>
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"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(comm);
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)
85 #define FLAG_LPT 0x80
87 #define MAX_PORTS 9
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];
111 static struct DosDeviceStruct COM[MAX_PORTS];
112 static struct DosDeviceStruct LPT[MAX_PORTS];
114 /* update window's semi documented modem status register */
115 /* see knowledge base Q101417 */
116 static void COMM_MSRUpdate( HANDLE handle, UCHAR * pMsr )
118 UCHAR tmpmsr=0;
119 DWORD mstat=0;
121 if(!GetCommModemStatus(handle,&mstat))
122 return;
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;
131 static struct DosDeviceStruct *GetDeviceStruct(int index)
133 if ((index&0x7F)<=MAX_PORTS) {
134 if (!(index&FLAG_LPT)) {
135 if (COM[index].handle)
136 return &COM[index];
137 } else {
138 index &= 0x7f;
139 if (LPT[index].handle)
140 return &LPT[index];
144 return NULL;
147 static int GetCommPort_ov(LPOVERLAPPED ov, int write)
149 int x;
151 for (x=0; x<MAX_PORTS; x++) {
152 if (ov == (write?&COM[x].write_ov:&COM[x].read_ov))
153 return x;
156 return -1;
159 static int WinError(void)
161 TRACE("errno = %d\n", errno);
162 switch (errno) {
163 default:
164 return CE_IOE;
168 static unsigned comm_inbuf(struct DosDeviceStruct *ptr)
170 return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
171 + ptr->ibuf_head - ptr->ibuf_tail;
174 static unsigned comm_outbuf(struct DosDeviceStruct *ptr)
176 return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
177 + ptr->obuf_head - ptr->obuf_tail;
180 static void comm_waitread(struct DosDeviceStruct *ptr);
181 static void comm_waitwrite(struct DosDeviceStruct *ptr);
183 static VOID WINAPI COMM16_ReadComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
185 int prev ;
186 WORD mask = 0;
187 int cid = GetCommPort_ov(ov,0);
188 struct DosDeviceStruct *ptr;
190 if(cid<0) {
191 ERR("async write with bad overlapped pointer\n");
192 return;
194 ptr = &COM[cid];
196 /* we get cancelled when CloseComm is called */
197 if (status==STATUS_CANCELLED)
199 TRACE("Cancelled\n");
200 return;
203 /* read data from comm port */
204 if (status != STATUS_SUCCESS) {
205 ERR("async read failed %08lx\n",status);
206 COM[cid].commerror = CE_RXOVER;
207 return;
209 TRACE("async read completed %ld bytes\n",len);
211 prev = comm_inbuf(ptr);
213 /* check for events */
214 if ((ptr->eventmask & EV_RXFLAG) &&
215 memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) {
216 *(WORD*)(COM[cid].unknown) |= EV_RXFLAG;
217 mask |= CN_EVENT;
219 if (ptr->eventmask & EV_RXCHAR) {
220 *(WORD*)(COM[cid].unknown) |= EV_RXCHAR;
221 mask |= CN_EVENT;
224 /* advance buffer position */
225 ptr->ibuf_head += len;
226 if (ptr->ibuf_head >= ptr->ibuf_size)
227 ptr->ibuf_head = 0;
229 /* check for notification */
230 if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
231 (comm_inbuf(ptr)>=ptr->n_read)) {
232 /* passed the receive notification threshold */
233 mask |= CN_RECEIVE;
236 /* send notifications, if any */
237 if (ptr->wnd && mask) {
238 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
239 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
242 /* on real windows, this could cause problems, since it is recursive */
243 /* restart the receive */
244 comm_waitread(ptr);
247 /* this is meant to work like write() */
248 static INT COMM16_WriteFile(HANDLE hComm, LPCVOID buffer, DWORD len)
250 OVERLAPPED ov;
251 DWORD count= -1;
253 ZeroMemory(&ov,sizeof(ov));
254 ov.hEvent = CreateEventA(NULL,0,0,NULL);
255 if(ov.hEvent==INVALID_HANDLE_VALUE)
256 return -1;
258 if(!WriteFile(hComm,buffer,len,&count,&ov))
260 if(GetLastError()==ERROR_IO_PENDING)
262 GetOverlappedResult(hComm,&ov,&count,TRUE);
265 CloseHandle(ov.hEvent);
267 return count;
270 static VOID WINAPI COMM16_WriteComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
272 int prev, bleft;
273 WORD mask = 0;
274 int cid = GetCommPort_ov(ov,1);
275 struct DosDeviceStruct *ptr;
277 if(cid<0) {
278 ERR("async write with bad overlapped pointer\n");
279 return;
281 ptr = &COM[cid];
283 /* read data from comm port */
284 if (status != STATUS_SUCCESS) {
285 ERR("async write failed\n");
286 COM[cid].commerror = CE_RXOVER;
287 return;
289 TRACE("async write completed %ld bytes\n",len);
291 /* update the buffer pointers */
292 prev = comm_outbuf(&COM[cid]);
293 ptr->obuf_tail += len;
294 if (ptr->obuf_tail >= ptr->obuf_size)
295 ptr->obuf_tail = 0;
297 /* write any TransmitCommChar character */
298 if (ptr->xmit>=0) {
299 len = COMM16_WriteFile(ptr->handle, &(ptr->xmit), 1);
300 if (len > 0) ptr->xmit = -1;
303 /* write from output queue */
304 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
305 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
307 /* check for notification */
308 if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
309 (comm_outbuf(ptr)<ptr->n_write)) {
310 /* passed the transmit notification threshold */
311 mask |= CN_TRANSMIT;
314 /* send notifications, if any */
315 if (ptr->wnd && mask) {
316 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
317 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
320 /* start again if necessary */
321 if(bleft)
322 comm_waitwrite(ptr);
325 static void comm_waitread(struct DosDeviceStruct *ptr)
327 int bleft;
328 COMSTAT stat;
330 /* FIXME: get timeouts working properly so we can read bleft bytes */
331 bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ?
332 (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
334 /* find out how many bytes are left in the buffer */
335 if(ClearCommError(ptr->handle,NULL,&stat))
336 bleft = (bleft<stat.cbInQue) ? bleft : stat.cbInQue;
337 else
338 bleft = 1;
340 /* always read at least one byte */
341 if(bleft==0)
342 bleft++;
344 ReadFileEx(ptr->handle,
345 ptr->inbuf + ptr->ibuf_head,
346 bleft,
347 &ptr->read_ov,
348 COMM16_ReadComplete);
351 static void comm_waitwrite(struct DosDeviceStruct *ptr)
353 int bleft;
355 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
356 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
357 WriteFileEx(ptr->handle,
358 ptr->outbuf + ptr->obuf_tail,
359 bleft,
360 &ptr->write_ov,
361 COMM16_WriteComplete);
364 /*****************************************************************************
365 * COMM16_DCBtoDCB16 (Internal)
367 INT16 COMM16_DCBtoDCB16(LPDCB lpdcb, LPDCB16 lpdcb16)
369 if(lpdcb->BaudRate<0x10000)
370 lpdcb16->BaudRate = lpdcb->BaudRate;
371 else if(lpdcb->BaudRate==115200)
372 lpdcb16->BaudRate = 57601;
373 else {
374 WARN("Baud rate can't be converted\n");
375 lpdcb16->BaudRate = 57601;
377 lpdcb16->ByteSize = lpdcb->ByteSize;
378 lpdcb16->fParity = lpdcb->fParity;
379 lpdcb16->Parity = lpdcb->Parity;
380 lpdcb16->StopBits = lpdcb->StopBits;
382 lpdcb16->RlsTimeout = 50;
383 lpdcb16->CtsTimeout = 50;
384 lpdcb16->DsrTimeout = 50;
385 lpdcb16->fNull = 0;
386 lpdcb16->fChEvt = 0;
387 lpdcb16->fBinary = 1;
389 lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_HANDSHAKE);
390 lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_HANDSHAKE);
391 lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow;
392 lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow;
393 lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE);
395 lpdcb16->fInX = lpdcb->fInX;
397 lpdcb16->fOutX = lpdcb->fOutX;
399 lpdcb16->XonChar =
400 lpdcb16->XoffChar =
402 lpdcb16->XonLim = 10;
403 lpdcb16->XoffLim = 10;
405 return 0;
409 /**************************************************************************
410 * BuildCommDCB (USER.213)
412 * According to the ECMA-234 (368.3) the function will return FALSE on
413 * success, otherwise it will return -1.
415 INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
417 /* "COM1:96,n,8,1" */
418 /* 012345 */
419 int port;
420 DCB dcb;
422 TRACE("(%s), ptr %p\n", device, lpdcb);
424 if (strncasecmp(device,"COM",3))
425 return -1;
426 port = device[3] - '0';
428 if (port-- == 0) {
429 ERR("BUG ! COM0 can't exist!\n");
430 return -1;
433 memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
435 lpdcb->Id = port;
436 dcb.DCBlength = sizeof(DCB);
438 if (strchr(device,'=')) /* block new style */
439 return -1;
441 if(!BuildCommDCBA(device,&dcb))
442 return -1;
444 return COMM16_DCBtoDCB16(&dcb, lpdcb);
447 /*****************************************************************************
448 * OpenComm (USER.200)
450 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
452 int port;
453 HANDLE handle;
455 TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue);
457 if (strlen(device) < 4)
458 return IE_BADID;
460 port = device[3] - '0';
462 if (port-- == 0)
463 ERR("BUG ! COM0 or LPT0 don't exist !\n");
465 if (!strncasecmp(device,"COM",3))
467 if (COM[port].handle)
468 return IE_OPEN;
470 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
471 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
472 FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, 0 );
473 if (handle == INVALID_HANDLE_VALUE) {
474 return IE_HARDWARE;
475 } else {
476 memset(COM[port].unknown, 0, sizeof(COM[port].unknown));
477 COM[port].seg_unknown = 0;
478 COM[port].handle = handle;
479 COM[port].commerror = 0;
480 COM[port].eventmask = 0;
481 COM[port].evtchar = 0; /* FIXME: default? */
482 /* save terminal state */
483 GetCommState16(port,&COM[port].dcb);
484 /* init priority characters */
485 COM[port].unget = -1;
486 COM[port].xmit = -1;
487 /* allocate buffers */
488 COM[port].ibuf_size = cbInQueue;
489 COM[port].ibuf_head = COM[port].ibuf_tail = 0;
490 COM[port].obuf_size = cbOutQueue;
491 COM[port].obuf_head = COM[port].obuf_tail = 0;
493 COM[port].inbuf = malloc(cbInQueue);
494 if (COM[port].inbuf) {
495 COM[port].outbuf = malloc(cbOutQueue);
496 if (!COM[port].outbuf)
497 free(COM[port].inbuf);
498 } else COM[port].outbuf = NULL;
499 if (!COM[port].outbuf) {
500 /* not enough memory */
501 CloseHandle(COM[port].handle);
502 ERR("out of memory\n");
503 return IE_MEMORY;
506 ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED));
507 ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED));
509 comm_waitread( &COM[port] );
510 USER16_AlertableWait++;
512 return port;
515 else
516 if (!strncasecmp(device,"LPT",3)) {
518 if (LPT[port].handle)
519 return IE_OPEN;
521 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
522 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
523 if (handle == INVALID_HANDLE_VALUE) {
524 return IE_HARDWARE;
525 } else {
526 LPT[port].handle = handle;
527 LPT[port].commerror = 0;
528 LPT[port].eventmask = 0;
529 return port|FLAG_LPT;
532 return IE_BADID;
535 /*****************************************************************************
536 * CloseComm (USER.207)
538 INT16 WINAPI CloseComm16(INT16 cid)
540 struct DosDeviceStruct *ptr;
542 TRACE("cid=%d\n", cid);
543 if ((ptr = GetDeviceStruct(cid)) == NULL) {
544 FIXME("no cid=%d found!\n", cid);
545 return -1;
547 if (!(cid&FLAG_LPT)) {
548 /* COM port */
549 UnMapLS( COM[cid].seg_unknown );
550 USER16_AlertableWait--;
551 CancelIo(ptr->handle);
553 /* free buffers */
554 free(ptr->outbuf);
555 free(ptr->inbuf);
557 /* reset modem lines */
558 SetCommState16(&COM[cid].dcb);
561 if (!CloseHandle(ptr->handle)) {
562 ptr->commerror = WinError();
563 /* FIXME: should we clear ptr->handle here? */
564 return -1;
565 } else {
566 ptr->commerror = 0;
567 ptr->handle = 0;
568 return 0;
572 /*****************************************************************************
573 * SetCommBreak (USER.210)
575 INT16 WINAPI SetCommBreak16(INT16 cid)
577 struct DosDeviceStruct *ptr;
579 TRACE("cid=%d\n", cid);
580 if ((ptr = GetDeviceStruct(cid)) == NULL) {
581 FIXME("no cid=%d found!\n", cid);
582 return -1;
585 ptr->suspended = 1;
586 ptr->commerror = 0;
587 return 0;
590 /*****************************************************************************
591 * ClearCommBreak (USER.211)
593 INT16 WINAPI ClearCommBreak16(INT16 cid)
595 struct DosDeviceStruct *ptr;
597 TRACE("cid=%d\n", cid);
598 if (!(ptr = GetDeviceStruct(cid))) {
599 FIXME("no cid=%d found!\n", cid);
600 return -1;
602 ptr->suspended = 0;
603 ptr->commerror = 0;
604 return 0;
607 /*****************************************************************************
608 * EscapeCommFunction (USER.214)
610 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
612 struct DosDeviceStruct *ptr;
614 TRACE("cid=%d, function=%d\n", cid, nFunction);
616 switch(nFunction) {
617 case GETMAXCOM:
618 TRACE("GETMAXCOM\n");
619 return 4; /* FIXME */
621 case GETMAXLPT:
622 TRACE("GETMAXLPT\n");
623 return FLAG_LPT + 3; /* FIXME */
625 case GETBASEIRQ:
626 TRACE("GETBASEIRQ\n");
627 /* FIXME: use tables */
628 /* just fake something for now */
629 if (cid & FLAG_LPT) {
630 /* LPT1: irq 7, LPT2: irq 5 */
631 return (cid & 0x7f) ? 5 : 7;
632 } else {
633 /* COM1: irq 4, COM2: irq 3,
634 COM3: irq 4, COM4: irq 3 */
635 return 4 - (cid & 1);
639 if ((ptr = GetDeviceStruct(cid)) == NULL) {
640 FIXME("no cid=%d found!\n", cid);
641 return -1;
644 switch (nFunction) {
645 case RESETDEV:
646 case CLRDTR:
647 case CLRRTS:
648 case SETDTR:
649 case SETRTS:
650 case SETXOFF:
651 case SETXON:
652 if(EscapeCommFunction(ptr->handle,nFunction))
653 return 0;
654 else {
655 ptr->commerror = WinError();
656 return -1;
659 case CLRBREAK:
660 case SETBREAK:
661 default:
662 WARN("(cid=%d,nFunction=%d): Unknown function\n",
663 cid, nFunction);
665 return -1;
668 /*****************************************************************************
669 * FlushComm (USER.215)
671 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
673 DWORD queue;
674 struct DosDeviceStruct *ptr;
676 TRACE("cid=%d, queue=%d\n", cid, fnQueue);
677 if ((ptr = GetDeviceStruct(cid)) == NULL) {
678 FIXME("no cid=%d found!\n", cid);
679 return -1;
681 switch (fnQueue) {
682 case 0:
683 queue = PURGE_TXABORT;
684 ptr->obuf_tail = ptr->obuf_head;
685 break;
686 case 1:
687 queue = PURGE_RXABORT;
688 ptr->ibuf_head = ptr->ibuf_tail;
689 break;
690 default:
691 WARN("(cid=%d,fnQueue=%d):Unknown queue\n",
692 cid, fnQueue);
693 return -1;
696 if (!PurgeComm(ptr->handle,queue)) {
697 ptr->commerror = WinError();
698 return -1;
699 } else {
700 ptr->commerror = 0;
701 return 0;
705 /********************************************************************
706 * GetCommError (USER.203)
708 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
710 int temperror;
711 struct DosDeviceStruct *ptr;
712 unsigned char *stol;
714 if ((ptr = GetDeviceStruct(cid)) == NULL) {
715 FIXME("no handle for cid = %0x!\n",cid);
716 return -1;
718 if (cid&FLAG_LPT) {
719 WARN(" cid %d not comm port\n",cid);
720 return CE_MODE;
722 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
723 COMM_MSRUpdate( ptr->handle, stol );
725 if (lpStat) {
726 lpStat->status = 0;
728 SleepEx(1,TRUE);
730 lpStat->cbOutQue = comm_outbuf(ptr);
731 lpStat->cbInQue = comm_inbuf(ptr);
733 TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n",
734 cid, ptr->commerror, lpStat->status, lpStat->cbInQue,
735 lpStat->cbOutQue, *stol);
737 else
738 TRACE("cid %d, error %d, lpStat NULL stol %x\n",
739 cid, ptr->commerror, *stol);
741 /* Return any errors and clear it */
742 temperror = ptr->commerror;
743 ptr->commerror = 0;
744 return(temperror);
747 /*****************************************************************************
748 * SetCommEventMask (USER.208)
750 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
752 struct DosDeviceStruct *ptr;
753 unsigned char *stol;
755 TRACE("cid %d,mask %d\n",cid,fuEvtMask);
756 if ((ptr = GetDeviceStruct(cid)) == NULL) {
757 FIXME("no handle for cid = %0x!\n",cid);
758 return (SEGPTR)NULL;
761 ptr->eventmask = fuEvtMask;
763 if (cid&FLAG_LPT) {
764 WARN(" cid %d not comm port\n",cid);
765 return (SEGPTR)NULL;
767 /* it's a COM port ? -> modify flags */
768 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
769 COMM_MSRUpdate( ptr->handle, stol );
771 TRACE(" modem dcd construct %x\n",*stol);
772 if (!COM[cid].seg_unknown) COM[cid].seg_unknown = MapLS( COM[cid].unknown );
773 return COM[cid].seg_unknown;
776 /*****************************************************************************
777 * GetCommEventMask (USER.209)
779 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
781 struct DosDeviceStruct *ptr;
782 WORD events;
784 TRACE("cid %d, mask %d\n", cid, fnEvtClear);
785 if ((ptr = GetDeviceStruct(cid)) == NULL) {
786 FIXME("no handle for cid = %0x!\n",cid);
787 return 0;
790 if (cid&FLAG_LPT) {
791 WARN(" cid %d not comm port\n",cid);
792 return 0;
795 events = *(WORD*)(COM[cid].unknown) & fnEvtClear;
796 *(WORD*)(COM[cid].unknown) &= ~fnEvtClear;
797 return events;
800 /*****************************************************************************
801 * SetCommState (USER.201)
803 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
805 struct DosDeviceStruct *ptr;
806 DCB dcb;
808 TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb);
809 if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
810 FIXME("no handle for cid = %0x!\n",lpdcb->Id);
811 return -1;
814 memset(&dcb,0,sizeof(dcb));
815 dcb.DCBlength = sizeof(dcb);
818 * according to MSDN, we should first interpret lpdcb->BaudRate as follows:
819 * 1. if the baud rate is a CBR constant, interpret it.
820 * 2. if it is greater than 57600, the baud rate is 115200
821 * 3. use the actual baudrate
822 * steps 2 and 3 are equivilent to 16550 baudrate divisor = 115200/BaudRate
823 * see http://support.microsoft.com/support/kb/articles/q108/9/28.asp
825 switch(lpdcb->BaudRate)
827 case CBR_110: dcb.BaudRate = 110; break;
828 case CBR_300: dcb.BaudRate = 300; break;
829 case CBR_600: dcb.BaudRate = 600; break;
830 case CBR_1200: dcb.BaudRate = 1200; break;
831 case CBR_2400: dcb.BaudRate = 2400; break;
832 case CBR_4800: dcb.BaudRate = 4800; break;
833 case CBR_9600: dcb.BaudRate = 9600; break;
834 case CBR_14400: dcb.BaudRate = 14400; break;
835 case CBR_19200: dcb.BaudRate = 19200; break;
836 case CBR_38400: dcb.BaudRate = 38400; break;
837 case CBR_56000: dcb.BaudRate = 56000; break;
838 case CBR_128000: dcb.BaudRate = 128000; break;
839 case CBR_256000: dcb.BaudRate = 256000; break;
840 default:
841 if(lpdcb->BaudRate>57600)
842 dcb.BaudRate = 115200;
843 else
844 dcb.BaudRate = lpdcb->BaudRate;
847 dcb.ByteSize=lpdcb->ByteSize;
848 dcb.StopBits=lpdcb->StopBits;
850 dcb.fParity=lpdcb->fParity;
851 dcb.Parity=lpdcb->Parity;
853 dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow;
855 if (lpdcb->fDtrflow || lpdcb->fRtsflow)
856 dcb.fRtsControl = TRUE;
858 if (lpdcb->fDtrDisable)
859 dcb.fDtrControl = TRUE;
861 ptr->evtchar = lpdcb->EvtChar;
863 dcb.fInX = lpdcb->fInX;
864 dcb.fOutX = lpdcb->fOutX;
866 if (!SetCommState(ptr->handle,&dcb)) {
867 ptr->commerror = WinError();
868 return -1;
869 } else {
870 ptr->commerror = 0;
871 return 0;
875 /*****************************************************************************
876 * GetCommState (USER.202)
878 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
880 struct DosDeviceStruct *ptr;
881 DCB dcb;
883 TRACE("cid %d, ptr %p\n", cid, lpdcb);
884 if ((ptr = GetDeviceStruct(cid)) == NULL) {
885 FIXME("no handle for cid = %0x!\n",cid);
886 return -1;
888 if (!GetCommState(ptr->handle,&dcb)) {
889 ptr->commerror = WinError();
890 return -1;
893 lpdcb->Id = cid;
895 COMM16_DCBtoDCB16(&dcb,lpdcb);
897 lpdcb->EvtChar = ptr->evtchar;
899 ptr->commerror = 0;
900 return 0;
903 /*****************************************************************************
904 * TransmitCommChar (USER.206)
906 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
908 struct DosDeviceStruct *ptr;
910 TRACE("cid %d, data %d \n", cid, chTransmit);
911 if ((ptr = GetDeviceStruct(cid)) == NULL) {
912 FIXME("no handle for cid = %0x!\n",cid);
913 return -1;
916 if (ptr->suspended) {
917 ptr->commerror = IE_HARDWARE;
918 return -1;
921 if (ptr->xmit >= 0) {
922 /* character already queued */
923 /* FIXME: which error would Windows return? */
924 ptr->commerror = CE_TXFULL;
925 return -1;
928 if (ptr->obuf_head == ptr->obuf_tail) {
929 /* transmit queue empty, try to transmit directly */
930 if(1!=COMM16_WriteFile(ptr->handle, &chTransmit, 1))
932 /* didn't work, queue it */
933 ptr->xmit = chTransmit;
934 comm_waitwrite(ptr);
936 } else {
937 /* data in queue, let this char be transmitted next */
938 ptr->xmit = chTransmit;
939 comm_waitwrite(ptr);
942 ptr->commerror = 0;
943 return 0;
946 /*****************************************************************************
947 * UngetCommChar (USER.212)
949 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
951 struct DosDeviceStruct *ptr;
953 TRACE("cid %d (char %d)\n", cid, chUnget);
954 if ((ptr = GetDeviceStruct(cid)) == NULL) {
955 FIXME("no handle for cid = %0x!\n",cid);
956 return -1;
959 if (ptr->suspended) {
960 ptr->commerror = IE_HARDWARE;
961 return -1;
964 if (ptr->unget>=0) {
965 /* character already queued */
966 /* FIXME: which error would Windows return? */
967 ptr->commerror = CE_RXOVER;
968 return -1;
971 ptr->unget = chUnget;
973 ptr->commerror = 0;
974 return 0;
977 /*****************************************************************************
978 * ReadComm (USER.204)
980 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
982 int status, length;
983 struct DosDeviceStruct *ptr;
984 LPSTR orgBuf = lpvBuf;
986 TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
987 if ((ptr = GetDeviceStruct(cid)) == NULL) {
988 FIXME("no handle for cid = %0x!\n",cid);
989 return -1;
992 if (ptr->suspended) {
993 ptr->commerror = IE_HARDWARE;
994 return -1;
997 if(0==comm_inbuf(ptr))
998 SleepEx(1,TRUE);
1000 /* read unget character */
1001 if (ptr->unget>=0) {
1002 *lpvBuf++ = ptr->unget;
1003 ptr->unget = -1;
1005 length = 1;
1006 } else
1007 length = 0;
1009 /* read from receive buffer */
1010 while (length < cbRead) {
1011 status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1012 ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1013 if (!status) break;
1014 if ((cbRead - length) < status)
1015 status = cbRead - length;
1017 memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1018 ptr->ibuf_tail += status;
1019 if (ptr->ibuf_tail >= ptr->ibuf_size)
1020 ptr->ibuf_tail = 0;
1021 lpvBuf += status;
1022 length += status;
1025 TRACE("%s\n", debugstr_an( orgBuf, length ));
1026 ptr->commerror = 0;
1027 return length;
1030 /*****************************************************************************
1031 * WriteComm (USER.205)
1033 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1035 int status, length;
1036 struct DosDeviceStruct *ptr;
1038 TRACE("cid %d, ptr %p, length %d\n",
1039 cid, lpvBuf, cbWrite);
1040 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1041 FIXME("no handle for cid = %0x!\n",cid);
1042 return -1;
1045 if (ptr->suspended) {
1046 ptr->commerror = IE_HARDWARE;
1047 return -1;
1050 TRACE("%s\n", debugstr_an( lpvBuf, cbWrite ));
1052 length = 0;
1053 while (length < cbWrite) {
1054 if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1055 /* no data queued, try to write directly */
1056 status = COMM16_WriteFile(ptr->handle, lpvBuf, cbWrite - length);
1057 if (status > 0) {
1058 lpvBuf += status;
1059 length += status;
1060 continue;
1063 /* can't write directly, put into transmit buffer */
1064 status = ((ptr->obuf_tail > ptr->obuf_head) ?
1065 (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1066 if (!status) break;
1067 if ((cbWrite - length) < status)
1068 status = cbWrite - length;
1069 memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1070 ptr->obuf_head += status;
1071 if (ptr->obuf_head >= ptr->obuf_size)
1072 ptr->obuf_head = 0;
1073 lpvBuf += status;
1074 length += status;
1075 comm_waitwrite(ptr);
1078 ptr->commerror = 0;
1079 return length;
1082 /***********************************************************************
1083 * EnableCommNotification (USER.245)
1085 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1086 INT16 cbWriteNotify, INT16 cbOutQueue )
1088 struct DosDeviceStruct *ptr;
1090 TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1091 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1092 FIXME("no handle for cid = %0x!\n",cid);
1093 return -1;
1095 ptr->wnd = WIN_Handle32( hwnd );
1096 ptr->n_read = cbWriteNotify;
1097 ptr->n_write = cbOutQueue;
1098 return TRUE;