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