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