wine/dlls/user.exe16/comm.c
2009-12-24 12:15:12 +01:00

1094 lines
26 KiB
C

/*
* 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 "wine/winuser16.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(const OVERLAPPED *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(const struct DosDeviceStruct *ptr)
{
return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
+ ptr->ibuf_head - ptr->ibuf_tail;
}
static unsigned comm_outbuf(const 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(const DCB *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 0;
}
ptr->eventmask = fuEvtMask;
if (cid&FLAG_LPT) {
WARN(" cid %d not comm port\n",cid);
return 0;
}
/* 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 equivalent to 16550 baudrate divisor = 115200/BaudRate
*/
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;
}