summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--win/tclWinSerial.c117
1 files changed, 104 insertions, 13 deletions
diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c
index 42a18f6..8503ac2 100644
--- a/win/tclWinSerial.c
+++ b/win/tclWinSerial.c
@@ -10,7 +10,7 @@
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
* Changes by Rolf.Schroedter@dlr.de June 25-27, 1999
*
- * RCS: @(#) $Id: tclWinSerial.c,v 1.8 1999/10/05 22:47:05 hobbs Exp $
+ * RCS: @(#) $Id: tclWinSerial.c,v 1.9 1999/11/24 20:55:17 hobbs Exp $
*/
#include "tclWinInt.h"
@@ -49,6 +49,13 @@ static int initialized = 0;
#define SERIAL_DEFAULT_BLOCKTIME 10 /* 10 msec */
/*
+ * Define Win32 read/write error masks returned by ClearCommError()
+ */
+#define SERIAL_READ_ERRORS ( CE_RXOVER | CE_OVERRUN | CE_RXPARITY \
+ | CE_FRAME | CE_BREAK )
+#define SERIAL_WRITE_ERRORS ( CE_TXFULL )
+
+/*
* This structure describes per-instance data for a serial based channel.
*/
@@ -66,6 +73,10 @@ typedef struct SerialInfo {
int writable; /* flag that the channel is readable */
int readable; /* flag that the channel is readable */
int blockTime; /* max. blocktime in msec */
+ DWORD error; /* pending error code returned by
+ * ClearCommError() */
+ DWORD lastError; /* last error code, can be fetched with
+ * fconfigure chan -lasterror */
} SerialInfo;
typedef struct ThreadSpecificData {
@@ -343,7 +354,6 @@ SerialCheckProc(
SerialEvent *evPtr;
int needEvent;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- DWORD cError;
COMSTAT cStat;
if (!(flags & TCL_FILE_EVENTS)) {
@@ -370,14 +380,18 @@ SerialCheckProc(
*/
if( infoPtr->watchMask & (TCL_WRITABLE | TCL_READABLE) ) {
- if( ClearCommError( infoPtr->handle, &cError, &cStat ) ) {
+ if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
/*
* Look for empty output buffer. If empty, poll.
*/
if( infoPtr->watchMask & TCL_WRITABLE ) {
- if( ((infoPtr->flags & SERIAL_WRITE) != 0) && \
- (cStat.cbOutQue == 0) ) {
+ /*
+ * force fileevent after serial write error
+ */
+ if (((infoPtr->flags & SERIAL_WRITE) != 0) &&
+ ((cStat.cbOutQue == 0) ||
+ (infoPtr->error & SERIAL_WRITE_ERRORS))) {
/*
* allow only one fileevent after each callback
*/
@@ -394,7 +408,11 @@ SerialCheckProc(
*/
if( infoPtr->watchMask & TCL_READABLE ) {
- if( cStat.cbInQue > 0 ) {
+ /*
+ * force fileevent after serial read error
+ */
+ if( (cStat.cbInQue > 0) ||
+ (infoPtr->error & SERIAL_READ_ERRORS) ) {
infoPtr->readable = 1;
needEvent = 1;
}
@@ -566,24 +584,29 @@ SerialInputProc(
SerialInfo *infoPtr = (SerialInfo *) instanceData;
DWORD bytesRead = 0;
DWORD err;
- DWORD cError;
COMSTAT cStat;
*errorCode = 0;
+ /*
+ * Check if there is a CommError pending from SerialCheckProc
+ */
+ if( infoPtr->error & SERIAL_READ_ERRORS ){
+ goto commError;
+ }
+
/*
* Look for characters already pending in windows queue.
* This is the mainly restored good old code from Tcl8.0
*/
- if( ClearCommError( infoPtr->handle, &cError, &cStat ) ) {
+ if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
/*
* Check for errors here, but not in the evSetup/Check procedures
*/
- if( cError != 0 ) {
- *errorCode = EIO;
- return -1;
+ if( infoPtr->error & SERIAL_READ_ERRORS ) {
+ goto commError;
}
if( infoPtr->flags & SERIAL_ASYNC ) {
/*
@@ -629,10 +652,16 @@ SerialInputProc(
}
return bytesRead;
-error:
+ error:
TclWinConvertError(GetLastError());
*errorCode = errno;
return -1;
+
+ commError:
+ infoPtr->lastError = infoPtr->error; /* save last error code */
+ infoPtr->error = 0; /* reset error code */
+ *errorCode = EIO; /* to return read-error only once */
+ return -1;
}
/*
@@ -666,6 +695,16 @@ SerialOutputProc(
*errorCode = 0;
/*
+ * Check if there is a CommError pending from SerialCheckProc
+ */
+ if( infoPtr->error & SERIAL_WRITE_ERRORS ){
+ infoPtr->lastError = infoPtr->error; /* save last error code */
+ infoPtr->error = 0; /* reset error code */
+ *errorCode = EIO; /* to return read-error only once */
+ return -1;
+ }
+
+ /*
* Check for a background error on the last write.
* Allow one write-fileevent after each callback
*/
@@ -926,6 +965,7 @@ TclWinOpenSerialChannel(handle, channelName, permissions)
infoPtr->readable = infoPtr->writable = 0;
infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME;
+ infoPtr->lastError = infoPtr->error = 0;
/*
* Files have default translation of AUTO and ^Z eof char, which
@@ -941,6 +981,45 @@ TclWinOpenSerialChannel(handle, channelName, permissions)
/*
*----------------------------------------------------------------------
*
+ * SerialErrorStr --
+ *
+ * Converts a Win32 serial error code to a list of readable errors
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+SerialErrorStr(error, dsPtr)
+ DWORD error; /* Win32 serial error code */
+ Tcl_DString *dsPtr; /* Where to store string */
+{
+ if( (error & CE_RXOVER) != 0) {
+ Tcl_DStringAppendElement(dsPtr, "RXOVER");
+ }
+ if( (error & CE_OVERRUN) != 0) {
+ Tcl_DStringAppendElement(dsPtr, "OVERRUN");
+ }
+ if( (error & CE_RXPARITY) != 0) {
+ Tcl_DStringAppendElement(dsPtr, "RXPARITY");
+ }
+ if( (error & CE_FRAME) != 0) {
+ Tcl_DStringAppendElement(dsPtr, "FRAME");
+ }
+ if( (error & CE_BREAK) != 0) {
+ Tcl_DStringAppendElement(dsPtr, "BREAK");
+ }
+ if( (error & CE_TXFULL) != 0) {
+ Tcl_DStringAppendElement(dsPtr, "TXFULL");
+ }
+ if( (error & ~(SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS)) != 0) {
+ char buf[TCL_INTEGER_SPACE + 1];
+ wsprintfA(buf, "%d", error);
+ Tcl_DStringAppendElement(dsPtr, buf);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* SerialSetOptionProc --
*
* Sets an option on a channel.
@@ -1103,9 +1182,21 @@ SerialGetOptionProc(instanceData, interp, optionName, dsPtr)
Tcl_DStringAppendElement(dsPtr, buf);
}
+ /*
+ * get option -lasterror
+ * option is readonly and returned by [fconfigure chan -lasterror]
+ * but not returned by unnamed [fconfigure chan]
+ */
+
+ if ( (len > 1) && (strncmp(optionName, "-lasterror", len) == 0) ) {
+ valid = 1;
+ SerialErrorStr(infoPtr->lastError, dsPtr);
+ }
+
if (valid) {
return TCL_OK;
} else {
- return Tcl_BadChannelOption(interp, optionName, "mode pollinterval");
+ return Tcl_BadChannelOption(interp, optionName,
+ "mode pollinterval lasterror");
}
}