mf/tests: Add some tests for EVR sink services.
[wine.git] / dlls / user.exe16 / comm.c
blob9125c6d0f370bd9890c1cf4ca3f6317e5c567ad1
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 <stdlib.h>
54 #include <stdarg.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <ctype.h>
60 #include "windef.h"
61 #include "winbase.h"
62 #include "wine/winuser16.h"
63 #include "user_private.h"
65 #include "wine/debug.h"
67 WINE_DEFAULT_DEBUG_CHANNEL(comm);
69 /* window's semi documented modem status register */
70 #define COMM_MSR_OFFSET 35
71 #define MSR_CTS 0x10
72 #define MSR_DSR 0x20
73 #define MSR_RI 0x40
74 #define MSR_RLSD 0x80
75 #define MSR_MASK (MSR_CTS|MSR_DSR|MSR_RI|MSR_RLSD)
77 #define FLAG_LPT 0x80
79 #define MAX_PORTS 9
81 struct DosDeviceStruct {
82 HANDLE handle;
83 BOOL suspended;
84 int unget,xmit;
85 int evtchar;
86 /* events */
87 int commerror, eventmask;
88 /* buffers */
89 char *inbuf,*outbuf;
90 unsigned ibuf_size,ibuf_head,ibuf_tail;
91 unsigned obuf_size,obuf_head,obuf_tail;
92 /* notifications */
93 HWND wnd;
94 int n_read, n_write;
95 OVERLAPPED read_ov, write_ov;
96 /* save terminal states */
97 DCB16 dcb;
98 /* pointer to unknown(==undocumented) comm structure */
99 SEGPTR seg_unknown;
100 BYTE unknown[40];
103 static struct DosDeviceStruct COM[MAX_PORTS];
104 static struct DosDeviceStruct LPT[MAX_PORTS];
106 /* update window's semi documented modem status register */
107 /* see knowledge base Q101417 */
108 static void COMM_MSRUpdate( HANDLE handle, UCHAR * pMsr )
110 UCHAR tmpmsr=0;
111 DWORD mstat=0;
113 if(!GetCommModemStatus(handle,&mstat))
114 return;
116 if(mstat & MS_CTS_ON) tmpmsr |= MSR_CTS;
117 if(mstat & MS_DSR_ON) tmpmsr |= MSR_DSR;
118 if(mstat & MS_RING_ON) tmpmsr |= MSR_RI;
119 if(mstat & MS_RLSD_ON) tmpmsr |= MSR_RLSD;
120 *pMsr = (*pMsr & ~MSR_MASK) | tmpmsr;
123 static struct DosDeviceStruct *GetDeviceStruct(int index)
125 if ((index&0x7F)<=MAX_PORTS) {
126 if (!(index&FLAG_LPT)) {
127 if (COM[index].handle)
128 return &COM[index];
129 } else {
130 index &= 0x7f;
131 if (LPT[index].handle)
132 return &LPT[index];
136 return NULL;
139 static int GetCommPort_ov(const OVERLAPPED *ov, int write)
141 int x;
143 for (x=0; x<MAX_PORTS; x++) {
144 if (ov == (write?&COM[x].write_ov:&COM[x].read_ov))
145 return x;
148 return -1;
151 static int WinError(void)
153 TRACE("errno = %d\n", errno);
154 switch (errno) {
155 default:
156 return CE_IOE;
160 static unsigned comm_inbuf(const struct DosDeviceStruct *ptr)
162 return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
163 + ptr->ibuf_head - ptr->ibuf_tail;
166 static unsigned comm_outbuf(const struct DosDeviceStruct *ptr)
168 return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
169 + ptr->obuf_head - ptr->obuf_tail;
172 static void comm_waitread(struct DosDeviceStruct *ptr);
173 static void comm_waitwrite(struct DosDeviceStruct *ptr);
175 static VOID WINAPI COMM16_ReadComplete(DWORD dwErrorCode, DWORD len, LPOVERLAPPED ov)
177 int prev;
178 WORD mask = 0;
179 int cid = GetCommPort_ov(ov,0);
180 struct DosDeviceStruct *ptr;
182 if(cid<0) {
183 ERR("async write with bad overlapped pointer\n");
184 return;
186 ptr = &COM[cid];
188 /* we get cancelled when CloseComm is called */
189 if (dwErrorCode==ERROR_OPERATION_ABORTED)
191 TRACE("Cancelled\n");
192 return;
195 /* read data from comm port */
196 if (dwErrorCode != NO_ERROR) {
197 ERR("async read failed, error %d\n",dwErrorCode);
198 COM[cid].commerror = CE_RXOVER;
199 return;
201 TRACE("async read completed %d bytes\n",len);
203 prev = comm_inbuf(ptr);
205 /* check for events */
206 if ((ptr->eventmask & EV_RXFLAG) &&
207 memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) {
208 *(WORD*)(COM[cid].unknown) |= EV_RXFLAG;
209 mask |= CN_EVENT;
211 if (ptr->eventmask & EV_RXCHAR) {
212 *(WORD*)(COM[cid].unknown) |= EV_RXCHAR;
213 mask |= CN_EVENT;
216 /* advance buffer position */
217 ptr->ibuf_head += len;
218 if (ptr->ibuf_head >= ptr->ibuf_size)
219 ptr->ibuf_head = 0;
221 /* check for notification */
222 if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
223 (comm_inbuf(ptr)>=ptr->n_read)) {
224 /* passed the receive notification threshold */
225 mask |= CN_RECEIVE;
228 /* send notifications, if any */
229 if (ptr->wnd && mask) {
230 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
231 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
234 /* on real windows, this could cause problems, since it is recursive */
235 /* restart the receive */
236 comm_waitread(ptr);
239 /* this is meant to work like write() */
240 static INT COMM16_WriteFile(HANDLE hComm, LPCVOID buffer, DWORD len)
242 OVERLAPPED ov;
243 DWORD count= -1;
245 ZeroMemory(&ov,sizeof(ov));
246 ov.hEvent = CreateEventW(NULL,0,0,NULL);
247 if(ov.hEvent==INVALID_HANDLE_VALUE)
248 return -1;
250 if(!WriteFile(hComm,buffer,len,&count,&ov))
252 if(GetLastError()==ERROR_IO_PENDING)
254 GetOverlappedResult(hComm,&ov,&count,TRUE);
257 CloseHandle(ov.hEvent);
259 return count;
262 static VOID WINAPI COMM16_WriteComplete(DWORD dwErrorCode, DWORD len, LPOVERLAPPED ov)
264 int prev, bleft;
265 WORD mask = 0;
266 int cid = GetCommPort_ov(ov,1);
267 struct DosDeviceStruct *ptr;
269 if(cid<0) {
270 ERR("async write with bad overlapped pointer\n");
271 return;
273 ptr = &COM[cid];
275 /* read data from comm port */
276 if (dwErrorCode != NO_ERROR) {
277 ERR("async write failed, error %d\n",dwErrorCode);
278 COM[cid].commerror = CE_RXOVER;
279 return;
281 TRACE("async write completed %d bytes\n",len);
283 /* update the buffer pointers */
284 prev = comm_outbuf(&COM[cid]);
285 ptr->obuf_tail += len;
286 if (ptr->obuf_tail >= ptr->obuf_size)
287 ptr->obuf_tail = 0;
289 /* write any TransmitCommChar character */
290 if (ptr->xmit>=0) {
291 len = COMM16_WriteFile(ptr->handle, &(ptr->xmit), 1);
292 if (len > 0) ptr->xmit = -1;
295 /* write from output queue */
296 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
297 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
299 /* check for notification */
300 if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
301 (comm_outbuf(ptr)<ptr->n_write)) {
302 /* passed the transmit notification threshold */
303 mask |= CN_TRANSMIT;
306 /* send notifications, if any */
307 if (ptr->wnd && mask) {
308 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
309 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
312 /* start again if necessary */
313 if(bleft)
314 comm_waitwrite(ptr);
317 static void comm_waitread(struct DosDeviceStruct *ptr)
319 unsigned int bleft;
320 COMSTAT stat;
322 /* FIXME: get timeouts working properly so we can read bleft bytes */
323 bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ?
324 (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
326 /* find out how many bytes are left in the buffer */
327 if(ClearCommError(ptr->handle,NULL,&stat))
328 bleft = (bleft<stat.cbInQue) ? bleft : stat.cbInQue;
329 else
330 bleft = 1;
332 /* always read at least one byte */
333 if(bleft==0)
334 bleft++;
336 ReadFileEx(ptr->handle,
337 ptr->inbuf + ptr->ibuf_head,
338 bleft,
339 &ptr->read_ov,
340 COMM16_ReadComplete);
343 static void comm_waitwrite(struct DosDeviceStruct *ptr)
345 int bleft;
347 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
348 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
349 WriteFileEx(ptr->handle,
350 ptr->outbuf + ptr->obuf_tail,
351 bleft,
352 &ptr->write_ov,
353 COMM16_WriteComplete);
356 /*****************************************************************************
357 * COMM16_DCBtoDCB16 (Internal)
359 static INT16 COMM16_DCBtoDCB16(const DCB *lpdcb, LPDCB16 lpdcb16)
361 if(lpdcb->BaudRate<0x10000)
362 lpdcb16->BaudRate = lpdcb->BaudRate;
363 else if(lpdcb->BaudRate==115200)
364 lpdcb16->BaudRate = 57601;
365 else {
366 WARN("Baud rate can't be converted\n");
367 lpdcb16->BaudRate = 57601;
369 lpdcb16->ByteSize = lpdcb->ByteSize;
370 lpdcb16->fParity = lpdcb->fParity;
371 lpdcb16->Parity = lpdcb->Parity;
372 lpdcb16->StopBits = lpdcb->StopBits;
374 lpdcb16->RlsTimeout = 50;
375 lpdcb16->CtsTimeout = 50;
376 lpdcb16->DsrTimeout = 50;
377 lpdcb16->fNull = 0;
378 lpdcb16->fChEvt = 0;
379 lpdcb16->fBinary = 1;
381 lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_HANDSHAKE);
382 lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_HANDSHAKE);
383 lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow;
384 lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow;
385 lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE);
387 lpdcb16->fInX = lpdcb->fInX;
389 lpdcb16->fOutX = lpdcb->fOutX;
391 lpdcb16->XonChar =
392 lpdcb16->XoffChar =
394 lpdcb16->XonLim = 10;
395 lpdcb16->XoffLim = 10;
397 return 0;
401 /**************************************************************************
402 * BuildCommDCB (USER.213)
404 * According to the ECMA-234 (368.3) the function will return FALSE on
405 * success, otherwise it will return -1.
407 INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
409 /* "COM1:96,n,8,1" */
410 /* 012345 */
411 int port;
412 DCB dcb;
414 TRACE("(%s), ptr %p\n", device, lpdcb);
416 if (strnicmp(device,"COM",3))
417 return -1;
418 port = device[3] - '0';
420 if (port-- == 0) {
421 ERR("BUG ! COM0 can't exist!\n");
422 return -1;
425 memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
427 lpdcb->Id = port;
428 dcb.DCBlength = sizeof(DCB);
430 if (strchr(device,'=')) /* block new style */
431 return -1;
433 if(!BuildCommDCBA(device,&dcb))
434 return -1;
436 return COMM16_DCBtoDCB16(&dcb, lpdcb);
439 /*****************************************************************************
440 * OpenComm (USER.200)
442 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
444 int port;
445 HANDLE handle;
447 TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue);
449 if (strlen(device) < 4)
450 return IE_BADID;
452 port = device[3] - '0';
454 if (port-- == 0)
455 ERR("BUG ! COM0 or LPT0 don't exist !\n");
457 if (!strnicmp(device,"COM",3))
459 if (COM[port].handle)
460 return IE_OPEN;
462 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
463 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
464 FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, 0 );
465 if (handle == INVALID_HANDLE_VALUE) {
466 return IE_HARDWARE;
467 } else {
468 memset(COM[port].unknown, 0, sizeof(COM[port].unknown));
469 COM[port].seg_unknown = 0;
470 COM[port].handle = handle;
471 COM[port].commerror = 0;
472 COM[port].eventmask = 0;
473 COM[port].evtchar = 0; /* FIXME: default? */
474 /* save terminal state */
475 GetCommState16(port,&COM[port].dcb);
476 /* init priority characters */
477 COM[port].unget = -1;
478 COM[port].xmit = -1;
479 /* allocate buffers */
480 COM[port].ibuf_size = cbInQueue;
481 COM[port].ibuf_head = COM[port].ibuf_tail = 0;
482 COM[port].obuf_size = cbOutQueue;
483 COM[port].obuf_head = COM[port].obuf_tail = 0;
485 COM[port].inbuf = HeapAlloc(GetProcessHeap(), 0, cbInQueue);
486 if (COM[port].inbuf) {
487 COM[port].outbuf = HeapAlloc( GetProcessHeap(), 0, cbOutQueue);
488 if (!COM[port].outbuf)
489 HeapFree( GetProcessHeap(), 0, COM[port].inbuf);
490 } else COM[port].outbuf = NULL;
491 if (!COM[port].outbuf) {
492 /* not enough memory */
493 CloseHandle(COM[port].handle);
494 ERR("out of memory\n");
495 return IE_MEMORY;
498 ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED));
499 ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED));
501 comm_waitread( &COM[port] );
502 USER16_AlertableWait++;
504 return port;
507 else
508 if (!strnicmp(device,"LPT",3)) {
510 if (LPT[port].handle)
511 return IE_OPEN;
513 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
514 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
515 if (handle == INVALID_HANDLE_VALUE) {
516 return IE_HARDWARE;
517 } else {
518 LPT[port].handle = handle;
519 LPT[port].commerror = 0;
520 LPT[port].eventmask = 0;
521 return port|FLAG_LPT;
524 return IE_BADID;
527 /*****************************************************************************
528 * CloseComm (USER.207)
530 INT16 WINAPI CloseComm16(INT16 cid)
532 struct DosDeviceStruct *ptr;
534 TRACE("cid=%d\n", cid);
535 if ((ptr = GetDeviceStruct(cid)) == NULL) {
536 FIXME("no cid=%d found!\n", cid);
537 return -1;
539 if (!(cid&FLAG_LPT)) {
540 /* COM port */
541 UnMapLS( COM[cid].seg_unknown );
542 USER16_AlertableWait--;
543 CancelIo(ptr->handle);
545 /* free buffers */
546 HeapFree( GetProcessHeap(), 0, ptr->outbuf);
547 HeapFree( GetProcessHeap(), 0, ptr->inbuf);
549 /* reset modem lines */
550 SetCommState16(&COM[cid].dcb);
553 if (!CloseHandle(ptr->handle)) {
554 ptr->commerror = WinError();
555 /* FIXME: should we clear ptr->handle here? */
556 return -1;
557 } else {
558 ptr->commerror = 0;
559 ptr->handle = 0;
560 return 0;
564 /*****************************************************************************
565 * SetCommBreak (USER.210)
567 INT16 WINAPI SetCommBreak16(INT16 cid)
569 struct DosDeviceStruct *ptr;
571 TRACE("cid=%d\n", cid);
572 if ((ptr = GetDeviceStruct(cid)) == NULL) {
573 FIXME("no cid=%d found!\n", cid);
574 return -1;
577 ptr->suspended = TRUE;
578 ptr->commerror = 0;
579 return 0;
582 /*****************************************************************************
583 * ClearCommBreak (USER.211)
585 INT16 WINAPI ClearCommBreak16(INT16 cid)
587 struct DosDeviceStruct *ptr;
589 TRACE("cid=%d\n", cid);
590 if (!(ptr = GetDeviceStruct(cid))) {
591 FIXME("no cid=%d found!\n", cid);
592 return -1;
594 ptr->suspended = FALSE;
595 ptr->commerror = 0;
596 return 0;
599 /*****************************************************************************
600 * EscapeCommFunction (USER.214)
602 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
604 struct DosDeviceStruct *ptr;
606 TRACE("cid=%d, function=%d\n", cid, nFunction);
608 switch(nFunction) {
609 case GETMAXCOM:
610 TRACE("GETMAXCOM\n");
611 return 4; /* FIXME */
613 case GETMAXLPT:
614 TRACE("GETMAXLPT\n");
615 return FLAG_LPT + 3; /* FIXME */
617 case GETBASEIRQ:
618 TRACE("GETBASEIRQ\n");
619 /* FIXME: use tables */
620 /* just fake something for now */
621 if (cid & FLAG_LPT) {
622 /* LPT1: irq 7, LPT2: irq 5 */
623 return (cid & 0x7f) ? 5 : 7;
624 } else {
625 /* COM1: irq 4, COM2: irq 3,
626 COM3: irq 4, COM4: irq 3 */
627 return 4 - (cid & 1);
631 if ((ptr = GetDeviceStruct(cid)) == NULL) {
632 FIXME("no cid=%d found!\n", cid);
633 return -1;
636 switch (nFunction) {
637 case RESETDEV:
638 case CLRDTR:
639 case CLRRTS:
640 case SETDTR:
641 case SETRTS:
642 case SETXOFF:
643 case SETXON:
644 if(EscapeCommFunction(ptr->handle,nFunction))
645 return 0;
646 else {
647 ptr->commerror = WinError();
648 return -1;
651 case CLRBREAK:
652 case SETBREAK:
653 default:
654 WARN("(cid=%d,nFunction=%d): Unknown function\n",
655 cid, nFunction);
657 return -1;
660 /*****************************************************************************
661 * FlushComm (USER.215)
663 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
665 DWORD queue;
666 struct DosDeviceStruct *ptr;
668 TRACE("cid=%d, queue=%d\n", cid, fnQueue);
669 if ((ptr = GetDeviceStruct(cid)) == NULL) {
670 FIXME("no cid=%d found!\n", cid);
671 return -1;
673 switch (fnQueue) {
674 case 0:
675 queue = PURGE_TXABORT;
676 ptr->obuf_tail = ptr->obuf_head;
677 break;
678 case 1:
679 queue = PURGE_RXABORT;
680 ptr->ibuf_head = ptr->ibuf_tail;
681 break;
682 default:
683 WARN("(cid=%d,fnQueue=%d):Unknown queue\n",
684 cid, fnQueue);
685 return -1;
688 if (!PurgeComm(ptr->handle,queue)) {
689 ptr->commerror = WinError();
690 return -1;
691 } else {
692 ptr->commerror = 0;
693 return 0;
697 /********************************************************************
698 * GetCommError (USER.203)
700 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
702 int temperror;
703 struct DosDeviceStruct *ptr;
704 unsigned char *stol;
706 if ((ptr = GetDeviceStruct(cid)) == NULL) {
707 FIXME("no handle for cid = %0x!\n",cid);
708 return -1;
710 if (cid&FLAG_LPT) {
711 WARN(" cid %d not comm port\n",cid);
712 return CE_MODE;
714 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
715 COMM_MSRUpdate( ptr->handle, stol );
717 if (lpStat) {
718 lpStat->status = 0;
720 if (comm_inbuf(ptr) == 0)
721 SleepEx(1,TRUE);
723 lpStat->cbOutQue = comm_outbuf(ptr);
724 lpStat->cbInQue = comm_inbuf(ptr);
726 TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n",
727 cid, ptr->commerror, lpStat->status, lpStat->cbInQue,
728 lpStat->cbOutQue, *stol);
730 else
731 TRACE("cid %d, error %d, lpStat NULL stol %x\n",
732 cid, ptr->commerror, *stol);
734 /* Return any errors and clear it */
735 temperror = ptr->commerror;
736 ptr->commerror = 0;
737 return(temperror);
740 /*****************************************************************************
741 * SetCommEventMask (USER.208)
743 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
745 struct DosDeviceStruct *ptr;
746 unsigned char *stol;
748 TRACE("cid %d,mask %d\n",cid,fuEvtMask);
749 if ((ptr = GetDeviceStruct(cid)) == NULL) {
750 FIXME("no handle for cid = %0x!\n",cid);
751 return 0;
754 ptr->eventmask = fuEvtMask;
756 if (cid&FLAG_LPT) {
757 WARN(" cid %d not comm port\n",cid);
758 return 0;
760 /* it's a COM port ? -> modify flags */
761 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
762 COMM_MSRUpdate( ptr->handle, stol );
764 TRACE(" modem dcd construct %x\n",*stol);
765 if (!COM[cid].seg_unknown) COM[cid].seg_unknown = MapLS( COM[cid].unknown );
766 return COM[cid].seg_unknown;
769 /*****************************************************************************
770 * GetCommEventMask (USER.209)
772 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
774 struct DosDeviceStruct *ptr;
775 WORD events;
777 TRACE("cid %d, mask %d\n", cid, fnEvtClear);
778 if ((ptr = GetDeviceStruct(cid)) == NULL) {
779 FIXME("no handle for cid = %0x!\n",cid);
780 return 0;
783 if (cid&FLAG_LPT) {
784 WARN(" cid %d not comm port\n",cid);
785 return 0;
788 events = *(WORD*)(COM[cid].unknown) & fnEvtClear;
789 *(WORD*)(COM[cid].unknown) &= ~fnEvtClear;
790 return events;
793 /*****************************************************************************
794 * SetCommState (USER.201)
796 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
798 struct DosDeviceStruct *ptr;
799 DCB dcb;
801 TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb);
802 if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
803 FIXME("no handle for cid = %0x!\n",lpdcb->Id);
804 return -1;
807 memset(&dcb,0,sizeof(dcb));
808 dcb.DCBlength = sizeof(dcb);
811 * according to MSDN, we should first interpret lpdcb->BaudRate as follows:
812 * 1. if the baud rate is a CBR constant, interpret it.
813 * 2. if it is greater than 57600, the baud rate is 115200
814 * 3. use the actual baudrate
815 * steps 2 and 3 are equivalent to 16550 baudrate divisor = 115200/BaudRate
817 switch(lpdcb->BaudRate)
819 case CBR_110: dcb.BaudRate = 110; break;
820 case CBR_300: dcb.BaudRate = 300; break;
821 case CBR_600: dcb.BaudRate = 600; break;
822 case CBR_1200: dcb.BaudRate = 1200; break;
823 case CBR_2400: dcb.BaudRate = 2400; break;
824 case CBR_4800: dcb.BaudRate = 4800; break;
825 case CBR_9600: dcb.BaudRate = 9600; break;
826 case CBR_14400: dcb.BaudRate = 14400; break;
827 case CBR_19200: dcb.BaudRate = 19200; break;
828 case CBR_38400: dcb.BaudRate = 38400; break;
829 case CBR_56000: dcb.BaudRate = 56000; break;
830 case CBR_128000: dcb.BaudRate = 128000; break;
831 case CBR_256000: dcb.BaudRate = 256000; break;
832 default:
833 if(lpdcb->BaudRate>57600)
834 dcb.BaudRate = 115200;
835 else
836 dcb.BaudRate = lpdcb->BaudRate;
839 dcb.ByteSize=lpdcb->ByteSize;
840 dcb.StopBits=lpdcb->StopBits;
842 dcb.fParity=lpdcb->fParity;
843 dcb.Parity=lpdcb->Parity;
845 dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow;
847 if (lpdcb->fDtrflow || lpdcb->fRtsflow)
848 dcb.fRtsControl = TRUE;
850 if (lpdcb->fDtrDisable)
851 dcb.fDtrControl = TRUE;
853 ptr->evtchar = lpdcb->EvtChar;
855 dcb.fInX = lpdcb->fInX;
856 dcb.fOutX = lpdcb->fOutX;
858 if (!SetCommState(ptr->handle,&dcb)) {
859 ptr->commerror = WinError();
860 return -1;
861 } else {
862 ptr->commerror = 0;
863 return 0;
867 /*****************************************************************************
868 * GetCommState (USER.202)
870 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
872 struct DosDeviceStruct *ptr;
873 DCB dcb;
875 TRACE("cid %d, ptr %p\n", cid, lpdcb);
876 if ((ptr = GetDeviceStruct(cid)) == NULL) {
877 FIXME("no handle for cid = %0x!\n",cid);
878 return -1;
880 if (!GetCommState(ptr->handle,&dcb)) {
881 ptr->commerror = WinError();
882 return -1;
885 lpdcb->Id = cid;
887 COMM16_DCBtoDCB16(&dcb,lpdcb);
889 lpdcb->EvtChar = ptr->evtchar;
891 ptr->commerror = 0;
892 return 0;
895 /*****************************************************************************
896 * TransmitCommChar (USER.206)
898 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
900 struct DosDeviceStruct *ptr;
902 TRACE("cid %d, data %d\n", cid, chTransmit);
903 if ((ptr = GetDeviceStruct(cid)) == NULL) {
904 FIXME("no handle for cid = %0x!\n",cid);
905 return -1;
908 if (ptr->suspended) {
909 ptr->commerror = IE_HARDWARE;
910 return -1;
913 if (ptr->xmit >= 0) {
914 /* character already queued */
915 /* FIXME: which error would Windows return? */
916 ptr->commerror = CE_TXFULL;
917 return -1;
920 if (ptr->obuf_head == ptr->obuf_tail) {
921 /* transmit queue empty, try to transmit directly */
922 if(1!=COMM16_WriteFile(ptr->handle, &chTransmit, 1))
924 /* didn't work, queue it */
925 ptr->xmit = chTransmit;
926 comm_waitwrite(ptr);
928 } else {
929 /* data in queue, let this char be transmitted next */
930 ptr->xmit = chTransmit;
931 comm_waitwrite(ptr);
934 ptr->commerror = 0;
935 return 0;
938 /*****************************************************************************
939 * UngetCommChar (USER.212)
941 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
943 struct DosDeviceStruct *ptr;
945 TRACE("cid %d (char %d)\n", cid, chUnget);
946 if ((ptr = GetDeviceStruct(cid)) == NULL) {
947 FIXME("no handle for cid = %0x!\n",cid);
948 return -1;
951 if (ptr->suspended) {
952 ptr->commerror = IE_HARDWARE;
953 return -1;
956 if (ptr->unget>=0) {
957 /* character already queued */
958 /* FIXME: which error would Windows return? */
959 ptr->commerror = CE_RXOVER;
960 return -1;
963 ptr->unget = chUnget;
965 ptr->commerror = 0;
966 return 0;
969 /*****************************************************************************
970 * ReadComm (USER.204)
972 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
974 int status, length;
975 struct DosDeviceStruct *ptr;
976 LPSTR orgBuf = lpvBuf;
978 TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
979 if ((ptr = GetDeviceStruct(cid)) == NULL) {
980 FIXME("no handle for cid = %0x!\n",cid);
981 return -1;
984 if (ptr->suspended) {
985 ptr->commerror = IE_HARDWARE;
986 return -1;
989 if(0==comm_inbuf(ptr))
990 SleepEx(1,TRUE);
992 /* read unget character */
993 if (ptr->unget>=0) {
994 *lpvBuf++ = ptr->unget;
995 ptr->unget = -1;
997 length = 1;
998 } else
999 length = 0;
1001 /* read from receive buffer */
1002 while (length < cbRead) {
1003 status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1004 ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1005 if (!status) break;
1006 if ((cbRead - length) < status)
1007 status = cbRead - length;
1009 memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1010 ptr->ibuf_tail += status;
1011 if (ptr->ibuf_tail >= ptr->ibuf_size)
1012 ptr->ibuf_tail = 0;
1013 lpvBuf += status;
1014 length += status;
1017 TRACE("%s\n", debugstr_an( orgBuf, length ));
1018 ptr->commerror = 0;
1019 return length;
1022 /*****************************************************************************
1023 * WriteComm (USER.205)
1025 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1027 int status, length;
1028 struct DosDeviceStruct *ptr;
1030 TRACE("cid %d, ptr %p, length %d\n",
1031 cid, lpvBuf, cbWrite);
1032 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1033 FIXME("no handle for cid = %0x!\n",cid);
1034 return -1;
1037 if (ptr->suspended) {
1038 ptr->commerror = IE_HARDWARE;
1039 return -1;
1042 TRACE("%s\n", debugstr_an( lpvBuf, cbWrite ));
1044 length = 0;
1045 while (length < cbWrite) {
1046 if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1047 /* no data queued, try to write directly */
1048 status = COMM16_WriteFile(ptr->handle, lpvBuf, cbWrite - length);
1049 if (status > 0) {
1050 lpvBuf += status;
1051 length += status;
1052 continue;
1055 /* can't write directly, put into transmit buffer */
1056 status = ((ptr->obuf_tail > ptr->obuf_head) ?
1057 (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1058 if (!status) break;
1059 if ((cbWrite - length) < status)
1060 status = cbWrite - length;
1061 memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1062 ptr->obuf_head += status;
1063 if (ptr->obuf_head >= ptr->obuf_size)
1064 ptr->obuf_head = 0;
1065 lpvBuf += status;
1066 length += status;
1067 comm_waitwrite(ptr);
1070 ptr->commerror = 0;
1071 return length;
1074 /***********************************************************************
1075 * EnableCommNotification (USER.245)
1077 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1078 INT16 cbWriteNotify, INT16 cbOutQueue )
1080 struct DosDeviceStruct *ptr;
1082 TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1083 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1084 FIXME("no handle for cid = %0x!\n",cid);
1085 return -1;
1087 ptr->wnd = WIN_Handle32( hwnd );
1088 ptr->n_read = cbWriteNotify;
1089 ptr->n_write = cbOutQueue;
1090 return TRUE;