| /* | |
| * DEC 93 Erik Bos <erik@xs4all.nl> | |
| * | |
| * Copyright 1996 Marcus Meissner | |
| * | |
| * Copyright 2001 Mike McCormack | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| * | |
| * History: | |
| * | |
| * Mar 31, 1999. Ove Kåven <ovek@arcticnet.no> | |
| * - Implemented buffers and EnableCommNotification. | |
| * | |
| * Apr 3, 1999. Lawson Whitney <lawson_whitney@juno.com> | |
| * - Fixed the modem control part of EscapeCommFunction16. | |
| * | |
| * Mar 3, 1999. Ove Kåven <ovek@arcticnet.no> | |
| * - Use port indices instead of unixfds for win16 | |
| * - Moved things around (separated win16 and win32 routines) | |
| * - Added some hints on how to implement buffers and EnableCommNotification. | |
| * | |
| * May 26, 1997. Fixes and comments by Rick Richardson <rick@dgii.com> [RER] | |
| * - ptr->fd wasn't getting cleared on close. | |
| * - GetCommEventMask() and GetCommError() didn't do much of anything. | |
| * IMHO, they are still wrong, but they at least implement the RXCHAR | |
| * event and return I/O queue sizes, which makes the app I'm interested | |
| * in (analog devices EZKIT DSP development system) work. | |
| * | |
| * August 12, 1997. Take a bash at SetCommEventMask - Lawson Whitney | |
| * <lawson_whitney@juno.com> | |
| * July 6, 1998. Fixes and comments by Valentijn Sessink | |
| * <vsessink@ic.uva.nl> [V] | |
| * Oktober 98, Rein Klazes [RHK] | |
| * A program that wants to monitor the modem status line (RLSD/DCD) may | |
| * poll the modem status register in the commMask structure. I update the bit | |
| * in GetCommError, waiting for an implementation of communication events. | |
| * | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdlib.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <ctype.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "wine/winuser16.h" | |
| #include "win.h" | |
| #include "user_private.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(comm); | |
| /* window's semi documented modem status register */ | |
| #define COMM_MSR_OFFSET 35 | |
| #define MSR_CTS 0x10 | |
| #define MSR_DSR 0x20 | |
| #define MSR_RI 0x40 | |
| #define MSR_RLSD 0x80 | |
| #define MSR_MASK (MSR_CTS|MSR_DSR|MSR_RI|MSR_RLSD) | |
| #define FLAG_LPT 0x80 | |
| #define MAX_PORTS 9 | |
| struct DosDeviceStruct { | |
| HANDLE handle; | |
| int suspended; | |
| int unget,xmit; | |
| int evtchar; | |
| /* events */ | |
| int commerror, eventmask; | |
| /* buffers */ | |
| char *inbuf,*outbuf; | |
| unsigned ibuf_size,ibuf_head,ibuf_tail; | |
| unsigned obuf_size,obuf_head,obuf_tail; | |
| /* notifications */ | |
| HWND wnd; | |
| int n_read, n_write; | |
| OVERLAPPED read_ov, write_ov; | |
| /* save terminal states */ | |
| DCB16 dcb; | |
| /* pointer to unknown(==undocumented) comm structure */ | |
| SEGPTR seg_unknown; | |
| BYTE unknown[40]; | |
| }; | |
| static struct DosDeviceStruct COM[MAX_PORTS]; | |
| static struct DosDeviceStruct LPT[MAX_PORTS]; | |
| /* update window's semi documented modem status register */ | |
| /* see knowledge base Q101417 */ | |
| static void COMM_MSRUpdate( HANDLE handle, UCHAR * pMsr ) | |
| { | |
| UCHAR tmpmsr=0; | |
| DWORD mstat=0; | |
| if(!GetCommModemStatus(handle,&mstat)) | |
| return; | |
| if(mstat & MS_CTS_ON) tmpmsr |= MSR_CTS; | |
| if(mstat & MS_DSR_ON) tmpmsr |= MSR_DSR; | |
| if(mstat & MS_RING_ON) tmpmsr |= MSR_RI; | |
| if(mstat & MS_RLSD_ON) tmpmsr |= MSR_RLSD; | |
| *pMsr = (*pMsr & ~MSR_MASK) | tmpmsr; | |
| } | |
| static struct DosDeviceStruct *GetDeviceStruct(int index) | |
| { | |
| if ((index&0x7F)<=MAX_PORTS) { | |
| if (!(index&FLAG_LPT)) { | |
| if (COM[index].handle) | |
| return &COM[index]; | |
| } else { | |
| index &= 0x7f; | |
| if (LPT[index].handle) | |
| return &LPT[index]; | |
| } | |
| } | |
| return NULL; | |
| } | |
| static int GetCommPort_ov(LPOVERLAPPED ov, int write) | |
| { | |
| int x; | |
| for (x=0; x<MAX_PORTS; x++) { | |
| if (ov == (write?&COM[x].write_ov:&COM[x].read_ov)) | |
| return x; | |
| } | |
| return -1; | |
| } | |
| static int WinError(void) | |
| { | |
| TRACE("errno = %d\n", errno); | |
| switch (errno) { | |
| default: | |
| return CE_IOE; | |
| } | |
| } | |
| static unsigned comm_inbuf(struct DosDeviceStruct *ptr) | |
| { | |
| return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0) | |
| + ptr->ibuf_head - ptr->ibuf_tail; | |
| } | |
| static unsigned comm_outbuf(struct DosDeviceStruct *ptr) | |
| { | |
| return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0) | |
| + ptr->obuf_head - ptr->obuf_tail; | |
| } | |
| static void comm_waitread(struct DosDeviceStruct *ptr); | |
| static void comm_waitwrite(struct DosDeviceStruct *ptr); | |
| static VOID WINAPI COMM16_ReadComplete(DWORD dwErrorCode, DWORD len, LPOVERLAPPED ov) | |
| { | |
| int prev; | |
| WORD mask = 0; | |
| int cid = GetCommPort_ov(ov,0); | |
| struct DosDeviceStruct *ptr; | |
| if(cid<0) { | |
| ERR("async write with bad overlapped pointer\n"); | |
| return; | |
| } | |
| ptr = &COM[cid]; | |
| /* we get cancelled when CloseComm is called */ | |
| if (dwErrorCode==ERROR_OPERATION_ABORTED) | |
| { | |
| TRACE("Cancelled\n"); | |
| return; | |
| } | |
| /* read data from comm port */ | |
| if (dwErrorCode != NO_ERROR) { | |
| ERR("async read failed, error %ld\n",dwErrorCode); | |
| COM[cid].commerror = CE_RXOVER; | |
| return; | |
| } | |
| TRACE("async read completed %ld bytes\n",len); | |
| prev = comm_inbuf(ptr); | |
| /* check for events */ | |
| if ((ptr->eventmask & EV_RXFLAG) && | |
| memchr(ptr->inbuf + ptr->ibuf_head, ptr->evtchar, len)) { | |
| *(WORD*)(COM[cid].unknown) |= EV_RXFLAG; | |
| mask |= CN_EVENT; | |
| } | |
| if (ptr->eventmask & EV_RXCHAR) { | |
| *(WORD*)(COM[cid].unknown) |= EV_RXCHAR; | |
| mask |= CN_EVENT; | |
| } | |
| /* advance buffer position */ | |
| ptr->ibuf_head += len; | |
| if (ptr->ibuf_head >= ptr->ibuf_size) | |
| ptr->ibuf_head = 0; | |
| /* check for notification */ | |
| if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) && | |
| (comm_inbuf(ptr)>=ptr->n_read)) { | |
| /* passed the receive notification threshold */ | |
| mask |= CN_RECEIVE; | |
| } | |
| /* send notifications, if any */ | |
| if (ptr->wnd && mask) { | |
| TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask); | |
| PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask); | |
| } | |
| /* on real windows, this could cause problems, since it is recursive */ | |
| /* restart the receive */ | |
| comm_waitread(ptr); | |
| } | |
| /* this is meant to work like write() */ | |
| static INT COMM16_WriteFile(HANDLE hComm, LPCVOID buffer, DWORD len) | |
| { | |
| OVERLAPPED ov; | |
| DWORD count= -1; | |
| ZeroMemory(&ov,sizeof(ov)); | |
| ov.hEvent = CreateEventW(NULL,0,0,NULL); | |
| if(ov.hEvent==INVALID_HANDLE_VALUE) | |
| return -1; | |
| if(!WriteFile(hComm,buffer,len,&count,&ov)) | |
| { | |
| if(GetLastError()==ERROR_IO_PENDING) | |
| { | |
| GetOverlappedResult(hComm,&ov,&count,TRUE); | |
| } | |
| } | |
| CloseHandle(ov.hEvent); | |
| return count; | |
| } | |
| static VOID WINAPI COMM16_WriteComplete(DWORD dwErrorCode, DWORD len, LPOVERLAPPED ov) | |
| { | |
| int prev, bleft; | |
| WORD mask = 0; | |
| int cid = GetCommPort_ov(ov,1); | |
| struct DosDeviceStruct *ptr; | |
| if(cid<0) { | |
| ERR("async write with bad overlapped pointer\n"); | |
| return; | |
| } | |
| ptr = &COM[cid]; | |
| /* read data from comm port */ | |
| if (dwErrorCode != NO_ERROR) { | |
| ERR("async write failed, error %ld\n",dwErrorCode); | |
| COM[cid].commerror = CE_RXOVER; | |
| return; | |
| } | |
| TRACE("async write completed %ld bytes\n",len); | |
| /* update the buffer pointers */ | |
| prev = comm_outbuf(&COM[cid]); | |
| ptr->obuf_tail += len; | |
| if (ptr->obuf_tail >= ptr->obuf_size) | |
| ptr->obuf_tail = 0; | |
| /* write any TransmitCommChar character */ | |
| if (ptr->xmit>=0) { | |
| len = COMM16_WriteFile(ptr->handle, &(ptr->xmit), 1); | |
| if (len > 0) ptr->xmit = -1; | |
| } | |
| /* write from output queue */ | |
| bleft = ((ptr->obuf_tail <= ptr->obuf_head) ? | |
| ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail; | |
| /* check for notification */ | |
| if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) && | |
| (comm_outbuf(ptr)<ptr->n_write)) { | |
| /* passed the transmit notification threshold */ | |
| mask |= CN_TRANSMIT; | |
| } | |
| /* send notifications, if any */ | |
| if (ptr->wnd && mask) { | |
| TRACE("notifying %p: cid=%d, mask=%02x\n", ptr->wnd, cid, mask); | |
| PostMessageA(ptr->wnd, WM_COMMNOTIFY, cid, mask); | |
| } | |
| /* start again if necessary */ | |
| if(bleft) | |
| comm_waitwrite(ptr); | |
| } | |
| static void comm_waitread(struct DosDeviceStruct *ptr) | |
| { | |
| unsigned int bleft; | |
| COMSTAT stat; | |
| /* FIXME: get timeouts working properly so we can read bleft bytes */ | |
| bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ? | |
| (ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head; | |
| /* find out how many bytes are left in the buffer */ | |
| if(ClearCommError(ptr->handle,NULL,&stat)) | |
| bleft = (bleft<stat.cbInQue) ? bleft : stat.cbInQue; | |
| else | |
| bleft = 1; | |
| /* always read at least one byte */ | |
| if(bleft==0) | |
| bleft++; | |
| ReadFileEx(ptr->handle, | |
| ptr->inbuf + ptr->ibuf_head, | |
| bleft, | |
| &ptr->read_ov, | |
| COMM16_ReadComplete); | |
| } | |
| static void comm_waitwrite(struct DosDeviceStruct *ptr) | |
| { | |
| int bleft; | |
| bleft = ((ptr->obuf_tail <= ptr->obuf_head) ? | |
| ptr->obuf_head : ptr->obuf_size) - ptr->obuf_tail; | |
| WriteFileEx(ptr->handle, | |
| ptr->outbuf + ptr->obuf_tail, | |
| bleft, | |
| &ptr->write_ov, | |
| COMM16_WriteComplete); | |
| } | |
| /***************************************************************************** | |
| * COMM16_DCBtoDCB16 (Internal) | |
| */ | |
| INT16 COMM16_DCBtoDCB16(LPDCB lpdcb, LPDCB16 lpdcb16) | |
| { | |
| if(lpdcb->BaudRate<0x10000) | |
| lpdcb16->BaudRate = lpdcb->BaudRate; | |
| else if(lpdcb->BaudRate==115200) | |
| lpdcb16->BaudRate = 57601; | |
| else { | |
| WARN("Baud rate can't be converted\n"); | |
| lpdcb16->BaudRate = 57601; | |
| } | |
| lpdcb16->ByteSize = lpdcb->ByteSize; | |
| lpdcb16->fParity = lpdcb->fParity; | |
| lpdcb16->Parity = lpdcb->Parity; | |
| lpdcb16->StopBits = lpdcb->StopBits; | |
| lpdcb16->RlsTimeout = 50; | |
| lpdcb16->CtsTimeout = 50; | |
| lpdcb16->DsrTimeout = 50; | |
| lpdcb16->fNull = 0; | |
| lpdcb16->fChEvt = 0; | |
| lpdcb16->fBinary = 1; | |
| lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_HANDSHAKE); | |
| lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_HANDSHAKE); | |
| lpdcb16->fOutxCtsFlow = lpdcb->fOutxCtsFlow; | |
| lpdcb16->fOutxDsrFlow = lpdcb->fOutxDsrFlow; | |
| lpdcb16->fDtrDisable = (lpdcb->fDtrControl==DTR_CONTROL_DISABLE); | |
| lpdcb16->fInX = lpdcb->fInX; | |
| lpdcb16->fOutX = lpdcb->fOutX; | |
| /* | |
| lpdcb16->XonChar = | |
| lpdcb16->XoffChar = | |
| */ | |
| lpdcb16->XonLim = 10; | |
| lpdcb16->XoffLim = 10; | |
| return 0; | |
| } | |
| /************************************************************************** | |
| * BuildCommDCB (USER.213) | |
| * | |
| * According to the ECMA-234 (368.3) the function will return FALSE on | |
| * success, otherwise it will return -1. | |
| */ | |
| INT16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb) | |
| { | |
| /* "COM1:96,n,8,1" */ | |
| /* 012345 */ | |
| int port; | |
| DCB dcb; | |
| TRACE("(%s), ptr %p\n", device, lpdcb); | |
| if (strncasecmp(device,"COM",3)) | |
| return -1; | |
| port = device[3] - '0'; | |
| if (port-- == 0) { | |
| ERR("BUG ! COM0 can't exist!\n"); | |
| return -1; | |
| } | |
| memset(lpdcb, 0, sizeof(DCB16)); /* initialize */ | |
| lpdcb->Id = port; | |
| dcb.DCBlength = sizeof(DCB); | |
| if (strchr(device,'=')) /* block new style */ | |
| return -1; | |
| if(!BuildCommDCBA(device,&dcb)) | |
| return -1; | |
| return COMM16_DCBtoDCB16(&dcb, lpdcb); | |
| } | |
| /***************************************************************************** | |
| * OpenComm (USER.200) | |
| */ | |
| INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue) | |
| { | |
| int port; | |
| HANDLE handle; | |
| TRACE("%s, %d, %d\n", device, cbInQueue, cbOutQueue); | |
| if (strlen(device) < 4) | |
| return IE_BADID; | |
| port = device[3] - '0'; | |
| if (port-- == 0) | |
| ERR("BUG ! COM0 or LPT0 don't exist !\n"); | |
| if (!strncasecmp(device,"COM",3)) | |
| { | |
| if (COM[port].handle) | |
| return IE_OPEN; | |
| handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE, | |
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, | |
| FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, 0 ); | |
| if (handle == INVALID_HANDLE_VALUE) { | |
| return IE_HARDWARE; | |
| } else { | |
| memset(COM[port].unknown, 0, sizeof(COM[port].unknown)); | |
| COM[port].seg_unknown = 0; | |
| COM[port].handle = handle; | |
| COM[port].commerror = 0; | |
| COM[port].eventmask = 0; | |
| COM[port].evtchar = 0; /* FIXME: default? */ | |
| /* save terminal state */ | |
| GetCommState16(port,&COM[port].dcb); | |
| /* init priority characters */ | |
| COM[port].unget = -1; | |
| COM[port].xmit = -1; | |
| /* allocate buffers */ | |
| COM[port].ibuf_size = cbInQueue; | |
| COM[port].ibuf_head = COM[port].ibuf_tail = 0; | |
| COM[port].obuf_size = cbOutQueue; | |
| COM[port].obuf_head = COM[port].obuf_tail = 0; | |
| COM[port].inbuf = HeapAlloc(GetProcessHeap(), 0, cbInQueue); | |
| if (COM[port].inbuf) { | |
| COM[port].outbuf = HeapAlloc( GetProcessHeap(), 0, cbOutQueue); | |
| if (!COM[port].outbuf) | |
| HeapFree( GetProcessHeap(), 0, COM[port].inbuf); | |
| } else COM[port].outbuf = NULL; | |
| if (!COM[port].outbuf) { | |
| /* not enough memory */ | |
| CloseHandle(COM[port].handle); | |
| ERR("out of memory\n"); | |
| return IE_MEMORY; | |
| } | |
| ZeroMemory(&COM[port].read_ov,sizeof (OVERLAPPED)); | |
| ZeroMemory(&COM[port].write_ov,sizeof (OVERLAPPED)); | |
| comm_waitread( &COM[port] ); | |
| USER16_AlertableWait++; | |
| return port; | |
| } | |
| } | |
| else | |
| if (!strncasecmp(device,"LPT",3)) { | |
| if (LPT[port].handle) | |
| return IE_OPEN; | |
| handle = CreateFileA(device, GENERIC_READ|GENERIC_WRITE, | |
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 ); | |
| if (handle == INVALID_HANDLE_VALUE) { | |
| return IE_HARDWARE; | |
| } else { | |
| LPT[port].handle = handle; | |
| LPT[port].commerror = 0; | |
| LPT[port].eventmask = 0; | |
| return port|FLAG_LPT; | |
| } | |
| } | |
| return IE_BADID; | |
| } | |
| /***************************************************************************** | |
| * CloseComm (USER.207) | |
| */ | |
| INT16 WINAPI CloseComm16(INT16 cid) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid=%d\n", cid); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no cid=%d found!\n", cid); | |
| return -1; | |
| } | |
| if (!(cid&FLAG_LPT)) { | |
| /* COM port */ | |
| UnMapLS( COM[cid].seg_unknown ); | |
| USER16_AlertableWait--; | |
| CancelIo(ptr->handle); | |
| /* free buffers */ | |
| HeapFree( GetProcessHeap(), 0, ptr->outbuf); | |
| HeapFree( GetProcessHeap(), 0, ptr->inbuf); | |
| /* reset modem lines */ | |
| SetCommState16(&COM[cid].dcb); | |
| } | |
| if (!CloseHandle(ptr->handle)) { | |
| ptr->commerror = WinError(); | |
| /* FIXME: should we clear ptr->handle here? */ | |
| return -1; | |
| } else { | |
| ptr->commerror = 0; | |
| ptr->handle = 0; | |
| return 0; | |
| } | |
| } | |
| /***************************************************************************** | |
| * SetCommBreak (USER.210) | |
| */ | |
| INT16 WINAPI SetCommBreak16(INT16 cid) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid=%d\n", cid); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no cid=%d found!\n", cid); | |
| return -1; | |
| } | |
| ptr->suspended = 1; | |
| ptr->commerror = 0; | |
| return 0; | |
| } | |
| /***************************************************************************** | |
| * ClearCommBreak (USER.211) | |
| */ | |
| INT16 WINAPI ClearCommBreak16(INT16 cid) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid=%d\n", cid); | |
| if (!(ptr = GetDeviceStruct(cid))) { | |
| FIXME("no cid=%d found!\n", cid); | |
| return -1; | |
| } | |
| ptr->suspended = 0; | |
| ptr->commerror = 0; | |
| return 0; | |
| } | |
| /***************************************************************************** | |
| * EscapeCommFunction (USER.214) | |
| */ | |
| LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid=%d, function=%d\n", cid, nFunction); | |
| switch(nFunction) { | |
| case GETMAXCOM: | |
| TRACE("GETMAXCOM\n"); | |
| return 4; /* FIXME */ | |
| case GETMAXLPT: | |
| TRACE("GETMAXLPT\n"); | |
| return FLAG_LPT + 3; /* FIXME */ | |
| case GETBASEIRQ: | |
| TRACE("GETBASEIRQ\n"); | |
| /* FIXME: use tables */ | |
| /* just fake something for now */ | |
| if (cid & FLAG_LPT) { | |
| /* LPT1: irq 7, LPT2: irq 5 */ | |
| return (cid & 0x7f) ? 5 : 7; | |
| } else { | |
| /* COM1: irq 4, COM2: irq 3, | |
| COM3: irq 4, COM4: irq 3 */ | |
| return 4 - (cid & 1); | |
| } | |
| } | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no cid=%d found!\n", cid); | |
| return -1; | |
| } | |
| switch (nFunction) { | |
| case RESETDEV: | |
| case CLRDTR: | |
| case CLRRTS: | |
| case SETDTR: | |
| case SETRTS: | |
| case SETXOFF: | |
| case SETXON: | |
| if(EscapeCommFunction(ptr->handle,nFunction)) | |
| return 0; | |
| else { | |
| ptr->commerror = WinError(); | |
| return -1; | |
| } | |
| case CLRBREAK: | |
| case SETBREAK: | |
| default: | |
| WARN("(cid=%d,nFunction=%d): Unknown function\n", | |
| cid, nFunction); | |
| } | |
| return -1; | |
| } | |
| /***************************************************************************** | |
| * FlushComm (USER.215) | |
| */ | |
| INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue) | |
| { | |
| DWORD queue; | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid=%d, queue=%d\n", cid, fnQueue); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no cid=%d found!\n", cid); | |
| return -1; | |
| } | |
| switch (fnQueue) { | |
| case 0: | |
| queue = PURGE_TXABORT; | |
| ptr->obuf_tail = ptr->obuf_head; | |
| break; | |
| case 1: | |
| queue = PURGE_RXABORT; | |
| ptr->ibuf_head = ptr->ibuf_tail; | |
| break; | |
| default: | |
| WARN("(cid=%d,fnQueue=%d):Unknown queue\n", | |
| cid, fnQueue); | |
| return -1; | |
| } | |
| if (!PurgeComm(ptr->handle,queue)) { | |
| ptr->commerror = WinError(); | |
| return -1; | |
| } else { | |
| ptr->commerror = 0; | |
| return 0; | |
| } | |
| } | |
| /******************************************************************** | |
| * GetCommError (USER.203) | |
| */ | |
| INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat) | |
| { | |
| int temperror; | |
| struct DosDeviceStruct *ptr; | |
| unsigned char *stol; | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return -1; | |
| } | |
| if (cid&FLAG_LPT) { | |
| WARN(" cid %d not comm port\n",cid); | |
| return CE_MODE; | |
| } | |
| stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET; | |
| COMM_MSRUpdate( ptr->handle, stol ); | |
| if (lpStat) { | |
| lpStat->status = 0; | |
| SleepEx(1,TRUE); | |
| lpStat->cbOutQue = comm_outbuf(ptr); | |
| lpStat->cbInQue = comm_inbuf(ptr); | |
| TRACE("cid %d, error %d, stat %d in %d out %d, stol %x\n", | |
| cid, ptr->commerror, lpStat->status, lpStat->cbInQue, | |
| lpStat->cbOutQue, *stol); | |
| } | |
| else | |
| TRACE("cid %d, error %d, lpStat NULL stol %x\n", | |
| cid, ptr->commerror, *stol); | |
| /* Return any errors and clear it */ | |
| temperror = ptr->commerror; | |
| ptr->commerror = 0; | |
| return(temperror); | |
| } | |
| /***************************************************************************** | |
| * SetCommEventMask (USER.208) | |
| */ | |
| SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| unsigned char *stol; | |
| TRACE("cid %d,mask %d\n",cid,fuEvtMask); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return (SEGPTR)NULL; | |
| } | |
| ptr->eventmask = fuEvtMask; | |
| if (cid&FLAG_LPT) { | |
| WARN(" cid %d not comm port\n",cid); | |
| return (SEGPTR)NULL; | |
| } | |
| /* it's a COM port ? -> modify flags */ | |
| stol = (unsigned char *)COM[cid].unknown + COMM_MSR_OFFSET; | |
| COMM_MSRUpdate( ptr->handle, stol ); | |
| TRACE(" modem dcd construct %x\n",*stol); | |
| if (!COM[cid].seg_unknown) COM[cid].seg_unknown = MapLS( COM[cid].unknown ); | |
| return COM[cid].seg_unknown; | |
| } | |
| /***************************************************************************** | |
| * GetCommEventMask (USER.209) | |
| */ | |
| UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| WORD events; | |
| TRACE("cid %d, mask %d\n", cid, fnEvtClear); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return 0; | |
| } | |
| if (cid&FLAG_LPT) { | |
| WARN(" cid %d not comm port\n",cid); | |
| return 0; | |
| } | |
| events = *(WORD*)(COM[cid].unknown) & fnEvtClear; | |
| *(WORD*)(COM[cid].unknown) &= ~fnEvtClear; | |
| return events; | |
| } | |
| /***************************************************************************** | |
| * SetCommState (USER.201) | |
| */ | |
| INT16 WINAPI SetCommState16(LPDCB16 lpdcb) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| DCB dcb; | |
| TRACE("cid %d, ptr %p\n", lpdcb->Id, lpdcb); | |
| if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",lpdcb->Id); | |
| return -1; | |
| } | |
| memset(&dcb,0,sizeof(dcb)); | |
| dcb.DCBlength = sizeof(dcb); | |
| /* | |
| * according to MSDN, we should first interpret lpdcb->BaudRate as follows: | |
| * 1. if the baud rate is a CBR constant, interpret it. | |
| * 2. if it is greater than 57600, the baud rate is 115200 | |
| * 3. use the actual baudrate | |
| * steps 2 and 3 are equivilent to 16550 baudrate divisor = 115200/BaudRate | |
| * see http://support.microsoft.com/support/kb/articles/q108/9/28.asp | |
| */ | |
| switch(lpdcb->BaudRate) | |
| { | |
| case CBR_110: dcb.BaudRate = 110; break; | |
| case CBR_300: dcb.BaudRate = 300; break; | |
| case CBR_600: dcb.BaudRate = 600; break; | |
| case CBR_1200: dcb.BaudRate = 1200; break; | |
| case CBR_2400: dcb.BaudRate = 2400; break; | |
| case CBR_4800: dcb.BaudRate = 4800; break; | |
| case CBR_9600: dcb.BaudRate = 9600; break; | |
| case CBR_14400: dcb.BaudRate = 14400; break; | |
| case CBR_19200: dcb.BaudRate = 19200; break; | |
| case CBR_38400: dcb.BaudRate = 38400; break; | |
| case CBR_56000: dcb.BaudRate = 56000; break; | |
| case CBR_128000: dcb.BaudRate = 128000; break; | |
| case CBR_256000: dcb.BaudRate = 256000; break; | |
| default: | |
| if(lpdcb->BaudRate>57600) | |
| dcb.BaudRate = 115200; | |
| else | |
| dcb.BaudRate = lpdcb->BaudRate; | |
| } | |
| dcb.ByteSize=lpdcb->ByteSize; | |
| dcb.StopBits=lpdcb->StopBits; | |
| dcb.fParity=lpdcb->fParity; | |
| dcb.Parity=lpdcb->Parity; | |
| dcb.fOutxCtsFlow = lpdcb->fOutxCtsFlow; | |
| if (lpdcb->fDtrflow || lpdcb->fRtsflow) | |
| dcb.fRtsControl = TRUE; | |
| if (lpdcb->fDtrDisable) | |
| dcb.fDtrControl = TRUE; | |
| ptr->evtchar = lpdcb->EvtChar; | |
| dcb.fInX = lpdcb->fInX; | |
| dcb.fOutX = lpdcb->fOutX; | |
| if (!SetCommState(ptr->handle,&dcb)) { | |
| ptr->commerror = WinError(); | |
| return -1; | |
| } else { | |
| ptr->commerror = 0; | |
| return 0; | |
| } | |
| } | |
| /***************************************************************************** | |
| * GetCommState (USER.202) | |
| */ | |
| INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| DCB dcb; | |
| TRACE("cid %d, ptr %p\n", cid, lpdcb); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return -1; | |
| } | |
| if (!GetCommState(ptr->handle,&dcb)) { | |
| ptr->commerror = WinError(); | |
| return -1; | |
| } | |
| lpdcb->Id = cid; | |
| COMM16_DCBtoDCB16(&dcb,lpdcb); | |
| lpdcb->EvtChar = ptr->evtchar; | |
| ptr->commerror = 0; | |
| return 0; | |
| } | |
| /***************************************************************************** | |
| * TransmitCommChar (USER.206) | |
| */ | |
| INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid %d, data %d \n", cid, chTransmit); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return -1; | |
| } | |
| if (ptr->suspended) { | |
| ptr->commerror = IE_HARDWARE; | |
| return -1; | |
| } | |
| if (ptr->xmit >= 0) { | |
| /* character already queued */ | |
| /* FIXME: which error would Windows return? */ | |
| ptr->commerror = CE_TXFULL; | |
| return -1; | |
| } | |
| if (ptr->obuf_head == ptr->obuf_tail) { | |
| /* transmit queue empty, try to transmit directly */ | |
| if(1!=COMM16_WriteFile(ptr->handle, &chTransmit, 1)) | |
| { | |
| /* didn't work, queue it */ | |
| ptr->xmit = chTransmit; | |
| comm_waitwrite(ptr); | |
| } | |
| } else { | |
| /* data in queue, let this char be transmitted next */ | |
| ptr->xmit = chTransmit; | |
| comm_waitwrite(ptr); | |
| } | |
| ptr->commerror = 0; | |
| return 0; | |
| } | |
| /***************************************************************************** | |
| * UngetCommChar (USER.212) | |
| */ | |
| INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid %d (char %d)\n", cid, chUnget); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return -1; | |
| } | |
| if (ptr->suspended) { | |
| ptr->commerror = IE_HARDWARE; | |
| return -1; | |
| } | |
| if (ptr->unget>=0) { | |
| /* character already queued */ | |
| /* FIXME: which error would Windows return? */ | |
| ptr->commerror = CE_RXOVER; | |
| return -1; | |
| } | |
| ptr->unget = chUnget; | |
| ptr->commerror = 0; | |
| return 0; | |
| } | |
| /***************************************************************************** | |
| * ReadComm (USER.204) | |
| */ | |
| INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead) | |
| { | |
| int status, length; | |
| struct DosDeviceStruct *ptr; | |
| LPSTR orgBuf = lpvBuf; | |
| TRACE("cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return -1; | |
| } | |
| if (ptr->suspended) { | |
| ptr->commerror = IE_HARDWARE; | |
| return -1; | |
| } | |
| if(0==comm_inbuf(ptr)) | |
| SleepEx(1,TRUE); | |
| /* read unget character */ | |
| if (ptr->unget>=0) { | |
| *lpvBuf++ = ptr->unget; | |
| ptr->unget = -1; | |
| length = 1; | |
| } else | |
| length = 0; | |
| /* read from receive buffer */ | |
| while (length < cbRead) { | |
| status = ((ptr->ibuf_head < ptr->ibuf_tail) ? | |
| ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail; | |
| if (!status) break; | |
| if ((cbRead - length) < status) | |
| status = cbRead - length; | |
| memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status); | |
| ptr->ibuf_tail += status; | |
| if (ptr->ibuf_tail >= ptr->ibuf_size) | |
| ptr->ibuf_tail = 0; | |
| lpvBuf += status; | |
| length += status; | |
| } | |
| TRACE("%s\n", debugstr_an( orgBuf, length )); | |
| ptr->commerror = 0; | |
| return length; | |
| } | |
| /***************************************************************************** | |
| * WriteComm (USER.205) | |
| */ | |
| INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite) | |
| { | |
| int status, length; | |
| struct DosDeviceStruct *ptr; | |
| TRACE("cid %d, ptr %p, length %d\n", | |
| cid, lpvBuf, cbWrite); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return -1; | |
| } | |
| if (ptr->suspended) { | |
| ptr->commerror = IE_HARDWARE; | |
| return -1; | |
| } | |
| TRACE("%s\n", debugstr_an( lpvBuf, cbWrite )); | |
| length = 0; | |
| while (length < cbWrite) { | |
| if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) { | |
| /* no data queued, try to write directly */ | |
| status = COMM16_WriteFile(ptr->handle, lpvBuf, cbWrite - length); | |
| if (status > 0) { | |
| lpvBuf += status; | |
| length += status; | |
| continue; | |
| } | |
| } | |
| /* can't write directly, put into transmit buffer */ | |
| status = ((ptr->obuf_tail > ptr->obuf_head) ? | |
| (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head; | |
| if (!status) break; | |
| if ((cbWrite - length) < status) | |
| status = cbWrite - length; | |
| memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status); | |
| ptr->obuf_head += status; | |
| if (ptr->obuf_head >= ptr->obuf_size) | |
| ptr->obuf_head = 0; | |
| lpvBuf += status; | |
| length += status; | |
| comm_waitwrite(ptr); | |
| } | |
| ptr->commerror = 0; | |
| return length; | |
| } | |
| /*********************************************************************** | |
| * EnableCommNotification (USER.245) | |
| */ | |
| BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd, | |
| INT16 cbWriteNotify, INT16 cbOutQueue ) | |
| { | |
| struct DosDeviceStruct *ptr; | |
| TRACE("(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue); | |
| if ((ptr = GetDeviceStruct(cid)) == NULL) { | |
| FIXME("no handle for cid = %0x!\n",cid); | |
| return -1; | |
| } | |
| ptr->wnd = WIN_Handle32( hwnd ); | |
| ptr->n_read = cbWriteNotify; | |
| ptr->n_write = cbOutQueue; | |
| return TRUE; | |
| } |