| /* | 
 |  * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 %d\n",dwErrorCode); | 
 | 		COM[cid].commerror = CE_RXOVER; | 
 | 		return; | 
 | 	} | 
 | 	TRACE("async read completed %d 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 %d\n",dwErrorCode); | 
 | 		COM[cid].commerror = CE_RXOVER; | 
 | 		return; | 
 | 	} | 
 | 	TRACE("async write completed %d 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) | 
 |  */ | 
 | static 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; | 
 |  | 
 |                if (comm_inbuf(ptr) == 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; | 
 | } |