1134 lines
27 KiB
C
1134 lines
27 KiB
C
/*
|
|
* DEC 93 Erik Bos <erik@xs4all.nl>
|
|
*
|
|
* Copyright 1996 Marcus Meissner
|
|
*
|
|
* Copyright 2001 Mike McCormack
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winreg.h"
|
|
#include "winuser.h"
|
|
#include "wine/winuser16.h"
|
|
#include "wine/port.h"
|
|
#include "heap.h"
|
|
#include "win.h"
|
|
#include "winerror.h"
|
|
|
|
#include "debugtools.h"
|
|
|
|
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 {
|
|
char *devicename; /* /dev/ttyS0 */
|
|
HANDLE handle;
|
|
int suspended;
|
|
int unget,xmit;
|
|
int baudrate;
|
|
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 */
|
|
LPCVOID *unknown;
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
void COMM_Init(void)
|
|
{
|
|
int x;
|
|
char option[10], temp[256], *btemp;
|
|
HKEY hkey;
|
|
|
|
for (x=0; x!=MAX_PORTS; x++) {
|
|
strcpy(option,"COMx");
|
|
option[3] = '1' + x;
|
|
option[4] = '\0';
|
|
|
|
/* default value */
|
|
strcpy(temp, "*");
|
|
|
|
if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\serialports", &hkey))
|
|
{
|
|
DWORD type, count = sizeof(temp);
|
|
|
|
RegQueryValueExA(hkey, option, 0, &type, temp, &count);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
if (!strcmp(temp, "*") || *temp == '\0')
|
|
COM[x].devicename = NULL;
|
|
else {
|
|
btemp = strchr(temp,',');
|
|
if (btemp != NULL) {
|
|
*btemp++ = '\0';
|
|
COM[x].baudrate = atoi(btemp);
|
|
} else {
|
|
COM[x].baudrate = -1;
|
|
}
|
|
if ((COM[x].devicename = malloc(strlen(temp)+1)) == NULL)
|
|
WARN("Can't malloc for device info!\n");
|
|
else {
|
|
COM[x].handle = 0;
|
|
strcpy(COM[x].devicename, temp);
|
|
TRACE("%s = %s\n", option, COM[x].devicename);
|
|
}
|
|
}
|
|
|
|
strcpy(option, "LPTx");
|
|
option[3] = '1' + x;
|
|
option[4] = '\0';
|
|
|
|
/* default value */
|
|
strcpy(temp, "*");
|
|
|
|
if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\parallelports", &hkey))
|
|
{
|
|
DWORD type, count = sizeof(temp);
|
|
|
|
RegQueryValueExA(hkey, option, 0, &type, temp, &count);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
if (!strcmp(temp, "*") || *temp == '\0')
|
|
LPT[x].devicename = NULL;
|
|
else {
|
|
if ((LPT[x].devicename = malloc(strlen(temp)+1)) == NULL)
|
|
WARN("Can't malloc for device info!\n");
|
|
else {
|
|
LPT[x].handle = 0;
|
|
strcpy(LPT[x].devicename, temp);
|
|
TRACE("%s = %s\n", option, LPT[x].devicename);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
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 ValidCOMPort(int x)
|
|
{
|
|
return(x < MAX_PORTS ? (int) COM[x].devicename : 0);
|
|
}
|
|
|
|
static int ValidLPTPort(int x)
|
|
{
|
|
return(x < MAX_PORTS ? (int) LPT[x].devicename : 0);
|
|
}
|
|
|
|
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];
|
|
|
|
/* read data from comm port */
|
|
if (status != STATUS_SUCCESS) {
|
|
ERR("async read failed\n");
|
|
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 %04x: 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);
|
|
}
|
|
|
|
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) {
|
|
if(!WriteFile(ptr->handle, &(ptr->xmit), 1, &len, NULL))
|
|
len = -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 %04x: 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)
|
|
{
|
|
int bleft;
|
|
|
|
bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ?
|
|
(ptr->ibuf_tail-1) : ptr->ibuf_size) - ptr->ibuf_head;
|
|
/* FIXME: get timeouts working properly so we can read bleft bytes */
|
|
ReadFileEx(ptr->handle,
|
|
ptr->inbuf + ptr->ibuf_head,
|
|
1,
|
|
&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->fDtrDisable = 0;
|
|
|
|
lpdcb16->fDtrflow = (lpdcb->fDtrControl==DTR_CONTROL_ENABLE);
|
|
lpdcb16->fRtsflow = (lpdcb->fRtsControl==RTS_CONTROL_ENABLE);
|
|
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;
|
|
}
|
|
|
|
if (!ValidCOMPort(port)) {
|
|
FIXME("invalid COM port %d?\n",port);
|
|
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)) {
|
|
|
|
TRACE("%s = %s\n", device, COM[port].devicename);
|
|
|
|
if (!ValidCOMPort(port))
|
|
return IE_BADID;
|
|
|
|
if (COM[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) {
|
|
ERR("Couldn't open %s ! (%s)\n", COM[port].devicename, strerror(errno));
|
|
return IE_HARDWARE;
|
|
} else {
|
|
COM[port].unknown = SEGPTR_ALLOC(40);
|
|
memset(COM[port].unknown, 0, 40);
|
|
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);
|
|
/* set default parameters */
|
|
if(COM[port].baudrate>-1){
|
|
DCB16 dcb;
|
|
memcpy(&dcb,&COM[port].dcb,sizeof dcb);
|
|
dcb.BaudRate=COM[port].baudrate;
|
|
/* more defaults:
|
|
* databits, parity, stopbits
|
|
*/
|
|
SetCommState16( &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 = malloc(cbInQueue);
|
|
if (COM[port].inbuf) {
|
|
COM[port].outbuf = malloc(cbOutQueue);
|
|
if (!COM[port].outbuf)
|
|
free(COM[port].inbuf);
|
|
} else COM[port].outbuf = NULL;
|
|
if (!COM[port].outbuf) {
|
|
/* not enough memory */
|
|
SetCommState16(&COM[port].dcb);
|
|
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));
|
|
COM[port].read_ov.hEvent = CreateEventA(NULL,0,0,NULL);
|
|
COM[port].write_ov.hEvent = CreateEventA(NULL,0,0,NULL);
|
|
|
|
comm_waitread( &COM[port] );
|
|
|
|
return port;
|
|
}
|
|
}
|
|
else
|
|
if (!strncasecmp(device,"LPT",3)) {
|
|
|
|
if (!ValidLPTPort(port))
|
|
return IE_BADID;
|
|
|
|
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 */
|
|
SEGPTR_FREE(COM[cid].unknown); /* [LW] */
|
|
|
|
CloseHandle(COM[cid].read_ov.hEvent);
|
|
CloseHandle(COM[cid].write_ov.hEvent);
|
|
|
|
/* free buffers */
|
|
free(ptr->outbuf);
|
|
free(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;
|
|
int max;
|
|
|
|
TRACE("cid=%d, function=%d\n", cid, nFunction);
|
|
|
|
switch(nFunction) {
|
|
case GETMAXCOM:
|
|
TRACE("GETMAXCOM\n");
|
|
for (max = MAX_PORTS;!COM[max].devicename;max--)
|
|
;
|
|
return max;
|
|
|
|
case GETMAXLPT:
|
|
TRACE("GETMAXLPT\n");
|
|
for (max = MAX_PORTS;!LPT[max].devicename;max--)
|
|
;
|
|
return FLAG_LPT + max;
|
|
|
|
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) {
|
|
HANDLE rw_events[2];
|
|
|
|
lpStat->status = 0;
|
|
|
|
rw_events[0] = COM[cid].read_ov.hEvent;
|
|
rw_events[1] = COM[cid].write_ov.hEvent;
|
|
|
|
WaitForMultipleObjectsEx(2,&rw_events[0],FALSE,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) || !ValidCOMPort(cid)) {
|
|
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);
|
|
return SEGPTR_GET(COM[cid].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) || !ValidCOMPort(cid)) {
|
|
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;
|
|
if(lpdcb->BaudRate==57601)
|
|
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 */
|
|
DWORD len;
|
|
if(!WriteFile(ptr->handle, &chTransmit, 1, &len, NULL)) {
|
|
/* 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;
|
|
}
|
|
|
|
/* 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", length, orgBuf);
|
|
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", cbWrite, lpvBuf );
|
|
|
|
length = 0;
|
|
while (length < cbWrite) {
|
|
if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
|
|
/* no data queued, try to write directly */
|
|
if(!WriteFile(ptr->handle, lpvBuf, cbWrite - length, (LPDWORD)&status, NULL))
|
|
status = -1;
|
|
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;
|
|
}
|
|
|