|  | /* | 
|  | * 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 "ntstatus.h" | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  | #include "winreg.h" | 
|  | #include "winuser.h" | 
|  | #include "wine/winuser16.h" | 
|  | #include "win.h" | 
|  | #include "winerror.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 status, 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 (status==STATUS_CANCELLED) | 
|  | { | 
|  | TRACE("Cancelled\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* read data from comm port */ | 
|  | if (status != STATUS_SUCCESS) { | 
|  | ERR("async read failed %08lx\n",status); | 
|  | 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 = CreateEventA(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 status, 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 (status != STATUS_SUCCESS) { | 
|  | ERR("async write failed\n"); | 
|  | 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; | 
|  | } |