tclWinSerial.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:61k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclWinSerial.c --
  3.  *
  4.  *  This file implements the Windows-specific serial port functions,
  5.  *  and the "serial" channel driver.
  6.  *
  7.  * Copyright (c) 1999 by Scriptics Corp.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * Serial functionality implemented by Rolf.Schroedter@dlr.de
  13.  *
  14.  * RCS: @(#) $Id: tclWinSerial.c,v 1.25.2.4 2008/01/14 00:11:22 hobbs Exp $
  15.  */
  16. #include "tclWinInt.h"
  17. #include <fcntl.h>
  18. #include <io.h>
  19. #include <sys/stat.h>
  20. /*
  21.  * The following variable is used to tell whether this module has been
  22.  * initialized.
  23.  */
  24. static int initialized = 0;
  25. /*
  26.  * The serialMutex locks around access to the initialized variable, and it is
  27.  * used to protect background threads from being terminated while they are
  28.  * using APIs that hold locks.
  29.  */
  30. TCL_DECLARE_MUTEX(serialMutex)
  31. /*
  32.  * Bit masks used in the flags field of the SerialInfo structure below.
  33.  */
  34. #define SERIAL_PENDING  (1<<0)  /* Message is pending in the queue. */
  35. #define SERIAL_ASYNC    (1<<1)  /* Channel is non-blocking. */
  36. /*
  37.  * Bit masks used in the sharedFlags field of the SerialInfo structure below.
  38.  */
  39. #define SERIAL_EOF      (1<<2)  /* Serial has reached EOF. */
  40. #define SERIAL_ERROR    (1<<4)
  41. /*
  42.  * Default time to block between checking status on the serial port.
  43.  */
  44. #define SERIAL_DEFAULT_BLOCKTIME    10  /* 10 msec */
  45. /*
  46.  * Define Win32 read/write error masks returned by ClearCommError()
  47.  */
  48. #define SERIAL_READ_ERRORS      ( CE_RXOVER | CE_OVERRUN | CE_RXPARITY 
  49.                                 | CE_FRAME  | CE_BREAK )
  50. #define SERIAL_WRITE_ERRORS     ( CE_TXFULL | CE_PTO )
  51. /*
  52.  * This structure describes per-instance data for a serial based channel.
  53.  */
  54. typedef struct SerialInfo {
  55.     HANDLE handle;
  56.     struct SerialInfo *nextPtr; /* Pointer to next registered serial. */
  57.     Tcl_Channel channel;        /* Pointer to channel structure. */
  58.     int validMask;              /* OR'ed combination of TCL_READABLE,
  59.                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  60.                                  * which operations are valid on the file. */
  61.     int watchMask;              /* OR'ed combination of TCL_READABLE,
  62.                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  63.                                  * which events should be reported. */
  64.     int flags;                  /* State flags, see above for a list. */
  65.     int readable;               /* flag that the channel is readable */
  66.     int writable;               /* flag that the channel is writable */
  67.     int blockTime;              /* max. blocktime in msec */
  68.     unsigned int lastEventTime; /* Time in milliseconds since last readable event */
  69. /* Next readable event only after blockTime */
  70.     DWORD error;                /* pending error code returned by
  71.                                  * ClearCommError() */
  72.     DWORD lastError;            /* last error code, can be fetched with
  73.                                  * fconfigure chan -lasterror */
  74.     DWORD sysBufRead;           /* Win32 system buffer size for read ops, 
  75.                                  * default=4096 */
  76.     DWORD sysBufWrite;          /* Win32 system buffer size for write ops, 
  77.                                  * default=4096 */
  78.     Tcl_ThreadId threadId;      /* Thread to which events should be reported.
  79.                                  * This value is used by the reader/writer
  80.                                  * threads. */
  81.     OVERLAPPED osRead;          /* OVERLAPPED structure for read operations */
  82.     OVERLAPPED osWrite;         /* OVERLAPPED structure for write operations */
  83.     HANDLE writeThread;         /* Handle to writer thread. */
  84.     CRITICAL_SECTION csWrite;   /* Writer thread synchronisation */
  85.     HANDLE evWritable;          /* Manual-reset event to signal when the
  86.                                  * writer thread has finished waiting for
  87.                                  * the current buffer to be written. */
  88.     HANDLE evStartWriter;       /* Auto-reset event used by the main thread to
  89.                                  * signal when the writer thread should attempt
  90.                                  * to write to the serial. */
  91.     HANDLE evStopWriter; /* Auto-reset event used by the main thread to
  92.                                  * signal when the writer thread should close.
  93.  */
  94.     DWORD writeError;           /* An error caused by the last background
  95.                                  * write.  Set to 0 if no error has been
  96.                                  * detected.  This word is shared with the
  97.                                  * writer thread so access must be
  98.                                  * synchronized with the evWritable object.
  99.                                  */
  100.     char *writeBuf;             /* Current background output buffer.
  101.                                  * Access is synchronized with the evWritable
  102.                                  * object. */
  103.     int writeBufLen;            /* Size of write buffer.  Access is
  104.                                  * synchronized with the evWritable
  105.                                  * object. */
  106.     int toWrite;                /* Current amount to be written.  Access is
  107.                                  * synchronized with the evWritable object. */
  108.     int writeQueue;             /* Number of bytes pending in output queue.
  109.                                  * Offset to DCB.cbInQue.
  110.                                  * Used to query [fconfigure -queue] */
  111. } SerialInfo;
  112. typedef struct ThreadSpecificData {
  113.     /*
  114.      * The following pointer refers to the head of the list of serials
  115.      * that are being watched for file events.
  116.      */
  117.     SerialInfo *firstSerialPtr;
  118. } ThreadSpecificData;
  119. static Tcl_ThreadDataKey dataKey;
  120. /*
  121.  * The following structure is what is added to the Tcl event queue when
  122.  * serial events are generated.
  123.  */
  124. typedef struct SerialEvent {
  125.     Tcl_Event header;       /* Information that is standard for
  126.                              * all events. */
  127.     SerialInfo *infoPtr;    /* Pointer to serial info structure.  Note
  128.                              * that we still have to verify that the
  129.                              * serial exists before dereferencing this
  130.                              * pointer. */
  131. } SerialEvent;
  132. /*
  133.  * We don't use timeouts.
  134.  */
  135. static COMMTIMEOUTS no_timeout = {
  136.     0,               /* ReadIntervalTimeout */
  137.     0,               /* ReadTotalTimeoutMultiplier */
  138.     0,               /* ReadTotalTimeoutConstant */
  139.     0,               /* WriteTotalTimeoutMultiplier */
  140.     0,               /* WriteTotalTimeoutConstant */
  141. };
  142. /*
  143.  * Declarations for functions used only in this file.
  144.  */
  145. static int      SerialBlockProc(ClientData instanceData, int mode);
  146. static void     SerialCheckProc(ClientData clientData, int flags);
  147. static int      SerialCloseProc(ClientData instanceData,
  148.                 Tcl_Interp *interp);
  149. static int      SerialEventProc(Tcl_Event *evPtr, int flags);
  150. static void     SerialExitHandler(ClientData clientData);
  151. static int      SerialGetHandleProc(ClientData instanceData,
  152.                 int direction, ClientData *handlePtr);
  153. static ThreadSpecificData *SerialInit(void);
  154. static int      SerialInputProc(ClientData instanceData, char *buf,
  155.                 int toRead, int *errorCode);
  156. static int      SerialOutputProc(ClientData instanceData, CONST char *buf,
  157.                 int toWrite, int *errorCode);
  158. static void     SerialSetupProc(ClientData clientData, int flags);
  159. static void     SerialWatchProc(ClientData instanceData, int mask);
  160. static void     ProcExitHandler(ClientData clientData);
  161. static int       SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData,
  162.                 Tcl_Interp *interp, CONST char *optionName,
  163.                 Tcl_DString *dsPtr));
  164. static int       SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData,
  165.                 Tcl_Interp *interp, CONST char *optionName,
  166.                 CONST char *value));
  167. static DWORD WINAPI     SerialWriterThread(LPVOID arg);
  168. static void             SerialThreadActionProc _ANSI_ARGS_ ((
  169.    ClientData instanceData, int action));
  170. /*
  171.  * This structure describes the channel type structure for command serial
  172.  * based IO.
  173.  */
  174. static Tcl_ChannelType serialChannelType = {
  175.     "serial",                   /* Type name. */
  176.     TCL_CHANNEL_VERSION_4,      /* v4 channel */
  177.     SerialCloseProc,            /* Close proc. */
  178.     SerialInputProc,            /* Input proc. */
  179.     SerialOutputProc,           /* Output proc. */
  180.     NULL,                       /* Seek proc. */
  181.     SerialSetOptionProc,        /* Set option proc. */
  182.     SerialGetOptionProc,        /* Get option proc. */
  183.     SerialWatchProc,            /* Set up notifier to watch the channel. */
  184.     SerialGetHandleProc,        /* Get an OS handle from channel. */
  185.     NULL,                       /* close2proc. */
  186.     SerialBlockProc,            /* Set blocking or non-blocking mode.*/
  187.     NULL,                       /* flush proc. */
  188.     NULL,                       /* handler proc. */
  189.     NULL,                       /* wide seek proc */
  190.     SerialThreadActionProc,     /* thread action proc */
  191. };
  192. /*
  193.  *----------------------------------------------------------------------
  194.  *
  195.  * SerialInit --
  196.  *
  197.  *  This function initializes the static variables for this file.
  198.  *
  199.  * Results:
  200.  *  None.
  201.  *
  202.  * Side effects:
  203.  *  Creates a new event source.
  204.  *
  205.  *----------------------------------------------------------------------
  206.  */
  207. static ThreadSpecificData *
  208. SerialInit()
  209. {
  210.     ThreadSpecificData *tsdPtr;
  211.     /*
  212.      * Check the initialized flag first, then check it again in the mutex.
  213.      * This is a speed enhancement.
  214.      */
  215.     if (!initialized) {
  216.         Tcl_MutexLock(&serialMutex);
  217.         if (!initialized) {
  218.             initialized = 1;
  219.             Tcl_CreateExitHandler(ProcExitHandler, NULL);
  220.         }
  221.         Tcl_MutexUnlock(&serialMutex);
  222.     }
  223.     tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
  224.     if (tsdPtr == NULL) {
  225.         tsdPtr = TCL_TSD_INIT(&dataKey);
  226.         tsdPtr->firstSerialPtr = NULL;
  227.         Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
  228.         Tcl_CreateThreadExitHandler(SerialExitHandler, NULL);
  229.     }
  230.     return tsdPtr;
  231. }
  232. /*
  233.  *----------------------------------------------------------------------
  234.  *
  235.  * SerialExitHandler --
  236.  *
  237.  *  This function is called to cleanup the serial module before
  238.  *  Tcl is unloaded.
  239.  *
  240.  * Results:
  241.  *  None.
  242.  *
  243.  * Side effects:
  244.  *  Removes the serial event source.
  245.  *
  246.  *----------------------------------------------------------------------
  247.  */
  248. static void
  249. SerialExitHandler(
  250.     ClientData clientData)  /* Old window proc */
  251. {
  252.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  253.     SerialInfo *infoPtr;
  254.     /*
  255.      * Clear all eventually pending output.
  256.      * Otherwise Tcl's exit could totally block,
  257.      * because it performs a blocking flush on all open channels.
  258.      * Note that serial write operations may be blocked due to handshake.
  259.      */
  260.     for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
  261.             infoPtr = infoPtr->nextPtr) {
  262.         PurgeComm(infoPtr->handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR 
  263.             | PURGE_RXCLEAR);
  264.     }
  265.     Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);
  266. }
  267. /*
  268.  *----------------------------------------------------------------------
  269.  *
  270.  * ProcExitHandler --
  271.  *
  272.  *  This function is called to cleanup the process list before
  273.  *  Tcl is unloaded.
  274.  *
  275.  * Results:
  276.  *  None.
  277.  *
  278.  * Side effects:
  279.  *  Resets the process list.
  280.  *
  281.  *----------------------------------------------------------------------
  282.  */
  283. static void
  284. ProcExitHandler(
  285.     ClientData clientData)  /* Old window proc */
  286. {
  287.     Tcl_MutexLock(&serialMutex);
  288.     initialized = 0;
  289.     Tcl_MutexUnlock(&serialMutex);
  290. }
  291. /*
  292.  *----------------------------------------------------------------------
  293.  *
  294.  * SerialBlockTime --
  295.  *
  296.  *  Wrapper to set Tcl's block time in msec
  297.  *
  298.  * Results:
  299.  *  None.
  300.  *----------------------------------------------------------------------
  301.  */
  302. static void
  303. SerialBlockTime(
  304.     int msec)          /* milli-seconds */
  305. {
  306.     Tcl_Time blockTime;
  307.     blockTime.sec  =  msec / 1000;
  308.     blockTime.usec = (msec % 1000) * 1000;
  309.     Tcl_SetMaxBlockTime(&blockTime);
  310. }
  311. /*
  312.  *----------------------------------------------------------------------
  313.  *
  314.  * SerialGetMilliseconds --
  315.  *
  316.  *  Get current time in milliseconds,
  317.  *  Don't care about integer overruns
  318.  *
  319.  * Results:
  320.  *  None.
  321.  *----------------------------------------------------------------------
  322.  */
  323. static unsigned int
  324. SerialGetMilliseconds(
  325.     void)
  326. {
  327.     Tcl_Time time;
  328.     TclpGetTime(&time);
  329.     return (time.sec * 1000 + time.usec / 1000);
  330. }
  331. /*
  332.  *----------------------------------------------------------------------
  333.  *
  334.  * SerialSetupProc --
  335.  *
  336.  *  This procedure is invoked before Tcl_DoOneEvent blocks waiting
  337.  *  for an event.
  338.  *
  339.  * Results:
  340.  *  None.
  341.  *
  342.  * Side effects:
  343.  *  Adjusts the block time if needed.
  344.  *
  345.  *----------------------------------------------------------------------
  346.  */
  347. void
  348. SerialSetupProc(
  349.     ClientData data,    /* Not used. */
  350.     int flags)          /* Event flags as passed to Tcl_DoOneEvent. */
  351. {
  352.     SerialInfo *infoPtr;
  353.     int block = 1;
  354.     int msec = INT_MAX; /* min. found block time */
  355.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  356.     if (!(flags & TCL_FILE_EVENTS)) {
  357.         return;
  358.     }
  359.     /*
  360.      * Look to see if any events handlers installed. If they are, do not block.
  361.      */
  362.     for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
  363.             infoPtr = infoPtr->nextPtr) {
  364.         if (infoPtr->watchMask & TCL_WRITABLE) {
  365.             if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
  366.                 block = 0;
  367.                 msec = min( msec, infoPtr->blockTime );
  368.             }
  369.         }
  370.         if( infoPtr->watchMask & TCL_READABLE ) {
  371.             block = 0;
  372.             msec = min( msec, infoPtr->blockTime );
  373.         }
  374.     }
  375.     if (!block) {
  376.         SerialBlockTime(msec);
  377.     }
  378. }
  379. /*
  380.  *----------------------------------------------------------------------
  381.  *
  382.  * SerialCheckProc --
  383.  *
  384.  *  This procedure is called by Tcl_DoOneEvent to check the serial
  385.  *  event source for events.
  386.  *
  387.  * Results:
  388.  *  None.
  389.  *
  390.  * Side effects:
  391.  *  May queue an event.
  392.  *
  393.  *----------------------------------------------------------------------
  394.  */
  395. static void
  396. SerialCheckProc(
  397.     ClientData data,    /* Not used. */
  398.     int flags)          /* Event flags as passed to Tcl_DoOneEvent. */
  399. {
  400.     SerialInfo *infoPtr;
  401.     SerialEvent *evPtr;
  402.     int needEvent;
  403.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  404.     COMSTAT cStat;
  405.     unsigned int time;
  406.     if (!(flags & TCL_FILE_EVENTS)) {
  407.         return;
  408.     }
  409.     /*
  410.      * Queue events for any ready serials that don't already have events
  411.      * queued.
  412.      */
  413.     for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
  414.             infoPtr = infoPtr->nextPtr) {
  415.         if (infoPtr->flags & SERIAL_PENDING) {
  416.             continue;
  417.         }
  418.         needEvent = 0;
  419.         /*
  420.          * If WRITABLE watch mask is set
  421.          * look for infoPtr->evWritable object
  422.          */
  423.         if (infoPtr->watchMask & TCL_WRITABLE) {
  424.             if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
  425.                 infoPtr->writable = 1;
  426.                 needEvent = 1;
  427.             }
  428.         }
  429.         
  430.         /*
  431.          * If READABLE watch mask is set
  432.          * call ClearCommError to poll cbInQue
  433.          * Window errors are ignored here
  434.          */
  435.         if( infoPtr->watchMask & TCL_READABLE ) {
  436.             if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
  437.                 /*
  438.                  * Look for characters already pending in windows queue.
  439.                  * If they are, poll.
  440.                  */
  441.                 if( infoPtr->watchMask & TCL_READABLE ) {
  442.                     /*
  443.                      * force fileevent after serial read error
  444.                      */
  445.                     if( (cStat.cbInQue > 0) ||
  446.                             (infoPtr->error & SERIAL_READ_ERRORS) ) {
  447.                         infoPtr->readable = 1;
  448. time = SerialGetMilliseconds();
  449. if ((unsigned int) (time - infoPtr->lastEventTime)
  450. >= (unsigned int) infoPtr->blockTime) {
  451.     needEvent = 1;
  452.     infoPtr->lastEventTime = time;
  453. }
  454.                     }
  455.                 }
  456.             }
  457.         }
  458.         /*
  459.          * Queue an event if the serial is signaled for reading or writing.
  460.          */
  461.         if (needEvent) {
  462.             infoPtr->flags |= SERIAL_PENDING;
  463.             evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent));
  464.             evPtr->header.proc = SerialEventProc;
  465.             evPtr->infoPtr = infoPtr;
  466.             Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
  467.         }
  468.     }
  469. }
  470. /*
  471.  *----------------------------------------------------------------------
  472.  *
  473.  * SerialBlockProc --
  474.  *
  475.  *  Set blocking or non-blocking mode on channel.
  476.  *
  477.  * Results:
  478.  *  0 if successful, errno when failed.
  479.  *
  480.  * Side effects:
  481.  *  Sets the device into blocking or non-blocking mode.
  482.  *
  483.  *----------------------------------------------------------------------
  484.  */
  485. static int
  486. SerialBlockProc(
  487.     ClientData instanceData,    /* Instance data for channel. */
  488.     int mode)                   /* TCL_MODE_BLOCKING or
  489.                                  * TCL_MODE_NONBLOCKING. */
  490. {
  491.     int errorCode = 0;
  492.     SerialInfo *infoPtr = (SerialInfo *) instanceData;
  493.     /*
  494.      * Only serial READ can be switched between blocking & nonblocking
  495.      * using COMMTIMEOUTS.
  496.      * Serial write emulates blocking & nonblocking by the SerialWriterThread.
  497.      */
  498.     if (mode == TCL_MODE_NONBLOCKING) {
  499.         infoPtr->flags |= SERIAL_ASYNC;
  500.     } else {
  501.         infoPtr->flags &= ~(SERIAL_ASYNC);
  502.     }
  503.     return errorCode;
  504. }
  505. /*
  506.  *----------------------------------------------------------------------
  507.  *
  508.  * SerialCloseProc --
  509.  *
  510.  *  Closes a serial based IO channel.
  511.  *
  512.  * Results:
  513.  *  0 on success, errno otherwise.
  514.  *
  515.  * Side effects:
  516.  *  Closes the physical channel.
  517.  *
  518.  *----------------------------------------------------------------------
  519.  */
  520. static int
  521. SerialCloseProc(
  522.     ClientData instanceData,    /* Pointer to SerialInfo structure. */
  523.     Tcl_Interp *interp)         /* For error reporting. */
  524. {
  525.     SerialInfo *serialPtr = (SerialInfo *) instanceData;
  526.     int errorCode, result = 0;
  527.     SerialInfo *infoPtr, **nextPtrPtr;
  528.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  529.     DWORD exitCode;
  530.     errorCode = 0;
  531.     if (serialPtr->validMask & TCL_READABLE) {
  532.         PurgeComm(serialPtr->handle, PURGE_RXABORT | PURGE_RXCLEAR);
  533.         CloseHandle(serialPtr->osRead.hEvent);
  534.     }
  535.     serialPtr->validMask &= ~TCL_READABLE;
  536.  
  537.     if (serialPtr->validMask & TCL_WRITABLE) {
  538.         /*
  539.          * Generally we cannot wait for a pending write operation
  540.          * because it may hang due to handshake
  541.          *    WaitForSingleObject(serialPtr->evWritable, INFINITE);
  542.          */
  543. /*
  544.  * The thread may have already closed on it's own.  Check it's
  545.  * exit code.
  546.  */
  547. GetExitCodeThread(serialPtr->writeThread, &exitCode);
  548. if (exitCode == STILL_ACTIVE) {
  549.     /*
  550.      * Set the stop event so that if the writer thread is
  551.      * blocked in SerialWriterThread on WaitForMultipleEvents, it
  552.      * will exit cleanly.
  553.      */
  554.     SetEvent(serialPtr->evStopWriter);
  555.     /*
  556.      * Wait at most 20 milliseconds for the writer thread to
  557.      * close.
  558.      */
  559.     if (WaitForSingleObject(serialPtr->writeThread, 20)
  560.     == WAIT_TIMEOUT) {
  561. /*
  562.  * Forcibly terminate the background thread as a last
  563.  * resort.  Note that we need to guard against
  564.  * terminating the thread while it is in the middle of
  565.  * Tcl_ThreadAlert because it won't be able to release
  566.  * the notifier lock.
  567.  */
  568. Tcl_MutexLock(&serialMutex);
  569. /* BUG: this leaks memory */
  570. TerminateThread(serialPtr->writeThread, 0);
  571. Tcl_MutexUnlock(&serialMutex);
  572.     }
  573. }
  574.         CloseHandle(serialPtr->writeThread);
  575. CloseHandle(serialPtr->osWrite.hEvent);
  576.         CloseHandle(serialPtr->evWritable);
  577.         CloseHandle(serialPtr->evStartWriter);
  578.         CloseHandle(serialPtr->evStopWriter);
  579.         serialPtr->writeThread = NULL;
  580.         PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR);
  581.     }
  582.     serialPtr->validMask &= ~TCL_WRITABLE;
  583.     DeleteCriticalSection(&serialPtr->csWrite);
  584.     /*
  585.      * Don't close the Win32 handle if the handle is a standard channel
  586.      * during the thread exit process.  Otherwise, one thread may kill
  587.      * the stdio of another.
  588.      */
  589.     if (!TclInThreadExit()
  590. || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
  591. && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
  592. && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
  593.     if (CloseHandle(serialPtr->handle) == FALSE) {
  594. TclWinConvertError(GetLastError());
  595. errorCode = errno;
  596.     }
  597.     }
  598.     serialPtr->watchMask &= serialPtr->validMask;
  599.     /*
  600.      * Remove the file from the list of watched files.
  601.      */
  602.     for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr;
  603.     infoPtr != NULL;
  604.     nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
  605.         if (infoPtr == (SerialInfo *)serialPtr) {
  606.             *nextPtrPtr = infoPtr->nextPtr;
  607.             break;
  608.         }
  609.     }
  610.     /*
  611.      * Wrap the error file into a channel and give it to the cleanup
  612.      * routine.
  613.      */
  614.     if (serialPtr->writeBuf != NULL) {
  615.         ckfree(serialPtr->writeBuf);
  616.         serialPtr->writeBuf = NULL;
  617.     }
  618.     ckfree((char*) serialPtr);
  619.     if (errorCode == 0) {
  620.         return result;
  621.     }
  622.     return errorCode;
  623. }
  624. /*
  625.  *----------------------------------------------------------------------
  626.  *
  627.  * blockingRead --
  628.  *
  629.  *  Perform a blocking read into the buffer given. Returns
  630.  *  count of how many bytes were actually read, and an error indication.
  631.  *
  632.  * Results:
  633.  *  A count of how many bytes were read is returned and an error
  634.  *  indication is returned.
  635.  *
  636.  * Side effects:
  637.  *  Reads input from the actual channel.
  638.  *
  639.  *----------------------------------------------------------------------
  640.  */
  641. static int
  642. blockingRead( 
  643.     SerialInfo *infoPtr,    /* Serial info structure */
  644.     LPVOID buf,             /* The input buffer pointer */
  645.     DWORD  bufSize,         /* The number of bytes to read */
  646.     LPDWORD  lpRead,        /* Returns number of bytes read */ 
  647.     LPOVERLAPPED osPtr )    /* OVERLAPPED structure */
  648. {
  649.     /*
  650.      *  Perform overlapped blocking read. 
  651.      *  1. Reset the overlapped event
  652.      *  2. Start overlapped read operation
  653.      *  3. Wait for completion
  654.      */
  655.     /* 
  656.      * Set Offset to ZERO, otherwise NT4.0 may report an error.
  657.      */
  658.     osPtr->Offset = osPtr->OffsetHigh = 0;
  659.     ResetEvent(osPtr->hEvent);
  660.     if (! ReadFile(infoPtr->handle, buf, bufSize, lpRead, osPtr) ) {
  661.         if (GetLastError() != ERROR_IO_PENDING) {
  662.             /* ReadFile failed, but it isn't delayed. Report error. */
  663.             return FALSE;
  664.         } else {   
  665.             /* Read is pending, wait for completion, timeout ? */
  666.             if (! GetOverlappedResult(infoPtr->handle, osPtr, lpRead, TRUE) ) {
  667.                 return FALSE;
  668.             }
  669.         }
  670.     } else {
  671.         /* ReadFile completed immediately. */
  672.     }
  673.     return TRUE;
  674. }
  675. /*
  676.  *----------------------------------------------------------------------
  677.  *
  678.  * blockingWrite --
  679.  *
  680.  *  Perform a blocking write from the buffer given. Returns
  681.  *  count of how many bytes were actually written, and an error indication.
  682.  *
  683.  * Results:
  684.  *  A count of how many bytes were written is returned and an error
  685.  *  indication is returned.
  686.  *
  687.  * Side effects:
  688.  *  Writes output to the actual channel.
  689.  *
  690.  *----------------------------------------------------------------------
  691.  */
  692. static int
  693. blockingWrite(
  694.     SerialInfo *infoPtr,    /* Serial info structure */
  695.     LPVOID  buf,            /* The output buffer pointer */
  696.     DWORD   bufSize,        /* The number of bytes to write */
  697.     LPDWORD lpWritten,      /* Returns number of bytes written */ 
  698.     LPOVERLAPPED osPtr )    /* OVERLAPPED structure */
  699. {
  700.     int result;
  701.     /*
  702.     *  Perform overlapped blocking write. 
  703.     *  1. Reset the overlapped event
  704.     *  2. Remove these bytes from the output queue counter
  705.     *  3. Start overlapped write operation
  706.     *  3. Remove these bytes from the output queue counter
  707.     *  4. Wait for completion
  708.     *  5. Adjust the output queue counter
  709.     */
  710.     ResetEvent(osPtr->hEvent);
  711.     EnterCriticalSection(&infoPtr->csWrite);
  712.     infoPtr->writeQueue -= bufSize;
  713. /* 
  714. * Set Offset to ZERO, otherwise NT4.0 may report an error 
  715. */
  716. osPtr->Offset = osPtr->OffsetHigh = 0;
  717.     result = WriteFile(infoPtr->handle, buf, bufSize, lpWritten, osPtr);
  718.     LeaveCriticalSection(&infoPtr->csWrite);
  719.     if (result == FALSE ) {
  720.         int err = GetLastError();
  721.         switch (err) {
  722.         case ERROR_IO_PENDING:
  723.             /* Write is pending, wait for completion */
  724.             if (! GetOverlappedResult(infoPtr->handle, osPtr, lpWritten, TRUE) ) {
  725.                 return FALSE;
  726.             }
  727.             break;
  728.         case ERROR_COUNTER_TIMEOUT:
  729.             /* Write timeout handled in SerialOutputProc */
  730.             break;
  731.         default:
  732.             /* WriteFile failed, but it isn't delayed. Report error */
  733.             return FALSE;
  734.         }
  735.     } else {
  736.         /* WriteFile completed immediately. */
  737.     }
  738.     EnterCriticalSection(&infoPtr->csWrite);
  739.     infoPtr->writeQueue += (*lpWritten - bufSize);
  740.     LeaveCriticalSection(&infoPtr->csWrite);
  741.     return TRUE;
  742. }
  743. /*
  744.  *----------------------------------------------------------------------
  745.  *
  746.  * SerialInputProc --
  747.  *
  748.  *  Reads input from the IO channel into the buffer given. Returns
  749.  *  count of how many bytes were actually read, and an error indication.
  750.  *
  751.  * Results:
  752.  *  A count of how many bytes were read is returned and an error
  753.  *  indication is returned in an output argument.
  754.  *
  755.  * Side effects:
  756.  *  Reads input from the actual channel.
  757.  *
  758.  *----------------------------------------------------------------------
  759.  */
  760. static int
  761. SerialInputProc(
  762.     ClientData instanceData,    /* Serial state. */
  763.     char *buf,                  /* Where to store data read. */
  764.     int bufSize,                /* How much space is available
  765.                                  * in the buffer? */
  766.     int *errorCode)             /* Where to store error code. */
  767. {
  768.     SerialInfo *infoPtr = (SerialInfo *) instanceData;
  769.     DWORD bytesRead = 0;
  770.     COMSTAT cStat;
  771.     *errorCode = 0;
  772.     /*
  773.      * Check if there is a CommError pending from SerialCheckProc
  774.      */
  775.     if( infoPtr->error & SERIAL_READ_ERRORS ){
  776.         goto commError;
  777.     }
  778.     /*
  779.      * Look for characters already pending in windows queue.
  780.      * This is the mainly restored good old code from Tcl8.0
  781.      */
  782.     if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
  783.         /*
  784.          * Check for errors here, but not in the evSetup/Check procedures
  785.          */
  786.         if( infoPtr->error & SERIAL_READ_ERRORS ) {
  787.             goto commError;
  788.         }
  789.         if( infoPtr->flags & SERIAL_ASYNC ) {
  790.             /*
  791.              * NON_BLOCKING mode:
  792.              * Avoid blocking by reading more bytes than available
  793.              * in input buffer
  794.              */
  795.             if( cStat.cbInQue > 0 ) {
  796.                 if( (DWORD) bufSize > cStat.cbInQue ) {
  797.                     bufSize = cStat.cbInQue;
  798.                 }
  799.             } else {
  800.                 errno = *errorCode = EAGAIN;
  801.                 return -1;
  802.             }
  803.         } else {
  804.             /*
  805.              * BLOCKING mode:
  806.              * Tcl trys to read a full buffer of 4 kBytes here
  807.              */
  808.             if( cStat.cbInQue > 0 ) {
  809.                 if( (DWORD) bufSize > cStat.cbInQue ) {
  810.                     bufSize = cStat.cbInQue;
  811.                 }
  812.             } else {
  813.                 bufSize = 1;
  814.             }
  815.         }
  816.     }
  817.     if( bufSize == 0 ) {
  818.         return bytesRead = 0;
  819.     }
  820.     /*
  821.     *  Perform blocking read. Doesn't block in non-blocking mode, 
  822.     *  because we checked the number of available bytes.
  823.     */
  824.     if (blockingRead(infoPtr, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
  825.             &infoPtr->osRead) == FALSE) {
  826.         goto error;
  827.     }
  828.     return bytesRead;
  829. error:
  830.     TclWinConvertError(GetLastError());
  831.     *errorCode = errno;
  832.     return -1;
  833. commError:
  834.     infoPtr->lastError = infoPtr->error;  /* save last error code */
  835.     infoPtr->error = 0;                   /* reset error code */
  836.     *errorCode = EIO;                     /* to return read-error only once */
  837.     return -1;
  838. }
  839. /*
  840.  *----------------------------------------------------------------------
  841.  *
  842.  * SerialOutputProc --
  843.  *
  844.  *  Writes the given output on the IO channel. Returns count of how
  845.  *  many characters were actually written, and an error indication.
  846.  *
  847.  * Results:
  848.  *  A count of how many characters were written is returned and an
  849.  *  error indication is returned in an output argument.
  850.  *
  851.  * Side effects:
  852.  *  Writes output on the actual channel.
  853.  *
  854.  *----------------------------------------------------------------------
  855.  */
  856. static int
  857. SerialOutputProc(
  858.     ClientData instanceData,    /* Serial state. */
  859.     CONST char *buf,            /* The data buffer. */
  860.     int toWrite,                /* How many bytes to write? */
  861.     int *errorCode)             /* Where to store error code. */
  862. {
  863.     SerialInfo *infoPtr = (SerialInfo *) instanceData;
  864.     DWORD bytesWritten, timeout;
  865.     *errorCode = 0;
  866.     /*
  867.      * At EXIT Tcl trys to flush all open channels in blocking mode.
  868.      * We avoid blocking output after ExitProc or CloseHandler(chan)
  869.      * has been called by checking the corrresponding variables.
  870.      */
  871.     if( ! initialized || TclInExit() ) {
  872.         return toWrite;
  873.     }
  874.     /*
  875.      * Check if there is a CommError pending from SerialCheckProc
  876.      */
  877.     if( infoPtr->error & SERIAL_WRITE_ERRORS ){
  878.         infoPtr->lastError = infoPtr->error;  /* save last error code */
  879.         infoPtr->error = 0;                   /* reset error code */
  880.         errno = EIO;            
  881.         goto error;
  882.     }
  883.     timeout = (infoPtr->flags & SERIAL_ASYNC) ? 0 : INFINITE;
  884.     if (WaitForSingleObject(infoPtr->evWritable, timeout) == WAIT_TIMEOUT) {
  885.         /*
  886.          * The writer thread is blocked waiting for a write to complete
  887.          * and the channel is in non-blocking mode.
  888.          */
  889.         errno = EWOULDBLOCK;
  890.         goto error1;
  891.     }
  892.     /*
  893.      * Check for a background error on the last write.
  894.      */
  895.     if (infoPtr->writeError) {
  896.         TclWinConvertError(infoPtr->writeError);
  897.         infoPtr->writeError = 0;
  898.         goto error1;
  899.     }
  900.     /*
  901.      * Remember the number of bytes in output queue
  902.      */
  903.     EnterCriticalSection(&infoPtr->csWrite);
  904.     infoPtr->writeQueue += toWrite;
  905.     LeaveCriticalSection(&infoPtr->csWrite);
  906.     if (infoPtr->flags & SERIAL_ASYNC) {
  907.         /*
  908.          * The serial is non-blocking, so copy the data into the output
  909.          * buffer and restart the writer thread.
  910.          */
  911.         if (toWrite > infoPtr->writeBufLen) {
  912.             /*
  913.              * Reallocate the buffer to be large enough to hold the data.
  914.              */
  915.             if (infoPtr->writeBuf) {
  916.                 ckfree(infoPtr->writeBuf);
  917.             }
  918.             infoPtr->writeBufLen = toWrite;
  919.             infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
  920.         }
  921.         memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
  922.         infoPtr->toWrite = toWrite;
  923.         ResetEvent(infoPtr->evWritable);
  924.         SetEvent(infoPtr->evStartWriter);
  925.         bytesWritten = (DWORD) toWrite;
  926.     } else {
  927.         /*
  928.         * In the blocking case, just try to write the buffer directly.
  929.         * This avoids an unnecessary copy.
  930.         */
  931.         if (! blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
  932.                 &bytesWritten, &infoPtr->osWrite) ) {
  933.             goto writeError;
  934.         }
  935.         if (bytesWritten != (DWORD) toWrite) {
  936.             /* Write timeout */
  937.             infoPtr->lastError |= CE_PTO;
  938.             errno = EIO;
  939.             goto error;
  940.         }
  941.     }
  942.     return (int) bytesWritten;
  943. writeError:
  944.     TclWinConvertError(GetLastError());
  945. error:
  946.     /* 
  947.      * Reset the output queue counter on error during blocking output 
  948.      */
  949. /*
  950.     EnterCriticalSection(&infoPtr->csWrite);
  951.     infoPtr->writeQueue = 0;
  952.     LeaveCriticalSection(&infoPtr->csWrite);
  953. */
  954.   error1: 
  955.     *errorCode = errno;
  956.     return -1;
  957. }
  958. /*
  959.  *----------------------------------------------------------------------
  960.  *
  961.  * SerialEventProc --
  962.  *
  963.  *  This function is invoked by Tcl_ServiceEvent when a file event
  964.  *  reaches the front of the event queue.  This procedure invokes
  965.  *  Tcl_NotifyChannel on the serial.
  966.  *
  967.  * Results:
  968.  *  Returns 1 if the event was handled, meaning it should be removed
  969.  *  from the queue.  Returns 0 if the event was not handled, meaning
  970.  *  it should stay on the queue.  The only time the event isn't
  971.  *  handled is if the TCL_FILE_EVENTS flag bit isn't set.
  972.  *
  973.  * Side effects:
  974.  *  Whatever the notifier callback does.
  975.  *
  976.  *----------------------------------------------------------------------
  977.  */
  978. static int
  979. SerialEventProc(
  980.     Tcl_Event *evPtr,   /* Event to service. */
  981.     int flags)          /* Flags that indicate what events to
  982.                          * handle, such as TCL_FILE_EVENTS. */
  983. {
  984.     SerialEvent *serialEvPtr = (SerialEvent *)evPtr;
  985.     SerialInfo *infoPtr;
  986.     int mask;
  987.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  988.     if (!(flags & TCL_FILE_EVENTS)) {
  989.         return 0;
  990.     }
  991.     /*
  992.      * Search through the list of watched serials for the one whose handle
  993.      * matches the event.  We do this rather than simply dereferencing
  994.      * the handle in the event so that serials can be deleted while the
  995.      * event is in the queue.
  996.      */
  997.     for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
  998.             infoPtr = infoPtr->nextPtr) {
  999.         if (serialEvPtr->infoPtr == infoPtr) {
  1000.             infoPtr->flags &= ~(SERIAL_PENDING);
  1001.             break;
  1002.         }
  1003.     }
  1004.     /*
  1005.      * Remove stale events.
  1006.      */
  1007.     if (!infoPtr) {
  1008.         return 1;
  1009.     }
  1010.     /*
  1011.      * Check to see if the serial is readable.  Note
  1012.      * that we can't tell if a serial is writable, so we always report it
  1013.      * as being writable unless we have detected EOF.
  1014.      */
  1015.     mask = 0;
  1016.     if( infoPtr->watchMask & TCL_WRITABLE ) {
  1017.         if( infoPtr->writable ) {
  1018.             mask |= TCL_WRITABLE;
  1019.             infoPtr->writable = 0;
  1020.         }
  1021.     }
  1022.     if( infoPtr->watchMask & TCL_READABLE ) {
  1023.         if( infoPtr->readable ) {
  1024.             mask |= TCL_READABLE;
  1025.             infoPtr->readable = 0;
  1026.         }
  1027.     }
  1028.     /*
  1029.      * Inform the channel of the events.
  1030.      */
  1031.     Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
  1032.     return 1;
  1033. }
  1034. /*
  1035.  *----------------------------------------------------------------------
  1036.  *
  1037.  * SerialWatchProc --
  1038.  *
  1039.  *  Called by the notifier to set up to watch for events on this
  1040.  *  channel.
  1041.  *
  1042.  * Results:
  1043.  *  None.
  1044.  *
  1045.  * Side effects:
  1046.  *  None.
  1047.  *
  1048.  *----------------------------------------------------------------------
  1049.  */
  1050. static void
  1051. SerialWatchProc(
  1052.     ClientData instanceData,     /* Serial state. */
  1053.     int mask)                    /* What events to watch for, OR-ed
  1054.                                   * combination of TCL_READABLE,
  1055.                                   * TCL_WRITABLE and TCL_EXCEPTION. */
  1056. {
  1057.     SerialInfo **nextPtrPtr, *ptr;
  1058.     SerialInfo *infoPtr = (SerialInfo *) instanceData;
  1059.     int oldMask = infoPtr->watchMask;
  1060.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1061.     /*
  1062.      * Since the file is always ready for events, we set the block time
  1063.      * so we will poll.
  1064.      */
  1065.     infoPtr->watchMask = mask & infoPtr->validMask;
  1066.     if (infoPtr->watchMask) {
  1067.         if (!oldMask) {
  1068.             infoPtr->nextPtr = tsdPtr->firstSerialPtr;
  1069.             tsdPtr->firstSerialPtr = infoPtr;
  1070.         }
  1071.         SerialBlockTime(infoPtr->blockTime);
  1072.     } else {
  1073.         if (oldMask) {
  1074.             /*
  1075.              * Remove the serial port from the list of watched serial ports.
  1076.              */
  1077.             for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr;
  1078.                     ptr != NULL;
  1079.                     nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
  1080.                 if (infoPtr == ptr) {
  1081.                     *nextPtrPtr = ptr->nextPtr;
  1082.                     break;
  1083.                 }
  1084.             }
  1085.         }
  1086.     }
  1087. }
  1088. /*
  1089.  *----------------------------------------------------------------------
  1090.  *
  1091.  * SerialGetHandleProc --
  1092.  *
  1093.  *  Called from Tcl_GetChannelHandle to retrieve OS handles from
  1094.  *  inside a command serial port based channel.
  1095.  *
  1096.  * Results:
  1097.  *  Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  1098.  *  there is no handle for the specified direction.
  1099.  *
  1100.  * Side effects:
  1101.  *  None.
  1102.  *
  1103.  *----------------------------------------------------------------------
  1104.  */
  1105. static int
  1106. SerialGetHandleProc(
  1107.     ClientData instanceData,    /* The serial state. */
  1108.     int direction,              /* TCL_READABLE or TCL_WRITABLE */
  1109.     ClientData *handlePtr)      /* Where to store the handle.  */
  1110. {
  1111.     SerialInfo *infoPtr = (SerialInfo *) instanceData;
  1112.     *handlePtr = (ClientData) infoPtr->handle;
  1113.     return TCL_OK;
  1114. }
  1115. /*
  1116.  *----------------------------------------------------------------------
  1117.  *
  1118.  * SerialWriterThread --
  1119.  *
  1120.  *      This function runs in a separate thread and writes data
  1121.  *      onto a serial.
  1122.  *
  1123.  * Results:
  1124.  *      Always returns 0.
  1125.  *
  1126.  * Side effects:
  1127.  *      Signals the main thread when an output operation is completed.
  1128.  *      May cause the main thread to wake up by posting a message.  
  1129.  *
  1130.  *----------------------------------------------------------------------
  1131.  */
  1132. static DWORD WINAPI
  1133. SerialWriterThread(LPVOID arg)
  1134. {
  1135.     SerialInfo *infoPtr = (SerialInfo *)arg;
  1136.     DWORD bytesWritten, toWrite, waitResult;
  1137.     char *buf;
  1138.     OVERLAPPED myWrite; /* have an own OVERLAPPED in this thread */
  1139.     HANDLE wEvents[2];
  1140.     /*
  1141.      * The stop event takes precedence by being first in the list.
  1142.      */
  1143.     wEvents[0] = infoPtr->evStopWriter;
  1144.     wEvents[1] = infoPtr->evStartWriter;
  1145.     for (;;) {
  1146.         /*
  1147.          * Wait for the main thread to signal before attempting to write.
  1148.          */
  1149. waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
  1150. if (waitResult != (WAIT_OBJECT_0 + 1)) {
  1151.     /*
  1152.      * The start event was not signaled.  It might be the stop event
  1153.      * or an error, so exit.
  1154.      */
  1155.     break;
  1156. }
  1157.         buf = infoPtr->writeBuf;
  1158.         toWrite = infoPtr->toWrite;
  1159.         myWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1160.         /*
  1161.          * Loop until all of the bytes are written or an error occurs.
  1162.          */
  1163.         while (toWrite > 0) {
  1164.             /*
  1165.             *  Check for pending writeError
  1166.             *  Ignore all write operations until the user has been notified
  1167.             */
  1168.             if (infoPtr->writeError) {
  1169.                 break;
  1170.             }
  1171.             if (blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite, 
  1172.                     &bytesWritten, &myWrite) == FALSE) {
  1173.                 infoPtr->writeError = GetLastError();
  1174.                 break;
  1175.             }
  1176.             if (bytesWritten != toWrite) {
  1177.                 /* Write timeout */
  1178.                 infoPtr->writeError = ERROR_WRITE_FAULT;
  1179.                 break;
  1180.             }
  1181.             toWrite -= bytesWritten;
  1182.             buf += bytesWritten;
  1183.         }
  1184.         CloseHandle(myWrite.hEvent);
  1185.         /*
  1186.          * Signal the main thread by signalling the evWritable event and
  1187.          * then waking up the notifier thread.
  1188.          */
  1189.         SetEvent(infoPtr->evWritable);
  1190.         /*
  1191.          * Alert the foreground thread.  Note that we need to treat this like
  1192.          * a critical section so the foreground thread does not terminate
  1193.          * this thread while we are holding a mutex in the notifier code.
  1194.          */
  1195.         Tcl_MutexLock(&serialMutex);
  1196. if (infoPtr->threadId != NULL) {
  1197.     /* TIP #218. When in flight ignore the event, no one will receive it anyway */
  1198.     Tcl_ThreadAlert(infoPtr->threadId);
  1199. }
  1200.         Tcl_MutexUnlock(&serialMutex);
  1201.     }
  1202.     return 0;
  1203. }
  1204. /*
  1205.  *----------------------------------------------------------------------
  1206.  *
  1207.  * TclWinSerialReopen --
  1208.  *
  1209.  *  Reopens the serial port with the OVERLAPPED FLAG set
  1210.  *
  1211.  * Results:
  1212.  *  Returns the new handle, or INVALID_HANDLE_VALUE
  1213.  *  Normally there shouldn't be any error, 
  1214.  *  because the same channel has previously been succeesfully opened.
  1215.  *
  1216.  * Side effects:
  1217.  *  May close the original handle
  1218.  *
  1219.  *----------------------------------------------------------------------
  1220.  */
  1221. HANDLE
  1222. TclWinSerialReopen(handle, name, access)
  1223.     HANDLE handle;
  1224.     CONST TCHAR *name;
  1225.     DWORD access;
  1226. {
  1227.     ThreadSpecificData *tsdPtr;
  1228.     tsdPtr = SerialInit();
  1229.     /* 
  1230.     * Multithreaded I/O needs the overlapped flag set
  1231.     * otherwise ClearCommError blocks under Windows NT/2000 until serial
  1232.     * output is finished
  1233.     */
  1234.     if (CloseHandle(handle) == FALSE) {
  1235.         return INVALID_HANDLE_VALUE;
  1236.     }
  1237.     handle = (*tclWinProcs->createFileProc)(name, access, 
  1238.                 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
  1239.     return handle;
  1240. }
  1241. /*
  1242.  *----------------------------------------------------------------------
  1243.  *
  1244.  * TclWinOpenSerialChannel --
  1245.  *
  1246.  *  Constructs a Serial port channel for the specified standard OS handle.
  1247.  *      This is a helper function to break up the construction of
  1248.  *      channels into File, Console, or Serial.
  1249.  *
  1250.  * Results:
  1251.  *  Returns the new channel, or NULL.
  1252.  *
  1253.  * Side effects:
  1254.  *  May open the channel
  1255.  *
  1256.  *----------------------------------------------------------------------
  1257.  */
  1258. Tcl_Channel
  1259. TclWinOpenSerialChannel(handle, channelName, permissions)
  1260.     HANDLE handle;
  1261.     char *channelName;
  1262.     int permissions;
  1263. {
  1264.     SerialInfo *infoPtr;
  1265.     DWORD id;
  1266.     SerialInit();
  1267.     infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo));
  1268.     memset(infoPtr, 0, sizeof(SerialInfo));
  1269.     infoPtr->validMask     = permissions;
  1270.     infoPtr->handle        = handle;
  1271.     infoPtr->channel       = (Tcl_Channel) NULL;
  1272.     infoPtr->readable      = 0; 
  1273.     infoPtr->writable      = 1;
  1274.     infoPtr->toWrite       = infoPtr->writeQueue = 0;
  1275.     infoPtr->blockTime     = SERIAL_DEFAULT_BLOCKTIME;
  1276.     infoPtr->lastEventTime = 0;
  1277.     infoPtr->lastError     = infoPtr->error = 0;
  1278.     infoPtr->threadId      = Tcl_GetCurrentThread();
  1279.     infoPtr->sysBufRead    = 4096;
  1280.     infoPtr->sysBufWrite   = 4096;
  1281.     /*
  1282.      * Use the pointer to keep the channel names unique, in case
  1283.      * the handles are shared between multiple channels (stdin/stdout).
  1284.      */
  1285.     wsprintfA(channelName, "file%lx", (int) infoPtr);
  1286.     infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName,
  1287.             (ClientData) infoPtr, permissions);
  1288.     SetupComm(handle, infoPtr->sysBufRead, infoPtr->sysBufWrite);
  1289.     PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR 
  1290.             | PURGE_RXCLEAR);
  1291.     /*
  1292.      * default is blocking
  1293.      */
  1294.     SetCommTimeouts(handle, &no_timeout);
  1295.     InitializeCriticalSection(&infoPtr->csWrite);
  1296.     if (permissions & TCL_READABLE) {
  1297.         infoPtr->osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1298.     }
  1299.     if (permissions & TCL_WRITABLE) {
  1300.         /* 
  1301.         * Initially the channel is writable
  1302.         * and the writeThread is idle.
  1303.         */ 
  1304.         infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1305.         infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL);
  1306.         infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1307. infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1308.         infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
  1309.             infoPtr, 0, &id);
  1310.     }
  1311.     /*
  1312.      * Files have default translation of AUTO and ^Z eof char, which
  1313.      * means that a ^Z will be accepted as EOF when reading.
  1314.      */
  1315.     Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
  1316.     Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "32 {}");
  1317.     return infoPtr->channel;
  1318. }
  1319. /*
  1320.  *----------------------------------------------------------------------
  1321.  *
  1322.  * SerialErrorStr --
  1323.  *
  1324.  *  Converts a Win32 serial error code to a list of readable errors
  1325.  *
  1326.  *----------------------------------------------------------------------
  1327.  */
  1328. static void
  1329. SerialErrorStr(error, dsPtr)
  1330.     DWORD error;           /* Win32 serial error code */
  1331.     Tcl_DString *dsPtr;    /* Where to store string */
  1332. {
  1333.     if( (error & CE_RXOVER) != 0) {
  1334.                 Tcl_DStringAppendElement(dsPtr, "RXOVER");
  1335.     }
  1336.     if( (error & CE_OVERRUN) != 0) {
  1337.                 Tcl_DStringAppendElement(dsPtr, "OVERRUN");
  1338.     }
  1339.     if( (error & CE_RXPARITY) != 0) {
  1340.                 Tcl_DStringAppendElement(dsPtr, "RXPARITY");
  1341.     }
  1342.     if( (error & CE_FRAME) != 0) {
  1343.                 Tcl_DStringAppendElement(dsPtr, "FRAME");
  1344.     }
  1345.     if( (error & CE_BREAK) != 0) {
  1346.                 Tcl_DStringAppendElement(dsPtr, "BREAK");
  1347.     }
  1348.     if( (error & CE_TXFULL) != 0) {
  1349.                 Tcl_DStringAppendElement(dsPtr, "TXFULL");
  1350.     }
  1351.     if( (error & CE_PTO) != 0) {    /* PTO used to signal WRITE-TIMEOUT */
  1352.                 Tcl_DStringAppendElement(dsPtr, "TIMEOUT");
  1353.     }
  1354.     if( (error & ~((DWORD) (SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS))) != 0) {
  1355.                 char buf[TCL_INTEGER_SPACE + 1];
  1356.                 wsprintfA(buf, "%d", error);
  1357.                 Tcl_DStringAppendElement(dsPtr, buf);
  1358.     }
  1359. }
  1360. /*
  1361.  *----------------------------------------------------------------------
  1362.  *
  1363.  * SerialModemStatusStr --
  1364.  *
  1365.  *  Converts a Win32 modem status list of readable flags
  1366.  *
  1367.  *----------------------------------------------------------------------
  1368.  */
  1369. static void
  1370. SerialModemStatusStr(status, dsPtr)
  1371.     DWORD status;          /* Win32 modem status */
  1372.     Tcl_DString *dsPtr;    /* Where to store string */
  1373. {
  1374.     Tcl_DStringAppendElement(dsPtr, "CTS");
  1375.     Tcl_DStringAppendElement(dsPtr, (status & MS_CTS_ON)  ?  "1" : "0");
  1376.     Tcl_DStringAppendElement(dsPtr, "DSR");
  1377.     Tcl_DStringAppendElement(dsPtr, (status & MS_DSR_ON)   ? "1" : "0");
  1378.     Tcl_DStringAppendElement(dsPtr, "RING");
  1379.     Tcl_DStringAppendElement(dsPtr, (status & MS_RING_ON)  ? "1" : "0");
  1380.     Tcl_DStringAppendElement(dsPtr, "DCD");
  1381.     Tcl_DStringAppendElement(dsPtr, (status & MS_RLSD_ON)  ? "1" : "0");
  1382. }
  1383. /*
  1384.  *----------------------------------------------------------------------
  1385.  *
  1386.  * SerialSetOptionProc --
  1387.  *
  1388.  *  Sets an option on a channel.
  1389.  *
  1390.  * Results:
  1391.  *  A standard Tcl result. Also sets the interp's result on error if
  1392.  *  interp is not NULL.
  1393.  *
  1394.  * Side effects:
  1395.  *  May modify an option on a device.
  1396.  *
  1397.  *----------------------------------------------------------------------
  1398.  */
  1399. static int
  1400. SerialSetOptionProc(instanceData, interp, optionName, value)
  1401.     ClientData instanceData; /* File state. */
  1402.     Tcl_Interp *interp; /* For error reporting - can be NULL. */
  1403.     CONST char *optionName; /* Which option to set? */
  1404.     CONST char *value; /* New value for option. */
  1405. {
  1406.     SerialInfo *infoPtr;
  1407.     DCB dcb;
  1408.     BOOL result, flag;
  1409.     size_t len, vlen;
  1410.     Tcl_DString ds;
  1411.     CONST TCHAR *native;
  1412.     int argc;
  1413.     CONST char **argv;
  1414.     infoPtr = (SerialInfo *) instanceData;
  1415.     /*
  1416.      * Parse options
  1417.      */
  1418.     len = strlen(optionName);
  1419.     vlen = strlen(value);
  1420.     /*
  1421.      * Option -mode baud,parity,databits,stopbits
  1422.      */
  1423.     if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
  1424. if (! GetCommState(infoPtr->handle, &dcb)) {
  1425.     if (interp) {
  1426. Tcl_AppendResult(interp, 
  1427. "can't get comm state", (char *) NULL);
  1428.     }
  1429.     return TCL_ERROR;
  1430. }
  1431. native = Tcl_WinUtfToTChar(value, -1, &ds);
  1432. result = (*tclWinProcs->buildCommDCBProc)(native, &dcb);
  1433. Tcl_DStringFree(&ds);
  1434. if (result == FALSE) {
  1435.     if (interp) {
  1436. Tcl_AppendResult(interp,
  1437. "bad value for -mode: should be baud,parity,data,stop",
  1438. (char *) NULL);
  1439.     }
  1440.     return TCL_ERROR;
  1441. }
  1442. /* Default settings for serial communications */ 
  1443. dcb.fBinary = TRUE;
  1444. dcb.fErrorChar = FALSE;
  1445. dcb.fNull = FALSE;
  1446. dcb.fAbortOnError = FALSE;
  1447. if (! SetCommState(infoPtr->handle, &dcb) ) {
  1448.     if (interp) {
  1449. Tcl_AppendResult(interp, 
  1450. "can't set comm state", (char *) NULL);
  1451.     }
  1452.     return TCL_ERROR;
  1453. }
  1454. return TCL_OK;
  1455.     }
  1456.     /*
  1457.      * Option -handshake none|xonxoff|rtscts|dtrdsr
  1458.      */
  1459.     if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
  1460. if (! GetCommState(infoPtr->handle, &dcb)) {
  1461.     if (interp) {
  1462. Tcl_AppendResult(interp, 
  1463. "can't get comm state", (char *) NULL);
  1464.     }
  1465.     return TCL_ERROR;
  1466. }
  1467. /*
  1468.  * Reset all handshake options
  1469.  * DTR and RTS are ON by default
  1470.  */
  1471. dcb.fOutX = dcb.fInX = FALSE;
  1472. dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fDsrSensitivity = FALSE;
  1473. dcb.fDtrControl = DTR_CONTROL_ENABLE;
  1474. dcb.fRtsControl = RTS_CONTROL_ENABLE;
  1475. dcb.fTXContinueOnXoff = FALSE;
  1476. /*
  1477.  * Adjust the handshake limits.
  1478.  * Yes, the XonXoff limits seem to influence even hardware handshake
  1479.  */
  1480. dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
  1481. dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
  1482. if (strnicmp(value, "NONE", vlen) == 0) {
  1483.     /* leave all handshake options disabled */
  1484. } else if (strnicmp(value, "XONXOFF", vlen) == 0) {
  1485.     dcb.fOutX = dcb.fInX = TRUE;
  1486. } else if (strnicmp(value, "RTSCTS", vlen) == 0) {
  1487.     dcb.fOutxCtsFlow = TRUE;
  1488.     dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
  1489. } else if (strnicmp(value, "DTRDSR", vlen) == 0) {
  1490.     dcb.fOutxDsrFlow = TRUE;
  1491.     dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
  1492. } else {
  1493.     if (interp) {
  1494. Tcl_AppendResult(interp, "bad value for -handshake: ",
  1495. "must be one of xonxoff, rtscts, dtrdsr or none",
  1496. (char *) NULL);
  1497. return TCL_ERROR;
  1498.     }
  1499. }
  1500. if (! SetCommState(infoPtr->handle, &dcb)) {
  1501.     if (interp) {
  1502. Tcl_AppendResult(interp, 
  1503. "can't set comm state", (char *) NULL);
  1504.     }
  1505.     return TCL_ERROR;
  1506. }
  1507. return TCL_OK;
  1508.     }
  1509.     /*
  1510.      * Option -xchar {x11 x13}
  1511.      */
  1512.     if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
  1513. if (! GetCommState(infoPtr->handle, &dcb)) {
  1514.     if (interp) {
  1515. Tcl_AppendResult(interp, 
  1516. "can't get comm state", (char *) NULL);
  1517.     }
  1518.     return TCL_ERROR;
  1519. }
  1520. if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
  1521.     return TCL_ERROR;
  1522. }
  1523. if (argc == 2) {
  1524.     dcb.XonChar  = argv[0][0];
  1525.     dcb.XoffChar = argv[1][0];
  1526.     ckfree((char *) argv);
  1527. } else {
  1528.     if (interp) {
  1529. Tcl_AppendResult(interp,
  1530. "bad value for -xchar: should be a list of two elements",
  1531. (char *) NULL);
  1532.     }
  1533.     ckfree((char *) argv);
  1534.     return TCL_ERROR;
  1535. }
  1536. if (! SetCommState(infoPtr->handle, &dcb)) {
  1537.     if (interp) {
  1538. Tcl_AppendResult(interp,
  1539. "can't set comm state", (char *) NULL);
  1540.     }
  1541.     return TCL_ERROR;
  1542. }
  1543. return TCL_OK;
  1544.     }
  1545.     /*
  1546.      * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
  1547.      */
  1548.     if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
  1549. int i, result = TCL_OK;
  1550. if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
  1551.     return TCL_ERROR;
  1552. }
  1553. if ((argc % 2) == 1) {
  1554.     if (interp) {
  1555. Tcl_AppendResult(interp,
  1556. "bad value for -ttycontrol: should be a list of signal,value pairs",
  1557. (char *) NULL);
  1558.     }
  1559.     ckfree((char *) argv);
  1560.     return TCL_ERROR;
  1561. }
  1562. for (i = 0; i < argc - 1; i += 2) {
  1563.     if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
  1564. result = TCL_ERROR;
  1565. break;
  1566.     }
  1567.     if (strnicmp(argv[i], "DTR", strlen(argv[i])) == 0) {
  1568. if (!EscapeCommFunction(infoPtr->handle, flag ?
  1569.     (DWORD) SETDTR : (DWORD) CLRDTR)) {
  1570.     if (interp) {
  1571. Tcl_AppendResult(interp,
  1572. "can't set DTR signal", (char *) NULL);
  1573.     }
  1574.     result = TCL_ERROR;
  1575.     break;
  1576. }
  1577.     } else if (strnicmp(argv[i], "RTS", strlen(argv[i])) == 0) {
  1578. if (!EscapeCommFunction(infoPtr->handle, flag ?
  1579.     (DWORD) SETRTS : (DWORD) CLRRTS)) {
  1580.     if (interp) {
  1581. Tcl_AppendResult(interp,
  1582. "can't set RTS signal", (char *) NULL);
  1583.     }
  1584.     result = TCL_ERROR;
  1585.     break;
  1586. }
  1587.     } else if (strnicmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
  1588. if (!EscapeCommFunction(infoPtr->handle, flag ?
  1589.     (DWORD) SETBREAK : (DWORD) CLRBREAK)) {
  1590.     if (interp) {
  1591. Tcl_AppendResult(interp,
  1592. "can't set BREAK signal", (char *) NULL);
  1593.     }
  1594.     result = TCL_ERROR;
  1595.     break;
  1596. }
  1597.     } else {
  1598. if (interp) {
  1599.     Tcl_AppendResult(interp, "bad signal for -ttycontrol: ",
  1600.     "must be DTR, RTS or BREAK", (char *) NULL);
  1601. }
  1602. result = TCL_ERROR;
  1603. break;
  1604.     }
  1605. }
  1606. ckfree((char *) argv);
  1607. return result;
  1608.     }
  1609.     /*
  1610.      * Option -sysbuffer {read_size write_size}
  1611.      * Option -sysbuffer read_size 
  1612.      */
  1613.     if ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0)) {
  1614. /*
  1615.  * -sysbuffer 4096 or -sysbuffer {64536 4096}
  1616.  */
  1617. size_t inSize = (size_t) -1, outSize = (size_t) -1;
  1618. if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
  1619.     return TCL_ERROR;
  1620. }
  1621. if (argc == 1) {
  1622.     inSize = atoi(argv[0]);
  1623.     outSize = infoPtr->sysBufWrite;
  1624. } else if (argc == 2) {
  1625.     inSize  = atoi(argv[0]);
  1626.     outSize = atoi(argv[1]);
  1627. }
  1628. ckfree((char *) argv);
  1629. if ((inSize <= 0) || (outSize <= 0)) {
  1630.     if (interp) {
  1631. Tcl_AppendResult(interp,
  1632. "bad value for -sysbuffer: should be a list of one or two integers > 0",
  1633. (char *) NULL);
  1634.     }
  1635.     return TCL_ERROR;
  1636. }
  1637. if (! SetupComm(infoPtr->handle, inSize, outSize)) {
  1638.     if (interp) {
  1639. Tcl_AppendResult(interp, 
  1640. "can't setup comm buffers", (char *) NULL);
  1641.     }
  1642.     return TCL_ERROR;
  1643. }
  1644. infoPtr->sysBufRead  = inSize;
  1645. infoPtr->sysBufWrite = outSize;
  1646. /*
  1647.  * Adjust the handshake limits.
  1648.  * Yes, the XonXoff limits seem to influence even hardware handshake
  1649.  */
  1650. if (! GetCommState(infoPtr->handle, &dcb)) {
  1651.     if (interp) {
  1652. Tcl_AppendResult(interp, 
  1653. "can't get comm state", (char *) NULL);
  1654.     }
  1655.     return TCL_ERROR;
  1656. }
  1657. dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
  1658. dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
  1659. if (! SetCommState(infoPtr->handle, &dcb)) {
  1660.     if (interp) {
  1661. Tcl_AppendResult(interp, 
  1662. "can't set comm state", (char *) NULL);
  1663.     }
  1664.     return TCL_ERROR;
  1665. }
  1666. return TCL_OK;
  1667.     }
  1668.     /*
  1669.      * Option -pollinterval msec
  1670.      */
  1671.     if ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0)) {
  1672. if ( Tcl_GetInt(interp, value, &(infoPtr->blockTime)) != TCL_OK ) {
  1673.     return TCL_ERROR;
  1674. }
  1675. return TCL_OK;
  1676.     }
  1677.     /*
  1678.      * Option -timeout msec
  1679.      */
  1680.     if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
  1681. int msec;
  1682. COMMTIMEOUTS tout = {0,0,0,0,0};
  1683. if ( Tcl_GetInt(interp, value, &msec) != TCL_OK ) {
  1684.     return TCL_ERROR;
  1685. }
  1686. tout.ReadTotalTimeoutConstant = msec;
  1687. if (! SetCommTimeouts(infoPtr->handle, &tout)) {
  1688.     if (interp) {
  1689. Tcl_AppendResult(interp, 
  1690. "can't set comm timeouts", (char *) NULL);
  1691.     }
  1692.     return TCL_ERROR;
  1693. }
  1694. return TCL_OK;
  1695.     }
  1696.     return Tcl_BadChannelOption(interp, optionName,
  1697.     "mode handshake pollinterval sysbuffer timeout ttycontrol xchar");
  1698. }
  1699. /*
  1700.  *----------------------------------------------------------------------
  1701.  *
  1702.  * SerialGetOptionProc --
  1703.  *
  1704.  *  Gets a mode associated with an IO channel. If the optionName arg
  1705.  *  is non NULL, retrieves the value of that option. If the optionName
  1706.  *  arg is NULL, retrieves a list of alternating option names and
  1707.  *  values for the given channel.
  1708.  *
  1709.  * Results:
  1710.  *  A standard Tcl result. Also sets the supplied DString to the
  1711.  *  string value of the option(s) returned.
  1712.  *
  1713.  * Side effects:
  1714.  *  The string returned by this function is in static storage and
  1715.  *  may be reused at any time subsequent to the call.
  1716.  *
  1717.  *----------------------------------------------------------------------
  1718.  */
  1719. static int
  1720. SerialGetOptionProc(instanceData, interp, optionName, dsPtr)
  1721.     ClientData instanceData; /* File state. */
  1722.     Tcl_Interp *interp; /* For error reporting - can be NULL. */
  1723.     CONST char *optionName; /* Option to get. */
  1724.     Tcl_DString *dsPtr; /* Where to store value(s). */
  1725. {
  1726.     SerialInfo *infoPtr;
  1727.     DCB dcb;
  1728.     size_t len;
  1729.     int valid = 0;  /* flag if valid option parsed */
  1730.     infoPtr = (SerialInfo *) instanceData;
  1731.     if (optionName == NULL) {
  1732. len = 0;
  1733.     } else {
  1734. len = strlen(optionName);
  1735.     }
  1736.     /*
  1737.      * get option -mode
  1738.      */
  1739.     if (len == 0) {
  1740. Tcl_DStringAppendElement(dsPtr, "-mode");
  1741.     }
  1742.     if ((len == 0) ||
  1743.     ((len > 2) && (strncmp(optionName, "-mode", len) == 0))) {
  1744. char parity;
  1745. char *stop;
  1746. char buf[2 * TCL_INTEGER_SPACE + 16];
  1747. if (! GetCommState(infoPtr->handle, &dcb)) {
  1748.     if (interp) {
  1749. Tcl_AppendResult(interp, 
  1750. "can't get comm state", (char *) NULL);
  1751.     }
  1752.     return TCL_ERROR;
  1753. }
  1754. valid = 1;
  1755. parity = 'n';
  1756. if (dcb.Parity <= 4) {
  1757.     parity = "noems"[dcb.Parity];
  1758. }
  1759. stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
  1760.     (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
  1761. wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity,
  1762. dcb.ByteSize, stop);
  1763. Tcl_DStringAppendElement(dsPtr, buf);
  1764.     }
  1765.     /*
  1766.      * get option -pollinterval
  1767.      */
  1768.     if (len == 0) {
  1769. Tcl_DStringAppendElement(dsPtr, "-pollinterval");
  1770.     }
  1771.     if ((len == 0) ||
  1772.     ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0))) {
  1773. char buf[TCL_INTEGER_SPACE + 1];
  1774. valid = 1;
  1775. wsprintfA(buf, "%d", infoPtr->blockTime);
  1776. Tcl_DStringAppendElement(dsPtr, buf);
  1777.     }
  1778.     /*
  1779.      * get option -sysbuffer
  1780.      */
  1781.     if (len == 0) {
  1782. Tcl_DStringAppendElement(dsPtr, "-sysbuffer");
  1783. Tcl_DStringStartSublist(dsPtr);
  1784.     }
  1785.     if ((len == 0) ||
  1786.     ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0))) {
  1787. char buf[TCL_INTEGER_SPACE + 1];
  1788. valid = 1;
  1789. wsprintfA(buf, "%d", infoPtr->sysBufRead);
  1790. Tcl_DStringAppendElement(dsPtr, buf);
  1791. wsprintfA(buf, "%d", infoPtr->sysBufWrite);
  1792. Tcl_DStringAppendElement(dsPtr, buf);
  1793.     }
  1794.     if (len == 0) {
  1795. Tcl_DStringEndSublist(dsPtr);
  1796.     }
  1797.     /*
  1798.      * get option -xchar
  1799.      */
  1800.     if (len == 0) {
  1801. Tcl_DStringAppendElement(dsPtr, "-xchar");
  1802. Tcl_DStringStartSublist(dsPtr);
  1803.     }
  1804.     if ((len == 0) ||
  1805.     ((len > 1) && (strncmp(optionName, "-xchar", len) == 0))) {
  1806. char buf[4];
  1807. valid = 1;
  1808. if (! GetCommState(infoPtr->handle, &dcb)) {
  1809.     if (interp) {
  1810. Tcl_AppendResult(interp, 
  1811. "can't get comm state", (char *) NULL);
  1812.     }
  1813.     return TCL_ERROR;
  1814. }
  1815. sprintf(buf, "%c", dcb.XonChar);
  1816. Tcl_DStringAppendElement(dsPtr, buf);
  1817. sprintf(buf, "%c", dcb.XoffChar);
  1818. Tcl_DStringAppendElement(dsPtr, buf);
  1819.     }
  1820.     if (len == 0) {
  1821. Tcl_DStringEndSublist(dsPtr);
  1822.     }
  1823.     /*
  1824.      * get option -lasterror
  1825.      * option is readonly and returned by [fconfigure chan -lasterror]
  1826.      * but not returned by unnamed [fconfigure chan]
  1827.      */
  1828.     if ( (len > 1) && (strncmp(optionName, "-lasterror", len) == 0) ) {
  1829. valid = 1;
  1830. SerialErrorStr(infoPtr->lastError, dsPtr);
  1831.     }
  1832.     /*
  1833.      * get option -queue
  1834.      * option is readonly and returned by [fconfigure chan -queue]
  1835.      */
  1836.     if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
  1837. char buf[TCL_INTEGER_SPACE + 1];
  1838. COMSTAT cStat;
  1839. DWORD error;
  1840. int inBuffered, outBuffered, count;
  1841. valid = 1;
  1842. /* 
  1843.  * Query the pending data in Tcl's internal queues
  1844.  */
  1845. inBuffered  = Tcl_InputBuffered(infoPtr->channel);
  1846. outBuffered = Tcl_OutputBuffered(infoPtr->channel);
  1847. /*
  1848.  * Query the number of bytes in our output queue:
  1849.  *     1. The bytes pending in the output thread
  1850.  *     2. The bytes in the system drivers buffer
  1851.  * The writer thread should not interfere this action.
  1852.  */
  1853. EnterCriticalSection(&infoPtr->csWrite);
  1854. ClearCommError( infoPtr->handle, &error, &cStat );
  1855. count = (int)cStat.cbOutQue + infoPtr->writeQueue;
  1856. LeaveCriticalSection(&infoPtr->csWrite);
  1857. wsprintfA(buf, "%d", inBuffered + cStat.cbInQue); 
  1858. Tcl_DStringAppendElement(dsPtr, buf);
  1859. wsprintfA(buf, "%d", outBuffered + count); 
  1860. Tcl_DStringAppendElement(dsPtr, buf);
  1861.     }
  1862.     /*
  1863.      * get option -ttystatus
  1864.      * option is readonly and returned by [fconfigure chan -ttystatus]
  1865.      * but not returned by unnamed [fconfigure chan]
  1866.      */
  1867.     if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
  1868. DWORD status;
  1869. if (! GetCommModemStatus(infoPtr->handle, &status)) {
  1870.     if (interp) {
  1871. Tcl_AppendResult(interp, 
  1872. "can't get tty status", (char *) NULL);
  1873.     }
  1874.     return TCL_ERROR;
  1875. }
  1876. valid = 1;
  1877. SerialModemStatusStr(status, dsPtr);
  1878.     }
  1879.     if (valid) {
  1880. return TCL_OK;
  1881.     } else {
  1882. return Tcl_BadChannelOption(interp, optionName,
  1883. "mode pollinterval lasterror queue sysbuffer ttystatus xchar");
  1884.     }
  1885. }
  1886. /*
  1887.  *----------------------------------------------------------------------
  1888.  *
  1889.  * SerialThreadActionProc --
  1890.  *
  1891.  * Insert or remove any thread local refs to this channel.
  1892.  *
  1893.  * Results:
  1894.  * None.
  1895.  *
  1896.  * Side effects:
  1897.  * Changes thread local list of valid channels.
  1898.  *
  1899.  *----------------------------------------------------------------------
  1900.  */
  1901. static void
  1902. SerialThreadActionProc (instanceData, action)
  1903.      ClientData instanceData;
  1904.      int action;
  1905. {
  1906.     SerialInfo *infoPtr = (SerialInfo *) instanceData;
  1907.     /* We do not access firstSerialPtr in the thread structures. This is
  1908.      * not for all serials managed by the thread, but only those we are
  1909.      * watching. Removal of the filevent handlers before transfer thus
  1910.      * takes care of this structure.
  1911.      */
  1912.     Tcl_MutexLock(&serialMutex);
  1913.     if (action == TCL_CHANNEL_THREAD_INSERT) {
  1914.         /* We can't copy the thread information from the channel when
  1915.  * the channel is created. At this time the channel back
  1916.  * pointer has not been set yet. However in that case the
  1917.  * threadId has already been set by TclpCreateCommandChannel
  1918.  * itself, so the structure is still good.
  1919.  */
  1920.         SerialInit ();
  1921.         if (infoPtr->channel != NULL) {
  1922.     infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
  1923. }
  1924.     } else {
  1925. infoPtr->threadId = NULL;
  1926.     }
  1927.     Tcl_MutexUnlock(&serialMutex);
  1928. }