- use Interlocked* functions in AddRef and Release.
[wine.git] / dlls / user / comm16.c
blob09e197cfdd238af92de477be5ba595ea59a11cdd
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"
54 #include "wine/port.h"
56 #include <stdlib.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <errno.h>
61 #include <ctype.h>
63 #include "ntstatus.h"
64 #include "windef.h"
65 #include "winbase.h"
66 #include "wingdi.h"
67 #include "wine/winuser16.h"
68 #include "win.h"
69 #include "user_private.h"
71 #include "wine/debug.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(comm);
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)
83 #define FLAG_LPT 0x80
85 #define MAX_PORTS 9
87 struct DosDeviceStruct {
88 HANDLE handle;
89 int suspended;
90 int unget,xmit;
91 int evtchar;
92 /* events */
93 int commerror, eventmask;
94 /* buffers */
95 char *inbuf,*outbuf;
96 unsigned ibuf_size,ibuf_head,ibuf_tail;
97 unsigned obuf_size,obuf_head,obuf_tail;
98 /* notifications */
99 HWND wnd;
100 int n_read, n_write;
101 OVERLAPPED read_ov, write_ov;
102 /* save terminal states */
103 DCB16 dcb;
104 /* pointer to unknown(==undocumented) comm structure */
105 SEGPTR seg_unknown;
106 BYTE unknown[40];
109 static struct DosDeviceStruct COM[MAX_PORTS];
110 static struct DosDeviceStruct LPT[MAX_PORTS];
112 /* update window's semi documented modem status register */
113 /* see knowledge base Q101417 */
114 static void COMM_MSRUpdate( HANDLE handle, UCHAR * pMsr )
116 UCHAR tmpmsr=0;
117 DWORD mstat=0;
119 if(!GetCommModemStatus(handle,&mstat))
120 return;
122 if(mstat & MS_CTS_ON) tmpmsr |= MSR_CTS;
123 if(mstat & MS_DSR_ON) tmpmsr |= MSR_DSR;
124 if(mstat & MS_RING_ON) tmpmsr |= MSR_RI;
125 if(mstat & MS_RLSD_ON) tmpmsr |= MSR_RLSD;
126 *pMsr = (*pMsr & ~MSR_MASK) | tmpmsr;
129 static struct DosDeviceStruct *GetDeviceStruct(int index)
131 if ((index&0x7F)<=MAX_PORTS) {
132 if (!(index&FLAG_LPT)) {
133 if (COM[index].handle)
134 return &COM[index];
135 } else {
136 index &= 0x7f;
137 if (LPT[index].handle)
138 return &LPT[index];
142 return NULL;
145 static int GetCommPort_ov(LPOVERLAPPED ov, int write)
147 int x;
149 for (x=0; x<MAX_PORTS; x++) {
150 if (ov == (write?&COM[x].write_ov:&COM[x].read_ov))
151 return x;
154 return -1;
157 static int WinError(void)
159 TRACE("errno = %d\n", errno);
160 switch (errno) {
161 default:
162 return CE_IOE;
166 static unsigned comm_inbuf(struct DosDeviceStruct *ptr)
168 return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
169 + ptr->ibuf_head - ptr->ibuf_tail;
172 static unsigned comm_outbuf(struct DosDeviceStruct *ptr)
174 return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
175 + ptr->obuf_head - ptr->obuf_tail;
178 static void comm_waitread(struct DosDeviceStruct *ptr);
179 static void comm_waitwrite(struct DosDeviceStruct *ptr);
181 static VOID WINAPI COMM16_ReadComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
183 int prev ;
184 WORD mask = 0;
185 int cid = GetCommPort_ov(ov,0);
186 struct DosDeviceStruct *ptr;
188 if(cid<0) {
189 ERR("async write with bad overlapped pointer\n");
190 return;
192 ptr = &COM[cid];
194 /* we get cancelled when CloseComm is called */
195 if (status==STATUS_CANCELLED)
197 TRACE("Cancelled\n");
198 return;
201 /* read data from comm port */
202 if (status != STATUS_SUCCESS) {
203 ERR("async read failed %08lx\n",status);
204 COM[cid].commerror = CE_RXOVER;
205 return;
207 TRACE("async read completed %ld bytes\n",len);
209 prev = comm_inbuf(ptr);
211 /* check for events */
212 if ((ptr->eventmask & EV_RXFLAG) &&
213 memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) {
214 *(WORD*)(COM[cid].unknown) |= EV_RXFLAG;
215 mask |= CN_EVENT;
217 if (ptr->eventmask & EV_RXCHAR) {
218 *(WORD*)(COM[cid].unknown) |= EV_RXCHAR;
219 mask |= CN_EVENT;
222 /* advance buffer position */
223 ptr->ibuf_head += len;
224 if (ptr->ibuf_head >= ptr->ibuf_size)
225 ptr->ibuf_head = 0;
227 /* check for notification */
228 if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
229 (comm_inbuf(ptr)>=ptr->n_read)) {
230 /* passed the receive notification threshold */
231 mask |= CN_RECEIVE;
234 /* send notifications, if any */
235 if (ptr->wnd && mask) {
236 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
237 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
240 /* on real windows, this could cause problems, since it is recursive */
241 /* restart the receive */
242 comm_waitread(ptr);
245 /* this is meant to work like write() */
246 static INT COMM16_WriteFile(HANDLE hComm, LPCVOID buffer, DWORD len)
248 OVERLAPPED ov;
249 DWORD count= -1;
251 ZeroMemory(&ov,sizeof(ov));
252 ov.hEvent = CreateEventW(NULL,0,0,NULL);
253 if(ov.hEvent==INVALID_HANDLE_VALUE)
254 return -1;
256 if(!WriteFile(hComm,buffer,len,&count,&ov))
258 if(GetLastError()==ERROR_IO_PENDING)
260 GetOverlappedResult(hComm,&ov,&count,TRUE);
263 CloseHandle(ov.hEvent);
265 return count;
268 static VOID WINAPI COMM16_WriteComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
270 int prev, bleft;
271 WORD mask = 0;
272 int cid = GetCommPort_ov(ov,1);
273 struct DosDeviceStruct *ptr;
275 if(cid<0) {
276 ERR("async write with bad overlapped pointer\n");
277 return;
279 ptr = &COM[cid];
281 /* read data from comm port */
282 if (status != STATUS_SUCCESS) {
283 ERR("async write failed\n");
284 COM[cid].commerror = CE_RXOVER;
285 return;
287 TRACE("async write completed %ld bytes\n",len);
289 /* update the buffer pointers */
290 prev = comm_outbuf(&COM[cid]);
291 ptr->obuf_tail += len;
292 if (ptr->obuf_tail >= ptr->obuf_size)
293 ptr->obuf_tail = 0;
295 /* write any TransmitCommChar character */
296 if (ptr->xmit>=0) {
297 len = COMM16_WriteFile(ptr->handle, &(ptr->xmit), 1);
298 if (len > 0) ptr->xmit = -1;
301 /* write from output queue */
302 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
303 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
305 /* check for notification */
306 if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
307 (comm_outbuf(ptr)<ptr->n_write)) {
308 /* passed the transmit notification threshold */
309 mask |= CN_TRANSMIT;
312 /* send notifications, if any */
313 if (ptr->wnd && mask) {
314 TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
315 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
318 /* start again if necessary */
319 if(bleft)
320 comm_waitwrite(ptr);
323 static void comm_waitread(struct DosDeviceStruct *ptr)
325 unsigned int bleft;
326 COMSTAT stat;
328 /* FIXME: get timeouts working properly so we can read bleft bytes */
329 bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ?
330 (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
332 /* find out how many bytes are left in the buffer */
333 if(ClearCommError(ptr->handle,NULL,&stat))
334 bleft = (bleft<stat.cbInQue) ? bleft : stat.cbInQue;
335 else
336 bleft = 1;
338 /* always read at least one byte */
339 if(bleft==0)
340 bleft++;
342 ReadFileEx(ptr->handle,
343 ptr->inbuf + ptr->ibuf_head,
344 bleft,
345 &ptr->read_ov,
346 COMM16_ReadComplete);
349 static void comm_waitwrite(struct DosDeviceStruct *ptr)
351 int bleft;
353 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
354 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
355 WriteFileEx(ptr->handle,
356 ptr->outbuf + ptr->obuf_tail,
357 bleft,
358 &ptr->write_ov,
359 COMM16_WriteComplete);
362 /*****************************************************************************
363 * COMM16_DCBtoDCB16 (Internal)
365 INT16 COMM16_DCBtoDCB16(LPDCB lpdcb, LPDCB16 lpdcb16)
367 if(lpdcb->BaudRate<0x10000)
368 lpdcb16->BaudRate = lpdcb->BaudRate;
369 else if(lpdcb->BaudRate==115200)
370 lpdcb16->BaudRate = 57601;
371 else {
372 WARN("Baud rate can't be converted\n");
373 lpdcb16->BaudRate = 57601;
375 lpdcb16->ByteSize = lpdcb->ByteSize;
376 lpdcb16->fParity = lpdcb->fParity;
377 lpdcb16->Parity = lpdcb->Parity;
378 lpdcb16->StopBits = lpdcb->StopBits;
380 lpdcb16->RlsTimeout = 50;
381 lpdcb16->CtsTimeout = 50;
382 lpdcb16->DsrTimeout = 50;
383 lpdcb16->fNull = 0;
384 lpdcb16->fChEvt = 0;
385 lpdcb16->fBinary = 1;
387 lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_HANDSHAKE);
388 lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_HANDSHAKE);
389 lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow;
390 lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow;
391 lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE);
393 lpdcb16->fInX = lpdcb->fInX;
395 lpdcb16->fOutX = lpdcb->fOutX;
397 lpdcb16->XonChar =
398 lpdcb16->XoffChar =
400 lpdcb16->XonLim = 10;
401 lpdcb16->XoffLim = 10;
403 return 0;
407 /**************************************************************************
408 * BuildCommDCB (USER.213)
410 * According to the ECMA-234 (368.3) the function will return FALSE on
411 * success, otherwise it will return -1.
413 INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
415 /* "COM1:96,n,8,1" */
416 /* 012345 */
417 int port;
418 DCB dcb;
420 TRACE("(%s), ptr %p\n", device, lpdcb);
422 if (strncasecmp(device,"COM",3))
423 return -1;
424 port = device[3] - '0';
426 if (port-- == 0) {
427 ERR("BUG ! COM0 can't exist!\n");
428 return -1;
431 memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
433 lpdcb->Id = port;
434 dcb.DCBlength = sizeof(DCB);
436 if (strchr(device,'=')) /* block new style */
437 return -1;
439 if(!BuildCommDCBA(device,&dcb))
440 return -1;
442 return COMM16_DCBtoDCB16(&dcb, lpdcb);
445 /*****************************************************************************
446 * OpenComm (USER.200)
448 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
450 int port;
451 HANDLE handle;
453 TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue);
455 if (strlen(device) < 4)
456 return IE_BADID;
458 port = device[3] - '0';
460 if (port-- == 0)
461 ERR("BUG ! COM0 or LPT0 don't exist !\n");
463 if (!strncasecmp(device,"COM",3))
465 if (COM[port].handle)
466 return IE_OPEN;
468 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
469 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
470 FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, 0 );
471 if (handle == INVALID_HANDLE_VALUE) {
472 return IE_HARDWARE;
473 } else {
474 memset(COM[port].unknown, 0, sizeof(COM[port].unknown));
475 COM[port].seg_unknown = 0;
476 COM[port].handle = handle;
477 COM[port].commerror = 0;
478 COM[port].eventmask = 0;
479 COM[port].evtchar = 0; /* FIXME: default? */
480 /* save terminal state */
481 GetCommState16(port,&COM[port].dcb);
482 /* init priority characters */
483 COM[port].unget = -1;
484 COM[port].xmit = -1;
485 /* allocate buffers */
486 COM[port].ibuf_size = cbInQueue;
487 COM[port].ibuf_head = COM[port].ibuf_tail = 0;
488 COM[port].obuf_size = cbOutQueue;
489 COM[port].obuf_head = COM[port].obuf_tail = 0;
491 COM[port].inbuf = HeapAlloc(GetProcessHeap(), 0, cbInQueue);
492 if (COM[port].inbuf) {
493 COM[port].outbuf = HeapAlloc( GetProcessHeap(), 0, cbOutQueue);
494 if (!COM[port].outbuf)
495 HeapFree( GetProcessHeap(), 0, COM[port].inbuf);
496 } else COM[port].outbuf = NULL;
497 if (!COM[port].outbuf) {
498 /* not enough memory */
499 CloseHandle(COM[port].handle);
500 ERR("out of memory\n");
501 return IE_MEMORY;
504 ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED));
505 ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED));
507 comm_waitread( &COM[port] );
508 USER16_AlertableWait++;
510 return port;
513 else
514 if (!strncasecmp(device,"LPT",3)) {
516 if (LPT[port].handle)
517 return IE_OPEN;
519 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
520 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
521 if (handle == INVALID_HANDLE_VALUE) {
522 return IE_HARDWARE;
523 } else {
524 LPT[port].handle = handle;
525 LPT[port].commerror = 0;
526 LPT[port].eventmask = 0;
527 return port|FLAG_LPT;
530 return IE_BADID;
533 /*****************************************************************************
534 * CloseComm (USER.207)
536 INT16 WINAPI CloseComm16(INT16 cid)
538 struct DosDeviceStruct *ptr;
540 TRACE("cid=%d\n", cid);
541 if ((ptr = GetDeviceStruct(cid)) == NULL) {
542 FIXME("no cid=%d found!\n", cid);
543 return -1;
545 if (!(cid&FLAG_LPT)) {
546 /* COM port */
547 UnMapLS( COM[cid].seg_unknown );
548 USER16_AlertableWait--;
549 CancelIo(ptr->handle);
551 /* free buffers */
552 HeapFree( GetProcessHeap(), 0, ptr->outbuf);
553 HeapFree( GetProcessHeap(), 0, ptr->inbuf);
555 /* reset modem lines */
556 SetCommState16(&COM[cid].dcb);
559 if (!CloseHandle(ptr->handle)) {
560 ptr->commerror = WinError();
561 /* FIXME: should we clear ptr->handle here? */
562 return -1;
563 } else {
564 ptr->commerror = 0;
565 ptr->handle = 0;
566 return 0;
570 /*****************************************************************************
571 * SetCommBreak (USER.210)
573 INT16 WINAPI SetCommBreak16(INT16 cid)
575 struct DosDeviceStruct *ptr;
577 TRACE("cid=%d\n", cid);
578 if ((ptr = GetDeviceStruct(cid)) == NULL) {
579 FIXME("no cid=%d found!\n", cid);
580 return -1;
583 ptr->suspended = 1;
584 ptr->commerror = 0;
585 return 0;
588 /*****************************************************************************
589 * ClearCommBreak (USER.211)
591 INT16 WINAPI ClearCommBreak16(INT16 cid)
593 struct DosDeviceStruct *ptr;
595 TRACE("cid=%d\n", cid);
596 if (!(ptr = GetDeviceStruct(cid))) {
597 FIXME("no cid=%d found!\n", cid);
598 return -1;
600 ptr->suspended = 0;
601 ptr->commerror = 0;
602 return 0;
605 /*****************************************************************************
606 * EscapeCommFunction (USER.214)
608 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
610 struct DosDeviceStruct *ptr;
612 TRACE("cid=%d, function=%d\n", cid, nFunction);
614 switch(nFunction) {
615 case GETMAXCOM:
616 TRACE("GETMAXCOM\n");
617 return 4; /* FIXME */
619 case GETMAXLPT:
620 TRACE("GETMAXLPT\n");
621 return FLAG_LPT + 3; /* FIXME */
623 case GETBASEIRQ:
624 TRACE("GETBASEIRQ\n");
625 /* FIXME: use tables */
626 /* just fake something for now */
627 if (cid & FLAG_LPT) {
628 /* LPT1: irq 7, LPT2: irq 5 */
629 return (cid & 0x7f) ? 5 : 7;
630 } else {
631 /* COM1: irq 4, COM2: irq 3,
632 COM3: irq 4, COM4: irq 3 */
633 return 4 - (cid & 1);
637 if ((ptr = GetDeviceStruct(cid)) == NULL) {
638 FIXME("no cid=%d found!\n", cid);
639 return -1;
642 switch (nFunction) {
643 case RESETDEV:
644 case CLRDTR:
645 case CLRRTS:
646 case SETDTR:
647 case SETRTS:
648 case SETXOFF:
649 case SETXON:
650 if(EscapeCommFunction(ptr->handle,nFunction))
651 return 0;
652 else {
653 ptr->commerror = WinError();
654 return -1;
657 case CLRBREAK:
658 case SETBREAK:
659 default:
660 WARN("(cid=%d,nFunction=%d): Unknown function\n",
661 cid, nFunction);
663 return -1;
666 /*****************************************************************************
667 * FlushComm (USER.215)
669 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
671 DWORD queue;
672 struct DosDeviceStruct *ptr;
674 TRACE("cid=%d, queue=%d\n", cid, fnQueue);
675 if ((ptr = GetDeviceStruct(cid)) == NULL) {
676 FIXME("no cid=%d found!\n", cid);
677 return -1;
679 switch (fnQueue) {
680 case 0:
681 queue = PURGE_TXABORT;
682 ptr->obuf_tail = ptr->obuf_head;
683 break;
684 case 1:
685 queue = PURGE_RXABORT;
686 ptr->ibuf_head = ptr->ibuf_tail;
687 break;
688 default:
689 WARN("(cid=%d,fnQueue=%d):Unknown queue\n",
690 cid, fnQueue);
691 return -1;
694 if (!PurgeComm(ptr->handle,queue)) {
695 ptr->commerror = WinError();
696 return -1;
697 } else {
698 ptr->commerror = 0;
699 return 0;
703 /********************************************************************
704 * GetCommError (USER.203)
706 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
708 int temperror;
709 struct DosDeviceStruct *ptr;
710 unsigned char *stol;
712 if ((ptr = GetDeviceStruct(cid)) == NULL) {
713 FIXME("no handle for cid = %0x!\n",cid);
714 return -1;
716 if (cid&FLAG_LPT) {
717 WARN(" cid %d not comm port\n",cid);
718 return CE_MODE;
720 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
721 COMM_MSRUpdate( ptr->handle, stol );
723 if (lpStat) {
724 lpStat->status = 0;
726 SleepEx(1,TRUE);
728 lpStat->cbOutQue = comm_outbuf(ptr);
729 lpStat->cbInQue = comm_inbuf(ptr);
731 TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n",
732 cid, ptr->commerror, lpStat->status, lpStat->cbInQue,
733 lpStat->cbOutQue, *stol);
735 else
736 TRACE("cid %d, error %d, lpStat NULL stol %x\n",
737 cid, ptr->commerror, *stol);
739 /* Return any errors and clear it */
740 temperror = ptr->commerror;
741 ptr->commerror = 0;
742 return(temperror);
745 /*****************************************************************************
746 * SetCommEventMask (USER.208)
748 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
750 struct DosDeviceStruct *ptr;
751 unsigned char *stol;
753 TRACE("cid %d,mask %d\n",cid,fuEvtMask);
754 if ((ptr = GetDeviceStruct(cid)) == NULL) {
755 FIXME("no handle for cid = %0x!\n",cid);
756 return (SEGPTR)NULL;
759 ptr->eventmask = fuEvtMask;
761 if (cid&FLAG_LPT) {
762 WARN(" cid %d not comm port\n",cid);
763 return (SEGPTR)NULL;
765 /* it's a COM port ? -> modify flags */
766 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
767 COMM_MSRUpdate( ptr->handle, stol );
769 TRACE(" modem dcd construct %x\n",*stol);
770 if (!COM[cid].seg_unknown) COM[cid].seg_unknown = MapLS( COM[cid].unknown );
771 return COM[cid].seg_unknown;
774 /*****************************************************************************
775 * GetCommEventMask (USER.209)
777 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
779 struct DosDeviceStruct *ptr;
780 WORD events;
782 TRACE("cid %d, mask %d\n", cid, fnEvtClear);
783 if ((ptr = GetDeviceStruct(cid)) == NULL) {
784 FIXME("no handle for cid = %0x!\n",cid);
785 return 0;
788 if (cid&FLAG_LPT) {
789 WARN(" cid %d not comm port\n",cid);
790 return 0;
793 events = *(WORD*)(COM[cid].unknown) & fnEvtClear;
794 *(WORD*)(COM[cid].unknown) &= ~fnEvtClear;
795 return events;
798 /*****************************************************************************
799 * SetCommState (USER.201)
801 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
803 struct DosDeviceStruct *ptr;
804 DCB dcb;
806 TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb);
807 if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
808 FIXME("no handle for cid = %0x!\n",lpdcb->Id);
809 return -1;
812 memset(&dcb,0,sizeof(dcb));
813 dcb.DCBlength = sizeof(dcb);
816 * according to MSDN, we should first interpret lpdcb->BaudRate as follows:
817 * 1. if the baud rate is a CBR constant, interpret it.
818 * 2. if it is greater than 57600, the baud rate is 115200
819 * 3. use the actual baudrate
820 * steps 2 and 3 are equivilent to 16550 baudrate divisor = 115200/BaudRate
821 * see http://support.microsoft.com/support/kb/articles/q108/9/28.asp
823 switch(lpdcb->BaudRate)
825 case CBR_110: dcb.BaudRate = 110; break;
826 case CBR_300: dcb.BaudRate = 300; break;
827 case CBR_600: dcb.BaudRate = 600; break;
828 case CBR_1200: dcb.BaudRate = 1200; break;
829 case CBR_2400: dcb.BaudRate = 2400; break;
830 case CBR_4800: dcb.BaudRate = 4800; break;
831 case CBR_9600: dcb.BaudRate = 9600; break;
832 case CBR_14400: dcb.BaudRate = 14400; break;
833 case CBR_19200: dcb.BaudRate = 19200; break;
834 case CBR_38400: dcb.BaudRate = 38400; break;
835 case CBR_56000: dcb.BaudRate = 56000; break;
836 case CBR_128000: dcb.BaudRate = 128000; break;
837 case CBR_256000: dcb.BaudRate = 256000; break;
838 default:
839 if(lpdcb->BaudRate>57600)
840 dcb.BaudRate = 115200;
841 else
842 dcb.BaudRate = lpdcb->BaudRate;
845 dcb.ByteSize=lpdcb->ByteSize;
846 dcb.StopBits=lpdcb->StopBits;
848 dcb.fParity=lpdcb->fParity;
849 dcb.Parity=lpdcb->Parity;
851 dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow;
853 if (lpdcb->fDtrflow || lpdcb->fRtsflow)
854 dcb.fRtsControl = TRUE;
856 if (lpdcb->fDtrDisable)
857 dcb.fDtrControl = TRUE;
859 ptr->evtchar = lpdcb->EvtChar;
861 dcb.fInX = lpdcb->fInX;
862 dcb.fOutX = lpdcb->fOutX;
864 if (!SetCommState(ptr->handle,&dcb)) {
865 ptr->commerror = WinError();
866 return -1;
867 } else {
868 ptr->commerror = 0;
869 return 0;
873 /*****************************************************************************
874 * GetCommState (USER.202)
876 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
878 struct DosDeviceStruct *ptr;
879 DCB dcb;
881 TRACE("cid %d, ptr %p\n", cid, lpdcb);
882 if ((ptr = GetDeviceStruct(cid)) == NULL) {
883 FIXME("no handle for cid = %0x!\n",cid);
884 return -1;
886 if (!GetCommState(ptr->handle,&dcb)) {
887 ptr->commerror = WinError();
888 return -1;
891 lpdcb->Id = cid;
893 COMM16_DCBtoDCB16(&dcb,lpdcb);
895 lpdcb->EvtChar = ptr->evtchar;
897 ptr->commerror = 0;
898 return 0;
901 /*****************************************************************************
902 * TransmitCommChar (USER.206)
904 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
906 struct DosDeviceStruct *ptr;
908 TRACE("cid %d, data %d \n", cid, chTransmit);
909 if ((ptr = GetDeviceStruct(cid)) == NULL) {
910 FIXME("no handle for cid = %0x!\n",cid);
911 return -1;
914 if (ptr->suspended) {
915 ptr->commerror = IE_HARDWARE;
916 return -1;
919 if (ptr->xmit >= 0) {
920 /* character already queued */
921 /* FIXME: which error would Windows return? */
922 ptr->commerror = CE_TXFULL;
923 return -1;
926 if (ptr->obuf_head == ptr->obuf_tail) {
927 /* transmit queue empty, try to transmit directly */
928 if(1!=COMM16_WriteFile(ptr->handle, &chTransmit, 1))
930 /* didn't work, queue it */
931 ptr->xmit = chTransmit;
932 comm_waitwrite(ptr);
934 } else {
935 /* data in queue, let this char be transmitted next */
936 ptr->xmit = chTransmit;
937 comm_waitwrite(ptr);
940 ptr->commerror = 0;
941 return 0;
944 /*****************************************************************************
945 * UngetCommChar (USER.212)
947 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
949 struct DosDeviceStruct *ptr;
951 TRACE("cid %d (char %d)\n", cid, chUnget);
952 if ((ptr = GetDeviceStruct(cid)) == NULL) {
953 FIXME("no handle for cid = %0x!\n",cid);
954 return -1;
957 if (ptr->suspended) {
958 ptr->commerror = IE_HARDWARE;
959 return -1;
962 if (ptr->unget>=0) {
963 /* character already queued */
964 /* FIXME: which error would Windows return? */
965 ptr->commerror = CE_RXOVER;
966 return -1;
969 ptr->unget = chUnget;
971 ptr->commerror = 0;
972 return 0;
975 /*****************************************************************************
976 * ReadComm (USER.204)
978 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
980 int status, length;
981 struct DosDeviceStruct *ptr;
982 LPSTR orgBuf = lpvBuf;
984 TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
985 if ((ptr = GetDeviceStruct(cid)) == NULL) {
986 FIXME("no handle for cid = %0x!\n",cid);
987 return -1;
990 if (ptr->suspended) {
991 ptr->commerror = IE_HARDWARE;
992 return -1;
995 if(0==comm_inbuf(ptr))
996 SleepEx(1,TRUE);
998 /* read unget character */
999 if (ptr->unget>=0) {
1000 *lpvBuf++ = ptr->unget;
1001 ptr->unget = -1;
1003 length = 1;
1004 } else
1005 length = 0;
1007 /* read from receive buffer */
1008 while (length < cbRead) {
1009 status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1010 ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1011 if (!status) break;
1012 if ((cbRead - length) < status)
1013 status = cbRead - length;
1015 memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1016 ptr->ibuf_tail += status;
1017 if (ptr->ibuf_tail >= ptr->ibuf_size)
1018 ptr->ibuf_tail = 0;
1019 lpvBuf += status;
1020 length += status;
1023 TRACE("%s\n", debugstr_an( orgBuf, length ));
1024 ptr->commerror = 0;
1025 return length;
1028 /*****************************************************************************
1029 * WriteComm (USER.205)
1031 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1033 int status, length;
1034 struct DosDeviceStruct *ptr;
1036 TRACE("cid %d, ptr %p, length %d\n",
1037 cid, lpvBuf, cbWrite);
1038 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1039 FIXME("no handle for cid = %0x!\n",cid);
1040 return -1;
1043 if (ptr->suspended) {
1044 ptr->commerror = IE_HARDWARE;
1045 return -1;
1048 TRACE("%s\n", debugstr_an( lpvBuf, cbWrite ));
1050 length = 0;
1051 while (length < cbWrite) {
1052 if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1053 /* no data queued, try to write directly */
1054 status = COMM16_WriteFile(ptr->handle, lpvBuf, cbWrite - length);
1055 if (status > 0) {
1056 lpvBuf += status;
1057 length += status;
1058 continue;
1061 /* can't write directly, put into transmit buffer */
1062 status = ((ptr->obuf_tail > ptr->obuf_head) ?
1063 (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1064 if (!status) break;
1065 if ((cbWrite - length) < status)
1066 status = cbWrite - length;
1067 memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1068 ptr->obuf_head += status;
1069 if (ptr->obuf_head >= ptr->obuf_size)
1070 ptr->obuf_head = 0;
1071 lpvBuf += status;
1072 length += status;
1073 comm_waitwrite(ptr);
1076 ptr->commerror = 0;
1077 return length;
1080 /***********************************************************************
1081 * EnableCommNotification (USER.245)
1083 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1084 INT16 cbWriteNotify, INT16 cbOutQueue )
1086 struct DosDeviceStruct *ptr;
1088 TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1089 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1090 FIXME("no handle for cid = %0x!\n",cid);
1091 return -1;
1093 ptr->wnd = WIN_Handle32( hwnd );
1094 ptr->n_read = cbWriteNotify;
1095 ptr->n_write = cbOutQueue;
1096 return TRUE;