📄 tclwinserial.c
字号:
/* * tclWinSerial.c -- * * 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. * * Serial functionality implemented by Rolf.Schroedter@dlr.de * * RCS: @(#) $Id: tclWinSerial.c,v 1.21 2002/07/19 13:59:10 dkf Exp $ */#include "tclWinInt.h"#include <fcntl.h>#include <io.h>#include <sys/stat.h>/* * The following variable is used to tell whether this module has been * initialized. */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. *//* * Bit masks used in the sharedFlags field of the SerialInfo structure below. */#define SERIAL_EOF (1<<2) /* Serial has reached EOF. */#define SERIAL_ERROR (1<<4)/* * Default time to block between checking status on the serial port. */#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 | CE_PTO )/* * This structure describes per-instance data for a serial based channel. */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. */ int readable; /* flag that the channel is readable */ int writable; /* flag that the channel is writable */ int blockTime; /* max. blocktime in msec */ unsigned int lastEventTime; /* Time in milliseconds since last readable event */ /* Next readable event only after blockTime */ DWORD error; /* pending error code returned by * ClearCommError() */ DWORD lastError; /* last error code, can be fetched with * fconfigure chan -lasterror */ DWORD sysBufRead; /* Win32 system buffer size for read ops, * default=4096 */ DWORD sysBufWrite; /* Win32 system buffer size for write ops, * default=4096 */ Tcl_ThreadId threadId; /* Thread to which events should be reported. * This value is used by the reader/writer * threads. */ OVERLAPPED osRead; /* OVERLAPPED structure for read operations */ OVERLAPPED osWrite; /* OVERLAPPED structure for write operations */ HANDLE writeThread; /* Handle to writer thread. */ CRITICAL_SECTION csWrite; /* Writer thread synchronisation */ HANDLE evWritable; /* Manual-reset event to signal when the * writer thread has finished waiting for * the current buffer to be written. */ HANDLE evStartWriter; /* Auto-reset event used by the main thread to * signal when the writer thread should attempt * to write to 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 evWritable object. */ char *writeBuf; /* Current background output buffer. * Access is synchronized with the evWritable * object. */ int writeBufLen; /* Size of write buffer. Access is * synchronized with the evWritable * object. */ int toWrite; /* Current amount to be written. Access is * synchronized with the evWritable object. */ int writeQueue; /* Number of bytes pending in output queue. * Offset to DCB.cbInQue. * Used to query [fconfigure -queue] */} SerialInfo;typedef struct ThreadSpecificData { /* * The following pointer refers to the head of the list of serials * that are being watched for file events. */ SerialInfo *firstSerialPtr;} ThreadSpecificData;static Tcl_ThreadDataKey dataKey;/* * The following structure is what is added to the Tcl event queue when * serial events are generated. */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. */} SerialEvent;/* * We don't use timeouts. */static COMMTIMEOUTS no_timeout = { 0, /* ReadIntervalTimeout */ 0, /* ReadTotalTimeoutMultiplier */ 0, /* 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 ThreadSpecificData *SerialInit(void);static int SerialInputProc(ClientData instanceData, char *buf, int toRead, int *errorCode);static int SerialOutputProc(ClientData instanceData, CONST 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, CONST char *optionName, Tcl_DString *dsPtr));static int SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, CONST char *optionName, CONST char *value));static DWORD WINAPI SerialWriterThread(LPVOID arg);/* * This structure describes the channel type structure for command serial * based IO. */static Tcl_ChannelType serialChannelType = { "serial", /* Type name. */ TCL_CHANNEL_VERSION_2, /* v2 channel */ 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. */ NULL, /* close2proc. */ SerialBlockProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */};/* *---------------------------------------------------------------------- * * SerialInit -- * * This function initializes the static variables for this file. * * Results: * None. * * Side effects: * Creates a new event source. * *---------------------------------------------------------------------- */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); } tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->firstSerialPtr = NULL; Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL); Tcl_CreateThreadExitHandler(SerialExitHandler, NULL); } return tsdPtr;}/* *---------------------------------------------------------------------- * * SerialExitHandler -- * * This function is called to cleanup the serial module before * Tcl is unloaded. * * Results: * None. * * Side effects: * Removes the serial event source. * *---------------------------------------------------------------------- */static voidSerialExitHandler( ClientData clientData) /* Old window proc */{ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); SerialInfo *infoPtr; /* * Clear all eventually pending output. * Otherwise Tcl's exit could totally block, * because it performs a blocking flush on all open channels. * Note that serial write operations may be blocked due to handshake. */ for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { PurgeComm(infoPtr->handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); } Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);}/* *---------------------------------------------------------------------- * * ProcExitHandler -- * * This function is called to cleanup the process list before * Tcl is unloaded. * * Results: * None. * * Side effects: * Resets the process list. * *---------------------------------------------------------------------- */static voidProcExitHandler( ClientData clientData) /* Old window proc */{ Tcl_MutexLock(&serialMutex); initialized = 0; Tcl_MutexUnlock(&serialMutex);}/* *---------------------------------------------------------------------- * * SerialBlockTime -- * * Wrapper to set Tcl's block time in msec * * Results: * None. *---------------------------------------------------------------------- */static voidSerialBlockTime( int msec) /* milli-seconds */{ Tcl_Time blockTime; blockTime.sec = msec / 1000; blockTime.usec = (msec % 1000) * 1000; Tcl_SetMaxBlockTime(&blockTime);}/* *---------------------------------------------------------------------- * * SerialGetMilliseconds -- * * Get current time in milliseconds, * Don't care about integer overruns * * Results: * None. *---------------------------------------------------------------------- */static unsigned intSerialGetMilliseconds( void){ Tcl_Time time; TclpGetTime(&time); return (time.sec * 1000 + time.usec / 1000);}/* *---------------------------------------------------------------------- * * SerialSetupProc -- * * This procedure is invoked before Tcl_DoOneEvent blocks waiting * for an event. * * Results: * None. * * Side effects: * Adjusts the block time if needed. * *---------------------------------------------------------------------- */voidSerialSetupProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ SerialInfo *infoPtr; int block = 1; int msec = INT_MAX; /* min. found block time */ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (!(flags & TCL_FILE_EVENTS)) { return; } /* * Look to see if any events handlers installed. If they are, do not block. */ for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->watchMask & TCL_WRITABLE) { if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) { block = 0; msec = min( msec, infoPtr->blockTime ); } } if( infoPtr->watchMask & TCL_READABLE ) { block = 0; msec = min( msec, infoPtr->blockTime );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -