Added LGPL standard comment, and copyright notices where necessary.
[wine/multimedia.git] / dlls / user / comm16.c
blobe306aee439c5ec7a92fe87edda90fe4b9c27f4e4
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 <stdio.h>
57 #include <string.h>
58 #include <errno.h>
59 #include <ctype.h>
61 #include "windef.h"
62 #include "winbase.h"
63 #include "wingdi.h"
64 #include "winreg.h"
65 #include "winuser.h"
66 #include "wine/winuser16.h"
67 #include "wine/port.h"
68 #include "win.h"
69 #include "winerror.h"
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 char *devicename; /* /dev/ttyS0 */
89 HANDLE handle;
90 int suspended;
91 int unget,xmit;
92 int baudrate;
93 int evtchar;
94 /* events */
95 int commerror, eventmask;
96 /* buffers */
97 char *inbuf,*outbuf;
98 unsigned ibuf_size,ibuf_head,ibuf_tail;
99 unsigned obuf_size,obuf_head,obuf_tail;
100 /* notifications */
101 HWND wnd;
102 int n_read, n_write;
103 OVERLAPPED read_ov, write_ov;
104 /* save terminal states */
105 DCB16 dcb;
106 /* pointer to unknown(==undocumented) comm structure */
107 SEGPTR seg_unknown;
108 BYTE unknown[40];
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 void COMM_Init(void)
133 int x;
134 char option[10], temp[256], *btemp;
135 HKEY hkey;
137 for (x=0; x!=MAX_PORTS; x++) {
138 strcpy(option,"COMx");
139 option[3] = '1' + x;
140 option[4] = '\0';
142 /* default value */
143 strcpy(temp, "*");
145 if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\serialports", &hkey))
147 DWORD type, count = sizeof(temp);
149 RegQueryValueExA(hkey, option, 0, &type, temp, &count);
150 RegCloseKey(hkey);
153 if (!strcmp(temp, "*") || *temp == '\0')
154 COM[x].devicename = NULL;
155 else {
156 btemp = strchr(temp,',');
157 if (btemp != NULL) {
158 *btemp++ = '\0';
159 COM[x].baudrate = atoi(btemp);
160 } else {
161 COM[x].baudrate = -1;
163 if ((COM[x].devicename = malloc(strlen(temp)+1)) == NULL)
164 WARN("Can't malloc for device info!\n");
165 else {
166 COM[x].handle = 0;
167 strcpy(COM[x].devicename, temp);
168 TRACE("%s = %s\n", option, COM[x].devicename);
172 strcpy(option, "LPTx");
173 option[3] = '1' + x;
174 option[4] = '\0';
176 /* default value */
177 strcpy(temp, "*");
179 if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\parallelports", &hkey))
181 DWORD type, count = sizeof(temp);
183 RegQueryValueExA(hkey, option, 0, &type, temp, &count);
184 RegCloseKey(hkey);
187 if (!strcmp(temp, "*") || *temp == '\0')
188 LPT[x].devicename = NULL;
189 else {
190 if ((LPT[x].devicename = malloc(strlen(temp)+1)) == NULL)
191 WARN("Can't malloc for device info!\n");
192 else {
193 LPT[x].handle = 0;
194 strcpy(LPT[x].devicename, temp);
195 TRACE("%s = %s\n", option, LPT[x].devicename);
203 static struct DosDeviceStruct *GetDeviceStruct(int index)
205 if ((index&0x7F)<=MAX_PORTS) {
206 if (!(index&FLAG_LPT)) {
207 if (COM[index].handle)
208 return &COM[index];
209 } else {
210 index &= 0x7f;
211 if (LPT[index].handle)
212 return &LPT[index];
216 return NULL;
219 static int GetCommPort_ov(LPOVERLAPPED ov, int write)
221 int x;
223 for (x=0; x<MAX_PORTS; x++) {
224 if (ov == (write?&COM[x].write_ov:&COM[x].read_ov))
225 return x;
228 return -1;
231 static int ValidCOMPort(int x)
233 return(x < MAX_PORTS ? (int) COM[x].devicename : 0);
236 static int ValidLPTPort(int x)
238 return(x < MAX_PORTS ? (int) LPT[x].devicename : 0);
241 static int WinError(void)
243 TRACE("errno = %d\n", errno);
244 switch (errno) {
245 default:
246 return CE_IOE;
250 static unsigned comm_inbuf(struct DosDeviceStruct *ptr)
252 return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
253 + ptr->ibuf_head - ptr->ibuf_tail;
256 static unsigned comm_outbuf(struct DosDeviceStruct *ptr)
258 return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
259 + ptr->obuf_head - ptr->obuf_tail;
262 static void comm_waitread(struct DosDeviceStruct *ptr);
263 static void comm_waitwrite(struct DosDeviceStruct *ptr);
265 static VOID WINAPI COMM16_ReadComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
267 int prev ;
268 WORD mask = 0;
269 int cid = GetCommPort_ov(ov,0);
270 struct DosDeviceStruct *ptr;
272 if(cid<0) {
273 ERR("async write with bad overlapped pointer\n");
274 return;
276 ptr = &COM[cid];
278 /* we get cancelled when CloseComm is called */
279 if (status==STATUS_CANCELLED)
281 TRACE("Cancelled\n");
282 return;
285 /* read data from comm port */
286 if (status != STATUS_SUCCESS) {
287 ERR("async read failed %08lx\n",status);
288 COM[cid].commerror = CE_RXOVER;
289 return;
291 TRACE("async read completed %ld bytes\n",len);
293 prev = comm_inbuf(ptr);
295 /* check for events */
296 if ((ptr->eventmask & EV_RXFLAG) &&
297 memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) {
298 *(WORD*)(COM[cid].unknown) |= EV_RXFLAG;
299 mask |= CN_EVENT;
301 if (ptr->eventmask & EV_RXCHAR) {
302 *(WORD*)(COM[cid].unknown) |= EV_RXCHAR;
303 mask |= CN_EVENT;
306 /* advance buffer position */
307 ptr->ibuf_head += len;
308 if (ptr->ibuf_head >= ptr->ibuf_size)
309 ptr->ibuf_head = 0;
311 /* check for notification */
312 if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
313 (comm_inbuf(ptr)>=ptr->n_read)) {
314 /* passed the receive notification threshold */
315 mask |= CN_RECEIVE;
318 /* send notifications, if any */
319 if (ptr->wnd && mask) {
320 TRACE("notifying %04x: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
321 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
324 /* on real windows, this could cause problems, since it is recursive */
325 /* restart the receive */
326 comm_waitread(ptr);
329 /* this is meant to work like write() */
330 static INT COMM16_WriteFile(HANDLE hComm, LPCVOID buffer, DWORD len)
332 OVERLAPPED ov;
333 DWORD count= -1;
335 ZeroMemory(&ov,sizeof ov);
336 ov.hEvent = CreateEventA(NULL,0,0,NULL);
337 if(ov.hEvent==INVALID_HANDLE_VALUE)
338 return -1;
340 if(!WriteFile(hComm,buffer,len,&count,&ov))
342 if(GetLastError()==ERROR_IO_PENDING)
344 GetOverlappedResult(hComm,&ov,&count,TRUE);
347 CloseHandle(ov.hEvent);
349 return count;
352 static VOID WINAPI COMM16_WriteComplete(DWORD status, DWORD len, LPOVERLAPPED ov)
354 int prev, bleft;
355 WORD mask = 0;
356 int cid = GetCommPort_ov(ov,1);
357 struct DosDeviceStruct *ptr;
359 if(cid<0) {
360 ERR("async write with bad overlapped pointer\n");
361 return;
363 ptr = &COM[cid];
365 /* read data from comm port */
366 if (status != STATUS_SUCCESS) {
367 ERR("async write failed\n");
368 COM[cid].commerror = CE_RXOVER;
369 return;
371 TRACE("async write completed %ld bytes\n",len);
373 /* update the buffer pointers */
374 prev = comm_outbuf(&COM[cid]);
375 ptr->obuf_tail += len;
376 if (ptr->obuf_tail >= ptr->obuf_size)
377 ptr->obuf_tail = 0;
379 /* write any TransmitCommChar character */
380 if (ptr->xmit>=0) {
381 len = COMM16_WriteFile(ptr->handle, &(ptr->xmit), 1);
382 if (len > 0) ptr->xmit = -1;
385 /* write from output queue */
386 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
387 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
389 /* check for notification */
390 if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
391 (comm_outbuf(ptr)<ptr->n_write)) {
392 /* passed the transmit notification threshold */
393 mask |= CN_TRANSMIT;
396 /* send notifications, if any */
397 if (ptr->wnd && mask) {
398 TRACE("notifying %04x: cid=%d, mask=%02x\n", ptr->wnd, cid, mask);
399 PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask);
402 /* start again if necessary */
403 if(bleft)
404 comm_waitwrite(ptr);
407 static void comm_waitread(struct DosDeviceStruct *ptr)
409 int bleft;
410 COMSTAT stat;
412 /* FIXME: get timeouts working properly so we can read bleft bytes */
413 bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ?
414 (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
416 /* find out how many bytes are left in the buffer */
417 if(ClearCommError(ptr->handle,NULL,&stat))
418 bleft = (bleft<stat.cbInQue) ? bleft : stat.cbInQue;
419 else
420 bleft = 1;
422 /* always read at least one byte */
423 if(bleft==0)
424 bleft++;
426 ReadFileEx(ptr->handle,
427 ptr->inbuf + ptr->ibuf_head,
428 bleft,
429 &ptr->read_ov,
430 COMM16_ReadComplete);
433 static void comm_waitwrite(struct DosDeviceStruct *ptr)
435 int bleft;
437 bleft = ((ptr->obuf_tail <= ptr->obuf_head) ?
438 ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail;
439 WriteFileEx(ptr->handle,
440 ptr->outbuf + ptr->obuf_tail,
441 bleft,
442 &ptr->write_ov,
443 COMM16_WriteComplete);
446 /*****************************************************************************
447 * COMM16_DCBtoDCB16 (Internal)
449 INT16 COMM16_DCBtoDCB16(LPDCB lpdcb, LPDCB16 lpdcb16)
451 if(lpdcb->BaudRate<0x10000)
452 lpdcb16->BaudRate = lpdcb->BaudRate;
453 else if(lpdcb->BaudRate==115200)
454 lpdcb16->BaudRate = 57601;
455 else {
456 WARN("Baud rate can't be converted\n");
457 lpdcb16->BaudRate = 57601;
459 lpdcb16->ByteSize = lpdcb->ByteSize;
460 lpdcb16->fParity = lpdcb->fParity;
461 lpdcb16->Parity = lpdcb->Parity;
462 lpdcb16->StopBits = lpdcb->StopBits;
464 lpdcb16->RlsTimeout = 50;
465 lpdcb16->CtsTimeout = 50;
466 lpdcb16->DsrTimeout = 50;
467 lpdcb16->fNull = 0;
468 lpdcb16->fChEvt = 0;
469 lpdcb16->fBinary = 1;
471 lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_HANDSHAKE);
472 lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_HANDSHAKE);
473 lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow;
474 lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow;
475 lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE);
477 lpdcb16->fInX = lpdcb->fInX;
479 lpdcb16->fOutX = lpdcb->fOutX;
481 lpdcb16->XonChar =
482 lpdcb16->XoffChar =
484 lpdcb16->XonLim = 10;
485 lpdcb16->XoffLim = 10;
487 return 0;
491 /**************************************************************************
492 * BuildCommDCB (USER.213)
494 * According to the ECMA-234 (368.3) the function will return FALSE on
495 * success, otherwise it will return -1.
497 INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
499 /* "COM1:96,n,8,1" */
500 /* 012345 */
501 int port;
502 DCB dcb;
504 TRACE("(%s), ptr %p\n", device, lpdcb);
506 if (strncasecmp(device,"COM",3))
507 return -1;
508 port = device[3] - '0';
510 if (port-- == 0) {
511 ERR("BUG ! COM0 can't exist!\n");
512 return -1;
515 if (!ValidCOMPort(port)) {
516 FIXME("invalid COM port %d?\n",port);
517 return -1;
520 memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
522 lpdcb->Id = port;
523 dcb.DCBlength = sizeof(DCB);
525 if (strchr(device,'=')) /* block new style */
526 return -1;
528 if(!BuildCommDCBA(device,&dcb))
529 return -1;
531 return COMM16_DCBtoDCB16(&dcb, lpdcb);
534 /*****************************************************************************
535 * OpenComm (USER.200)
537 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
539 int port;
540 HANDLE handle;
542 TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue);
544 if (strlen(device) < 4)
545 return IE_BADID;
547 port = device[3] - '0';
549 if (port-- == 0)
550 ERR("BUG ! COM0 or LPT0 don't exist !\n");
552 if (!strncasecmp(device,"COM",3)) {
554 TRACE("%s = %s\n", device, COM[port].devicename);
556 if (!ValidCOMPort(port))
557 return IE_BADID;
559 if (COM[port].handle)
560 return IE_OPEN;
562 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
563 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
564 FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, 0 );
565 if (handle == INVALID_HANDLE_VALUE) {
566 ERR("Couldn't open %s ! (%s)\n", COM[port].devicename, strerror(errno));
567 return IE_HARDWARE;
568 } else {
569 memset(COM[port].unknown, 0, sizeof(COM[port].unknown));
570 COM[port].seg_unknown = 0;
571 COM[port].handle = handle;
572 COM[port].commerror = 0;
573 COM[port].eventmask = 0;
574 COM[port].evtchar = 0; /* FIXME: default? */
575 /* save terminal state */
576 GetCommState16(port,&COM[port].dcb);
577 /* set default parameters */
578 if(COM[port].baudrate>-1){
579 DCB16 dcb;
580 memcpy(&dcb,&COM[port].dcb,sizeof dcb);
581 dcb.BaudRate=COM[port].baudrate;
582 /* more defaults:
583 * databits, parity, stopbits
585 SetCommState16( &dcb);
587 /* init priority characters */
588 COM[port].unget = -1;
589 COM[port].xmit = -1;
590 /* allocate buffers */
591 COM[port].ibuf_size = cbInQueue;
592 COM[port].ibuf_head = COM[port].ibuf_tail = 0;
593 COM[port].obuf_size = cbOutQueue;
594 COM[port].obuf_head = COM[port].obuf_tail = 0;
596 COM[port].inbuf = malloc(cbInQueue);
597 if (COM[port].inbuf) {
598 COM[port].outbuf = malloc(cbOutQueue);
599 if (!COM[port].outbuf)
600 free(COM[port].inbuf);
601 } else COM[port].outbuf = NULL;
602 if (!COM[port].outbuf) {
603 /* not enough memory */
604 SetCommState16(&COM[port].dcb);
605 CloseHandle(COM[port].handle);
606 ERR("out of memory\n");
607 return IE_MEMORY;
610 ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED));
611 ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED));
613 comm_waitread( &COM[port] );
614 USER16_AlertableWait++;
616 return port;
619 else
620 if (!strncasecmp(device,"LPT",3)) {
622 if (!ValidLPTPort(port))
623 return IE_BADID;
625 if (LPT[port].handle)
626 return IE_OPEN;
628 handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE,
629 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
630 if (handle == INVALID_HANDLE_VALUE) {
631 return IE_HARDWARE;
632 } else {
633 LPT[port].handle = handle;
634 LPT[port].commerror = 0;
635 LPT[port].eventmask = 0;
636 return port|FLAG_LPT;
639 return IE_BADID;
642 /*****************************************************************************
643 * CloseComm (USER.207)
645 INT16 WINAPI CloseComm16(INT16 cid)
647 struct DosDeviceStruct *ptr;
649 TRACE("cid=%d\n", cid);
650 if ((ptr = GetDeviceStruct(cid)) == NULL) {
651 FIXME("no cid=%d found!\n", cid);
652 return -1;
654 if (!(cid&FLAG_LPT)) {
655 /* COM port */
656 UnMapLS( COM[cid].seg_unknown );
657 USER16_AlertableWait--;
658 CancelIo(ptr->handle);
660 /* free buffers */
661 free(ptr->outbuf);
662 free(ptr->inbuf);
664 /* reset modem lines */
665 SetCommState16(&COM[cid].dcb);
668 if (!CloseHandle(ptr->handle)) {
669 ptr->commerror = WinError();
670 /* FIXME: should we clear ptr->handle here? */
671 return -1;
672 } else {
673 ptr->commerror = 0;
674 ptr->handle = 0;
675 return 0;
679 /*****************************************************************************
680 * SetCommBreak (USER.210)
682 INT16 WINAPI SetCommBreak16(INT16 cid)
684 struct DosDeviceStruct *ptr;
686 TRACE("cid=%d\n", cid);
687 if ((ptr = GetDeviceStruct(cid)) == NULL) {
688 FIXME("no cid=%d found!\n", cid);
689 return -1;
692 ptr->suspended = 1;
693 ptr->commerror = 0;
694 return 0;
697 /*****************************************************************************
698 * ClearCommBreak (USER.211)
700 INT16 WINAPI ClearCommBreak16(INT16 cid)
702 struct DosDeviceStruct *ptr;
704 TRACE("cid=%d\n", cid);
705 if (!(ptr = GetDeviceStruct(cid))) {
706 FIXME("no cid=%d found!\n", cid);
707 return -1;
709 ptr->suspended = 0;
710 ptr->commerror = 0;
711 return 0;
714 /*****************************************************************************
715 * EscapeCommFunction (USER.214)
717 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
719 struct DosDeviceStruct *ptr;
720 int max;
722 TRACE("cid=%d, function=%d\n", cid, nFunction);
724 switch(nFunction) {
725 case GETMAXCOM:
726 TRACE("GETMAXCOM\n");
727 for (max = MAX_PORTS;!COM[max].devicename;max--)
729 return max;
731 case GETMAXLPT:
732 TRACE("GETMAXLPT\n");
733 for (max = MAX_PORTS;!LPT[max].devicename;max--)
735 return FLAG_LPT + max;
737 case GETBASEIRQ:
738 TRACE("GETBASEIRQ\n");
739 /* FIXME: use tables */
740 /* just fake something for now */
741 if (cid & FLAG_LPT) {
742 /* LPT1: irq 7, LPT2: irq 5 */
743 return (cid & 0x7f) ? 5 : 7;
744 } else {
745 /* COM1: irq 4, COM2: irq 3,
746 COM3: irq 4, COM4: irq 3 */
747 return 4 - (cid & 1);
751 if ((ptr = GetDeviceStruct(cid)) == NULL) {
752 FIXME("no cid=%d found!\n", cid);
753 return -1;
756 switch (nFunction) {
757 case RESETDEV:
758 case CLRDTR:
759 case CLRRTS:
760 case SETDTR:
761 case SETRTS:
762 case SETXOFF:
763 case SETXON:
764 if(EscapeCommFunction(ptr->handle,nFunction))
765 return 0;
766 else {
767 ptr->commerror = WinError();
768 return -1;
771 case CLRBREAK:
772 case SETBREAK:
773 default:
774 WARN("(cid=%d,nFunction=%d): Unknown function\n",
775 cid, nFunction);
777 return -1;
780 /*****************************************************************************
781 * FlushComm (USER.215)
783 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
785 DWORD queue;
786 struct DosDeviceStruct *ptr;
788 TRACE("cid=%d, queue=%d\n", cid, fnQueue);
789 if ((ptr = GetDeviceStruct(cid)) == NULL) {
790 FIXME("no cid=%d found!\n", cid);
791 return -1;
793 switch (fnQueue) {
794 case 0:
795 queue = PURGE_TXABORT;
796 ptr->obuf_tail = ptr->obuf_head;
797 break;
798 case 1:
799 queue = PURGE_RXABORT;
800 ptr->ibuf_head = ptr->ibuf_tail;
801 break;
802 default:
803 WARN("(cid=%d,fnQueue=%d):Unknown queue\n",
804 cid, fnQueue);
805 return -1;
808 if (!PurgeComm(ptr->handle,queue)) {
809 ptr->commerror = WinError();
810 return -1;
811 } else {
812 ptr->commerror = 0;
813 return 0;
817 /********************************************************************
818 * GetCommError (USER.203)
820 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
822 int temperror;
823 struct DosDeviceStruct *ptr;
824 unsigned char *stol;
826 if ((ptr = GetDeviceStruct(cid)) == NULL) {
827 FIXME("no handle for cid = %0x!\n",cid);
828 return -1;
830 if (cid&FLAG_LPT) {
831 WARN(" cid %d not comm port\n",cid);
832 return CE_MODE;
834 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
835 COMM_MSRUpdate( ptr->handle, stol );
837 if (lpStat) {
838 lpStat->status = 0;
840 WaitForMultipleObjectsEx(0,NULL,FALSE,1,TRUE);
842 lpStat->cbOutQue = comm_outbuf(ptr);
843 lpStat->cbInQue = comm_inbuf(ptr);
845 TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n",
846 cid, ptr->commerror, lpStat->status, lpStat->cbInQue,
847 lpStat->cbOutQue, *stol);
849 else
850 TRACE("cid %d, error %d, lpStat NULL stol %x\n",
851 cid, ptr->commerror, *stol);
853 /* Return any errors and clear it */
854 temperror = ptr->commerror;
855 ptr->commerror = 0;
856 return(temperror);
859 /*****************************************************************************
860 * SetCommEventMask (USER.208)
862 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
864 struct DosDeviceStruct *ptr;
865 unsigned char *stol;
867 TRACE("cid %d,mask %d\n",cid,fuEvtMask);
868 if ((ptr = GetDeviceStruct(cid)) == NULL) {
869 FIXME("no handle for cid = %0x!\n",cid);
870 return (SEGPTR)NULL;
873 ptr->eventmask = fuEvtMask;
875 if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
876 WARN(" cid %d not comm port\n",cid);
877 return (SEGPTR)NULL;
879 /* it's a COM port ? -> modify flags */
880 stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET;
881 COMM_MSRUpdate( ptr->handle, stol );
883 TRACE(" modem dcd construct %x\n",*stol);
884 if (!COM[cid].seg_unknown) COM[cid].seg_unknown = MapLS( COM[cid].unknown );
885 return COM[cid].seg_unknown;
888 /*****************************************************************************
889 * GetCommEventMask (USER.209)
891 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
893 struct DosDeviceStruct *ptr;
894 WORD events;
896 TRACE("cid %d, mask %d\n", cid, fnEvtClear);
897 if ((ptr = GetDeviceStruct(cid)) == NULL) {
898 FIXME("no handle for cid = %0x!\n",cid);
899 return 0;
902 if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
903 WARN(" cid %d not comm port\n",cid);
904 return 0;
907 events = *(WORD*)(COM[cid].unknown) & fnEvtClear;
908 *(WORD*)(COM[cid].unknown) &= ~fnEvtClear;
909 return events;
912 /*****************************************************************************
913 * SetCommState (USER.201)
915 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
917 struct DosDeviceStruct *ptr;
918 DCB dcb;
920 TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb);
921 if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
922 FIXME("no handle for cid = %0x!\n",lpdcb->Id);
923 return -1;
926 memset(&dcb,0,sizeof dcb);
927 dcb.DCBlength = sizeof dcb;
930 * according to MSDN, we should first interpret lpdcb->BaudRate as follows:
931 * 1. if the baud rate is a CBR constant, interpret it.
932 * 2. if it is greater than 57600, the baud rate is 115200
933 * 3. use the actual baudrate
934 * steps 2 and 3 are equivilent to 16550 baudrate divisor = 115200/BaudRate
935 * see http://support.microsoft.com/support/kb/articles/q108/9/28.asp
937 switch(lpdcb->BaudRate)
939 case CBR_110: dcb.BaudRate = 110; break;
940 case CBR_300: dcb.BaudRate = 300; break;
941 case CBR_600: dcb.BaudRate = 600; break;
942 case CBR_1200: dcb.BaudRate = 1200; break;
943 case CBR_2400: dcb.BaudRate = 2400; break;
944 case CBR_4800: dcb.BaudRate = 4800; break;
945 case CBR_9600: dcb.BaudRate = 9600; break;
946 case CBR_14400: dcb.BaudRate = 14400; break;
947 case CBR_19200: dcb.BaudRate = 19200; break;
948 case CBR_38400: dcb.BaudRate = 38400; break;
949 case CBR_56000: dcb.BaudRate = 56000; break;
950 case CBR_128000: dcb.BaudRate = 128000; break;
951 case CBR_256000: dcb.BaudRate = 256000; break;
952 default:
953 if(lpdcb->BaudRate>57600)
954 dcb.BaudRate = 115200;
955 else
956 dcb.BaudRate = lpdcb->BaudRate;
959 dcb.ByteSize=lpdcb->ByteSize;
960 dcb.StopBits=lpdcb->StopBits;
962 dcb.fParity=lpdcb->fParity;
963 dcb.Parity=lpdcb->Parity;
965 dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow;
967 if (lpdcb->fDtrflow || lpdcb->fRtsflow)
968 dcb.fRtsControl = TRUE;
970 if (lpdcb->fDtrDisable)
971 dcb.fDtrControl = TRUE;
973 ptr->evtchar = lpdcb->EvtChar;
975 dcb.fInX = lpdcb->fInX;
976 dcb.fOutX = lpdcb->fOutX;
978 if (!SetCommState(ptr->handle,&dcb)) {
979 ptr->commerror = WinError();
980 return -1;
981 } else {
982 ptr->commerror = 0;
983 return 0;
987 /*****************************************************************************
988 * GetCommState (USER.202)
990 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
992 struct DosDeviceStruct *ptr;
993 DCB dcb;
995 TRACE("cid %d, ptr %p\n", cid, lpdcb);
996 if ((ptr = GetDeviceStruct(cid)) == NULL) {
997 FIXME("no handle for cid = %0x!\n",cid);
998 return -1;
1000 if (!GetCommState(ptr->handle,&dcb)) {
1001 ptr->commerror = WinError();
1002 return -1;
1005 lpdcb->Id = cid;
1007 COMM16_DCBtoDCB16(&dcb,lpdcb);
1009 lpdcb->EvtChar = ptr->evtchar;
1011 ptr->commerror = 0;
1012 return 0;
1015 /*****************************************************************************
1016 * TransmitCommChar (USER.206)
1018 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
1020 struct DosDeviceStruct *ptr;
1022 TRACE("cid %d, data %d \n", cid, chTransmit);
1023 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1024 FIXME("no handle for cid = %0x!\n",cid);
1025 return -1;
1028 if (ptr->suspended) {
1029 ptr->commerror = IE_HARDWARE;
1030 return -1;
1033 if (ptr->xmit >= 0) {
1034 /* character already queued */
1035 /* FIXME: which error would Windows return? */
1036 ptr->commerror = CE_TXFULL;
1037 return -1;
1040 if (ptr->obuf_head == ptr->obuf_tail) {
1041 /* transmit queue empty, try to transmit directly */
1042 if(1!=COMM16_WriteFile(ptr->handle, &chTransmit, 1))
1044 /* didn't work, queue it */
1045 ptr->xmit = chTransmit;
1046 comm_waitwrite(ptr);
1048 } else {
1049 /* data in queue, let this char be transmitted next */
1050 ptr->xmit = chTransmit;
1051 comm_waitwrite(ptr);
1054 ptr->commerror = 0;
1055 return 0;
1058 /*****************************************************************************
1059 * UngetCommChar (USER.212)
1061 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
1063 struct DosDeviceStruct *ptr;
1065 TRACE("cid %d (char %d)\n", cid, chUnget);
1066 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1067 FIXME("no handle for cid = %0x!\n",cid);
1068 return -1;
1071 if (ptr->suspended) {
1072 ptr->commerror = IE_HARDWARE;
1073 return -1;
1076 if (ptr->unget>=0) {
1077 /* character already queued */
1078 /* FIXME: which error would Windows return? */
1079 ptr->commerror = CE_RXOVER;
1080 return -1;
1083 ptr->unget = chUnget;
1085 ptr->commerror = 0;
1086 return 0;
1089 /*****************************************************************************
1090 * ReadComm (USER.204)
1092 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
1094 int status, length;
1095 struct DosDeviceStruct *ptr;
1096 LPSTR orgBuf = lpvBuf;
1098 TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
1099 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1100 FIXME("no handle for cid = %0x!\n",cid);
1101 return -1;
1104 if (ptr->suspended) {
1105 ptr->commerror = IE_HARDWARE;
1106 return -1;
1109 if(0==comm_inbuf(ptr))
1110 WaitForMultipleObjectsEx(0,NULL,FALSE,1,TRUE);
1112 /* read unget character */
1113 if (ptr->unget>=0) {
1114 *lpvBuf++ = ptr->unget;
1115 ptr->unget = -1;
1117 length = 1;
1118 } else
1119 length = 0;
1121 /* read from receive buffer */
1122 while (length < cbRead) {
1123 status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1124 ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1125 if (!status) break;
1126 if ((cbRead - length) < status)
1127 status = cbRead - length;
1129 memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1130 ptr->ibuf_tail += status;
1131 if (ptr->ibuf_tail >= ptr->ibuf_size)
1132 ptr->ibuf_tail = 0;
1133 lpvBuf += status;
1134 length += status;
1137 TRACE("%s\n", debugstr_an( orgBuf, length ));
1138 ptr->commerror = 0;
1139 return length;
1142 /*****************************************************************************
1143 * WriteComm (USER.205)
1145 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1147 int status, length;
1148 struct DosDeviceStruct *ptr;
1150 TRACE("cid %d, ptr %p, length %d\n",
1151 cid, lpvBuf, cbWrite);
1152 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1153 FIXME("no handle for cid = %0x!\n",cid);
1154 return -1;
1157 if (ptr->suspended) {
1158 ptr->commerror = IE_HARDWARE;
1159 return -1;
1162 TRACE("%s\n", debugstr_an( lpvBuf, cbWrite ));
1164 length = 0;
1165 while (length < cbWrite) {
1166 if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1167 /* no data queued, try to write directly */
1168 status = COMM16_WriteFile(ptr->handle, lpvBuf, cbWrite - length);
1169 if (status > 0) {
1170 lpvBuf += status;
1171 length += status;
1172 continue;
1175 /* can't write directly, put into transmit buffer */
1176 status = ((ptr->obuf_tail > ptr->obuf_head) ?
1177 (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1178 if (!status) break;
1179 if ((cbWrite - length) < status)
1180 status = cbWrite - length;
1181 memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1182 ptr->obuf_head += status;
1183 if (ptr->obuf_head >= ptr->obuf_size)
1184 ptr->obuf_head = 0;
1185 lpvBuf += status;
1186 length += status;
1187 comm_waitwrite(ptr);
1190 ptr->commerror = 0;
1191 return length;
1194 /***********************************************************************
1195 * EnableCommNotification (USER.245)
1197 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1198 INT16 cbWriteNotify, INT16 cbOutQueue )
1200 struct DosDeviceStruct *ptr;
1202 TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1203 if ((ptr = GetDeviceStruct(cid)) == NULL) {
1204 FIXME("no handle for cid = %0x!\n",cid);
1205 return -1;
1207 ptr->wnd = WIN_Handle32( hwnd );
1208 ptr->n_read = cbWriteNotify;
1209 ptr->n_write = cbOutQueue;
1210 return TRUE;