723 lines
16 KiB
C
723 lines
16 KiB
C
|
/*
|
|||
|
* win/dpSerial.c
|
|||
|
*
|
|||
|
* Win32 Tcl_Channel implementation for serial ports
|
|||
|
*/
|
|||
|
|
|||
|
#include "generic/dpPort.h"
|
|||
|
#include "generic/dpInt.h"
|
|||
|
|
|||
|
/*
|
|||
|
* This is a Tcl function that is not exported but
|
|||
|
* is very handy for error reporting so we use it
|
|||
|
* anyhow.
|
|||
|
*/
|
|||
|
|
|||
|
extern void TclWinConvertError(DWORD errCode);
|
|||
|
|
|||
|
#define MAX_LENGTH 10
|
|||
|
#define NUM_PORTS 4
|
|||
|
|
|||
|
static char *portNames[NUM_PORTS] = {
|
|||
|
"COM1",
|
|||
|
"COM2",
|
|||
|
"COM3",
|
|||
|
"COM4"
|
|||
|
};
|
|||
|
|
|||
|
static unsigned long serialCount = 0;
|
|||
|
|
|||
|
int DppOpenSerialChannel _ANSI_ARGS_((Tcl_Interp *interp,
|
|||
|
ClientData instanceData, char *devStr, int flags));
|
|||
|
int DppSerialBlock _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mode));
|
|||
|
int DppSerialClose _ANSI_ARGS_((ClientData instanceData));
|
|||
|
int DppSerialInput _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *bufPtr, int bufSize, int *errorCodePtr));
|
|||
|
int DppSerialOutput _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *bufPtr, int toWrite, int *errorCodePtr));
|
|||
|
int DppSerialSetOption _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int optionName, int val));
|
|||
|
int DppSerialGetOption _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int opt, char *optionName,
|
|||
|
Tcl_DString *dsPtr));
|
|||
|
int DppSerialFileReady _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
void DppSerialWatchFile _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
static char * DppBaudRateConsToStr _ANSI_ARGS_((int rate));
|
|||
|
static int DppBaudRateNumToCons _ANSI_ARGS_((int rate));
|
|||
|
|
|||
|
static char * DppCheckDevice _ANSI_ARGS_((char *devStr));
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* ------------------------------------------------
|
|||
|
*
|
|||
|
* DppOpenSerialChannel -
|
|||
|
*
|
|||
|
* Creates a DP channel using the serial port specified
|
|||
|
* in dev (i.e. "COM1" or "COM2")
|
|||
|
*
|
|||
|
* We do not allow nonblocking IO since we are not
|
|||
|
* multithreaded.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* Tcl_Channel used for I/O.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* Memory allocated.
|
|||
|
*
|
|||
|
* ------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppOpenSerialChannel(interp, instanceData, devStr, flags)
|
|||
|
Tcl_Interp *interp;
|
|||
|
ClientData instanceData;
|
|||
|
char *devStr; /* (in) device to use */
|
|||
|
int flags; /* Bit 0: block Bit 1: read-only */
|
|||
|
{
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
char *openStr;
|
|||
|
HANDLE fd;
|
|||
|
DCB dcb;
|
|||
|
char channelName[10];
|
|||
|
Tcl_Channel chan;
|
|||
|
int mode = GENERIC_WRITE;
|
|||
|
|
|||
|
if ((openStr = DppCheckDevice(devStr)) == NULL) {
|
|||
|
Tcl_AppendResult(interp, "Unknown device \"", devStr, "\"", NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
if (flags & 0x2) {
|
|||
|
mode = 0;
|
|||
|
}
|
|||
|
|
|||
|
fd = CreateFile(openStr, GENERIC_READ | mode,
|
|||
|
0, NULL, OPEN_EXISTING, 0, NULL);
|
|||
|
|
|||
|
if (fd == INVALID_HANDLE_VALUE) {
|
|||
|
TclWinConvertError(GetLastError());
|
|||
|
Tcl_AppendResult(interp, "Error opening ", openStr, ": ",
|
|||
|
Tcl_PosixError(interp), NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Setup serial port to a default configuration
|
|||
|
*/
|
|||
|
|
|||
|
GetCommState(fd, &dcb);
|
|||
|
if (!BuildCommDCB("19200,N,8,1", &dcb)) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
SetCommState(fd, &dcb);
|
|||
|
|
|||
|
ssPtr->fd = fd;
|
|||
|
strcpy(ssPtr->deviceName, devStr);
|
|||
|
|
|||
|
/*
|
|||
|
* Set blocking mode for port
|
|||
|
*/
|
|||
|
|
|||
|
if (DppSerialSetOption((ClientData)ssPtr, DP_BLOCK, flags & 0x1)
|
|||
|
== TCL_ERROR) {
|
|||
|
goto error;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
|
|||
|
error:
|
|||
|
TclWinConvertError(GetLastError());
|
|||
|
Tcl_AppendResult(interp, "Error configuring serial device: ",
|
|||
|
Tcl_PosixError(interp), NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/* --------------------------------------------------
|
|||
|
*
|
|||
|
* DppSerialBlock --
|
|||
|
*
|
|||
|
* Sets serial channel to block or not.
|
|||
|
*
|
|||
|
* Our non-blocking implementation is a bit
|
|||
|
* strange. We are not using some sort of
|
|||
|
* callback/event mechanism but rather
|
|||
|
* emulating not blocking by simply reading
|
|||
|
* all we can at that moment and returning it.
|
|||
|
* The user MUST check to make sure the entire
|
|||
|
* message was received when reading or else
|
|||
|
* turning blocking on.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* TCL_OK or TCL_ERROR
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* ---------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppSerialBlock(instanceData, mode)
|
|||
|
ClientData instanceData;
|
|||
|
int mode;
|
|||
|
{
|
|||
|
if (mode == TCL_MODE_BLOCKING) {
|
|||
|
return DppSerialSetOption(instanceData, DP_BLOCK, 1);
|
|||
|
} else {
|
|||
|
return DppSerialSetOption(instanceData, DP_BLOCK, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* --------------------------------------------------
|
|||
|
*
|
|||
|
* SerialClose --
|
|||
|
*
|
|||
|
* Closes the serial port and frees memory
|
|||
|
* associated with the port.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* TCL_OK or TCL_ERROR
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* Channel is no longer available.
|
|||
|
*
|
|||
|
* ---------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppSerialClose(instanceData)
|
|||
|
ClientData instanceData;
|
|||
|
{
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
BOOL rc;
|
|||
|
|
|||
|
FlushFileBuffers(ssPtr->fd);
|
|||
|
rc = CloseHandle(ssPtr->fd);
|
|||
|
ckfree((char *)ssPtr);
|
|||
|
if (!rc) {
|
|||
|
return TCL_ERROR;
|
|||
|
} else {
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* --------------------------------------------------
|
|||
|
*
|
|||
|
* SerialInput --
|
|||
|
*
|
|||
|
* Reads upto bufSize bytes from serial port
|
|||
|
* into buf.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* Number of bytes read or -1 with Win32 error code
|
|||
|
* in errorCodePtr.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* buf is modified.
|
|||
|
*
|
|||
|
* -------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppSerialInput(instanceData, bufPtr, bufSize, errorCodePtr)
|
|||
|
ClientData instanceData;
|
|||
|
char *bufPtr;
|
|||
|
int bufSize;
|
|||
|
int *errorCodePtr;
|
|||
|
{
|
|||
|
BOOL rc;
|
|||
|
DWORD amount;
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
|
|||
|
rc = ReadFile(ssPtr->fd, bufPtr, bufSize, &amount, NULL);
|
|||
|
if (!rc) {
|
|||
|
TclWinConvertError(GetLastError());
|
|||
|
*errorCodePtr = Tcl_GetErrno();
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if (!amount) {
|
|||
|
// We read no data.
|
|||
|
// Check to see if we are in non-blocking mode
|
|||
|
// and return an EAGAIN error if we are...
|
|||
|
COMMTIMEOUTS cto;
|
|||
|
GetCommTimeouts(ssPtr->fd, &cto);
|
|||
|
if (cto.ReadIntervalTimeout == MAXDWORD) {
|
|||
|
*errorCodePtr = EAGAIN;
|
|||
|
} else {
|
|||
|
TclWinConvertError(GetLastError());
|
|||
|
*errorCodePtr = Tcl_GetErrno();
|
|||
|
}
|
|||
|
return -1;
|
|||
|
}
|
|||
|
return amount;
|
|||
|
}
|
|||
|
|
|||
|
/* --------------------------------------------------
|
|||
|
*
|
|||
|
* SerialOutput --
|
|||
|
*
|
|||
|
* Sends toWrite bytes out through the serial
|
|||
|
* port.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* Number of bytes written or -1 and a POSIX
|
|||
|
* error in errorCodePtr.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* ---------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppSerialOutput(instanceData, bufPtr, toWrite, errorCodePtr)
|
|||
|
ClientData instanceData;
|
|||
|
char *bufPtr;
|
|||
|
int toWrite;
|
|||
|
int *errorCodePtr;
|
|||
|
{
|
|||
|
BOOL rc;
|
|||
|
DWORD amount;
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
|
|||
|
rc = WriteFile(ssPtr->fd, bufPtr, toWrite, &amount, NULL);
|
|||
|
if (!rc) {
|
|||
|
TclWinConvertError(GetLastError());
|
|||
|
*errorCodePtr = Tcl_GetErrno();
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if (!amount) {
|
|||
|
// We wrote no data.
|
|||
|
// Check to see if we are in non-blocking mode
|
|||
|
// and return an EAGAIN error if we are...
|
|||
|
COMMTIMEOUTS cto;
|
|||
|
GetCommTimeouts(ssPtr->fd, &cto);
|
|||
|
if (cto.ReadIntervalTimeout == MAXDWORD) {
|
|||
|
*errorCodePtr = EAGAIN;
|
|||
|
} else {
|
|||
|
TclWinConvertError(GetLastError());
|
|||
|
*errorCodePtr = Tcl_GetErrno();
|
|||
|
}
|
|||
|
return -1;
|
|||
|
}
|
|||
|
FlushFileBuffers(ssPtr->fd);
|
|||
|
return amount;
|
|||
|
}
|
|||
|
|
|||
|
/* --------------------------------------------------
|
|||
|
*
|
|||
|
* Dpp_SetSerialState --
|
|||
|
*
|
|||
|
* Platform-specific serial option changer.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* TCL_OK or TCL_ERROR
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* ---------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppSerialSetOption(instanceData, optionName, val)
|
|||
|
ClientData instanceData;
|
|||
|
int optionName;
|
|||
|
int val;
|
|||
|
{
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
DCB settings;
|
|||
|
int rate;
|
|||
|
COMMTIMEOUTS cto;
|
|||
|
|
|||
|
if (!GetCommState(ssPtr->fd, &settings)) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
switch (optionName) {
|
|||
|
case DP_PARITY:
|
|||
|
if (val == PARITY_NONE) {
|
|||
|
settings.fParity = FALSE;
|
|||
|
settings.Parity = NOPARITY;
|
|||
|
} else {
|
|||
|
settings.fParity = TRUE;
|
|||
|
if (val == PARITY_EVEN) {
|
|||
|
settings.Parity = EVENPARITY;
|
|||
|
} else {
|
|||
|
settings.Parity = ODDPARITY;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
case DP_CHARSIZE:
|
|||
|
if (val == 7) {
|
|||
|
settings.ByteSize = 7;
|
|||
|
} else {
|
|||
|
settings.ByteSize = 8;
|
|||
|
}
|
|||
|
break;
|
|||
|
case DP_STOPBITS:
|
|||
|
if (val == 1) {
|
|||
|
settings.StopBits = ONESTOPBIT;
|
|||
|
} else {
|
|||
|
settings.StopBits = TWOSTOPBITS;
|
|||
|
}
|
|||
|
break;
|
|||
|
case DP_BAUDRATE:
|
|||
|
rate = DppBaudRateNumToCons(val);
|
|||
|
if (rate == -1) {
|
|||
|
char baud[7];
|
|||
|
sprintf(baud, "%ld", val);
|
|||
|
Tcl_SetErrno(EINVAL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
settings.BaudRate = rate;
|
|||
|
break;
|
|||
|
case DP_BLOCK:
|
|||
|
memset(&cto, 0, sizeof(COMMTIMEOUTS));
|
|||
|
if (val == 1) {
|
|||
|
/*
|
|||
|
* We want to block.
|
|||
|
* A read has numbytes * 1 s to complete
|
|||
|
* before the system will return an error.
|
|||
|
*
|
|||
|
* A byte MUST arrive at least every 3 seconds
|
|||
|
* during the read or we will timeout with
|
|||
|
* an error.
|
|||
|
*/
|
|||
|
cto.ReadTotalTimeoutMultiplier = 1000;
|
|||
|
cto.ReadIntervalTimeout = 3000;
|
|||
|
} else {
|
|||
|
/*
|
|||
|
* This line will set the serial port to:
|
|||
|
* READ - Return as much as possible without blocking
|
|||
|
* WRITE - Write buf then return
|
|||
|
*/
|
|||
|
cto.ReadIntervalTimeout = MAXDWORD;
|
|||
|
}
|
|||
|
SetCommTimeouts(ssPtr->fd, &cto);
|
|||
|
return TCL_OK;
|
|||
|
|
|||
|
default:
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (!SetCommState(ssPtr->fd, &settings)) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/* ----------------------------------------------------
|
|||
|
*
|
|||
|
* DppBaudRateNumToCons --
|
|||
|
*
|
|||
|
* Changes a numeric rate into a baudrate constant
|
|||
|
* understood by the platform.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* The baudrate constant or -1 on error.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* -----------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppBaudRateNumToCons(rate)
|
|||
|
int rate;
|
|||
|
{
|
|||
|
switch (rate) {
|
|||
|
case 1200:
|
|||
|
return CBR_1200;
|
|||
|
case 2400:
|
|||
|
return CBR_2400;
|
|||
|
case 4800:
|
|||
|
return CBR_4800;
|
|||
|
case 9600:
|
|||
|
return CBR_9600;
|
|||
|
case 19200:
|
|||
|
return CBR_19200;
|
|||
|
case 38400:
|
|||
|
return CBR_38400;
|
|||
|
case 57600:
|
|||
|
return CBR_57600;
|
|||
|
case 115200:
|
|||
|
return CBR_115200;
|
|||
|
default:
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* ----------------------------------------------------
|
|||
|
*
|
|||
|
* DppSerialGetOption --
|
|||
|
*
|
|||
|
* Returns the value of the given option in dsPtr.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* TCL_OK or TCL_ERROR
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* -----------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppSerialGetOption(instanceData, opt, optionName, dsPtr)
|
|||
|
ClientData instanceData;
|
|||
|
int opt;
|
|||
|
char *optionName;
|
|||
|
Tcl_DString *dsPtr;
|
|||
|
{
|
|||
|
DCB commState;
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
char *rate;
|
|||
|
COMMTIMEOUTS cto;
|
|||
|
|
|||
|
GetCommState(ssPtr->fd, &commState);
|
|||
|
switch (opt) {
|
|||
|
case DP_PARITY:
|
|||
|
if (commState.Parity == EVENPARITY) {
|
|||
|
Tcl_DStringAppend(dsPtr, "even", -1);
|
|||
|
} else if (commState.Parity == ODDPARITY) {
|
|||
|
Tcl_DStringAppend(dsPtr, "odd", -1);
|
|||
|
} else if (commState.Parity == NOPARITY) {
|
|||
|
Tcl_DStringAppend(dsPtr, "none", -1);
|
|||
|
} else {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
case DP_BAUDRATE:
|
|||
|
rate = DppBaudRateConsToStr(commState.BaudRate);
|
|||
|
Tcl_DStringAppend(dsPtr, rate, -1);
|
|||
|
return TCL_OK;
|
|||
|
case DP_CHARSIZE:
|
|||
|
if (commState.ByteSize == 7) {
|
|||
|
Tcl_DStringAppend(dsPtr, "7", -1);
|
|||
|
} else {
|
|||
|
Tcl_DStringAppend(dsPtr, "8", -1);
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
case DP_STOPBITS:
|
|||
|
if (commState.StopBits == ONESTOPBIT) {
|
|||
|
Tcl_DStringAppend(dsPtr, "1", -1);
|
|||
|
} else {
|
|||
|
Tcl_DStringAppend(dsPtr, "2", -1);
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
case DP_BLOCK:
|
|||
|
GetCommTimeouts(ssPtr->fd, &cto);
|
|||
|
if (cto.ReadIntervalTimeout < MAXDWORD) {
|
|||
|
Tcl_DStringAppend(dsPtr, "true", -1);
|
|||
|
} else {
|
|||
|
Tcl_DStringAppend(dsPtr, "false", -1);
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
case DP_DEVICENAME:
|
|||
|
Tcl_DStringAppend(dsPtr, ssPtr->deviceName, -1);
|
|||
|
return TCL_OK;
|
|||
|
default:
|
|||
|
{
|
|||
|
char errStr[128];
|
|||
|
sprintf(errStr, "bad option \"%s\": must be -blocking,"
|
|||
|
"-buffering, -buffersize, -eofchar, -translation,"
|
|||
|
" or a channel type specific option", optionName);
|
|||
|
Tcl_DStringAppend(dsPtr, errStr, -1);
|
|||
|
}
|
|||
|
Tcl_SetErrno(EINVAL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* ----------------------------------------------------
|
|||
|
*
|
|||
|
* DppBaudRateConsToStr --
|
|||
|
*
|
|||
|
* Translates a Win32 baudrate constant to a
|
|||
|
* human-readable string.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* Pointer to the string representing the baudrate.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* -----------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
char *
|
|||
|
DppBaudRateConsToStr(rate)
|
|||
|
int rate;
|
|||
|
{
|
|||
|
switch (rate) {
|
|||
|
case CBR_1200:
|
|||
|
return "1200";
|
|||
|
case CBR_2400:
|
|||
|
return "2400";
|
|||
|
case CBR_4800:
|
|||
|
return "4800";
|
|||
|
case CBR_9600:
|
|||
|
return "9600";
|
|||
|
case CBR_19200:
|
|||
|
return "19200";
|
|||
|
case CBR_38400:
|
|||
|
return "38400";
|
|||
|
case CBR_57600:
|
|||
|
return "57600";
|
|||
|
case CBR_115200:
|
|||
|
return "115200";
|
|||
|
default:
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* ----------------------------------------------------
|
|||
|
*
|
|||
|
* DppCheckDevice --
|
|||
|
*
|
|||
|
* Verifies that "checkStr" is a valid serial
|
|||
|
* device on the OS or the DP naming method of
|
|||
|
* "serialx". In Win32, we assume a device is
|
|||
|
* any of the form "COMx" where x is a single
|
|||
|
* digit number. If the name is given as
|
|||
|
* "serialx", we translate it into the OS term.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* TCL_OK and updates devStr if checkStr is valid or
|
|||
|
* TCL_ERROR if checkStr is invalid.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* -----------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
char *
|
|||
|
DppCheckDevice(devStr)
|
|||
|
char *devStr;
|
|||
|
{
|
|||
|
int num;
|
|||
|
|
|||
|
if (strlen(devStr) == 7) {
|
|||
|
if (_strnicmp(devStr, "serial", 6) == 0) {
|
|||
|
num = devStr[6] - '1';
|
|||
|
if ((num < 0) || (num > 3)) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
return portNames[num];
|
|||
|
}
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* ----------------------------------------------------
|
|||
|
*
|
|||
|
* DppSerialWatchFile --
|
|||
|
*
|
|||
|
* Sets up event handling on the serial channel.
|
|||
|
* We jsut set the event mask on the given handle.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* Immediately.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* -----------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
DppSerialWatchFile(instanceData, mask)
|
|||
|
ClientData instanceData;
|
|||
|
int mask;
|
|||
|
{
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
DWORD evts = 0;
|
|||
|
|
|||
|
if (mask & TCL_READABLE) {
|
|||
|
evts |= EV_RXCHAR;
|
|||
|
}
|
|||
|
if (mask & TCL_WRITABLE) {
|
|||
|
evts |= EV_TXEMPTY;
|
|||
|
}
|
|||
|
if (mask & TCL_EXCEPTION) {
|
|||
|
evts |= EV_ERR;
|
|||
|
}
|
|||
|
SetCommMask(ssPtr->fd, evts);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* ----------------------------------------------------
|
|||
|
*
|
|||
|
* DppSerialFileReady --
|
|||
|
*
|
|||
|
* Waits for an event to happen on the serial port.
|
|||
|
* CAUTION!!!!
|
|||
|
* This is different than the Tcl specs because
|
|||
|
* there is no way to see what events have
|
|||
|
* already happened: we MUST block until a new
|
|||
|
* event takes place.
|
|||
|
*
|
|||
|
* Returns
|
|||
|
*
|
|||
|
* A mask of events.
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
*
|
|||
|
* None.
|
|||
|
*
|
|||
|
* -----------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
DppSerialFileReady(instanceData, mask)
|
|||
|
ClientData instanceData;
|
|||
|
int mask;
|
|||
|
{
|
|||
|
SerialState *ssPtr = (SerialState *) instanceData;
|
|||
|
OVERLAPPED ovStr;
|
|||
|
DWORD evts;
|
|||
|
DWORD events = 0;
|
|||
|
|
|||
|
GetCommMask(ssPtr->fd, &evts);
|
|||
|
WaitCommEvent(ssPtr->fd, &evts, &ovStr);
|
|||
|
if (evts & EV_RXCHAR) {
|
|||
|
events |= TCL_READABLE;
|
|||
|
}
|
|||
|
if (evts & EV_TXEMPTY) {
|
|||
|
events |= TCL_WRITABLE;
|
|||
|
}
|
|||
|
if (evts & EV_ERR) {
|
|||
|
events |= TCL_EXCEPTION;
|
|||
|
}
|
|||
|
return events;
|
|||
|
}
|
|||
|
|