diff options
author | redman <redman> | 1999-07-07 02:37:30 (GMT) |
---|---|---|
committer | redman <redman> | 1999-07-07 02:37:30 (GMT) |
commit | 75c5493d11453483097ff1c5d8808602534ec857 (patch) | |
tree | 84dd7036062da6e3fefd947b588e6d57c880cfb6 | |
parent | 783338db2a8c129447827e4aff8cb679f0aeafad (diff) | |
download | tcl-75c5493d11453483097ff1c5d8808602534ec857.zip tcl-75c5493d11453483097ff1c5d8808602534ec857.tar.gz tcl-75c5493d11453483097ff1c5d8808602534ec857.tar.bz2 |
New windows serial port driver.
[Bug 1980 2217]
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | win/tclWinSerial.c | 1283 |
2 files changed, 491 insertions, 810 deletions
@@ -1,10 +1,18 @@ -1999-0706 <welch@scriptics.com> +1999-07-06 <redman@scriptics.com> + + * win/tclWinSerial.c: New implementation of serial port driver + from Rolf Shroedter (Rolf.Schroedter@dlr.de) that allows more than + one byte to be read from the port. Implemented using polling + instead of threads, there is a max. 10ms latency between checking the + port for file events. [Bug: 1980 2217] + +1999-07-06 <welch@scriptics.com> * library/http2.0/http.tcl: Fixed the -timeout option so it handles timeouts that occur during connection attempts to hosts that are down (the only case that really matters!) -1999-0703 <welch@scriptics.com> +1999-07-03 <welch@scriptics.com> * doc/ChnlStack.3: * generic/tcl.decls: @@ -12,7 +20,7 @@ from Andreas Kupres that adds new C APIs Tcl_StackChannel, Tcl_UnstackChannel, and Tcl_GetStackedChannel. -1999-0703 <welch@scriptics.com> +1999-07-03 <welch@scriptics.com> * generic/tclNotify.c: * unix/tclUnixNotfy.c: @@ -23,7 +31,7 @@ hook points in the notifiers to be able to replace the notifier calls at runtime The Xt notifier and test program use this hook. -1999-0703 <welch@scriptics.com> +1999-07-03 <welch@scriptics.com> * generic/tclParse.c: Changed parsing of variable names to allow empty array names. Now "$(foo)" is a variable reference! @@ -31,7 +39,7 @@ This change is requested by Jean-Luc Fontaine for his STOOOP package. -1999-0701 <redman@scriptics.com> +1999-07-01 <redman@scriptics.com> * generic/tclCmdAH.c: * generic/tclFCmd.c: Call TclStat instead of TclpStat in order to diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c index 35bc13c..3cdd905 100644 --- a/win/tclWinSerial.c +++ b/win/tclWinSerial.c @@ -1,15 +1,16 @@ /* * Tclwinserial.c -- * - * This file implements the Windows-specific serial port functions, - * and the "serial" channel driver. + * This file implements the Windows-specific serial port functions, + * and the "serial" channel driver. * * Copyright (c) 1999 by Scriptics Corp. * * See the file "license.terms" for information on usage and redistribution * 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.4 1999/04/21 21:50:34 rjohnson Exp $ + * RCS: @(#) $Id: tclWinSerial.c,v 1.5 1999/07/07 02:37:31 redman Exp $ */ #include "tclWinInt.h" @@ -27,28 +28,20 @@ static int initialized = 0; /* - * The serialMutex locks around access to the initialized variable, and it is - * used to protect background threads from being terminated while they are - * using APIs that hold locks. - */ - -TCL_DECLARE_MUTEX(serialMutex) - -/* * Bit masks used in the flags field of the SerialInfo structure below. */ -#define SERIAL_PENDING (1<<0) /* Message is pending in the queue. */ -#define SERIAL_ASYNC (1<<1) /* Channel is non-blocking. */ +#define SERIAL_PENDING (1<<0) /* Message is pending in the queue. */ +#define SERIAL_ASYNC (1<<1) /* Channel is non-blocking. */ /* * Bit masks used in the sharedFlags field of the SerialInfo structure below. */ -#define SERIAL_EOF (1<<2) /* Serial has reached EOF. */ -#define SERIAL_EXTRABYTE (1<<3) /* Extra byte consumed while waiting for data - */ -#define SERIAL_ERROR (1<<4) +#define SERIAL_EOF (1<<2) /* Serial has reached EOF. */ +#define SERIAL_ERROR (1<<4) +#define SERIAL_WRITE (1<<5) /* enables fileevent writable + * one time after write operation */ /* * This structure describes per-instance data for a serial based channel. @@ -56,55 +49,17 @@ TCL_DECLARE_MUTEX(serialMutex) typedef struct SerialInfo { HANDLE handle; - struct SerialInfo *nextPtr; /* Pointer to next registered serial. */ - Tcl_Channel channel; /* Pointer to channel structure. */ - int validMask; /* OR'ed combination of TCL_READABLE, - * TCL_WRITABLE, or TCL_EXCEPTION: indicates - * which operations are valid on the file. */ - int watchMask; /* OR'ed combination of TCL_READABLE, - * TCL_WRITABLE, or TCL_EXCEPTION: indicates - * which events should be reported. */ - int flags; /* State flags, see above for a list. */ - Tcl_ThreadId threadId; /* Thread to which events should be reported. - * This value is used by the reader/writer - * threads. */ - HANDLE writeThread; /* Handle to writer thread. */ - HANDLE readThread; /* Handle to reader thread. */ - HANDLE writable; /* Manual-reset event to signal when the - * writer thread has finished waiting for - * the current buffer to be written. */ - HANDLE readable; /* Manual-reset event to signal when the - * reader thread has finished waiting for - * input. */ - HANDLE startWriter; /* Auto-reset event used by the main thread to - * signal when the writer thread should attempt - * to write to the serial. */ - HANDLE startReader; /* Auto-reset event used by the main thread to - * signal when the reader thread should attempt - * to read from the serial. */ - DWORD writeError; /* An error caused by the last background - * write. Set to 0 if no error has been - * detected. This word is shared with the - * writer thread so access must be - * synchronized with the writable object. - */ - char *writeBuf; /* Current background output buffer. - * Access is synchronized with the writable - * object. */ - int writeBufLen; /* Size of write buffer. Access is - * synchronized with the writable - * object. */ - int toWrite; /* Current amount to be written. Access is - * synchronized with the writable object. */ - int readFlags; /* Flags that are shared with the reader - * thread. Access is synchronized with the - * readable object. */ - int writeFlags; /* Flags that are shared with the writer - * thread. Access is synchronized with the - * readable object. */ - int readyMask; /* Events that need to be checked on. */ - char extraByte; - + struct SerialInfo *nextPtr; /* Pointer to next registered serial. */ + Tcl_Channel channel; /* Pointer to channel structure. */ + int validMask; /* OR'ed combination of TCL_READABLE, + * TCL_WRITABLE, or TCL_EXCEPTION: indicates + * which operations are valid on the file. */ + int watchMask; /* OR'ed combination of TCL_READABLE, + * TCL_WRITABLE, or TCL_EXCEPTION: indicates + * which events should be reported. */ + int flags; /* State flags, see above for a list. */ + int writable; /* flag that the channel is readable */ + int readable; /* flag that the channel is readable */ } SerialInfo; typedef struct ThreadSpecificData { @@ -124,43 +79,64 @@ static Tcl_ThreadDataKey dataKey; */ typedef struct SerialEvent { - Tcl_Event header; /* Information that is standard for - * all events. */ - SerialInfo *infoPtr; /* Pointer to serial info structure. Note - * that we still have to verify that the - * serial exists before dereferencing this - * pointer. */ + Tcl_Event header; /* Information that is standard for + * all events. */ + SerialInfo *infoPtr; /* Pointer to serial info structure. Note + * that we still have to verify that the + * serial exists before dereferencing this + * pointer. */ } SerialEvent; /* + * Time to block between checking status on the serial port. + * For now, 10ms. + */ + +static Tcl_Time blockTime = { 0, 10000 }; + +COMMTIMEOUTS timeout_sync = { /* Timouts for blocking mode */ + MAXDWORD, /* ReadIntervalTimeout */ + MAXDWORD, /* ReadTotalTimeoutMultiplier */ + MAXDWORD-1, /* ReadTotalTimeoutConstant, + MAXDWORD-1 works for both Win95/NT */ + 0, /* WriteTotalTimeoutMultiplier */ + 0, /* WriteTotalTimeoutConstant */ +}; + +COMMTIMEOUTS timeout_async = { /* Timouts for non-blocking mode */ + 0, /* ReadIntervalTimeout */ + 0, /* ReadTotalTimeoutMultiplier */ + 1, /* ReadTotalTimeoutConstant */ + 0, /* WriteTotalTimeoutMultiplier */ + 0, /* WriteTotalTimeoutConstant */ +}; + +/* * Declarations for functions used only in this file. */ -static int SerialBlockProc(ClientData instanceData, int mode); -static void SerialCheckProc(ClientData clientData, int flags); -static int SerialCloseProc(ClientData instanceData, - Tcl_Interp *interp); -static int SerialEventProc(Tcl_Event *evPtr, int flags); -static void SerialExitHandler(ClientData clientData); -static int SerialGetHandleProc(ClientData instanceData, - int direction, ClientData *handlePtr); +static int SerialBlockProc(ClientData instanceData, int mode); +static void SerialCheckProc(ClientData clientData, int flags); +static int SerialCloseProc(ClientData instanceData, + Tcl_Interp *interp); +static int SerialEventProc(Tcl_Event *evPtr, int flags); +static void SerialExitHandler(ClientData clientData); +static int SerialGetHandleProc(ClientData instanceData, + int direction, ClientData *handlePtr); static ThreadSpecificData *SerialInit(void); -static int SerialInputProc(ClientData instanceData, char *buf, - int toRead, int *errorCode); -static int SerialOutputProc(ClientData instanceData, char *buf, - int toWrite, int *errorCode); -static DWORD WINAPI SerialReaderThread(LPVOID arg); -static void SerialSetupProc(ClientData clientData, int flags); -static void SerialWatchProc(ClientData instanceData, int mask); -static DWORD WINAPI SerialWriterThread(LPVOID arg); -static void ProcExitHandler(ClientData clientData); -static int WaitForRead(SerialInfo *infoPtr, int blocking); -static int SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData, - Tcl_Interp *interp, char *optionName, - Tcl_DString *dsPtr)); -static int SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData, - Tcl_Interp *interp, char *optionName, - char *value)); +static int SerialInputProc(ClientData instanceData, char *buf, + int toRead, int *errorCode); +static int SerialOutputProc(ClientData instanceData, char *buf, + int toWrite, int *errorCode); +static void SerialSetupProc(ClientData clientData, int flags); +static void SerialWatchProc(ClientData instanceData, int mask); +static void ProcExitHandler(ClientData clientData); +static int SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData, + Tcl_Interp *interp, char *optionName, + Tcl_DString *dsPtr)); +static int SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData, + Tcl_Interp *interp, char *optionName, + char *value)); /* * This structure describes the channel type structure for command serial @@ -168,16 +144,16 @@ static int SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData, */ static Tcl_ChannelType serialChannelType = { - "serial", /* Type name. */ - SerialBlockProc, /* Set blocking or non-blocking mode.*/ - SerialCloseProc, /* Close proc. */ - SerialInputProc, /* Input proc. */ - SerialOutputProc, /* Output proc. */ - NULL, /* Seek proc. */ - SerialSetOptionProc, /* Set option proc. */ - SerialGetOptionProc, /* Get option proc. */ - SerialWatchProc, /* Set up notifier to watch the channel. */ - SerialGetHandleProc, /* Get an OS handle from channel. */ + "serial", /* Type name. */ + SerialBlockProc, /* Set blocking or non-blocking mode.*/ + SerialCloseProc, /* Close proc. */ + SerialInputProc, /* Input proc. */ + SerialOutputProc, /* Output proc. */ + NULL, /* Seek proc. */ + SerialSetOptionProc, /* Set option proc. */ + SerialGetOptionProc, /* Get option proc. */ + SerialWatchProc, /* Set up notifier to watch the channel. */ + SerialGetHandleProc, /* Get an OS handle from channel. */ }; /* @@ -185,13 +161,13 @@ static Tcl_ChannelType serialChannelType = { * * SerialInit -- * - * This function initializes the static variables for this file. + * This function initializes the static variables for this file. * * Results: - * None. + * None. * * Side effects: - * Creates a new event source. + * Creates a new event source. * *---------------------------------------------------------------------- */ @@ -200,27 +176,25 @@ static ThreadSpecificData * SerialInit() { ThreadSpecificData *tsdPtr; - + /* * Check the initialized flag first, then check it again in the mutex. * This is a speed enhancement. */ if (!initialized) { - Tcl_MutexLock(&serialMutex); - if (!initialized) { - initialized = 1; - Tcl_CreateExitHandler(ProcExitHandler, NULL); - } - Tcl_MutexUnlock(&serialMutex); + if (!initialized) { + initialized = 1; + Tcl_CreateExitHandler(ProcExitHandler, NULL); + } } - + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); if (tsdPtr == NULL) { - tsdPtr = TCL_TSD_INIT(&dataKey); - tsdPtr->firstSerialPtr = NULL; - Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL); - Tcl_CreateThreadExitHandler(SerialExitHandler, NULL); + tsdPtr = TCL_TSD_INIT(&dataKey); + tsdPtr->firstSerialPtr = NULL; + Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL); + Tcl_CreateThreadExitHandler(SerialExitHandler, NULL); } return tsdPtr; } @@ -230,21 +204,21 @@ SerialInit() * * SerialExitHandler -- * - * This function is called to cleanup the serial module before - * Tcl is unloaded. + * This function is called to cleanup the serial module before + * Tcl is unloaded. * * Results: - * None. + * None. * * Side effects: - * Removes the serial event source. + * Removes the serial event source. * *---------------------------------------------------------------------- */ static void SerialExitHandler( - ClientData clientData) /* Old window proc */ + ClientData clientData) /* Old window proc */ { Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL); } @@ -254,25 +228,23 @@ SerialExitHandler( * * ProcExitHandler -- * - * This function is called to cleanup the process list before - * Tcl is unloaded. + * This function is called to cleanup the process list before + * Tcl is unloaded. * * Results: - * None. + * None. * * Side effects: - * Resets the process list. + * Resets the process list. * *---------------------------------------------------------------------- */ static void ProcExitHandler( - ClientData clientData) /* Old window proc */ + ClientData clientData) /* Old window proc */ { - Tcl_MutexLock(&serialMutex); initialized = 0; - Tcl_MutexUnlock(&serialMutex); } /* @@ -280,30 +252,29 @@ ProcExitHandler( * * SerialSetupProc -- * - * This procedure is invoked before Tcl_DoOneEvent blocks waiting - * for an event. + * This procedure is invoked before Tcl_DoOneEvent blocks waiting + * for an event. * * Results: - * None. + * None. * * Side effects: - * Adjusts the block time if needed. + * Adjusts the block time if needed. * *---------------------------------------------------------------------- */ void SerialSetupProc( - ClientData data, /* Not used. */ - int flags) /* Event flags as passed to Tcl_DoOneEvent. */ + ClientData data, /* Not used. */ + int flags) /* Event flags as passed to Tcl_DoOneEvent. */ { SerialInfo *infoPtr; - Tcl_Time blockTime = { 0, 0 }; int block = 1; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { - return; + return; } /* @@ -311,20 +282,15 @@ SerialSetupProc( */ for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL; - infoPtr = infoPtr->nextPtr) { - if (infoPtr->watchMask & TCL_WRITABLE) { - if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { - block = 0; - } - } - if (infoPtr->watchMask & TCL_READABLE) { - if (WaitForRead(infoPtr, 0) >= 0) { - block = 0; - } - } + infoPtr = infoPtr->nextPtr) { + + if( infoPtr->watchMask & (TCL_WRITABLE | TCL_READABLE) ) { + block = 0; + } } + if (!block) { - Tcl_SetMaxBlockTime(&blockTime); + Tcl_SetMaxBlockTime(&blockTime); } } @@ -333,67 +299,97 @@ SerialSetupProc( * * SerialCheckProc -- * - * This procedure is called by Tcl_DoOneEvent to check the serial - * event source for events. + * This procedure is called by Tcl_DoOneEvent to check the serial + * event source for events. * * Results: - * None. + * None. * * Side effects: - * May queue an event. + * May queue an event. * *---------------------------------------------------------------------- */ static void SerialCheckProc( - ClientData data, /* Not used. */ - int flags) /* Event flags as passed to Tcl_DoOneEvent. */ + ClientData data, /* Not used. */ + int flags) /* Event flags as passed to Tcl_DoOneEvent. */ { SerialInfo *infoPtr; SerialEvent *evPtr; int needEvent; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - + DWORD cError; + COMSTAT cStat; + if (!(flags & TCL_FILE_EVENTS)) { - return; + return; } /* * Queue events for any ready serials that don't already have events * queued. */ - + for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL; - infoPtr = infoPtr->nextPtr) { - if (infoPtr->flags & SERIAL_PENDING) { - continue; - } - - /* - * Queue an event if the serial is signaled for reading or writing. - */ - - needEvent = 0; - if (infoPtr->watchMask & TCL_WRITABLE) { - if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { - needEvent = 1; - } - } - - if (infoPtr->watchMask & TCL_READABLE) { - if (WaitForRead(infoPtr, 0) >= 0) { - needEvent = 1; - } - } - - if (needEvent) { - infoPtr->flags |= SERIAL_PENDING; - evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent)); - evPtr->header.proc = SerialEventProc; - evPtr->infoPtr = infoPtr; - Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); - } + infoPtr = infoPtr->nextPtr) { + if (infoPtr->flags & SERIAL_PENDING) { + continue; + } + + needEvent = 0; + + /* + * If any READABLE or WRITABLE watch mask is set + * call ClearCommError to poll cbInQue,cbOutQue + * Window errors are ignored here + */ + + if( infoPtr->watchMask & (TCL_WRITABLE | TCL_READABLE) ) { + if( ClearCommError( infoPtr->handle, &cError, &cStat ) ) { + /* + * Look for empty output buffer. If empty, poll. + */ + + if( infoPtr->watchMask & TCL_WRITABLE ) { + if( ((infoPtr->flags & SERIAL_WRITE) != 0) && \ + (cStat.cbOutQue == 0) ) { + /* + * allow only one fileevent after each callback + */ + + infoPtr->flags &= ~SERIAL_WRITE; + infoPtr->writable = 1; + needEvent = 1; + } + } + + /* + * Look for characters already pending in windows queue. + * If they are, poll. + */ + + if( infoPtr->watchMask & TCL_READABLE ) { + if( cStat.cbInQue > 0 ) { + infoPtr->readable = 1; + needEvent = 1; + } + } + } + } + + /* + * Queue an event if the serial is signaled for reading or writing. + */ + + if (needEvent) { + infoPtr->flags |= SERIAL_PENDING; + evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent)); + evPtr->header.proc = SerialEventProc; + evPtr->infoPtr = infoPtr; + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); + } } } @@ -402,23 +398,26 @@ SerialCheckProc( * * SerialBlockProc -- * - * Set blocking or non-blocking mode on channel. + * Set blocking or non-blocking mode on channel. * * Results: - * 0 if successful, errno when failed. + * 0 if successful, errno when failed. * * Side effects: - * Sets the device into blocking or non-blocking mode. + * Sets the device into blocking or non-blocking mode. * *---------------------------------------------------------------------- */ static int SerialBlockProc( - ClientData instanceData, /* Instance data for channel. */ - int mode) /* TCL_MODE_BLOCKING or + ClientData instanceData, /* Instance data for channel. */ + int mode) /* TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */ { + COMMTIMEOUTS *timeout; + int errorCode = 0; + SerialInfo *infoPtr = (SerialInfo *) instanceData; /* @@ -427,13 +426,19 @@ SerialBlockProc( * function by checking against a bit in the state. We set or unset the * bit here to cause the input function to emulate the correct behavior. */ - + if (mode == TCL_MODE_NONBLOCKING) { - infoPtr->flags |= SERIAL_ASYNC; + infoPtr->flags |= SERIAL_ASYNC; + timeout = &timeout_async; } else { - infoPtr->flags &= ~(SERIAL_ASYNC); + infoPtr->flags &= ~(SERIAL_ASYNC); + timeout = &timeout_sync; } - return 0; + if (SetCommTimeouts(infoPtr->handle, timeout) == FALSE) { + TclWinConvertError(GetLastError()); + errorCode = errno; + } + return errorCode; } /* @@ -441,21 +446,21 @@ SerialBlockProc( * * SerialCloseProc -- * - * Closes a serial based IO channel. + * Closes a serial based IO channel. * * Results: - * 0 on success, errno otherwise. + * 0 on success, errno otherwise. * * Side effects: - * Closes the physical channel. + * Closes the physical channel. * *---------------------------------------------------------------------- */ static int SerialCloseProc( - ClientData instanceData, /* Pointer to SerialInfo structure. */ - Tcl_Interp *interp) /* For error reporting. */ + ClientData instanceData, /* Pointer to SerialInfo structure. */ + Tcl_Interp *interp) /* For error reporting. */ { SerialInfo *serialPtr = (SerialInfo *) instanceData; int errorCode, result = 0; @@ -463,62 +468,7 @@ SerialCloseProc( ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); errorCode = 0; - if (serialPtr->readThread) { - /* - * Forcibly terminate the background thread. We cannot rely on the - * thread to cleanly terminate itself because we have no way of - * closing the handle without blocking in the case where the - * thread is in the middle of an I/O operation. Note that we need - * to guard against terminating the thread while it is in the - * middle of Tcl_ThreadAlert because it won't be able to release - * the notifier lock. - */ - - Tcl_MutexLock(&serialMutex); - TerminateThread(serialPtr->readThread, 0); - Tcl_MutexUnlock(&serialMutex); - - /* - * Wait for the thread to terminate. This ensures that we are - * completely cleaned up before we leave this function. - */ - - WaitForSingleObject(serialPtr->readThread, INFINITE); - CloseHandle(serialPtr->readThread); - CloseHandle(serialPtr->readable); - CloseHandle(serialPtr->startReader); - serialPtr->readThread = NULL; - } serialPtr->validMask &= ~TCL_READABLE; - - if (serialPtr->writeThread) { - WaitForSingleObject(serialPtr->writable, INFINITE); - - /* - * Forcibly terminate the background thread. We cannot rely on the - * thread to cleanly terminate itself because we have no way of - * closing the handle without blocking in the case where the - * thread is in the middle of an I/O operation. Note that we need - * to guard against terminating the thread while it is in the - * middle of Tcl_ThreadAlert because it won't be able to release - * the notifier lock. - */ - - Tcl_MutexLock(&serialMutex); - TerminateThread(serialPtr->writeThread, 0); - Tcl_MutexUnlock(&serialMutex); - - /* - * Wait for the thread to terminate. This ensures that we are - * completely cleaned up before we leave this function. - */ - - WaitForSingleObject(serialPtr->writeThread, INFINITE); - CloseHandle(serialPtr->writeThread); - CloseHandle(serialPtr->writable); - CloseHandle(serialPtr->startWriter); - serialPtr->writeThread = NULL; - } serialPtr->validMask &= ~TCL_WRITABLE; /* @@ -528,13 +478,13 @@ SerialCloseProc( */ if (!TclInExit() - || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle) - && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle) - && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) { - if (CloseHandle(serialPtr->handle) == FALSE) { - TclWinConvertError(GetLastError()); - errorCode = errno; - } + || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle) + && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle) + && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) { + if (CloseHandle(serialPtr->handle) == FALSE) { + TclWinConvertError(GetLastError()); + errorCode = errno; + } } serialPtr->watchMask &= serialPtr->validMask; @@ -544,12 +494,12 @@ SerialCloseProc( */ for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr; - infoPtr != NULL; - nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { - if (infoPtr == (SerialInfo *)serialPtr) { - *nextPtrPtr = infoPtr->nextPtr; - break; - } + infoPtr != NULL; + nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { + if (infoPtr == (SerialInfo *)serialPtr) { + *nextPtrPtr = infoPtr->nextPtr; + break; + } } /* @@ -557,11 +507,6 @@ SerialCloseProc( * routine. */ - if (serialPtr->writeBuf != NULL) { - ckfree(serialPtr->writeBuf); - serialPtr->writeBuf = NULL; - } - ckfree((char*) serialPtr); if (errorCode == 0) { @@ -575,77 +520,93 @@ SerialCloseProc( * * SerialInputProc -- * - * Reads input from the IO channel into the buffer given. Returns - * count of how many bytes were actually read, and an error indication. + * Reads input from the IO channel into the buffer given. Returns + * count of how many bytes were actually read, and an error indication. * * Results: - * A count of how many bytes were read is returned and an error - * indication is returned in an output argument. + * A count of how many bytes were read is returned and an error + * indication is returned in an output argument. * * Side effects: - * Reads input from the actual channel. + * Reads input from the actual channel. * *---------------------------------------------------------------------- */ - static int SerialInputProc( - ClientData instanceData, /* Serial state. */ - char *buf, /* Where to store data read. */ - int bufSize, /* How much space is available - * in the buffer? */ - int *errorCode) /* Where to store error code. */ + ClientData instanceData, /* Serial state. */ + char *buf, /* Where to store data read. */ + int bufSize, /* How much space is available + * in the buffer? */ + int *errorCode) /* Where to store error code. */ { SerialInfo *infoPtr = (SerialInfo *) instanceData; DWORD bytesRead = 0; - int result; DWORD err; + DWORD cError; + COMSTAT cStat; *errorCode = 0; /* - * Synchronize with the reader thread. - */ - - result = WaitForRead(infoPtr, (infoPtr->flags & SERIAL_ASYNC) ? 0 : 1); - - /* - * If an error occurred, return immediately. + * Look for characters already pending in windows queue. + * This is the mainly restored good old code from Tcl8.0 */ - - if (result == -1) { - *errorCode = errno; - return -1; - } - - if (infoPtr->readFlags & SERIAL_EXTRABYTE) { - /* - * If a byte was consumed waiting, then put it in the buffer. + if( ClearCommError( infoPtr->handle, &cError, &cStat ) ) { + /* + * Check for errors here, but not in the evSetup/Check procedures */ - *buf = infoPtr->extraByte; - infoPtr->readFlags &= ~SERIAL_EXTRABYTE; - buf++; - bufSize--; - bytesRead = 1; + if( cError != 0 ) { + *errorCode = EIO; + return -1; + } + if( infoPtr->flags & SERIAL_ASYNC ) { + /* + * NON_BLOCKING mode: + * Avoid blocking by reading more bytes than available + * in input buffer + */ - if (result == 0) { - return bytesRead; - } + if( cStat.cbInQue > 0 ) { + if( (DWORD) bufSize > cStat.cbInQue ) { + bufSize = cStat.cbInQue; + } + } else { + errno = *errorCode = EAGAIN; + return -1; + } + } else { + /* + * BLOCKING mode: + * Tcl trys to read a full buffer of 4 kBytes here + */ + + if( cStat.cbInQue > 0 ) { + if( (DWORD) bufSize > cStat.cbInQue ) { + bufSize = cStat.cbInQue; + } + } else { + bufSize = 1; + } + } } - + + if( bufSize == 0 ) { + return bytesRead = 0; + } + if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead, - NULL) == FALSE) { - err = GetLastError(); - if (err != ERROR_IO_PENDING) { - goto error; - } + NULL) == FALSE) { + err = GetLastError(); + if (err != ERROR_IO_PENDING) { + goto error; + } } - return bytesRead; - - error: + +error: TclWinConvertError(GetLastError()); *errorCode = errno; return -1; @@ -656,93 +617,55 @@ SerialInputProc( * * SerialOutputProc -- * - * Writes the given output on the IO channel. Returns count of how - * many characters were actually written, and an error indication. + * Writes the given output on the IO channel. Returns count of how + * many characters were actually written, and an error indication. * * Results: - * A count of how many characters were written is returned and an - * error indication is returned in an output argument. + * A count of how many characters were written is returned and an + * error indication is returned in an output argument. * * Side effects: - * Writes output on the actual channel. + * Writes output on the actual channel. * *---------------------------------------------------------------------- */ static int SerialOutputProc( - ClientData instanceData, /* Serial state. */ - char *buf, /* The data buffer. */ - int toWrite, /* How many bytes to write? */ - int *errorCode) /* Where to store error code. */ + ClientData instanceData, /* Serial state. */ + char *buf, /* The data buffer. */ + int toWrite, /* How many bytes to write? */ + int *errorCode) /* Where to store error code. */ { SerialInfo *infoPtr = (SerialInfo *) instanceData; - DWORD bytesWritten, timeout, err; + DWORD bytesWritten, err; *errorCode = 0; - timeout = (infoPtr->flags & SERIAL_ASYNC) ? 0 : INFINITE; - if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { - /* - * The writer thread is blocked waiting for a write to complete - * and the channel is in non-blocking mode. - */ - errno = EAGAIN; - goto error; - } - /* * Check for a background error on the last write. + * Allow one write-fileevent after each callback */ - - if (infoPtr->writeError) { - TclWinConvertError(infoPtr->writeError); - infoPtr->writeError = 0; - goto error; + + if( toWrite ) { + infoPtr->flags |= SERIAL_WRITE; } - - if (infoPtr->flags & SERIAL_ASYNC) { - /* - * The serial is non-blocking, so copy the data into the output - * buffer and restart the writer thread. - */ - - if (toWrite > infoPtr->writeBufLen) { - /* - * Reallocate the buffer to be large enough to hold the data. - */ - - if (infoPtr->writeBuf) { - ckfree(infoPtr->writeBuf); - } - infoPtr->writeBufLen = toWrite; - infoPtr->writeBuf = ckalloc(toWrite); - } - memcpy(infoPtr->writeBuf, buf, toWrite); - infoPtr->toWrite = toWrite; - ResetEvent(infoPtr->writable); - SetEvent(infoPtr->startWriter); - bytesWritten = toWrite; - } else { - /* - * In the blocking case, just try to write the buffer directly. - * This avoids an unnecessary copy. - */ - if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, - &bytesWritten, NULL) == FALSE) { - err = GetLastError(); - if (err != ERROR_IO_PENDING) { - TclWinConvertError(GetLastError()); - goto error; - } - } + + if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, + &bytesWritten, NULL) == FALSE) { + err = GetLastError(); + if (err != ERROR_IO_PENDING) { + TclWinConvertError(GetLastError()); + goto error; + } } + return bytesWritten; - - error: + +error: *errorCode = errno; return -1; - + } /* @@ -750,27 +673,27 @@ SerialOutputProc( * * SerialEventProc -- * - * This function is invoked by Tcl_ServiceEvent when a file event - * reaches the front of the event queue. This procedure invokes - * Tcl_NotifyChannel on the serial. + * This function is invoked by Tcl_ServiceEvent when a file event + * reaches the front of the event queue. This procedure invokes + * Tcl_NotifyChannel on the serial. * * Results: - * Returns 1 if the event was handled, meaning it should be removed - * from the queue. Returns 0 if the event was not handled, meaning - * it should stay on the queue. The only time the event isn't - * handled is if the TCL_FILE_EVENTS flag bit isn't set. + * Returns 1 if the event was handled, meaning it should be removed + * from the queue. Returns 0 if the event was not handled, meaning + * it should stay on the queue. The only time the event isn't + * handled is if the TCL_FILE_EVENTS flag bit isn't set. * * Side effects: - * Whatever the notifier callback does. + * Whatever the notifier callback does. * *---------------------------------------------------------------------- */ static int SerialEventProc( - Tcl_Event *evPtr, /* Event to service. */ - int flags) /* Flags that indicate what events to - * handle, such as TCL_FILE_EVENTS. */ + Tcl_Event *evPtr, /* Event to service. */ + int flags) /* Flags that indicate what events to + * handle, such as TCL_FILE_EVENTS. */ { SerialEvent *serialEvPtr = (SerialEvent *)evPtr; SerialInfo *infoPtr; @@ -778,9 +701,9 @@ SerialEventProc( ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { - return 0; + return 0; } - + /* * Search through the list of watched serials for the one whose handle * matches the event. We do this rather than simply dereferencing @@ -789,18 +712,19 @@ SerialEventProc( */ for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL; - infoPtr = infoPtr->nextPtr) { - if (serialEvPtr->infoPtr == infoPtr) { - infoPtr->flags &= ~(SERIAL_PENDING); - break; - } + infoPtr = infoPtr->nextPtr) { + if (serialEvPtr->infoPtr == infoPtr) { + infoPtr->flags &= ~(SERIAL_PENDING); + break; + } } + /* * Remove stale events. */ - + if (!infoPtr) { - return 1; + return 1; } /* @@ -810,20 +734,18 @@ SerialEventProc( */ mask = 0; - if (infoPtr->watchMask & TCL_WRITABLE) { - if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { - mask = TCL_WRITABLE; - } + if( infoPtr->watchMask & TCL_WRITABLE ) { + if( infoPtr->writable ) { + mask |= TCL_WRITABLE; + infoPtr->writable = 0; + } } - if (infoPtr->watchMask & TCL_READABLE) { - if (WaitForRead(infoPtr, 0) >= 0) { - if (infoPtr->readFlags & SERIAL_EOF) { - mask = TCL_READABLE; - } else { - mask |= TCL_READABLE; - } - } + if( infoPtr->watchMask & TCL_READABLE ) { + if( infoPtr->readable ) { + mask |= TCL_READABLE; + infoPtr->readable = 0; + } } /* @@ -839,24 +761,24 @@ SerialEventProc( * * SerialWatchProc -- * - * Called by the notifier to set up to watch for events on this - * channel. + * Called by the notifier to set up to watch for events on this + * channel. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ static void SerialWatchProc( - ClientData instanceData, /* Serial state. */ - int mask) /* What events to watch for, OR-ed - * combination of TCL_READABLE, - * TCL_WRITABLE and TCL_EXCEPTION. */ + ClientData instanceData, /* Serial state. */ + int mask) /* What events to watch for, OR-ed + * combination of TCL_READABLE, + * TCL_WRITABLE and TCL_EXCEPTION. */ { SerialInfo **nextPtrPtr, *ptr; SerialInfo *infoPtr = (SerialInfo *) instanceData; @@ -870,27 +792,26 @@ SerialWatchProc( infoPtr->watchMask = mask & infoPtr->validMask; if (infoPtr->watchMask) { - Tcl_Time blockTime = { 0, 0 }; - if (!oldMask) { - infoPtr->nextPtr = tsdPtr->firstSerialPtr; - tsdPtr->firstSerialPtr = infoPtr; - } - Tcl_SetMaxBlockTime(&blockTime); + if (!oldMask) { + infoPtr->nextPtr = tsdPtr->firstSerialPtr; + tsdPtr->firstSerialPtr = infoPtr; + } + Tcl_SetMaxBlockTime(&blockTime); } else { - if (oldMask) { + if (oldMask) { /* * Remove the serial port from the list of watched serial ports. */ - - for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr; - ptr != NULL; - nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { - if (infoPtr == ptr) { - *nextPtrPtr = ptr->nextPtr; - break; - } - } - } + + for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr; + ptr != NULL; + nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { + if (infoPtr == ptr) { + *nextPtrPtr = ptr->nextPtr; + break; + } + } + } } } @@ -899,24 +820,24 @@ SerialWatchProc( * * SerialGetHandleProc -- * - * Called from Tcl_GetChannelHandle to retrieve OS handles from - * inside a command serial port based channel. + * Called from Tcl_GetChannelHandle to retrieve OS handles from + * inside a command serial port based channel. * * Results: - * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if - * there is no handle for the specified direction. + * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if + * there is no handle for the specified direction. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ static int SerialGetHandleProc( - ClientData instanceData, /* The serial state. */ - int direction, /* TCL_READABLE or TCL_WRITABLE */ - ClientData *handlePtr) /* Where to store the handle. */ + ClientData instanceData, /* The serial state. */ + int direction, /* TCL_READABLE or TCL_WRITABLE */ + ClientData *handlePtr) /* Where to store the handle. */ { SerialInfo *infoPtr = (SerialInfo *) instanceData; @@ -927,250 +848,17 @@ SerialGetHandleProc( /* *---------------------------------------------------------------------- * - * WaitForRead -- - * - * Wait until some data is available, the serial is at - * EOF or the reader thread is blocked waiting for data (if the - * channel is in non-blocking mode). - * - * Results: - * Returns 1 if serial is readable. Returns 0 if there is no data - * on the serial, but there is buffered data. Returns -1 if an - * error occurred. If an error occurred, the threads may not - * be synchronized. - * - * Side effects: - * Updates the shared state flags and may consume 1 byte of data - * from the serial. If no error occurred, the reader thread is - * blocked waiting for a signal from the main thread. - * - *---------------------------------------------------------------------- - */ - -static int -WaitForRead( - SerialInfo *infoPtr, /* Serial state. */ - int blocking) /* Indicates whether call should be - * blocking or not. */ -{ - DWORD timeout; - HANDLE *handle = infoPtr->handle; - - while (1) { - /* - * Synchronize with the reader thread. - */ - - timeout = blocking ? INFINITE : 0; - if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { - /* - * The reader thread is blocked waiting for data and the channel - * is in non-blocking mode. - */ - - errno = EAGAIN; - return -1; - } - - /* - * At this point, the two threads are synchronized, so it is safe - * to access shared state. This code is not called in the ReaderThread - * in blocking mode, so it needs to be checked here. - */ - - /* - * If the serial has hit EOF, it is always readable. - */ - - if (infoPtr->readFlags & SERIAL_EOF) { - return 1; - } - - /* - * if there is an extra byte that was consumed while - * waiting, but no data in the queue, return 0 - */ - - if (infoPtr->readFlags & SERIAL_EXTRABYTE) { - return 0; - } else if ((infoPtr->readFlags & SERIAL_ERROR) == EIO) { - return -1; - } - - ResetEvent(infoPtr->readable); - SetEvent(infoPtr->startReader); - } -} - -/* - *---------------------------------------------------------------------- - * - * SerialReaderThread -- - * - * This function runs in a separate thread and waits for input - * to become available on a serial. - * - * Results: - * None. - * - * Side effects: - * Signals the main thread when input become available. May - * cause the main thread to wake up by posting a message. May - * consume one byte from the serial for each wait operation. - * - *---------------------------------------------------------------------- - */ - -static DWORD WINAPI -SerialReaderThread(LPVOID arg) -{ - SerialInfo *infoPtr = (SerialInfo *)arg; - HANDLE *handle = infoPtr->handle; - DWORD count; - - for (;;) { - /* - * Wait for the main thread to signal before attempting to wait. - */ - - WaitForSingleObject(infoPtr->startReader, INFINITE); - - /* - * Try waiting for a Comm event. - */ - - WaitCommEvent(handle, NULL, NULL); - - - /* - * try to read one byte - */ - - if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL) - != FALSE) { - - /* - * one byte was consumed while waiting to read, keep it - */ - - if (count != 0) { - infoPtr->readFlags |= SERIAL_EXTRABYTE; - } - - } - - /* - * Signal the main thread by signalling the readable event and - * then waking up the notifier thread. - */ - - SetEvent(infoPtr->readable); - - /* - * Alert the foreground thread. Note that we need to treat this like - * a critical section so the foreground thread does not terminate - * this thread while we are holding a mutex in the notifier code. - */ - - Tcl_MutexLock(&serialMutex); - Tcl_ThreadAlert(infoPtr->threadId); - Tcl_MutexUnlock(&serialMutex); - } - return 0; /* NOT REACHED */ -} - -/* - *---------------------------------------------------------------------- - * - * SerialWriterThread -- - * - * This function runs in a separate thread and writes data - * onto a serial. - * - * Results: - * Always returns 0. - * - * Side effects: - * Signals the main thread when an output operation is completed. - * May cause the main thread to wake up by posting a message. - * - *---------------------------------------------------------------------- - */ - -static DWORD WINAPI -SerialWriterThread(LPVOID arg) -{ - - SerialInfo *infoPtr = (SerialInfo *)arg; - HANDLE *handle = infoPtr->handle; - DWORD count, toWrite, err; - char *buf; - - for (;;) { - /* - * Wait for the main thread to signal before attempting to write. - */ - - WaitForSingleObject(infoPtr->startWriter, INFINITE); - - buf = infoPtr->writeBuf; - toWrite = infoPtr->toWrite; - - /* - * Loop until all of the bytes are written or an error occurs. - */ - - while (toWrite > 0) { - if (WriteFile(handle, (LPVOID) buf, (DWORD) toWrite, - &count, NULL) == FALSE) { - err = GetLastError(); - if (err != ERROR_IO_PENDING) { - TclWinConvertError(GetLastError()); - infoPtr->writeError = err; - break; - } - } else { - toWrite -= count; - buf += count; - } - } - - /* - * Signal the main thread by signalling the writable event and - * then waking up the notifier thread. - */ - - SetEvent(infoPtr->writable); - - /* - * Alert the foreground thread. Note that we need to treat this like - * a critical section so the foreground thread does not terminate - * this thread while we are holding a mutex in the notifier code. - */ - - Tcl_MutexLock(&serialMutex); - Tcl_ThreadAlert(infoPtr->threadId); - Tcl_MutexUnlock(&serialMutex); - } - return 0; /* NOT REACHED */ -} - - - -/* - *---------------------------------------------------------------------- - * * TclWinOpenSerialChannel -- * - * Constructs a Serial port channel for the specified standard OS handle. + * Constructs a Serial port channel for the specified standard OS handle. * This is a helper function to break up the construction of * channels into File, Console, or Serial. * * Results: - * Returns the new channel, or NULL. + * Returns the new channel, or NULL. * * Side effects: - * May open the channel + * May open the channel * *---------------------------------------------------------------------- */ @@ -1182,54 +870,38 @@ TclWinOpenSerialChannel(handle, channelName, permissions) int permissions; { SerialInfo *infoPtr; - COMMTIMEOUTS cto; ThreadSpecificData *tsdPtr; - DWORD id; tsdPtr = SerialInit(); - SetCommMask(handle, EV_RXCHAR); SetupComm(handle, 4096, 4096); PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR - | PURGE_RXCLEAR); - cto.ReadIntervalTimeout = MAXDWORD; - cto.ReadTotalTimeoutMultiplier = MAXDWORD; - cto.ReadTotalTimeoutConstant = 1; - cto.WriteTotalTimeoutMultiplier = 0; - cto.WriteTotalTimeoutConstant = 0; - SetCommTimeouts(handle, &cto); + | PURGE_RXCLEAR); + + /* + * default is blocking + */ + + SetCommTimeouts(handle, &timeout_sync); infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo)); memset(infoPtr, 0, sizeof(SerialInfo)); infoPtr->validMask = permissions; infoPtr->handle = handle; - + /* * Use the pointer to keep the channel names unique, in case * the handles are shared between multiple channels (stdin/stdout). */ wsprintfA(channelName, "file%lx", (int) infoPtr); - + infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName, (ClientData) infoPtr, permissions); - - - infoPtr->threadId = Tcl_GetCurrentThread(); - if (permissions & TCL_READABLE) { - infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL); - infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL); - infoPtr->readThread = CreateThread(NULL, 8000, SerialReaderThread, - infoPtr, 0, &id); - } - if (permissions & TCL_WRITABLE) { - infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); - infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); - infoPtr->writeThread = CreateThread(NULL, 8000, SerialWriterThread, - infoPtr, 0, &id); - } + + infoPtr->readable = infoPtr->writable = 0; /* * Files have default translation of AUTO and ^Z eof char, which @@ -1247,133 +919,134 @@ TclWinOpenSerialChannel(handle, channelName, permissions) * * SerialSetOptionProc -- * - * Sets an option on a channel. + * Sets an option on a channel. * * Results: - * A standard Tcl result. Also sets the interp's result on error if - * interp is not NULL. + * A standard Tcl result. Also sets the interp's result on error if + * interp is not NULL. * * Side effects: - * May modify an option on a device. + * May modify an option on a device. * *---------------------------------------------------------------------- */ -static int +static int SerialSetOptionProc(instanceData, interp, optionName, value) - ClientData instanceData; /* File state. */ - Tcl_Interp *interp; /* For error reporting - can be NULL. */ - char *optionName; /* Which option to set? */ - char *value; /* New value for option. */ -{ - SerialInfo *infoPtr; - DCB dcb; - int len; - BOOL result; - Tcl_DString ds; - TCHAR *native; - - infoPtr = (SerialInfo *) instanceData; - - len = strlen(optionName); - if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) { - if (GetCommState(infoPtr->handle, &dcb)) { - native = Tcl_WinUtfToTChar(value, -1, &ds); - result = (*tclWinProcs->buildCommDCBProc)(native, &dcb); - Tcl_DStringFree(&ds); - - if ((result == FALSE) || - (SetCommState(infoPtr->handle, &dcb) == FALSE)) { - /* - * one should separate the 2 errors... - */ - + ClientData instanceData; /* File state. */ + Tcl_Interp *interp; /* For error reporting - can be NULL. */ + char *optionName; /* Which option to set? */ + char *value; /* New value for option. */ + { + SerialInfo *infoPtr; + DCB dcb; + int len; + BOOL result; + Tcl_DString ds; + TCHAR *native; + + infoPtr = (SerialInfo *) instanceData; + + len = strlen(optionName); + if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) { + if (GetCommState(infoPtr->handle, &dcb)) { + native = Tcl_WinUtfToTChar(value, -1, &ds); + result = (*tclWinProcs->buildCommDCBProc)(native, &dcb); + Tcl_DStringFree(&ds); + + if ((result == FALSE) || + (SetCommState(infoPtr->handle, &dcb) == FALSE)) { + /* + * one should separate the 2 errors... + */ + + if (interp) { + Tcl_AppendResult(interp, + "bad value for -mode: should be ", + "baud,parity,data,stop", NULL); + } + return TCL_ERROR; + } else { + return TCL_OK; + } + } else { if (interp) { - Tcl_AppendResult(interp, "bad value for -mode: should be ", - "baud,parity,data,stop", NULL); - } - return TCL_ERROR; - } else { - return TCL_OK; - } - } else { - if (interp) { - Tcl_AppendResult(interp, "can't get comm state", NULL); - } - return TCL_ERROR; - } - } else { - return Tcl_BadChannelOption(interp, optionName, "mode"); + Tcl_AppendResult(interp, "can't get comm state", NULL); + } + return TCL_ERROR; + } + } else { + return Tcl_BadChannelOption(interp, optionName, "mode"); + } } -} /* *---------------------------------------------------------------------- * * SerialGetOptionProc -- * - * Gets a mode associated with an IO channel. If the optionName arg - * is non NULL, retrieves the value of that option. If the optionName - * arg is NULL, retrieves a list of alternating option names and - * values for the given channel. + * Gets a mode associated with an IO channel. If the optionName arg + * is non NULL, retrieves the value of that option. If the optionName + * arg is NULL, retrieves a list of alternating option names and + * values for the given channel. * * Results: - * A standard Tcl result. Also sets the supplied DString to the - * string value of the option(s) returned. + * A standard Tcl result. Also sets the supplied DString to the + * string value of the option(s) returned. * * Side effects: - * The string returned by this function is in static storage and - * may be reused at any time subsequent to the call. + * The string returned by this function is in static storage and + * may be reused at any time subsequent to the call. * *---------------------------------------------------------------------- */ -static int +static int SerialGetOptionProc(instanceData, interp, optionName, dsPtr) - ClientData instanceData; /* File state. */ - Tcl_Interp *interp; /* For error reporting - can be NULL. */ - char *optionName; /* Option to get. */ - Tcl_DString *dsPtr; /* Where to store value(s). */ + ClientData instanceData; /* File state. */ + Tcl_Interp *interp; /* For error reporting - can be NULL. */ + char *optionName; /* Option to get. */ + Tcl_DString *dsPtr; /* Where to store value(s). */ { SerialInfo *infoPtr; DCB dcb; int len; - + infoPtr = (SerialInfo *) instanceData; - + if (optionName == NULL) { - Tcl_DStringAppendElement(dsPtr, "-mode"); - len = 0; + Tcl_DStringAppendElement(dsPtr, "-mode"); + len = 0; } else { - len = strlen(optionName); + len = strlen(optionName); } if ((len == 0) || - ((len > 1) && (strncmp(optionName, "-mode", len) == 0))) { - if (GetCommState(infoPtr->handle, &dcb) == 0) { + ((len > 1) && (strncmp(optionName, "-mode", len) == 0))) { + if (GetCommState(infoPtr->handle, &dcb) == 0) { /* * shouldn't we flag an error instead ? */ - - Tcl_DStringAppendElement(dsPtr, ""); - - } else { - char parity; - char *stop; - char buf[2 * TCL_INTEGER_SPACE + 16]; - - parity = 'n'; - if (dcb.Parity < 4) { - parity = "noems"[dcb.Parity]; - } - - stop = (dcb.StopBits == ONESTOPBIT) ? "1" : - (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2"; - - wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity, dcb.ByteSize, - stop); - Tcl_DStringAppendElement(dsPtr, buf); - } - return TCL_OK; + + Tcl_DStringAppendElement(dsPtr, ""); + + } else { + char parity; + char *stop; + char buf[2 * TCL_INTEGER_SPACE + 16]; + + parity = 'n'; + if (dcb.Parity < 4) { + parity = "noems"[dcb.Parity]; + } + + stop = (dcb.StopBits == ONESTOPBIT) ? "1" : + (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2"; + + wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity, + dcb.ByteSize, stop); + Tcl_DStringAppendElement(dsPtr, buf); + } + return TCL_OK; } else { - return Tcl_BadChannelOption(interp, optionName, "mode"); + return Tcl_BadChannelOption(interp, optionName, "mode"); } } |