Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/win/tclWinSerial.c @ 68

Last change on this file since 68 was 25, checked in by landauf, 16 years ago

added tcl to libs

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