os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinSerial.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinSerial.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,2171 @@
     1.4 +/*
     1.5 + * tclWinSerial.c --
     1.6 + *
     1.7 + *  This file implements the Windows-specific serial port functions,
     1.8 + *  and the "serial" channel driver.
     1.9 + *
    1.10 + * Copyright (c) 1999 by Scriptics Corp.
    1.11 + *
    1.12 + * See the file "license.terms" for information on usage and redistribution
    1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.14 + *
    1.15 + * Serial functionality implemented by Rolf.Schroedter@dlr.de
    1.16 + *
    1.17 + * RCS: @(#) $Id: tclWinSerial.c,v 1.25.2.3 2005/10/05 06:33:52 hobbs Exp $
    1.18 + */
    1.19 +
    1.20 +#include "tclWinInt.h"
    1.21 +
    1.22 +#include <fcntl.h>
    1.23 +#include <io.h>
    1.24 +#include <sys/stat.h>
    1.25 +
    1.26 +/*
    1.27 + * The following variable is used to tell whether this module has been
    1.28 + * initialized.
    1.29 + */
    1.30 +
    1.31 +static int initialized = 0;
    1.32 +
    1.33 +/*
    1.34 + * The serialMutex locks around access to the initialized variable, and it is
    1.35 + * used to protect background threads from being terminated while they are
    1.36 + * using APIs that hold locks.
    1.37 + */
    1.38 +
    1.39 +TCL_DECLARE_MUTEX(serialMutex)
    1.40 +
    1.41 +/*
    1.42 + * Bit masks used in the flags field of the SerialInfo structure below.
    1.43 + */
    1.44 +
    1.45 +#define SERIAL_PENDING  (1<<0)  /* Message is pending in the queue. */
    1.46 +#define SERIAL_ASYNC    (1<<1)  /* Channel is non-blocking. */
    1.47 +
    1.48 +/*
    1.49 + * Bit masks used in the sharedFlags field of the SerialInfo structure below.
    1.50 + */
    1.51 +
    1.52 +#define SERIAL_EOF      (1<<2)  /* Serial has reached EOF. */
    1.53 +#define SERIAL_ERROR    (1<<4)
    1.54 +
    1.55 +/*
    1.56 + * Default time to block between checking status on the serial port.
    1.57 + */
    1.58 +#define SERIAL_DEFAULT_BLOCKTIME    10  /* 10 msec */
    1.59 +
    1.60 +/*
    1.61 + * Define Win32 read/write error masks returned by ClearCommError()
    1.62 + */
    1.63 +#define SERIAL_READ_ERRORS      ( CE_RXOVER | CE_OVERRUN | CE_RXPARITY \
    1.64 +                                | CE_FRAME  | CE_BREAK )
    1.65 +#define SERIAL_WRITE_ERRORS     ( CE_TXFULL | CE_PTO )
    1.66 +
    1.67 +/*
    1.68 + * This structure describes per-instance data for a serial based channel.
    1.69 + */
    1.70 +
    1.71 +typedef struct SerialInfo {
    1.72 +    HANDLE handle;
    1.73 +    struct SerialInfo *nextPtr; /* Pointer to next registered serial. */
    1.74 +    Tcl_Channel channel;        /* Pointer to channel structure. */
    1.75 +    int validMask;              /* OR'ed combination of TCL_READABLE,
    1.76 +                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
    1.77 +                                 * which operations are valid on the file. */
    1.78 +    int watchMask;              /* OR'ed combination of TCL_READABLE,
    1.79 +                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
    1.80 +                                 * which events should be reported. */
    1.81 +    int flags;                  /* State flags, see above for a list. */
    1.82 +    int readable;               /* flag that the channel is readable */
    1.83 +    int writable;               /* flag that the channel is writable */
    1.84 +    int blockTime;              /* max. blocktime in msec */
    1.85 +    unsigned int lastEventTime;	/* Time in milliseconds since last readable event */
    1.86 +				/* Next readable event only after blockTime */
    1.87 +    DWORD error;                /* pending error code returned by
    1.88 +                                 * ClearCommError() */
    1.89 +    DWORD lastError;            /* last error code, can be fetched with
    1.90 +                                 * fconfigure chan -lasterror */
    1.91 +    DWORD sysBufRead;           /* Win32 system buffer size for read ops, 
    1.92 +                                 * default=4096 */
    1.93 +    DWORD sysBufWrite;          /* Win32 system buffer size for write ops, 
    1.94 +                                 * default=4096 */
    1.95 +
    1.96 +    Tcl_ThreadId threadId;      /* Thread to which events should be reported.
    1.97 +                                 * This value is used by the reader/writer
    1.98 +                                 * threads. */
    1.99 +    OVERLAPPED osRead;          /* OVERLAPPED structure for read operations */
   1.100 +    OVERLAPPED osWrite;         /* OVERLAPPED structure for write operations */
   1.101 +    HANDLE writeThread;         /* Handle to writer thread. */
   1.102 +    CRITICAL_SECTION csWrite;   /* Writer thread synchronisation */
   1.103 +    HANDLE evWritable;          /* Manual-reset event to signal when the
   1.104 +                                 * writer thread has finished waiting for
   1.105 +                                 * the current buffer to be written. */
   1.106 +    HANDLE evStartWriter;       /* Auto-reset event used by the main thread to
   1.107 +                                 * signal when the writer thread should attempt
   1.108 +                                 * to write to the serial. */
   1.109 +    HANDLE evStopWriter;	/* Auto-reset event used by the main thread to
   1.110 +                                 * signal when the writer thread should close.
   1.111 +				 */
   1.112 +    DWORD writeError;           /* An error caused by the last background
   1.113 +                                 * write.  Set to 0 if no error has been
   1.114 +                                 * detected.  This word is shared with the
   1.115 +                                 * writer thread so access must be
   1.116 +                                 * synchronized with the evWritable object.
   1.117 +                                 */
   1.118 +    char *writeBuf;             /* Current background output buffer.
   1.119 +                                 * Access is synchronized with the evWritable
   1.120 +                                 * object. */
   1.121 +    int writeBufLen;            /* Size of write buffer.  Access is
   1.122 +                                 * synchronized with the evWritable
   1.123 +                                 * object. */
   1.124 +    int toWrite;                /* Current amount to be written.  Access is
   1.125 +                                 * synchronized with the evWritable object. */
   1.126 +    int writeQueue;             /* Number of bytes pending in output queue.
   1.127 +                                 * Offset to DCB.cbInQue.
   1.128 +                                 * Used to query [fconfigure -queue] */
   1.129 +} SerialInfo;
   1.130 +
   1.131 +typedef struct ThreadSpecificData {
   1.132 +    /*
   1.133 +     * The following pointer refers to the head of the list of serials
   1.134 +     * that are being watched for file events.
   1.135 +     */
   1.136 +
   1.137 +    SerialInfo *firstSerialPtr;
   1.138 +} ThreadSpecificData;
   1.139 +
   1.140 +static Tcl_ThreadDataKey dataKey;
   1.141 +
   1.142 +/*
   1.143 + * The following structure is what is added to the Tcl event queue when
   1.144 + * serial events are generated.
   1.145 + */
   1.146 +
   1.147 +typedef struct SerialEvent {
   1.148 +    Tcl_Event header;       /* Information that is standard for
   1.149 +                             * all events. */
   1.150 +    SerialInfo *infoPtr;    /* Pointer to serial info structure.  Note
   1.151 +                             * that we still have to verify that the
   1.152 +                             * serial exists before dereferencing this
   1.153 +                             * pointer. */
   1.154 +} SerialEvent;
   1.155 +
   1.156 +/*
   1.157 + * We don't use timeouts.
   1.158 + */
   1.159 +
   1.160 +static COMMTIMEOUTS no_timeout = {
   1.161 +    0,               /* ReadIntervalTimeout */
   1.162 +    0,               /* ReadTotalTimeoutMultiplier */
   1.163 +    0,               /* ReadTotalTimeoutConstant */
   1.164 +    0,               /* WriteTotalTimeoutMultiplier */
   1.165 +    0,               /* WriteTotalTimeoutConstant */
   1.166 +};
   1.167 +
   1.168 +/*
   1.169 + * Declarations for functions used only in this file.
   1.170 + */
   1.171 +
   1.172 +static int      SerialBlockProc(ClientData instanceData, int mode);
   1.173 +static void     SerialCheckProc(ClientData clientData, int flags);
   1.174 +static int      SerialCloseProc(ClientData instanceData,
   1.175 +                Tcl_Interp *interp);
   1.176 +static int      SerialEventProc(Tcl_Event *evPtr, int flags);
   1.177 +static void     SerialExitHandler(ClientData clientData);
   1.178 +static int      SerialGetHandleProc(ClientData instanceData,
   1.179 +                int direction, ClientData *handlePtr);
   1.180 +static ThreadSpecificData *SerialInit(void);
   1.181 +static int      SerialInputProc(ClientData instanceData, char *buf,
   1.182 +                int toRead, int *errorCode);
   1.183 +static int      SerialOutputProc(ClientData instanceData, CONST char *buf,
   1.184 +                int toWrite, int *errorCode);
   1.185 +static void     SerialSetupProc(ClientData clientData, int flags);
   1.186 +static void     SerialWatchProc(ClientData instanceData, int mask);
   1.187 +static void     ProcExitHandler(ClientData clientData);
   1.188 +static int       SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData,
   1.189 +                Tcl_Interp *interp, CONST char *optionName,
   1.190 +                Tcl_DString *dsPtr));
   1.191 +static int       SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData,
   1.192 +                Tcl_Interp *interp, CONST char *optionName,
   1.193 +                CONST char *value));
   1.194 +static DWORD WINAPI     SerialWriterThread(LPVOID arg);
   1.195 +
   1.196 +static void             SerialThreadActionProc _ANSI_ARGS_ ((
   1.197 +			   ClientData instanceData, int action));
   1.198 +
   1.199 +/*
   1.200 + * This structure describes the channel type structure for command serial
   1.201 + * based IO.
   1.202 + */
   1.203 +
   1.204 +static Tcl_ChannelType serialChannelType = {
   1.205 +    "serial",                   /* Type name. */
   1.206 +    TCL_CHANNEL_VERSION_4,      /* v4 channel */
   1.207 +    SerialCloseProc,            /* Close proc. */
   1.208 +    SerialInputProc,            /* Input proc. */
   1.209 +    SerialOutputProc,           /* Output proc. */
   1.210 +    NULL,                       /* Seek proc. */
   1.211 +    SerialSetOptionProc,        /* Set option proc. */
   1.212 +    SerialGetOptionProc,        /* Get option proc. */
   1.213 +    SerialWatchProc,            /* Set up notifier to watch the channel. */
   1.214 +    SerialGetHandleProc,        /* Get an OS handle from channel. */
   1.215 +    NULL,                       /* close2proc. */
   1.216 +    SerialBlockProc,            /* Set blocking or non-blocking mode.*/
   1.217 +    NULL,                       /* flush proc. */
   1.218 +    NULL,                       /* handler proc. */
   1.219 +    NULL,                       /* wide seek proc */
   1.220 +    SerialThreadActionProc,     /* thread action proc */
   1.221 +};
   1.222 +
   1.223 +/*
   1.224 + *----------------------------------------------------------------------
   1.225 + *
   1.226 + * SerialInit --
   1.227 + *
   1.228 + *  This function initializes the static variables for this file.
   1.229 + *
   1.230 + * Results:
   1.231 + *  None.
   1.232 + *
   1.233 + * Side effects:
   1.234 + *  Creates a new event source.
   1.235 + *
   1.236 + *----------------------------------------------------------------------
   1.237 + */
   1.238 +
   1.239 +static ThreadSpecificData *
   1.240 +SerialInit()
   1.241 +{
   1.242 +    ThreadSpecificData *tsdPtr;
   1.243 +
   1.244 +    /*
   1.245 +     * Check the initialized flag first, then check it again in the mutex.
   1.246 +     * This is a speed enhancement.
   1.247 +     */
   1.248 +
   1.249 +    if (!initialized) {
   1.250 +        Tcl_MutexLock(&serialMutex);
   1.251 +        if (!initialized) {
   1.252 +            initialized = 1;
   1.253 +            Tcl_CreateExitHandler(ProcExitHandler, NULL);
   1.254 +        }
   1.255 +        Tcl_MutexUnlock(&serialMutex);
   1.256 +    }
   1.257 +
   1.258 +    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   1.259 +    if (tsdPtr == NULL) {
   1.260 +        tsdPtr = TCL_TSD_INIT(&dataKey);
   1.261 +        tsdPtr->firstSerialPtr = NULL;
   1.262 +        Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
   1.263 +        Tcl_CreateThreadExitHandler(SerialExitHandler, NULL);
   1.264 +    }
   1.265 +    return tsdPtr;
   1.266 +}
   1.267 +
   1.268 +/*
   1.269 + *----------------------------------------------------------------------
   1.270 + *
   1.271 + * SerialExitHandler --
   1.272 + *
   1.273 + *  This function is called to cleanup the serial module before
   1.274 + *  Tcl is unloaded.
   1.275 + *
   1.276 + * Results:
   1.277 + *  None.
   1.278 + *
   1.279 + * Side effects:
   1.280 + *  Removes the serial event source.
   1.281 + *
   1.282 + *----------------------------------------------------------------------
   1.283 + */
   1.284 +
   1.285 +static void
   1.286 +SerialExitHandler(
   1.287 +    ClientData clientData)  /* Old window proc */
   1.288 +{
   1.289 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.290 +    SerialInfo *infoPtr;
   1.291 +
   1.292 +    /*
   1.293 +     * Clear all eventually pending output.
   1.294 +     * Otherwise Tcl's exit could totally block,
   1.295 +     * because it performs a blocking flush on all open channels.
   1.296 +     * Note that serial write operations may be blocked due to handshake.
   1.297 +     */
   1.298 +    for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
   1.299 +            infoPtr = infoPtr->nextPtr) {
   1.300 +        PurgeComm(infoPtr->handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR 
   1.301 +            | PURGE_RXCLEAR);
   1.302 +
   1.303 +    }
   1.304 +    Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);
   1.305 +}
   1.306 +
   1.307 +/*
   1.308 + *----------------------------------------------------------------------
   1.309 + *
   1.310 + * ProcExitHandler --
   1.311 + *
   1.312 + *  This function is called to cleanup the process list before
   1.313 + *  Tcl is unloaded.
   1.314 + *
   1.315 + * Results:
   1.316 + *  None.
   1.317 + *
   1.318 + * Side effects:
   1.319 + *  Resets the process list.
   1.320 + *
   1.321 + *----------------------------------------------------------------------
   1.322 + */
   1.323 +
   1.324 +static void
   1.325 +ProcExitHandler(
   1.326 +    ClientData clientData)  /* Old window proc */
   1.327 +{
   1.328 +    Tcl_MutexLock(&serialMutex);
   1.329 +    initialized = 0;
   1.330 +    Tcl_MutexUnlock(&serialMutex);
   1.331 +}
   1.332 +
   1.333 +/*
   1.334 + *----------------------------------------------------------------------
   1.335 + *
   1.336 + * SerialBlockTime --
   1.337 + *
   1.338 + *  Wrapper to set Tcl's block time in msec
   1.339 + *
   1.340 + * Results:
   1.341 + *  None.
   1.342 + *----------------------------------------------------------------------
   1.343 + */
   1.344 +
   1.345 +static void
   1.346 +SerialBlockTime(
   1.347 +    int msec)          /* milli-seconds */
   1.348 +{
   1.349 +    Tcl_Time blockTime;
   1.350 +
   1.351 +    blockTime.sec  =  msec / 1000;
   1.352 +    blockTime.usec = (msec % 1000) * 1000;
   1.353 +    Tcl_SetMaxBlockTime(&blockTime);
   1.354 +}
   1.355 +/*
   1.356 + *----------------------------------------------------------------------
   1.357 + *
   1.358 + * SerialGetMilliseconds --
   1.359 + *
   1.360 + *  Get current time in milliseconds,
   1.361 + *  Don't care about integer overruns
   1.362 + *
   1.363 + * Results:
   1.364 + *  None.
   1.365 + *----------------------------------------------------------------------
   1.366 + */
   1.367 +
   1.368 +static unsigned int
   1.369 +SerialGetMilliseconds(
   1.370 +    void)
   1.371 +{
   1.372 +    Tcl_Time time;
   1.373 +
   1.374 +    TclpGetTime(&time);
   1.375 +
   1.376 +    return (time.sec * 1000 + time.usec / 1000);
   1.377 +}
   1.378 +/*
   1.379 + *----------------------------------------------------------------------
   1.380 + *
   1.381 + * SerialSetupProc --
   1.382 + *
   1.383 + *  This procedure is invoked before Tcl_DoOneEvent blocks waiting
   1.384 + *  for an event.
   1.385 + *
   1.386 + * Results:
   1.387 + *  None.
   1.388 + *
   1.389 + * Side effects:
   1.390 + *  Adjusts the block time if needed.
   1.391 + *
   1.392 + *----------------------------------------------------------------------
   1.393 + */
   1.394 +
   1.395 +void
   1.396 +SerialSetupProc(
   1.397 +    ClientData data,    /* Not used. */
   1.398 +    int flags)          /* Event flags as passed to Tcl_DoOneEvent. */
   1.399 +{
   1.400 +    SerialInfo *infoPtr;
   1.401 +    int block = 1;
   1.402 +    int msec = INT_MAX; /* min. found block time */
   1.403 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.404 +
   1.405 +    if (!(flags & TCL_FILE_EVENTS)) {
   1.406 +        return;
   1.407 +    }
   1.408 +
   1.409 +    /*
   1.410 +     * Look to see if any events handlers installed. If they are, do not block.
   1.411 +     */
   1.412 +
   1.413 +    for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
   1.414 +            infoPtr = infoPtr->nextPtr) {
   1.415 +
   1.416 +        if (infoPtr->watchMask & TCL_WRITABLE) {
   1.417 +            if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
   1.418 +                block = 0;
   1.419 +                msec = min( msec, infoPtr->blockTime );
   1.420 +            }
   1.421 +        }
   1.422 +        if( infoPtr->watchMask & TCL_READABLE ) {
   1.423 +            block = 0;
   1.424 +            msec = min( msec, infoPtr->blockTime );
   1.425 +        }
   1.426 +    }
   1.427 +
   1.428 +    if (!block) {
   1.429 +        SerialBlockTime(msec);
   1.430 +    }
   1.431 +}
   1.432 +
   1.433 +/*
   1.434 + *----------------------------------------------------------------------
   1.435 + *
   1.436 + * SerialCheckProc --
   1.437 + *
   1.438 + *  This procedure is called by Tcl_DoOneEvent to check the serial
   1.439 + *  event source for events.
   1.440 + *
   1.441 + * Results:
   1.442 + *  None.
   1.443 + *
   1.444 + * Side effects:
   1.445 + *  May queue an event.
   1.446 + *
   1.447 + *----------------------------------------------------------------------
   1.448 + */
   1.449 +
   1.450 +static void
   1.451 +SerialCheckProc(
   1.452 +    ClientData data,    /* Not used. */
   1.453 +    int flags)          /* Event flags as passed to Tcl_DoOneEvent. */
   1.454 +{
   1.455 +    SerialInfo *infoPtr;
   1.456 +    SerialEvent *evPtr;
   1.457 +    int needEvent;
   1.458 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.459 +    COMSTAT cStat;
   1.460 +    unsigned int time;
   1.461 +
   1.462 +    if (!(flags & TCL_FILE_EVENTS)) {
   1.463 +        return;
   1.464 +    }
   1.465 +
   1.466 +    /*
   1.467 +     * Queue events for any ready serials that don't already have events
   1.468 +     * queued.
   1.469 +     */
   1.470 +
   1.471 +    for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
   1.472 +            infoPtr = infoPtr->nextPtr) {
   1.473 +        if (infoPtr->flags & SERIAL_PENDING) {
   1.474 +            continue;
   1.475 +        }
   1.476 +
   1.477 +        needEvent = 0;
   1.478 +
   1.479 +        /*
   1.480 +         * If WRITABLE watch mask is set
   1.481 +         * look for infoPtr->evWritable object
   1.482 +         */
   1.483 +        if (infoPtr->watchMask & TCL_WRITABLE) {
   1.484 +            if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
   1.485 +                infoPtr->writable = 1;
   1.486 +                needEvent = 1;
   1.487 +            }
   1.488 +        }
   1.489 +        
   1.490 +        /*
   1.491 +         * If READABLE watch mask is set
   1.492 +         * call ClearCommError to poll cbInQue
   1.493 +         * Window errors are ignored here
   1.494 +         */
   1.495 +
   1.496 +        if( infoPtr->watchMask & TCL_READABLE ) {
   1.497 +            if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
   1.498 +                /*
   1.499 +                 * Look for characters already pending in windows queue.
   1.500 +                 * If they are, poll.
   1.501 +                 */
   1.502 +
   1.503 +                if( infoPtr->watchMask & TCL_READABLE ) {
   1.504 +                    /*
   1.505 +                     * force fileevent after serial read error
   1.506 +                     */
   1.507 +                    if( (cStat.cbInQue > 0) ||
   1.508 +                            (infoPtr->error & SERIAL_READ_ERRORS) ) {
   1.509 +                        infoPtr->readable = 1;
   1.510 +			time = SerialGetMilliseconds();
   1.511 +			if ((unsigned int) (time - infoPtr->lastEventTime)
   1.512 +				>= (unsigned int) infoPtr->blockTime) {
   1.513 +			    needEvent = 1;
   1.514 +			    infoPtr->lastEventTime = time;
   1.515 +			}
   1.516 +                    }
   1.517 +                }
   1.518 +            }
   1.519 +        }
   1.520 +
   1.521 +        /*
   1.522 +         * Queue an event if the serial is signaled for reading or writing.
   1.523 +         */
   1.524 +        if (needEvent) {
   1.525 +            infoPtr->flags |= SERIAL_PENDING;
   1.526 +            evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent));
   1.527 +            evPtr->header.proc = SerialEventProc;
   1.528 +            evPtr->infoPtr = infoPtr;
   1.529 +            Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
   1.530 +        }
   1.531 +    }
   1.532 +}
   1.533 +
   1.534 +/*
   1.535 + *----------------------------------------------------------------------
   1.536 + *
   1.537 + * SerialBlockProc --
   1.538 + *
   1.539 + *  Set blocking or non-blocking mode on channel.
   1.540 + *
   1.541 + * Results:
   1.542 + *  0 if successful, errno when failed.
   1.543 + *
   1.544 + * Side effects:
   1.545 + *  Sets the device into blocking or non-blocking mode.
   1.546 + *
   1.547 + *----------------------------------------------------------------------
   1.548 + */
   1.549 +
   1.550 +static int
   1.551 +SerialBlockProc(
   1.552 +    ClientData instanceData,    /* Instance data for channel. */
   1.553 +    int mode)                   /* TCL_MODE_BLOCKING or
   1.554 +                                 * TCL_MODE_NONBLOCKING. */
   1.555 +{
   1.556 +    int errorCode = 0;
   1.557 +
   1.558 +    SerialInfo *infoPtr = (SerialInfo *) instanceData;
   1.559 +
   1.560 +    /*
   1.561 +     * Only serial READ can be switched between blocking & nonblocking
   1.562 +     * using COMMTIMEOUTS.
   1.563 +     * Serial write emulates blocking & nonblocking by the SerialWriterThread.
   1.564 +     */
   1.565 +
   1.566 +    if (mode == TCL_MODE_NONBLOCKING) {
   1.567 +        infoPtr->flags |= SERIAL_ASYNC;
   1.568 +    } else {
   1.569 +        infoPtr->flags &= ~(SERIAL_ASYNC);
   1.570 +    }
   1.571 +    return errorCode;
   1.572 +}
   1.573 +
   1.574 +/*
   1.575 + *----------------------------------------------------------------------
   1.576 + *
   1.577 + * SerialCloseProc --
   1.578 + *
   1.579 + *  Closes a serial based IO channel.
   1.580 + *
   1.581 + * Results:
   1.582 + *  0 on success, errno otherwise.
   1.583 + *
   1.584 + * Side effects:
   1.585 + *  Closes the physical channel.
   1.586 + *
   1.587 + *----------------------------------------------------------------------
   1.588 + */
   1.589 +
   1.590 +static int
   1.591 +SerialCloseProc(
   1.592 +    ClientData instanceData,    /* Pointer to SerialInfo structure. */
   1.593 +    Tcl_Interp *interp)         /* For error reporting. */
   1.594 +{
   1.595 +    SerialInfo *serialPtr = (SerialInfo *) instanceData;
   1.596 +    int errorCode, result = 0;
   1.597 +    SerialInfo *infoPtr, **nextPtrPtr;
   1.598 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.599 +    DWORD exitCode;
   1.600 +
   1.601 +    errorCode = 0;
   1.602 +
   1.603 +    if (serialPtr->validMask & TCL_READABLE) {
   1.604 +        PurgeComm(serialPtr->handle, PURGE_RXABORT | PURGE_RXCLEAR);
   1.605 +        CloseHandle(serialPtr->osRead.hEvent);
   1.606 +    }
   1.607 +    serialPtr->validMask &= ~TCL_READABLE;
   1.608 + 
   1.609 +    if (serialPtr->validMask & TCL_WRITABLE) {
   1.610 +
   1.611 +        /*
   1.612 +         * Generally we cannot wait for a pending write operation
   1.613 +         * because it may hang due to handshake
   1.614 +         *    WaitForSingleObject(serialPtr->evWritable, INFINITE);
   1.615 +         */
   1.616 +
   1.617 +	/*
   1.618 +	 * The thread may have already closed on it's own.  Check it's
   1.619 +	 * exit code.
   1.620 +	 */
   1.621 +
   1.622 +	GetExitCodeThread(serialPtr->writeThread, &exitCode);
   1.623 +
   1.624 +	if (exitCode == STILL_ACTIVE) {
   1.625 +	    /*
   1.626 +	     * Set the stop event so that if the writer thread is
   1.627 +	     * blocked in SerialWriterThread on WaitForMultipleEvents, it
   1.628 +	     * will exit cleanly.
   1.629 +	     */
   1.630 +
   1.631 +	    SetEvent(serialPtr->evStopWriter);
   1.632 +
   1.633 +	    /*
   1.634 +	     * Wait at most 20 milliseconds for the writer thread to
   1.635 +	     * close.
   1.636 +	     */
   1.637 +
   1.638 +	    if (WaitForSingleObject(serialPtr->writeThread, 20)
   1.639 +		    == WAIT_TIMEOUT) {
   1.640 +		/*
   1.641 +		 * Forcibly terminate the background thread as a last
   1.642 +		 * resort.  Note that we need to guard against
   1.643 +		 * terminating the thread while it is in the middle of
   1.644 +		 * Tcl_ThreadAlert because it won't be able to release
   1.645 +		 * the notifier lock.
   1.646 +		 */
   1.647 +
   1.648 +		Tcl_MutexLock(&serialMutex);
   1.649 +
   1.650 +		/* BUG: this leaks memory */
   1.651 +		TerminateThread(serialPtr->writeThread, 0);
   1.652 +
   1.653 +		Tcl_MutexUnlock(&serialMutex);
   1.654 +	    }
   1.655 +	}
   1.656 +
   1.657 +        CloseHandle(serialPtr->writeThread);
   1.658 +	CloseHandle(serialPtr->osWrite.hEvent);
   1.659 +	DeleteCriticalSection(&serialPtr->csWrite);
   1.660 +        CloseHandle(serialPtr->evWritable);
   1.661 +        CloseHandle(serialPtr->evStartWriter);
   1.662 +        CloseHandle(serialPtr->evStopWriter);
   1.663 +        serialPtr->writeThread = NULL;
   1.664 +
   1.665 +        PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR);
   1.666 +    }
   1.667 +    serialPtr->validMask &= ~TCL_WRITABLE;
   1.668 +
   1.669 +    /*
   1.670 +     * Don't close the Win32 handle if the handle is a standard channel
   1.671 +     * during the thread exit process.  Otherwise, one thread may kill
   1.672 +     * the stdio of another.
   1.673 +     */
   1.674 +
   1.675 +    if (!TclInThreadExit()
   1.676 +	|| ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
   1.677 +	&& (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
   1.678 +	&& (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
   1.679 +	    if (CloseHandle(serialPtr->handle) == FALSE) {
   1.680 +		TclWinConvertError(GetLastError());
   1.681 +		errorCode = errno;
   1.682 +	    }
   1.683 +    }
   1.684 +
   1.685 +    serialPtr->watchMask &= serialPtr->validMask;
   1.686 +
   1.687 +    /*
   1.688 +     * Remove the file from the list of watched files.
   1.689 +     */
   1.690 +
   1.691 +    for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr;
   1.692 +	    infoPtr != NULL;
   1.693 +	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
   1.694 +        if (infoPtr == (SerialInfo *)serialPtr) {
   1.695 +            *nextPtrPtr = infoPtr->nextPtr;
   1.696 +            break;
   1.697 +        }
   1.698 +    }
   1.699 +
   1.700 +    /*
   1.701 +     * Wrap the error file into a channel and give it to the cleanup
   1.702 +     * routine.
   1.703 +     */
   1.704 +    if (serialPtr->writeBuf != NULL) {
   1.705 +        ckfree(serialPtr->writeBuf);
   1.706 +        serialPtr->writeBuf = NULL;
   1.707 +    }
   1.708 +    ckfree((char*) serialPtr);
   1.709 +
   1.710 +    if (errorCode == 0) {
   1.711 +        return result;
   1.712 +    }
   1.713 +    return errorCode;
   1.714 +}
   1.715 +
   1.716 +/*
   1.717 + *----------------------------------------------------------------------
   1.718 + *
   1.719 + * blockingRead --
   1.720 + *
   1.721 + *  Perform a blocking read into the buffer given. Returns
   1.722 + *  count of how many bytes were actually read, and an error indication.
   1.723 + *
   1.724 + * Results:
   1.725 + *  A count of how many bytes were read is returned and an error
   1.726 + *  indication is returned.
   1.727 + *
   1.728 + * Side effects:
   1.729 + *  Reads input from the actual channel.
   1.730 + *
   1.731 + *----------------------------------------------------------------------
   1.732 + */
   1.733 +static int
   1.734 +blockingRead( 
   1.735 +    SerialInfo *infoPtr,    /* Serial info structure */
   1.736 +    LPVOID buf,             /* The input buffer pointer */
   1.737 +    DWORD  bufSize,         /* The number of bytes to read */
   1.738 +    LPDWORD  lpRead,        /* Returns number of bytes read */ 
   1.739 +    LPOVERLAPPED osPtr )    /* OVERLAPPED structure */
   1.740 +{
   1.741 +    /*
   1.742 +     *  Perform overlapped blocking read. 
   1.743 +     *  1. Reset the overlapped event
   1.744 +     *  2. Start overlapped read operation
   1.745 +     *  3. Wait for completion
   1.746 +     */
   1.747 +
   1.748 +    /* 
   1.749 +     * Set Offset to ZERO, otherwise NT4.0 may report an error.
   1.750 +     */
   1.751 +    osPtr->Offset = osPtr->OffsetHigh = 0;
   1.752 +    ResetEvent(osPtr->hEvent);
   1.753 +    if (! ReadFile(infoPtr->handle, buf, bufSize, lpRead, osPtr) ) {
   1.754 +        if (GetLastError() != ERROR_IO_PENDING) {
   1.755 +            /* ReadFile failed, but it isn't delayed. Report error. */
   1.756 +            return FALSE;
   1.757 +        } else {   
   1.758 +            /* Read is pending, wait for completion, timeout ? */
   1.759 +            if (! GetOverlappedResult(infoPtr->handle, osPtr, lpRead, TRUE) ) {
   1.760 +                return FALSE;
   1.761 +            }
   1.762 +        }
   1.763 +    } else {
   1.764 +        /* ReadFile completed immediately. */
   1.765 +    }
   1.766 +    return TRUE;
   1.767 +}
   1.768 +
   1.769 +/*
   1.770 + *----------------------------------------------------------------------
   1.771 + *
   1.772 + * blockingWrite --
   1.773 + *
   1.774 + *  Perform a blocking write from the buffer given. Returns
   1.775 + *  count of how many bytes were actually written, and an error indication.
   1.776 + *
   1.777 + * Results:
   1.778 + *  A count of how many bytes were written is returned and an error
   1.779 + *  indication is returned.
   1.780 + *
   1.781 + * Side effects:
   1.782 + *  Writes output to the actual channel.
   1.783 + *
   1.784 + *----------------------------------------------------------------------
   1.785 + */
   1.786 +static int
   1.787 +blockingWrite(
   1.788 +    SerialInfo *infoPtr,    /* Serial info structure */
   1.789 +    LPVOID  buf,            /* The output buffer pointer */
   1.790 +    DWORD   bufSize,        /* The number of bytes to write */
   1.791 +    LPDWORD lpWritten,      /* Returns number of bytes written */ 
   1.792 +    LPOVERLAPPED osPtr )    /* OVERLAPPED structure */
   1.793 +{
   1.794 +    int result;
   1.795 +    /*
   1.796 +    *  Perform overlapped blocking write. 
   1.797 +    *  1. Reset the overlapped event
   1.798 +    *  2. Remove these bytes from the output queue counter
   1.799 +    *  3. Start overlapped write operation
   1.800 +    *  3. Remove these bytes from the output queue counter
   1.801 +    *  4. Wait for completion
   1.802 +    *  5. Adjust the output queue counter
   1.803 +    */
   1.804 +    ResetEvent(osPtr->hEvent);
   1.805 +
   1.806 +    EnterCriticalSection(&infoPtr->csWrite);
   1.807 +    infoPtr->writeQueue -= bufSize;
   1.808 +	/* 
   1.809 +	* Set Offset to ZERO, otherwise NT4.0 may report an error 
   1.810 +	*/
   1.811 +	osPtr->Offset = osPtr->OffsetHigh = 0;
   1.812 +    result = WriteFile(infoPtr->handle, buf, bufSize, lpWritten, osPtr);
   1.813 +    LeaveCriticalSection(&infoPtr->csWrite);
   1.814 +
   1.815 +    if (result == FALSE ) {
   1.816 +        int err = GetLastError();
   1.817 +        switch (err) {
   1.818 +        case ERROR_IO_PENDING:
   1.819 +            /* Write is pending, wait for completion */
   1.820 +            if (! GetOverlappedResult(infoPtr->handle, osPtr, lpWritten, TRUE) ) {
   1.821 +                return FALSE;
   1.822 +            }
   1.823 +            break;
   1.824 +        case ERROR_COUNTER_TIMEOUT:
   1.825 +            /* Write timeout handled in SerialOutputProc */
   1.826 +            break;
   1.827 +        default:
   1.828 +            /* WriteFile failed, but it isn't delayed. Report error */
   1.829 +            return FALSE;
   1.830 +        }
   1.831 +    } else {
   1.832 +        /* WriteFile completed immediately. */
   1.833 +    }
   1.834 +
   1.835 +    EnterCriticalSection(&infoPtr->csWrite);
   1.836 +    infoPtr->writeQueue += (*lpWritten - bufSize);
   1.837 +    LeaveCriticalSection(&infoPtr->csWrite);
   1.838 +
   1.839 +    return TRUE;
   1.840 +}
   1.841 +
   1.842 +/*
   1.843 + *----------------------------------------------------------------------
   1.844 + *
   1.845 + * SerialInputProc --
   1.846 + *
   1.847 + *  Reads input from the IO channel into the buffer given. Returns
   1.848 + *  count of how many bytes were actually read, and an error indication.
   1.849 + *
   1.850 + * Results:
   1.851 + *  A count of how many bytes were read is returned and an error
   1.852 + *  indication is returned in an output argument.
   1.853 + *
   1.854 + * Side effects:
   1.855 + *  Reads input from the actual channel.
   1.856 + *
   1.857 + *----------------------------------------------------------------------
   1.858 + */
   1.859 +static int
   1.860 +SerialInputProc(
   1.861 +    ClientData instanceData,    /* Serial state. */
   1.862 +    char *buf,                  /* Where to store data read. */
   1.863 +    int bufSize,                /* How much space is available
   1.864 +                                 * in the buffer? */
   1.865 +    int *errorCode)             /* Where to store error code. */
   1.866 +{
   1.867 +    SerialInfo *infoPtr = (SerialInfo *) instanceData;
   1.868 +    DWORD bytesRead = 0;
   1.869 +    COMSTAT cStat;
   1.870 +
   1.871 +    *errorCode = 0;
   1.872 +
   1.873 +    /*
   1.874 +     * Check if there is a CommError pending from SerialCheckProc
   1.875 +     */
   1.876 +    if( infoPtr->error & SERIAL_READ_ERRORS ){
   1.877 +        goto commError;
   1.878 +    }
   1.879 +
   1.880 +    /*
   1.881 +     * Look for characters already pending in windows queue.
   1.882 +     * This is the mainly restored good old code from Tcl8.0
   1.883 +     */
   1.884 +
   1.885 +    if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
   1.886 +        /*
   1.887 +         * Check for errors here, but not in the evSetup/Check procedures
   1.888 +         */
   1.889 +
   1.890 +        if( infoPtr->error & SERIAL_READ_ERRORS ) {
   1.891 +            goto commError;
   1.892 +        }
   1.893 +        if( infoPtr->flags & SERIAL_ASYNC ) {
   1.894 +            /*
   1.895 +             * NON_BLOCKING mode:
   1.896 +             * Avoid blocking by reading more bytes than available
   1.897 +             * in input buffer
   1.898 +             */
   1.899 +
   1.900 +            if( cStat.cbInQue > 0 ) {
   1.901 +                if( (DWORD) bufSize > cStat.cbInQue ) {
   1.902 +                    bufSize = cStat.cbInQue;
   1.903 +                }
   1.904 +            } else {
   1.905 +                errno = *errorCode = EAGAIN;
   1.906 +                return -1;
   1.907 +            }
   1.908 +        } else {
   1.909 +            /*
   1.910 +             * BLOCKING mode:
   1.911 +             * Tcl trys to read a full buffer of 4 kBytes here
   1.912 +             */
   1.913 +
   1.914 +            if( cStat.cbInQue > 0 ) {
   1.915 +                if( (DWORD) bufSize > cStat.cbInQue ) {
   1.916 +                    bufSize = cStat.cbInQue;
   1.917 +                }
   1.918 +            } else {
   1.919 +                bufSize = 1;
   1.920 +            }
   1.921 +        }
   1.922 +    }
   1.923 +
   1.924 +    if( bufSize == 0 ) {
   1.925 +        return bytesRead = 0;
   1.926 +    }
   1.927 +
   1.928 +    /*
   1.929 +    *  Perform blocking read. Doesn't block in non-blocking mode, 
   1.930 +    *  because we checked the number of available bytes.
   1.931 +    */
   1.932 +    if (blockingRead(infoPtr, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
   1.933 +            &infoPtr->osRead) == FALSE) {
   1.934 +        goto error;
   1.935 +    }
   1.936 +    return bytesRead;
   1.937 +
   1.938 +error:
   1.939 +    TclWinConvertError(GetLastError());
   1.940 +    *errorCode = errno;
   1.941 +    return -1;
   1.942 +
   1.943 +commError:
   1.944 +    infoPtr->lastError = infoPtr->error;  /* save last error code */
   1.945 +    infoPtr->error = 0;                   /* reset error code */
   1.946 +    *errorCode = EIO;                     /* to return read-error only once */
   1.947 +    return -1;
   1.948 +}
   1.949 +
   1.950 +/*
   1.951 + *----------------------------------------------------------------------
   1.952 + *
   1.953 + * SerialOutputProc --
   1.954 + *
   1.955 + *  Writes the given output on the IO channel. Returns count of how
   1.956 + *  many characters were actually written, and an error indication.
   1.957 + *
   1.958 + * Results:
   1.959 + *  A count of how many characters were written is returned and an
   1.960 + *  error indication is returned in an output argument.
   1.961 + *
   1.962 + * Side effects:
   1.963 + *  Writes output on the actual channel.
   1.964 + *
   1.965 + *----------------------------------------------------------------------
   1.966 + */
   1.967 +
   1.968 +static int
   1.969 +SerialOutputProc(
   1.970 +    ClientData instanceData,    /* Serial state. */
   1.971 +    CONST char *buf,            /* The data buffer. */
   1.972 +    int toWrite,                /* How many bytes to write? */
   1.973 +    int *errorCode)             /* Where to store error code. */
   1.974 +{
   1.975 +    SerialInfo *infoPtr = (SerialInfo *) instanceData;
   1.976 +    DWORD bytesWritten, timeout;
   1.977 +
   1.978 +    *errorCode = 0;
   1.979 +
   1.980 +    /*
   1.981 +     * At EXIT Tcl trys to flush all open channels in blocking mode.
   1.982 +     * We avoid blocking output after ExitProc or CloseHandler(chan)
   1.983 +     * has been called by checking the corrresponding variables.
   1.984 +     */
   1.985 +    if( ! initialized || TclInExit() ) {
   1.986 +        return toWrite;
   1.987 +    }
   1.988 +
   1.989 +    /*
   1.990 +     * Check if there is a CommError pending from SerialCheckProc
   1.991 +     */
   1.992 +    if( infoPtr->error & SERIAL_WRITE_ERRORS ){
   1.993 +        infoPtr->lastError = infoPtr->error;  /* save last error code */
   1.994 +        infoPtr->error = 0;                   /* reset error code */
   1.995 +        errno = EIO;            
   1.996 +        goto error;
   1.997 +    }
   1.998 +
   1.999 +    timeout = (infoPtr->flags & SERIAL_ASYNC) ? 0 : INFINITE;
  1.1000 +    if (WaitForSingleObject(infoPtr->evWritable, timeout) == WAIT_TIMEOUT) {
  1.1001 +        /*
  1.1002 +         * The writer thread is blocked waiting for a write to complete
  1.1003 +         * and the channel is in non-blocking mode.
  1.1004 +         */
  1.1005 +
  1.1006 +        errno = EWOULDBLOCK;
  1.1007 +        goto error1;
  1.1008 +    }
  1.1009 +    /*
  1.1010 +     * Check for a background error on the last write.
  1.1011 +     */
  1.1012 +
  1.1013 +    if (infoPtr->writeError) {
  1.1014 +        TclWinConvertError(infoPtr->writeError);
  1.1015 +        infoPtr->writeError = 0;
  1.1016 +        goto error1;
  1.1017 +    }
  1.1018 +
  1.1019 +    /*
  1.1020 +     * Remember the number of bytes in output queue
  1.1021 +     */
  1.1022 +    EnterCriticalSection(&infoPtr->csWrite);
  1.1023 +    infoPtr->writeQueue += toWrite;
  1.1024 +    LeaveCriticalSection(&infoPtr->csWrite);
  1.1025 +
  1.1026 +    if (infoPtr->flags & SERIAL_ASYNC) {
  1.1027 +        /*
  1.1028 +         * The serial is non-blocking, so copy the data into the output
  1.1029 +         * buffer and restart the writer thread.
  1.1030 +         */
  1.1031 +
  1.1032 +        if (toWrite > infoPtr->writeBufLen) {
  1.1033 +            /*
  1.1034 +             * Reallocate the buffer to be large enough to hold the data.
  1.1035 +             */
  1.1036 +
  1.1037 +            if (infoPtr->writeBuf) {
  1.1038 +                ckfree(infoPtr->writeBuf);
  1.1039 +            }
  1.1040 +            infoPtr->writeBufLen = toWrite;
  1.1041 +            infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
  1.1042 +        }
  1.1043 +        memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
  1.1044 +        infoPtr->toWrite = toWrite;
  1.1045 +        ResetEvent(infoPtr->evWritable);
  1.1046 +        SetEvent(infoPtr->evStartWriter);
  1.1047 +        bytesWritten = (DWORD) toWrite;
  1.1048 +
  1.1049 +    } else {
  1.1050 +        /*
  1.1051 +        * In the blocking case, just try to write the buffer directly.
  1.1052 +        * This avoids an unnecessary copy.
  1.1053 +        */
  1.1054 +        if (! blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
  1.1055 +                &bytesWritten, &infoPtr->osWrite) ) {
  1.1056 +            goto writeError;
  1.1057 +        }
  1.1058 +        if (bytesWritten != (DWORD) toWrite) {
  1.1059 +            /* Write timeout */
  1.1060 +            infoPtr->lastError |= CE_PTO;
  1.1061 +            errno = EIO;
  1.1062 +            goto error;
  1.1063 +        }
  1.1064 +    }
  1.1065 +
  1.1066 +    return (int) bytesWritten;
  1.1067 +
  1.1068 +writeError:
  1.1069 +    TclWinConvertError(GetLastError());
  1.1070 +
  1.1071 +error:
  1.1072 +    /* 
  1.1073 +     * Reset the output queue counter on error during blocking output 
  1.1074 +     */
  1.1075 +/*
  1.1076 +    EnterCriticalSection(&infoPtr->csWrite);
  1.1077 +    infoPtr->writeQueue = 0;
  1.1078 +    LeaveCriticalSection(&infoPtr->csWrite);
  1.1079 +*/
  1.1080 +  error1: 
  1.1081 +    *errorCode = errno;
  1.1082 +    return -1;
  1.1083 +}
  1.1084 +
  1.1085 +/*
  1.1086 + *----------------------------------------------------------------------
  1.1087 + *
  1.1088 + * SerialEventProc --
  1.1089 + *
  1.1090 + *  This function is invoked by Tcl_ServiceEvent when a file event
  1.1091 + *  reaches the front of the event queue.  This procedure invokes
  1.1092 + *  Tcl_NotifyChannel on the serial.
  1.1093 + *
  1.1094 + * Results:
  1.1095 + *  Returns 1 if the event was handled, meaning it should be removed
  1.1096 + *  from the queue.  Returns 0 if the event was not handled, meaning
  1.1097 + *  it should stay on the queue.  The only time the event isn't
  1.1098 + *  handled is if the TCL_FILE_EVENTS flag bit isn't set.
  1.1099 + *
  1.1100 + * Side effects:
  1.1101 + *  Whatever the notifier callback does.
  1.1102 + *
  1.1103 + *----------------------------------------------------------------------
  1.1104 + */
  1.1105 +
  1.1106 +static int
  1.1107 +SerialEventProc(
  1.1108 +    Tcl_Event *evPtr,   /* Event to service. */
  1.1109 +    int flags)          /* Flags that indicate what events to
  1.1110 +                         * handle, such as TCL_FILE_EVENTS. */
  1.1111 +{
  1.1112 +    SerialEvent *serialEvPtr = (SerialEvent *)evPtr;
  1.1113 +    SerialInfo *infoPtr;
  1.1114 +    int mask;
  1.1115 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1.1116 +
  1.1117 +    if (!(flags & TCL_FILE_EVENTS)) {
  1.1118 +        return 0;
  1.1119 +    }
  1.1120 +
  1.1121 +    /*
  1.1122 +     * Search through the list of watched serials for the one whose handle
  1.1123 +     * matches the event.  We do this rather than simply dereferencing
  1.1124 +     * the handle in the event so that serials can be deleted while the
  1.1125 +     * event is in the queue.
  1.1126 +     */
  1.1127 +
  1.1128 +    for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
  1.1129 +            infoPtr = infoPtr->nextPtr) {
  1.1130 +        if (serialEvPtr->infoPtr == infoPtr) {
  1.1131 +            infoPtr->flags &= ~(SERIAL_PENDING);
  1.1132 +            break;
  1.1133 +        }
  1.1134 +    }
  1.1135 +
  1.1136 +    /*
  1.1137 +     * Remove stale events.
  1.1138 +     */
  1.1139 +
  1.1140 +    if (!infoPtr) {
  1.1141 +        return 1;
  1.1142 +    }
  1.1143 +
  1.1144 +    /*
  1.1145 +     * Check to see if the serial is readable.  Note
  1.1146 +     * that we can't tell if a serial is writable, so we always report it
  1.1147 +     * as being writable unless we have detected EOF.
  1.1148 +     */
  1.1149 +
  1.1150 +    mask = 0;
  1.1151 +    if( infoPtr->watchMask & TCL_WRITABLE ) {
  1.1152 +        if( infoPtr->writable ) {
  1.1153 +            mask |= TCL_WRITABLE;
  1.1154 +            infoPtr->writable = 0;
  1.1155 +        }
  1.1156 +    }
  1.1157 +
  1.1158 +    if( infoPtr->watchMask & TCL_READABLE ) {
  1.1159 +        if( infoPtr->readable ) {
  1.1160 +            mask |= TCL_READABLE;
  1.1161 +            infoPtr->readable = 0;
  1.1162 +        }
  1.1163 +    }
  1.1164 +
  1.1165 +    /*
  1.1166 +     * Inform the channel of the events.
  1.1167 +     */
  1.1168 +
  1.1169 +    Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
  1.1170 +    return 1;
  1.1171 +}
  1.1172 +
  1.1173 +/*
  1.1174 + *----------------------------------------------------------------------
  1.1175 + *
  1.1176 + * SerialWatchProc --
  1.1177 + *
  1.1178 + *  Called by the notifier to set up to watch for events on this
  1.1179 + *  channel.
  1.1180 + *
  1.1181 + * Results:
  1.1182 + *  None.
  1.1183 + *
  1.1184 + * Side effects:
  1.1185 + *  None.
  1.1186 + *
  1.1187 + *----------------------------------------------------------------------
  1.1188 + */
  1.1189 +
  1.1190 +static void
  1.1191 +SerialWatchProc(
  1.1192 +    ClientData instanceData,     /* Serial state. */
  1.1193 +    int mask)                    /* What events to watch for, OR-ed
  1.1194 +                                  * combination of TCL_READABLE,
  1.1195 +                                  * TCL_WRITABLE and TCL_EXCEPTION. */
  1.1196 +{
  1.1197 +    SerialInfo **nextPtrPtr, *ptr;
  1.1198 +    SerialInfo *infoPtr = (SerialInfo *) instanceData;
  1.1199 +    int oldMask = infoPtr->watchMask;
  1.1200 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1.1201 +
  1.1202 +    /*
  1.1203 +     * Since the file is always ready for events, we set the block time
  1.1204 +     * so we will poll.
  1.1205 +     */
  1.1206 +
  1.1207 +    infoPtr->watchMask = mask & infoPtr->validMask;
  1.1208 +    if (infoPtr->watchMask) {
  1.1209 +        if (!oldMask) {
  1.1210 +            infoPtr->nextPtr = tsdPtr->firstSerialPtr;
  1.1211 +            tsdPtr->firstSerialPtr = infoPtr;
  1.1212 +        }
  1.1213 +        SerialBlockTime(infoPtr->blockTime);
  1.1214 +    } else {
  1.1215 +        if (oldMask) {
  1.1216 +            /*
  1.1217 +             * Remove the serial port from the list of watched serial ports.
  1.1218 +             */
  1.1219 +
  1.1220 +            for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr;
  1.1221 +                    ptr != NULL;
  1.1222 +                    nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
  1.1223 +                if (infoPtr == ptr) {
  1.1224 +                    *nextPtrPtr = ptr->nextPtr;
  1.1225 +                    break;
  1.1226 +                }
  1.1227 +            }
  1.1228 +        }
  1.1229 +    }
  1.1230 +}
  1.1231 +
  1.1232 +/*
  1.1233 + *----------------------------------------------------------------------
  1.1234 + *
  1.1235 + * SerialGetHandleProc --
  1.1236 + *
  1.1237 + *  Called from Tcl_GetChannelHandle to retrieve OS handles from
  1.1238 + *  inside a command serial port based channel.
  1.1239 + *
  1.1240 + * Results:
  1.1241 + *  Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  1.1242 + *  there is no handle for the specified direction.
  1.1243 + *
  1.1244 + * Side effects:
  1.1245 + *  None.
  1.1246 + *
  1.1247 + *----------------------------------------------------------------------
  1.1248 + */
  1.1249 +
  1.1250 +static int
  1.1251 +SerialGetHandleProc(
  1.1252 +    ClientData instanceData,    /* The serial state. */
  1.1253 +    int direction,              /* TCL_READABLE or TCL_WRITABLE */
  1.1254 +    ClientData *handlePtr)      /* Where to store the handle.  */
  1.1255 +{
  1.1256 +    SerialInfo *infoPtr = (SerialInfo *) instanceData;
  1.1257 +
  1.1258 +    *handlePtr = (ClientData) infoPtr->handle;
  1.1259 +    return TCL_OK;
  1.1260 +}
  1.1261 +
  1.1262 +/*
  1.1263 + *----------------------------------------------------------------------
  1.1264 + *
  1.1265 + * SerialWriterThread --
  1.1266 + *
  1.1267 + *      This function runs in a separate thread and writes data
  1.1268 + *      onto a serial.
  1.1269 + *
  1.1270 + * Results:
  1.1271 + *      Always returns 0.
  1.1272 + *
  1.1273 + * Side effects:
  1.1274 + *      Signals the main thread when an output operation is completed.
  1.1275 + *      May cause the main thread to wake up by posting a message.  
  1.1276 + *
  1.1277 + *----------------------------------------------------------------------
  1.1278 + */
  1.1279 +
  1.1280 +static DWORD WINAPI
  1.1281 +SerialWriterThread(LPVOID arg)
  1.1282 +{
  1.1283 +
  1.1284 +    SerialInfo *infoPtr = (SerialInfo *)arg;
  1.1285 +    DWORD bytesWritten, toWrite, waitResult;
  1.1286 +    char *buf;
  1.1287 +    OVERLAPPED myWrite; /* have an own OVERLAPPED in this thread */
  1.1288 +    HANDLE wEvents[2];
  1.1289 +
  1.1290 +    /*
  1.1291 +     * The stop event takes precedence by being first in the list.
  1.1292 +     */
  1.1293 +    wEvents[0] = infoPtr->evStopWriter;
  1.1294 +    wEvents[1] = infoPtr->evStartWriter;
  1.1295 +
  1.1296 +    for (;;) {
  1.1297 +        /*
  1.1298 +         * Wait for the main thread to signal before attempting to write.
  1.1299 +         */
  1.1300 +
  1.1301 +	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
  1.1302 +
  1.1303 +	if (waitResult != (WAIT_OBJECT_0 + 1)) {
  1.1304 +	    /*
  1.1305 +	     * The start event was not signaled.  It might be the stop event
  1.1306 +	     * or an error, so exit.
  1.1307 +	     */
  1.1308 +
  1.1309 +	    break;
  1.1310 +	}
  1.1311 +
  1.1312 +        buf = infoPtr->writeBuf;
  1.1313 +        toWrite = infoPtr->toWrite;
  1.1314 +
  1.1315 +        myWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1.1316 +
  1.1317 +        /*
  1.1318 +         * Loop until all of the bytes are written or an error occurs.
  1.1319 +         */
  1.1320 +
  1.1321 +        while (toWrite > 0) {
  1.1322 +            /*
  1.1323 +            *  Check for pending writeError
  1.1324 +            *  Ignore all write operations until the user has been notified
  1.1325 +            */
  1.1326 +            if (infoPtr->writeError) {
  1.1327 +                break;
  1.1328 +            }
  1.1329 +            if (blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite, 
  1.1330 +                    &bytesWritten, &myWrite) == FALSE) {
  1.1331 +                infoPtr->writeError = GetLastError();
  1.1332 +                break;
  1.1333 +            }
  1.1334 +            if (bytesWritten != toWrite) {
  1.1335 +                /* Write timeout */
  1.1336 +                infoPtr->writeError = ERROR_WRITE_FAULT;
  1.1337 +                break;
  1.1338 +            }
  1.1339 +            toWrite -= bytesWritten;
  1.1340 +            buf += bytesWritten;
  1.1341 +        }
  1.1342 +
  1.1343 +        CloseHandle(myWrite.hEvent);
  1.1344 +        /*
  1.1345 +         * Signal the main thread by signalling the evWritable event and
  1.1346 +         * then waking up the notifier thread.
  1.1347 +         */
  1.1348 +        SetEvent(infoPtr->evWritable);
  1.1349 +
  1.1350 +        /*
  1.1351 +         * Alert the foreground thread.  Note that we need to treat this like
  1.1352 +         * a critical section so the foreground thread does not terminate
  1.1353 +         * this thread while we are holding a mutex in the notifier code.
  1.1354 +         */
  1.1355 +
  1.1356 +        Tcl_MutexLock(&serialMutex);
  1.1357 +	if (infoPtr->threadId != NULL) {
  1.1358 +	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
  1.1359 +	    Tcl_ThreadAlert(infoPtr->threadId);
  1.1360 +	}
  1.1361 +        Tcl_MutexUnlock(&serialMutex);
  1.1362 +    }
  1.1363 +
  1.1364 +    return 0;
  1.1365 +}
  1.1366 +
  1.1367 +
  1.1368 +/*
  1.1369 + *----------------------------------------------------------------------
  1.1370 + *
  1.1371 + * TclWinSerialReopen --
  1.1372 + *
  1.1373 + *  Reopens the serial port with the OVERLAPPED FLAG set
  1.1374 + *
  1.1375 + * Results:
  1.1376 + *  Returns the new handle, or INVALID_HANDLE_VALUE
  1.1377 + *  Normally there shouldn't be any error, 
  1.1378 + *  because the same channel has previously been succeesfully opened.
  1.1379 + *
  1.1380 + * Side effects:
  1.1381 + *  May close the original handle
  1.1382 + *
  1.1383 + *----------------------------------------------------------------------
  1.1384 + */
  1.1385 +
  1.1386 +HANDLE
  1.1387 +TclWinSerialReopen(handle, name, access)
  1.1388 +    HANDLE handle;
  1.1389 +    CONST TCHAR *name;
  1.1390 +    DWORD access;
  1.1391 +{
  1.1392 +    ThreadSpecificData *tsdPtr;
  1.1393 +
  1.1394 +    tsdPtr = SerialInit();
  1.1395 +
  1.1396 +    /* 
  1.1397 +    * Multithreaded I/O needs the overlapped flag set
  1.1398 +    * otherwise ClearCommError blocks under Windows NT/2000 until serial
  1.1399 +    * output is finished
  1.1400 +    */
  1.1401 +    if (CloseHandle(handle) == FALSE) {
  1.1402 +        return INVALID_HANDLE_VALUE;
  1.1403 +    }
  1.1404 +    handle = (*tclWinProcs->createFileProc)(name, access, 
  1.1405 +                0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
  1.1406 +    return handle;
  1.1407 +}
  1.1408 +/*
  1.1409 + *----------------------------------------------------------------------
  1.1410 + *
  1.1411 + * TclWinOpenSerialChannel --
  1.1412 + *
  1.1413 + *  Constructs a Serial port channel for the specified standard OS handle.
  1.1414 + *      This is a helper function to break up the construction of
  1.1415 + *      channels into File, Console, or Serial.
  1.1416 + *
  1.1417 + * Results:
  1.1418 + *  Returns the new channel, or NULL.
  1.1419 + *
  1.1420 + * Side effects:
  1.1421 + *  May open the channel
  1.1422 + *
  1.1423 + *----------------------------------------------------------------------
  1.1424 + */
  1.1425 +
  1.1426 +Tcl_Channel
  1.1427 +TclWinOpenSerialChannel(handle, channelName, permissions)
  1.1428 +    HANDLE handle;
  1.1429 +    char *channelName;
  1.1430 +    int permissions;
  1.1431 +{
  1.1432 +    SerialInfo *infoPtr;
  1.1433 +    DWORD id;
  1.1434 +
  1.1435 +    SerialInit();
  1.1436 +
  1.1437 +    infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo));
  1.1438 +    memset(infoPtr, 0, sizeof(SerialInfo));
  1.1439 +
  1.1440 +    infoPtr->validMask     = permissions;
  1.1441 +    infoPtr->handle        = handle;
  1.1442 +    infoPtr->channel       = (Tcl_Channel) NULL;
  1.1443 +    infoPtr->readable      = 0; 
  1.1444 +    infoPtr->writable      = 1;
  1.1445 +    infoPtr->toWrite       = infoPtr->writeQueue = 0;
  1.1446 +    infoPtr->blockTime     = SERIAL_DEFAULT_BLOCKTIME;
  1.1447 +    infoPtr->lastEventTime = 0;
  1.1448 +    infoPtr->lastError     = infoPtr->error = 0;
  1.1449 +    infoPtr->threadId      = Tcl_GetCurrentThread();
  1.1450 +    infoPtr->sysBufRead    = 4096;
  1.1451 +    infoPtr->sysBufWrite   = 4096;
  1.1452 +
  1.1453 +    /*
  1.1454 +     * Use the pointer to keep the channel names unique, in case
  1.1455 +     * the handles are shared between multiple channels (stdin/stdout).
  1.1456 +     */
  1.1457 +
  1.1458 +    wsprintfA(channelName, "file%lx", (int) infoPtr);
  1.1459 +
  1.1460 +    infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName,
  1.1461 +            (ClientData) infoPtr, permissions);
  1.1462 +
  1.1463 +
  1.1464 +    SetupComm(handle, infoPtr->sysBufRead, infoPtr->sysBufWrite);
  1.1465 +    PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR 
  1.1466 +            | PURGE_RXCLEAR);
  1.1467 +
  1.1468 +    /*
  1.1469 +     * default is blocking
  1.1470 +     */
  1.1471 +    SetCommTimeouts(handle, &no_timeout);
  1.1472 +
  1.1473 +
  1.1474 +    if (permissions & TCL_READABLE) {
  1.1475 +        infoPtr->osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1.1476 +    }
  1.1477 +    if (permissions & TCL_WRITABLE) {
  1.1478 +        /* 
  1.1479 +        * Initially the channel is writable
  1.1480 +        * and the writeThread is idle.
  1.1481 +        */ 
  1.1482 +        infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1.1483 +        infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL);
  1.1484 +        infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1.1485 +	infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1.1486 +        InitializeCriticalSection(&infoPtr->csWrite);
  1.1487 +        infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
  1.1488 +            infoPtr, 0, &id);
  1.1489 +    }
  1.1490 +
  1.1491 +    /*
  1.1492 +     * Files have default translation of AUTO and ^Z eof char, which
  1.1493 +     * means that a ^Z will be accepted as EOF when reading.
  1.1494 +     */
  1.1495 +
  1.1496 +    Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
  1.1497 +    Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
  1.1498 +
  1.1499 +    return infoPtr->channel;
  1.1500 +}
  1.1501 +
  1.1502 +/*
  1.1503 + *----------------------------------------------------------------------
  1.1504 + *
  1.1505 + * SerialErrorStr --
  1.1506 + *
  1.1507 + *  Converts a Win32 serial error code to a list of readable errors
  1.1508 + *
  1.1509 + *----------------------------------------------------------------------
  1.1510 + */
  1.1511 +static void
  1.1512 +SerialErrorStr(error, dsPtr)
  1.1513 +    DWORD error;           /* Win32 serial error code */
  1.1514 +    Tcl_DString *dsPtr;    /* Where to store string */
  1.1515 +{
  1.1516 +    if( (error & CE_RXOVER) != 0) {
  1.1517 +                Tcl_DStringAppendElement(dsPtr, "RXOVER");
  1.1518 +    }
  1.1519 +    if( (error & CE_OVERRUN) != 0) {
  1.1520 +                Tcl_DStringAppendElement(dsPtr, "OVERRUN");
  1.1521 +    }
  1.1522 +    if( (error & CE_RXPARITY) != 0) {
  1.1523 +                Tcl_DStringAppendElement(dsPtr, "RXPARITY");
  1.1524 +    }
  1.1525 +    if( (error & CE_FRAME) != 0) {
  1.1526 +                Tcl_DStringAppendElement(dsPtr, "FRAME");
  1.1527 +    }
  1.1528 +    if( (error & CE_BREAK) != 0) {
  1.1529 +                Tcl_DStringAppendElement(dsPtr, "BREAK");
  1.1530 +    }
  1.1531 +    if( (error & CE_TXFULL) != 0) {
  1.1532 +                Tcl_DStringAppendElement(dsPtr, "TXFULL");
  1.1533 +    }
  1.1534 +    if( (error & CE_PTO) != 0) {    /* PTO used to signal WRITE-TIMEOUT */
  1.1535 +                Tcl_DStringAppendElement(dsPtr, "TIMEOUT");
  1.1536 +    }
  1.1537 +    if( (error & ~((DWORD) (SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS))) != 0) {
  1.1538 +                char buf[TCL_INTEGER_SPACE + 1];
  1.1539 +                wsprintfA(buf, "%d", error);
  1.1540 +                Tcl_DStringAppendElement(dsPtr, buf);
  1.1541 +    }
  1.1542 +}
  1.1543 +/*
  1.1544 + *----------------------------------------------------------------------
  1.1545 + *
  1.1546 + * SerialModemStatusStr --
  1.1547 + *
  1.1548 + *  Converts a Win32 modem status list of readable flags
  1.1549 + *
  1.1550 + *----------------------------------------------------------------------
  1.1551 + */
  1.1552 +static void
  1.1553 +SerialModemStatusStr(status, dsPtr)
  1.1554 +    DWORD status;          /* Win32 modem status */
  1.1555 +    Tcl_DString *dsPtr;    /* Where to store string */
  1.1556 +{
  1.1557 +    Tcl_DStringAppendElement(dsPtr, "CTS");
  1.1558 +    Tcl_DStringAppendElement(dsPtr, (status & MS_CTS_ON)  ?  "1" : "0");
  1.1559 +    Tcl_DStringAppendElement(dsPtr, "DSR");
  1.1560 +    Tcl_DStringAppendElement(dsPtr, (status & MS_DSR_ON)   ? "1" : "0");
  1.1561 +    Tcl_DStringAppendElement(dsPtr, "RING");
  1.1562 +    Tcl_DStringAppendElement(dsPtr, (status & MS_RING_ON)  ? "1" : "0");
  1.1563 +    Tcl_DStringAppendElement(dsPtr, "DCD");
  1.1564 +    Tcl_DStringAppendElement(dsPtr, (status & MS_RLSD_ON)  ? "1" : "0");
  1.1565 +}
  1.1566 +
  1.1567 +/*
  1.1568 + *----------------------------------------------------------------------
  1.1569 + *
  1.1570 + * SerialSetOptionProc --
  1.1571 + *
  1.1572 + *  Sets an option on a channel.
  1.1573 + *
  1.1574 + * Results:
  1.1575 + *  A standard Tcl result. Also sets the interp's result on error if
  1.1576 + *  interp is not NULL.
  1.1577 + *
  1.1578 + * Side effects:
  1.1579 + *  May modify an option on a device.
  1.1580 + *
  1.1581 + *----------------------------------------------------------------------
  1.1582 + */
  1.1583 +static int
  1.1584 +SerialSetOptionProc(instanceData, interp, optionName, value)
  1.1585 +    ClientData instanceData;	/* File state. */
  1.1586 +    Tcl_Interp *interp;		/* For error reporting - can be NULL. */
  1.1587 +    CONST char *optionName;	/* Which option to set? */
  1.1588 +    CONST char *value;		/* New value for option. */
  1.1589 +{
  1.1590 +    SerialInfo *infoPtr;
  1.1591 +    DCB dcb;
  1.1592 +    BOOL result, flag;
  1.1593 +    size_t len, vlen;
  1.1594 +    Tcl_DString ds;
  1.1595 +    CONST TCHAR *native;
  1.1596 +    int argc;
  1.1597 +    CONST char **argv;
  1.1598 +
  1.1599 +    infoPtr = (SerialInfo *) instanceData;
  1.1600 +
  1.1601 +    /*
  1.1602 +     * Parse options
  1.1603 +     */
  1.1604 +    len = strlen(optionName);
  1.1605 +    vlen = strlen(value);
  1.1606 +
  1.1607 +    /*
  1.1608 +     * Option -mode baud,parity,databits,stopbits
  1.1609 +     */
  1.1610 +    if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
  1.1611 +	if (! GetCommState(infoPtr->handle, &dcb)) {
  1.1612 +	    if (interp) {
  1.1613 +		Tcl_AppendResult(interp, 
  1.1614 +			"can't get comm state", (char *) NULL);
  1.1615 +	    }
  1.1616 +	    return TCL_ERROR;
  1.1617 +	}
  1.1618 +	native = Tcl_WinUtfToTChar(value, -1, &ds);
  1.1619 +	result = (*tclWinProcs->buildCommDCBProc)(native, &dcb);
  1.1620 +	Tcl_DStringFree(&ds);
  1.1621 +
  1.1622 +	if (result == FALSE) {
  1.1623 +	    if (interp) {
  1.1624 +		Tcl_AppendResult(interp,
  1.1625 +			"bad value for -mode: should be baud,parity,data,stop",
  1.1626 +			(char *) NULL);
  1.1627 +	    }
  1.1628 +	    return TCL_ERROR;
  1.1629 +	}
  1.1630 +
  1.1631 +	/* Default settings for serial communications */ 
  1.1632 +	dcb.fBinary = TRUE;
  1.1633 +	dcb.fErrorChar = FALSE;
  1.1634 +	dcb.fNull = FALSE;
  1.1635 +	dcb.fAbortOnError = FALSE;
  1.1636 +
  1.1637 +	if (! SetCommState(infoPtr->handle, &dcb) ) {
  1.1638 +	    if (interp) {
  1.1639 +		Tcl_AppendResult(interp, 
  1.1640 +			"can't set comm state", (char *) NULL);
  1.1641 +	    }
  1.1642 +	    return TCL_ERROR;
  1.1643 +	}
  1.1644 +	return TCL_OK;
  1.1645 +    }
  1.1646 +
  1.1647 +    /*
  1.1648 +     * Option -handshake none|xonxoff|rtscts|dtrdsr
  1.1649 +     */
  1.1650 +    if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
  1.1651 +	if (! GetCommState(infoPtr->handle, &dcb)) {
  1.1652 +	    if (interp) {
  1.1653 +		Tcl_AppendResult(interp, 
  1.1654 +			"can't get comm state", (char *) NULL);
  1.1655 +	    }
  1.1656 +	    return TCL_ERROR;
  1.1657 +	}
  1.1658 +	/*
  1.1659 +	 * Reset all handshake options
  1.1660 +	 * DTR and RTS are ON by default
  1.1661 +	 */
  1.1662 +	dcb.fOutX = dcb.fInX = FALSE;
  1.1663 +	dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fDsrSensitivity = FALSE;
  1.1664 +	dcb.fDtrControl = DTR_CONTROL_ENABLE;
  1.1665 +	dcb.fRtsControl = RTS_CONTROL_ENABLE;
  1.1666 +	dcb.fTXContinueOnXoff = FALSE;
  1.1667 +
  1.1668 +	/*
  1.1669 +	 * Adjust the handshake limits.
  1.1670 +	 * Yes, the XonXoff limits seem to influence even hardware handshake
  1.1671 +	 */
  1.1672 +	dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
  1.1673 +	dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
  1.1674 +
  1.1675 +	if (strnicmp(value, "NONE", vlen) == 0) {
  1.1676 +	    /* leave all handshake options disabled */
  1.1677 +	} else if (strnicmp(value, "XONXOFF", vlen) == 0) {
  1.1678 +	    dcb.fOutX = dcb.fInX = TRUE;
  1.1679 +	} else if (strnicmp(value, "RTSCTS", vlen) == 0) {
  1.1680 +	    dcb.fOutxCtsFlow = TRUE;
  1.1681 +	    dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
  1.1682 +	} else if (strnicmp(value, "DTRDSR", vlen) == 0) {
  1.1683 +	    dcb.fOutxDsrFlow = TRUE;
  1.1684 +	    dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
  1.1685 +	} else {
  1.1686 +	    if (interp) {
  1.1687 +		Tcl_AppendResult(interp, "bad value for -handshake: ",
  1.1688 +			"must be one of xonxoff, rtscts, dtrdsr or none",
  1.1689 +			(char *) NULL);
  1.1690 +		return TCL_ERROR;
  1.1691 +	    }
  1.1692 +	}
  1.1693 +
  1.1694 +	if (! SetCommState(infoPtr->handle, &dcb)) {
  1.1695 +	    if (interp) {
  1.1696 +		Tcl_AppendResult(interp, 
  1.1697 +			"can't set comm state", (char *) NULL);
  1.1698 +	    }
  1.1699 +	    return TCL_ERROR;
  1.1700 +	}
  1.1701 +	return TCL_OK;
  1.1702 +    }
  1.1703 +
  1.1704 +    /*
  1.1705 +     * Option -xchar {\x11 \x13}
  1.1706 +     */
  1.1707 +    if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
  1.1708 +	if (! GetCommState(infoPtr->handle, &dcb)) {
  1.1709 +	    if (interp) {
  1.1710 +		Tcl_AppendResult(interp, 
  1.1711 +			"can't get comm state", (char *) NULL);
  1.1712 +	    }
  1.1713 +	    return TCL_ERROR;
  1.1714 +	}
  1.1715 +
  1.1716 +	if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
  1.1717 +	    return TCL_ERROR;
  1.1718 +	}
  1.1719 +	if (argc == 2) {
  1.1720 +	    dcb.XonChar	 = argv[0][0];
  1.1721 +	    dcb.XoffChar = argv[1][0];
  1.1722 +	    ckfree((char *) argv);
  1.1723 +	} else {
  1.1724 +	    if (interp) {
  1.1725 +		Tcl_AppendResult(interp,
  1.1726 +			"bad value for -xchar: should be a list of two elements",
  1.1727 +			(char *) NULL);
  1.1728 +	    }
  1.1729 +	    ckfree((char *) argv);
  1.1730 +	    return TCL_ERROR;
  1.1731 +	}
  1.1732 +
  1.1733 +	if (! SetCommState(infoPtr->handle, &dcb)) {
  1.1734 +	    if (interp) {
  1.1735 +		Tcl_AppendResult(interp,
  1.1736 +			"can't set comm state", (char *) NULL);
  1.1737 +	    }
  1.1738 +	    return TCL_ERROR;
  1.1739 +	}
  1.1740 +	return TCL_OK;
  1.1741 +    }
  1.1742 +
  1.1743 +    /*
  1.1744 +     * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
  1.1745 +     */
  1.1746 +    if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
  1.1747 +	int i, result = TCL_OK;
  1.1748 +
  1.1749 +	if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
  1.1750 +	    return TCL_ERROR;
  1.1751 +	}
  1.1752 +	if ((argc % 2) == 1) {
  1.1753 +	    if (interp) {
  1.1754 +		Tcl_AppendResult(interp,
  1.1755 +			"bad value for -ttycontrol: should be a list of signal,value pairs",
  1.1756 +			(char *) NULL);
  1.1757 +	    }
  1.1758 +	    ckfree((char *) argv);
  1.1759 +	    return TCL_ERROR;
  1.1760 +	}
  1.1761 +	for (i = 0; i < argc - 1; i += 2) {
  1.1762 +	    if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
  1.1763 +		result = TCL_ERROR;
  1.1764 +		break;
  1.1765 +	    }
  1.1766 +	    if (strnicmp(argv[i], "DTR", strlen(argv[i])) == 0) {
  1.1767 +		if (!EscapeCommFunction(infoPtr->handle, flag ?
  1.1768 +			    (DWORD) SETDTR : (DWORD) CLRDTR)) {
  1.1769 +		    if (interp) {
  1.1770 +			Tcl_AppendResult(interp,
  1.1771 +				"can't set DTR signal", (char *) NULL);
  1.1772 +		    }
  1.1773 +		    result = TCL_ERROR;
  1.1774 +		    break;
  1.1775 +		}
  1.1776 +	    } else if (strnicmp(argv[i], "RTS", strlen(argv[i])) == 0) {
  1.1777 +		if (!EscapeCommFunction(infoPtr->handle, flag ?
  1.1778 +			    (DWORD) SETRTS : (DWORD) CLRRTS)) {
  1.1779 +		    if (interp) {
  1.1780 +			Tcl_AppendResult(interp,
  1.1781 +				"can't set RTS signal", (char *) NULL);
  1.1782 +		    }
  1.1783 +		    result = TCL_ERROR;
  1.1784 +		    break;
  1.1785 +		}
  1.1786 +	    } else if (strnicmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
  1.1787 +		if (!EscapeCommFunction(infoPtr->handle, flag ?
  1.1788 +			    (DWORD) SETBREAK : (DWORD) CLRBREAK)) {
  1.1789 +		    if (interp) {
  1.1790 +			Tcl_AppendResult(interp,
  1.1791 +				"can't set BREAK signal", (char *) NULL);
  1.1792 +		    }
  1.1793 +		    result = TCL_ERROR;
  1.1794 +		    break;
  1.1795 +		}
  1.1796 +	    } else {
  1.1797 +		if (interp) {
  1.1798 +		    Tcl_AppendResult(interp, "bad signal for -ttycontrol: ",
  1.1799 +			    "must be DTR, RTS or BREAK", (char *) NULL);
  1.1800 +		}
  1.1801 +		result = TCL_ERROR;
  1.1802 +		break;
  1.1803 +	    }
  1.1804 +	}
  1.1805 +
  1.1806 +	ckfree((char *) argv);
  1.1807 +	return result;
  1.1808 +    }
  1.1809 +
  1.1810 +    /*
  1.1811 +     * Option -sysbuffer {read_size write_size}
  1.1812 +     * Option -sysbuffer read_size 
  1.1813 +     */
  1.1814 +    if ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0)) {
  1.1815 +	/*
  1.1816 +	 * -sysbuffer 4096 or -sysbuffer {64536 4096}
  1.1817 +	 */
  1.1818 +	size_t inSize = (size_t) -1, outSize = (size_t) -1;
  1.1819 +
  1.1820 +	if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
  1.1821 +	    return TCL_ERROR;
  1.1822 +	}
  1.1823 +	if (argc == 1) {
  1.1824 +	    inSize = atoi(argv[0]);
  1.1825 +	    outSize = infoPtr->sysBufWrite;
  1.1826 +	} else if (argc == 2) {
  1.1827 +	    inSize  = atoi(argv[0]);
  1.1828 +	    outSize = atoi(argv[1]);
  1.1829 +	}
  1.1830 +	ckfree((char *) argv);
  1.1831 +	if ((inSize <= 0) || (outSize <= 0)) {
  1.1832 +	    if (interp) {
  1.1833 +		Tcl_AppendResult(interp,
  1.1834 +			"bad value for -sysbuffer: should be a list of one or two integers > 0",
  1.1835 +			(char *) NULL);
  1.1836 +	    }
  1.1837 +	    return TCL_ERROR;
  1.1838 +	}
  1.1839 +	if (! SetupComm(infoPtr->handle, inSize, outSize)) {
  1.1840 +	    if (interp) {
  1.1841 +		Tcl_AppendResult(interp, 
  1.1842 +			"can't setup comm buffers", (char *) NULL);
  1.1843 +	    }
  1.1844 +	    return TCL_ERROR;
  1.1845 +	}
  1.1846 +	infoPtr->sysBufRead  = inSize;
  1.1847 +	infoPtr->sysBufWrite = outSize;
  1.1848 +
  1.1849 +	/*
  1.1850 +	 * Adjust the handshake limits.
  1.1851 +	 * Yes, the XonXoff limits seem to influence even hardware handshake
  1.1852 +	 */
  1.1853 +	if (! GetCommState(infoPtr->handle, &dcb)) {
  1.1854 +	    if (interp) {
  1.1855 +		Tcl_AppendResult(interp, 
  1.1856 +			"can't get comm state", (char *) NULL);
  1.1857 +	    }
  1.1858 +	    return TCL_ERROR;
  1.1859 +	}
  1.1860 +	dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
  1.1861 +	dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
  1.1862 +	if (! SetCommState(infoPtr->handle, &dcb)) {
  1.1863 +	    if (interp) {
  1.1864 +		Tcl_AppendResult(interp, 
  1.1865 +			"can't set comm state", (char *) NULL);
  1.1866 +	    }
  1.1867 +	    return TCL_ERROR;
  1.1868 +	}
  1.1869 +	return TCL_OK;
  1.1870 +    }
  1.1871 +
  1.1872 +    /*
  1.1873 +     * Option -pollinterval msec
  1.1874 +     */
  1.1875 +    if ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0)) {
  1.1876 +
  1.1877 +	if ( Tcl_GetInt(interp, value, &(infoPtr->blockTime)) != TCL_OK ) {
  1.1878 +	    return TCL_ERROR;
  1.1879 +	}
  1.1880 +	return TCL_OK;
  1.1881 +    }
  1.1882 +
  1.1883 +    /*
  1.1884 +     * Option -timeout msec
  1.1885 +     */
  1.1886 +    if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
  1.1887 +	int msec;
  1.1888 +	COMMTIMEOUTS tout = {0,0,0,0,0};
  1.1889 +
  1.1890 +	if ( Tcl_GetInt(interp, value, &msec) != TCL_OK ) {
  1.1891 +	    return TCL_ERROR;
  1.1892 +	}
  1.1893 +	tout.ReadTotalTimeoutConstant = msec;
  1.1894 +	if (! SetCommTimeouts(infoPtr->handle, &tout)) {
  1.1895 +	    if (interp) {
  1.1896 +		Tcl_AppendResult(interp, 
  1.1897 +			"can't set comm timeouts", (char *) NULL);
  1.1898 +	    }
  1.1899 +	    return TCL_ERROR;
  1.1900 +	}
  1.1901 +
  1.1902 +	return TCL_OK;
  1.1903 +    }
  1.1904 +
  1.1905 +    return Tcl_BadChannelOption(interp, optionName,
  1.1906 +	    "mode handshake pollinterval sysbuffer timeout ttycontrol xchar");
  1.1907 +}
  1.1908 +
  1.1909 +/*
  1.1910 + *----------------------------------------------------------------------
  1.1911 + *
  1.1912 + * SerialGetOptionProc --
  1.1913 + *
  1.1914 + *  Gets a mode associated with an IO channel. If the optionName arg
  1.1915 + *  is non NULL, retrieves the value of that option. If the optionName
  1.1916 + *  arg is NULL, retrieves a list of alternating option names and
  1.1917 + *  values for the given channel.
  1.1918 + *
  1.1919 + * Results:
  1.1920 + *  A standard Tcl result. Also sets the supplied DString to the
  1.1921 + *  string value of the option(s) returned.
  1.1922 + *
  1.1923 + * Side effects:
  1.1924 + *  The string returned by this function is in static storage and
  1.1925 + *  may be reused at any time subsequent to the call.
  1.1926 + *
  1.1927 + *----------------------------------------------------------------------
  1.1928 + */
  1.1929 +static int
  1.1930 +SerialGetOptionProc(instanceData, interp, optionName, dsPtr)
  1.1931 +    ClientData instanceData;	/* File state. */
  1.1932 +    Tcl_Interp *interp;		/* For error reporting - can be NULL. */
  1.1933 +    CONST char *optionName;	/* Option to get. */
  1.1934 +    Tcl_DString *dsPtr;		/* Where to store value(s). */
  1.1935 +{
  1.1936 +    SerialInfo *infoPtr;
  1.1937 +    DCB dcb;
  1.1938 +    size_t len;
  1.1939 +    int valid = 0;  /* flag if valid option parsed */
  1.1940 +
  1.1941 +    infoPtr = (SerialInfo *) instanceData;
  1.1942 +
  1.1943 +    if (optionName == NULL) {
  1.1944 +	len = 0;
  1.1945 +    } else {
  1.1946 +	len = strlen(optionName);
  1.1947 +    }
  1.1948 +
  1.1949 +    /*
  1.1950 +     * get option -mode
  1.1951 +     */
  1.1952 +
  1.1953 +    if (len == 0) {
  1.1954 +	Tcl_DStringAppendElement(dsPtr, "-mode");
  1.1955 +    }
  1.1956 +    if ((len == 0) ||
  1.1957 +	    ((len > 2) && (strncmp(optionName, "-mode", len) == 0))) {
  1.1958 +
  1.1959 +	char parity;
  1.1960 +	char *stop;
  1.1961 +	char buf[2 * TCL_INTEGER_SPACE + 16];
  1.1962 +
  1.1963 +	if (! GetCommState(infoPtr->handle, &dcb)) {
  1.1964 +	    if (interp) {
  1.1965 +		Tcl_AppendResult(interp, 
  1.1966 +			"can't get comm state", (char *) NULL);
  1.1967 +	    }
  1.1968 +	    return TCL_ERROR;
  1.1969 +	}
  1.1970 +
  1.1971 +	valid = 1;
  1.1972 +	parity = 'n';
  1.1973 +	if (dcb.Parity <= 4) {
  1.1974 +	    parity = "noems"[dcb.Parity];
  1.1975 +	}
  1.1976 +	stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
  1.1977 +	    (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
  1.1978 +
  1.1979 +	wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity,
  1.1980 +		dcb.ByteSize, stop);
  1.1981 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.1982 +    }
  1.1983 +
  1.1984 +    /*
  1.1985 +     * get option -pollinterval
  1.1986 +     */
  1.1987 +
  1.1988 +    if (len == 0) {
  1.1989 +	Tcl_DStringAppendElement(dsPtr, "-pollinterval");
  1.1990 +    }
  1.1991 +    if ((len == 0) ||
  1.1992 +	    ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0))) {
  1.1993 +	char buf[TCL_INTEGER_SPACE + 1];
  1.1994 +
  1.1995 +	valid = 1;
  1.1996 +	wsprintfA(buf, "%d", infoPtr->blockTime);
  1.1997 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.1998 +    }
  1.1999 +
  1.2000 +    /*
  1.2001 +     * get option -sysbuffer
  1.2002 +     */
  1.2003 +
  1.2004 +    if (len == 0) {
  1.2005 +	Tcl_DStringAppendElement(dsPtr, "-sysbuffer");
  1.2006 +	Tcl_DStringStartSublist(dsPtr);
  1.2007 +    }
  1.2008 +    if ((len == 0) ||
  1.2009 +	    ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0))) {
  1.2010 +
  1.2011 +	char buf[TCL_INTEGER_SPACE + 1];
  1.2012 +	valid = 1;
  1.2013 +
  1.2014 +	wsprintfA(buf, "%d", infoPtr->sysBufRead);
  1.2015 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.2016 +	wsprintfA(buf, "%d", infoPtr->sysBufWrite);
  1.2017 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.2018 +    }
  1.2019 +    if (len == 0) {
  1.2020 +	Tcl_DStringEndSublist(dsPtr);
  1.2021 +    }
  1.2022 +
  1.2023 +    /*
  1.2024 +     * get option -xchar
  1.2025 +     */
  1.2026 +
  1.2027 +    if (len == 0) {
  1.2028 +	Tcl_DStringAppendElement(dsPtr, "-xchar");
  1.2029 +	Tcl_DStringStartSublist(dsPtr);
  1.2030 +    }
  1.2031 +    if ((len == 0) ||
  1.2032 +	    ((len > 1) && (strncmp(optionName, "-xchar", len) == 0))) {
  1.2033 +
  1.2034 +	char buf[4];
  1.2035 +	valid = 1;
  1.2036 +
  1.2037 +	if (! GetCommState(infoPtr->handle, &dcb)) {
  1.2038 +	    if (interp) {
  1.2039 +		Tcl_AppendResult(interp, 
  1.2040 +			"can't get comm state", (char *) NULL);
  1.2041 +	    }
  1.2042 +	    return TCL_ERROR;
  1.2043 +	}
  1.2044 +	sprintf(buf, "%c", dcb.XonChar);
  1.2045 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.2046 +	sprintf(buf, "%c", dcb.XoffChar);
  1.2047 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.2048 +    }
  1.2049 +    if (len == 0) {
  1.2050 +	Tcl_DStringEndSublist(dsPtr);
  1.2051 +    }
  1.2052 +
  1.2053 +    /*
  1.2054 +     * get option -lasterror
  1.2055 +     * option is readonly and returned by [fconfigure chan -lasterror]
  1.2056 +     * but not returned by unnamed [fconfigure chan]
  1.2057 +     */
  1.2058 +
  1.2059 +    if ( (len > 1) && (strncmp(optionName, "-lasterror", len) == 0) ) {
  1.2060 +	valid = 1;
  1.2061 +	SerialErrorStr(infoPtr->lastError, dsPtr);
  1.2062 +    }
  1.2063 +
  1.2064 +    /*
  1.2065 +     * get option -queue
  1.2066 +     * option is readonly and returned by [fconfigure chan -queue]
  1.2067 +     */
  1.2068 +
  1.2069 +    if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
  1.2070 +	char buf[TCL_INTEGER_SPACE + 1];
  1.2071 +	COMSTAT cStat;
  1.2072 +	DWORD error;
  1.2073 +	int inBuffered, outBuffered, count;
  1.2074 +
  1.2075 +	valid = 1;
  1.2076 +
  1.2077 +	/* 
  1.2078 +	 * Query the pending data in Tcl's internal queues
  1.2079 +	 */
  1.2080 +	inBuffered  = Tcl_InputBuffered(infoPtr->channel);
  1.2081 +	outBuffered = Tcl_OutputBuffered(infoPtr->channel);
  1.2082 +
  1.2083 +	/*
  1.2084 +	 * Query the number of bytes in our output queue:
  1.2085 +	 *     1. The bytes pending in the output thread
  1.2086 +	 *     2. The bytes in the system drivers buffer
  1.2087 +	 * The writer thread should not interfere this action.
  1.2088 +	 */
  1.2089 +	EnterCriticalSection(&infoPtr->csWrite);
  1.2090 +	ClearCommError( infoPtr->handle, &error, &cStat );
  1.2091 +	count = (int)cStat.cbOutQue + infoPtr->writeQueue;
  1.2092 +	LeaveCriticalSection(&infoPtr->csWrite);
  1.2093 +
  1.2094 +	wsprintfA(buf, "%d", inBuffered + cStat.cbInQue); 
  1.2095 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.2096 +	wsprintfA(buf, "%d", outBuffered + count); 
  1.2097 +	Tcl_DStringAppendElement(dsPtr, buf);
  1.2098 +    }
  1.2099 +
  1.2100 +    /*
  1.2101 +     * get option -ttystatus
  1.2102 +     * option is readonly and returned by [fconfigure chan -ttystatus]
  1.2103 +     * but not returned by unnamed [fconfigure chan]
  1.2104 +     */
  1.2105 +    if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
  1.2106 +
  1.2107 +	DWORD status;
  1.2108 +
  1.2109 +	if (! GetCommModemStatus(infoPtr->handle, &status)) {
  1.2110 +	    if (interp) {
  1.2111 +		Tcl_AppendResult(interp, 
  1.2112 +			"can't get tty status", (char *) NULL);
  1.2113 +	    }
  1.2114 +	    return TCL_ERROR;
  1.2115 +	}
  1.2116 +	valid = 1;
  1.2117 +	SerialModemStatusStr(status, dsPtr);
  1.2118 +    }
  1.2119 +
  1.2120 +    if (valid) {
  1.2121 +	return TCL_OK;
  1.2122 +    } else {
  1.2123 +	return Tcl_BadChannelOption(interp, optionName,
  1.2124 +		"mode pollinterval lasterror queue sysbuffer ttystatus xchar");
  1.2125 +    }
  1.2126 +}
  1.2127 +
  1.2128 +/*
  1.2129 + *----------------------------------------------------------------------
  1.2130 + *
  1.2131 + * SerialThreadActionProc --
  1.2132 + *
  1.2133 + *	Insert or remove any thread local refs to this channel.
  1.2134 + *
  1.2135 + * Results:
  1.2136 + *	None.
  1.2137 + *
  1.2138 + * Side effects:
  1.2139 + *	Changes thread local list of valid channels.
  1.2140 + *
  1.2141 + *----------------------------------------------------------------------
  1.2142 + */
  1.2143 +
  1.2144 +static void
  1.2145 +SerialThreadActionProc (instanceData, action)
  1.2146 +     ClientData instanceData;
  1.2147 +     int action;
  1.2148 +{
  1.2149 +    SerialInfo *infoPtr = (SerialInfo *) instanceData;
  1.2150 +
  1.2151 +    /* We do not access firstSerialPtr in the thread structures. This is
  1.2152 +     * not for all serials managed by the thread, but only those we are
  1.2153 +     * watching. Removal of the filevent handlers before transfer thus
  1.2154 +     * takes care of this structure.
  1.2155 +     */
  1.2156 +
  1.2157 +    Tcl_MutexLock(&serialMutex);
  1.2158 +    if (action == TCL_CHANNEL_THREAD_INSERT) {
  1.2159 +        /* We can't copy the thread information from the channel when
  1.2160 +	 * the channel is created. At this time the channel back
  1.2161 +	 * pointer has not been set yet. However in that case the
  1.2162 +	 * threadId has already been set by TclpCreateCommandChannel
  1.2163 +	 * itself, so the structure is still good.
  1.2164 +	 */
  1.2165 +
  1.2166 +        SerialInit ();
  1.2167 +        if (infoPtr->channel != NULL) {
  1.2168 +	    infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
  1.2169 +	}
  1.2170 +    } else {
  1.2171 +	infoPtr->threadId = NULL;
  1.2172 +    }
  1.2173 +    Tcl_MutexUnlock(&serialMutex);
  1.2174 +}