os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinSerial.c
First public contribution.
4 * This file implements the Windows-specific serial port functions,
5 * and the "serial" channel driver.
7 * Copyright (c) 1999 by Scriptics Corp.
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 * Serial functionality implemented by Rolf.Schroedter@dlr.de
14 * RCS: @(#) $Id: tclWinSerial.c,v 1.25.2.3 2005/10/05 06:33:52 hobbs Exp $
17 #include "tclWinInt.h"
24 * The following variable is used to tell whether this module has been
28 static int initialized = 0;
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.
36 TCL_DECLARE_MUTEX(serialMutex)
39 * Bit masks used in the flags field of the SerialInfo structure below.
42 #define SERIAL_PENDING (1<<0) /* Message is pending in the queue. */
43 #define SERIAL_ASYNC (1<<1) /* Channel is non-blocking. */
46 * Bit masks used in the sharedFlags field of the SerialInfo structure below.
49 #define SERIAL_EOF (1<<2) /* Serial has reached EOF. */
50 #define SERIAL_ERROR (1<<4)
53 * Default time to block between checking status on the serial port.
55 #define SERIAL_DEFAULT_BLOCKTIME 10 /* 10 msec */
58 * Define Win32 read/write error masks returned by ClearCommError()
60 #define SERIAL_READ_ERRORS ( CE_RXOVER | CE_OVERRUN | CE_RXPARITY \
61 | CE_FRAME | CE_BREAK )
62 #define SERIAL_WRITE_ERRORS ( CE_TXFULL | CE_PTO )
65 * This structure describes per-instance data for a serial based channel.
68 typedef struct SerialInfo {
70 struct SerialInfo *nextPtr; /* Pointer to next registered serial. */
71 Tcl_Channel channel; /* Pointer to channel structure. */
72 int validMask; /* OR'ed combination of TCL_READABLE,
73 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
74 * which operations are valid on the file. */
75 int watchMask; /* OR'ed combination of TCL_READABLE,
76 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
77 * which events should be reported. */
78 int flags; /* State flags, see above for a list. */
79 int readable; /* flag that the channel is readable */
80 int writable; /* flag that the channel is writable */
81 int blockTime; /* max. blocktime in msec */
82 unsigned int lastEventTime; /* Time in milliseconds since last readable event */
83 /* Next readable event only after blockTime */
84 DWORD error; /* pending error code returned by
86 DWORD lastError; /* last error code, can be fetched with
87 * fconfigure chan -lasterror */
88 DWORD sysBufRead; /* Win32 system buffer size for read ops,
90 DWORD sysBufWrite; /* Win32 system buffer size for write ops,
93 Tcl_ThreadId threadId; /* Thread to which events should be reported.
94 * This value is used by the reader/writer
96 OVERLAPPED osRead; /* OVERLAPPED structure for read operations */
97 OVERLAPPED osWrite; /* OVERLAPPED structure for write operations */
98 HANDLE writeThread; /* Handle to writer thread. */
99 CRITICAL_SECTION csWrite; /* Writer thread synchronisation */
100 HANDLE evWritable; /* Manual-reset event to signal when the
101 * writer thread has finished waiting for
102 * the current buffer to be written. */
103 HANDLE evStartWriter; /* Auto-reset event used by the main thread to
104 * signal when the writer thread should attempt
105 * to write to the serial. */
106 HANDLE evStopWriter; /* Auto-reset event used by the main thread to
107 * signal when the writer thread should close.
109 DWORD writeError; /* An error caused by the last background
110 * write. Set to 0 if no error has been
111 * detected. This word is shared with the
112 * writer thread so access must be
113 * synchronized with the evWritable object.
115 char *writeBuf; /* Current background output buffer.
116 * Access is synchronized with the evWritable
118 int writeBufLen; /* Size of write buffer. Access is
119 * synchronized with the evWritable
121 int toWrite; /* Current amount to be written. Access is
122 * synchronized with the evWritable object. */
123 int writeQueue; /* Number of bytes pending in output queue.
124 * Offset to DCB.cbInQue.
125 * Used to query [fconfigure -queue] */
128 typedef struct ThreadSpecificData {
130 * The following pointer refers to the head of the list of serials
131 * that are being watched for file events.
134 SerialInfo *firstSerialPtr;
135 } ThreadSpecificData;
137 static Tcl_ThreadDataKey dataKey;
140 * The following structure is what is added to the Tcl event queue when
141 * serial events are generated.
144 typedef struct SerialEvent {
145 Tcl_Event header; /* Information that is standard for
147 SerialInfo *infoPtr; /* Pointer to serial info structure. Note
148 * that we still have to verify that the
149 * serial exists before dereferencing this
154 * We don't use timeouts.
157 static COMMTIMEOUTS no_timeout = {
158 0, /* ReadIntervalTimeout */
159 0, /* ReadTotalTimeoutMultiplier */
160 0, /* ReadTotalTimeoutConstant */
161 0, /* WriteTotalTimeoutMultiplier */
162 0, /* WriteTotalTimeoutConstant */
166 * Declarations for functions used only in this file.
169 static int SerialBlockProc(ClientData instanceData, int mode);
170 static void SerialCheckProc(ClientData clientData, int flags);
171 static int SerialCloseProc(ClientData instanceData,
173 static int SerialEventProc(Tcl_Event *evPtr, int flags);
174 static void SerialExitHandler(ClientData clientData);
175 static int SerialGetHandleProc(ClientData instanceData,
176 int direction, ClientData *handlePtr);
177 static ThreadSpecificData *SerialInit(void);
178 static int SerialInputProc(ClientData instanceData, char *buf,
179 int toRead, int *errorCode);
180 static int SerialOutputProc(ClientData instanceData, CONST char *buf,
181 int toWrite, int *errorCode);
182 static void SerialSetupProc(ClientData clientData, int flags);
183 static void SerialWatchProc(ClientData instanceData, int mask);
184 static void ProcExitHandler(ClientData clientData);
185 static int SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData,
186 Tcl_Interp *interp, CONST char *optionName,
187 Tcl_DString *dsPtr));
188 static int SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData,
189 Tcl_Interp *interp, CONST char *optionName,
191 static DWORD WINAPI SerialWriterThread(LPVOID arg);
193 static void SerialThreadActionProc _ANSI_ARGS_ ((
194 ClientData instanceData, int action));
197 * This structure describes the channel type structure for command serial
201 static Tcl_ChannelType serialChannelType = {
202 "serial", /* Type name. */
203 TCL_CHANNEL_VERSION_4, /* v4 channel */
204 SerialCloseProc, /* Close proc. */
205 SerialInputProc, /* Input proc. */
206 SerialOutputProc, /* Output proc. */
207 NULL, /* Seek proc. */
208 SerialSetOptionProc, /* Set option proc. */
209 SerialGetOptionProc, /* Get option proc. */
210 SerialWatchProc, /* Set up notifier to watch the channel. */
211 SerialGetHandleProc, /* Get an OS handle from channel. */
212 NULL, /* close2proc. */
213 SerialBlockProc, /* Set blocking or non-blocking mode.*/
214 NULL, /* flush proc. */
215 NULL, /* handler proc. */
216 NULL, /* wide seek proc */
217 SerialThreadActionProc, /* thread action proc */
221 *----------------------------------------------------------------------
225 * This function initializes the static variables for this file.
231 * Creates a new event source.
233 *----------------------------------------------------------------------
236 static ThreadSpecificData *
239 ThreadSpecificData *tsdPtr;
242 * Check the initialized flag first, then check it again in the mutex.
243 * This is a speed enhancement.
247 Tcl_MutexLock(&serialMutex);
250 Tcl_CreateExitHandler(ProcExitHandler, NULL);
252 Tcl_MutexUnlock(&serialMutex);
255 tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
256 if (tsdPtr == NULL) {
257 tsdPtr = TCL_TSD_INIT(&dataKey);
258 tsdPtr->firstSerialPtr = NULL;
259 Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
260 Tcl_CreateThreadExitHandler(SerialExitHandler, NULL);
266 *----------------------------------------------------------------------
268 * SerialExitHandler --
270 * This function is called to cleanup the serial module before
277 * Removes the serial event source.
279 *----------------------------------------------------------------------
284 ClientData clientData) /* Old window proc */
286 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
290 * Clear all eventually pending output.
291 * Otherwise Tcl's exit could totally block,
292 * because it performs a blocking flush on all open channels.
293 * Note that serial write operations may be blocked due to handshake.
295 for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
296 infoPtr = infoPtr->nextPtr) {
297 PurgeComm(infoPtr->handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
301 Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);
305 *----------------------------------------------------------------------
309 * This function is called to cleanup the process list before
316 * Resets the process list.
318 *----------------------------------------------------------------------
323 ClientData clientData) /* Old window proc */
325 Tcl_MutexLock(&serialMutex);
327 Tcl_MutexUnlock(&serialMutex);
331 *----------------------------------------------------------------------
335 * Wrapper to set Tcl's block time in msec
339 *----------------------------------------------------------------------
344 int msec) /* milli-seconds */
348 blockTime.sec = msec / 1000;
349 blockTime.usec = (msec % 1000) * 1000;
350 Tcl_SetMaxBlockTime(&blockTime);
353 *----------------------------------------------------------------------
355 * SerialGetMilliseconds --
357 * Get current time in milliseconds,
358 * Don't care about integer overruns
362 *----------------------------------------------------------------------
366 SerialGetMilliseconds(
373 return (time.sec * 1000 + time.usec / 1000);
376 *----------------------------------------------------------------------
380 * This procedure is invoked before Tcl_DoOneEvent blocks waiting
387 * Adjusts the block time if needed.
389 *----------------------------------------------------------------------
394 ClientData data, /* Not used. */
395 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
399 int msec = INT_MAX; /* min. found block time */
400 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
402 if (!(flags & TCL_FILE_EVENTS)) {
407 * Look to see if any events handlers installed. If they are, do not block.
410 for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
411 infoPtr = infoPtr->nextPtr) {
413 if (infoPtr->watchMask & TCL_WRITABLE) {
414 if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
416 msec = min( msec, infoPtr->blockTime );
419 if( infoPtr->watchMask & TCL_READABLE ) {
421 msec = min( msec, infoPtr->blockTime );
426 SerialBlockTime(msec);
431 *----------------------------------------------------------------------
435 * This procedure is called by Tcl_DoOneEvent to check the serial
436 * event source for events.
442 * May queue an event.
444 *----------------------------------------------------------------------
449 ClientData data, /* Not used. */
450 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
455 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
459 if (!(flags & TCL_FILE_EVENTS)) {
464 * Queue events for any ready serials that don't already have events
468 for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
469 infoPtr = infoPtr->nextPtr) {
470 if (infoPtr->flags & SERIAL_PENDING) {
477 * If WRITABLE watch mask is set
478 * look for infoPtr->evWritable object
480 if (infoPtr->watchMask & TCL_WRITABLE) {
481 if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
482 infoPtr->writable = 1;
488 * If READABLE watch mask is set
489 * call ClearCommError to poll cbInQue
490 * Window errors are ignored here
493 if( infoPtr->watchMask & TCL_READABLE ) {
494 if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
496 * Look for characters already pending in windows queue.
500 if( infoPtr->watchMask & TCL_READABLE ) {
502 * force fileevent after serial read error
504 if( (cStat.cbInQue > 0) ||
505 (infoPtr->error & SERIAL_READ_ERRORS) ) {
506 infoPtr->readable = 1;
507 time = SerialGetMilliseconds();
508 if ((unsigned int) (time - infoPtr->lastEventTime)
509 >= (unsigned int) infoPtr->blockTime) {
511 infoPtr->lastEventTime = time;
519 * Queue an event if the serial is signaled for reading or writing.
522 infoPtr->flags |= SERIAL_PENDING;
523 evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent));
524 evPtr->header.proc = SerialEventProc;
525 evPtr->infoPtr = infoPtr;
526 Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
532 *----------------------------------------------------------------------
536 * Set blocking or non-blocking mode on channel.
539 * 0 if successful, errno when failed.
542 * Sets the device into blocking or non-blocking mode.
544 *----------------------------------------------------------------------
549 ClientData instanceData, /* Instance data for channel. */
550 int mode) /* TCL_MODE_BLOCKING or
551 * TCL_MODE_NONBLOCKING. */
555 SerialInfo *infoPtr = (SerialInfo *) instanceData;
558 * Only serial READ can be switched between blocking & nonblocking
559 * using COMMTIMEOUTS.
560 * Serial write emulates blocking & nonblocking by the SerialWriterThread.
563 if (mode == TCL_MODE_NONBLOCKING) {
564 infoPtr->flags |= SERIAL_ASYNC;
566 infoPtr->flags &= ~(SERIAL_ASYNC);
572 *----------------------------------------------------------------------
576 * Closes a serial based IO channel.
579 * 0 on success, errno otherwise.
582 * Closes the physical channel.
584 *----------------------------------------------------------------------
589 ClientData instanceData, /* Pointer to SerialInfo structure. */
590 Tcl_Interp *interp) /* For error reporting. */
592 SerialInfo *serialPtr = (SerialInfo *) instanceData;
593 int errorCode, result = 0;
594 SerialInfo *infoPtr, **nextPtrPtr;
595 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
600 if (serialPtr->validMask & TCL_READABLE) {
601 PurgeComm(serialPtr->handle, PURGE_RXABORT | PURGE_RXCLEAR);
602 CloseHandle(serialPtr->osRead.hEvent);
604 serialPtr->validMask &= ~TCL_READABLE;
606 if (serialPtr->validMask & TCL_WRITABLE) {
609 * Generally we cannot wait for a pending write operation
610 * because it may hang due to handshake
611 * WaitForSingleObject(serialPtr->evWritable, INFINITE);
615 * The thread may have already closed on it's own. Check it's
619 GetExitCodeThread(serialPtr->writeThread, &exitCode);
621 if (exitCode == STILL_ACTIVE) {
623 * Set the stop event so that if the writer thread is
624 * blocked in SerialWriterThread on WaitForMultipleEvents, it
628 SetEvent(serialPtr->evStopWriter);
631 * Wait at most 20 milliseconds for the writer thread to
635 if (WaitForSingleObject(serialPtr->writeThread, 20)
638 * Forcibly terminate the background thread as a last
639 * resort. Note that we need to guard against
640 * terminating the thread while it is in the middle of
641 * Tcl_ThreadAlert because it won't be able to release
645 Tcl_MutexLock(&serialMutex);
647 /* BUG: this leaks memory */
648 TerminateThread(serialPtr->writeThread, 0);
650 Tcl_MutexUnlock(&serialMutex);
654 CloseHandle(serialPtr->writeThread);
655 CloseHandle(serialPtr->osWrite.hEvent);
656 DeleteCriticalSection(&serialPtr->csWrite);
657 CloseHandle(serialPtr->evWritable);
658 CloseHandle(serialPtr->evStartWriter);
659 CloseHandle(serialPtr->evStopWriter);
660 serialPtr->writeThread = NULL;
662 PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR);
664 serialPtr->validMask &= ~TCL_WRITABLE;
667 * Don't close the Win32 handle if the handle is a standard channel
668 * during the thread exit process. Otherwise, one thread may kill
669 * the stdio of another.
672 if (!TclInThreadExit()
673 || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
674 && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
675 && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
676 if (CloseHandle(serialPtr->handle) == FALSE) {
677 TclWinConvertError(GetLastError());
682 serialPtr->watchMask &= serialPtr->validMask;
685 * Remove the file from the list of watched files.
688 for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr;
690 nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
691 if (infoPtr == (SerialInfo *)serialPtr) {
692 *nextPtrPtr = infoPtr->nextPtr;
698 * Wrap the error file into a channel and give it to the cleanup
701 if (serialPtr->writeBuf != NULL) {
702 ckfree(serialPtr->writeBuf);
703 serialPtr->writeBuf = NULL;
705 ckfree((char*) serialPtr);
707 if (errorCode == 0) {
714 *----------------------------------------------------------------------
718 * Perform a blocking read into the buffer given. Returns
719 * count of how many bytes were actually read, and an error indication.
722 * A count of how many bytes were read is returned and an error
723 * indication is returned.
726 * Reads input from the actual channel.
728 *----------------------------------------------------------------------
732 SerialInfo *infoPtr, /* Serial info structure */
733 LPVOID buf, /* The input buffer pointer */
734 DWORD bufSize, /* The number of bytes to read */
735 LPDWORD lpRead, /* Returns number of bytes read */
736 LPOVERLAPPED osPtr ) /* OVERLAPPED structure */
739 * Perform overlapped blocking read.
740 * 1. Reset the overlapped event
741 * 2. Start overlapped read operation
742 * 3. Wait for completion
746 * Set Offset to ZERO, otherwise NT4.0 may report an error.
748 osPtr->Offset = osPtr->OffsetHigh = 0;
749 ResetEvent(osPtr->hEvent);
750 if (! ReadFile(infoPtr->handle, buf, bufSize, lpRead, osPtr) ) {
751 if (GetLastError() != ERROR_IO_PENDING) {
752 /* ReadFile failed, but it isn't delayed. Report error. */
755 /* Read is pending, wait for completion, timeout ? */
756 if (! GetOverlappedResult(infoPtr->handle, osPtr, lpRead, TRUE) ) {
761 /* ReadFile completed immediately. */
767 *----------------------------------------------------------------------
771 * Perform a blocking write from the buffer given. Returns
772 * count of how many bytes were actually written, and an error indication.
775 * A count of how many bytes were written is returned and an error
776 * indication is returned.
779 * Writes output to the actual channel.
781 *----------------------------------------------------------------------
785 SerialInfo *infoPtr, /* Serial info structure */
786 LPVOID buf, /* The output buffer pointer */
787 DWORD bufSize, /* The number of bytes to write */
788 LPDWORD lpWritten, /* Returns number of bytes written */
789 LPOVERLAPPED osPtr ) /* OVERLAPPED structure */
793 * Perform overlapped blocking write.
794 * 1. Reset the overlapped event
795 * 2. Remove these bytes from the output queue counter
796 * 3. Start overlapped write operation
797 * 3. Remove these bytes from the output queue counter
798 * 4. Wait for completion
799 * 5. Adjust the output queue counter
801 ResetEvent(osPtr->hEvent);
803 EnterCriticalSection(&infoPtr->csWrite);
804 infoPtr->writeQueue -= bufSize;
806 * Set Offset to ZERO, otherwise NT4.0 may report an error
808 osPtr->Offset = osPtr->OffsetHigh = 0;
809 result = WriteFile(infoPtr->handle, buf, bufSize, lpWritten, osPtr);
810 LeaveCriticalSection(&infoPtr->csWrite);
812 if (result == FALSE ) {
813 int err = GetLastError();
815 case ERROR_IO_PENDING:
816 /* Write is pending, wait for completion */
817 if (! GetOverlappedResult(infoPtr->handle, osPtr, lpWritten, TRUE) ) {
821 case ERROR_COUNTER_TIMEOUT:
822 /* Write timeout handled in SerialOutputProc */
825 /* WriteFile failed, but it isn't delayed. Report error */
829 /* WriteFile completed immediately. */
832 EnterCriticalSection(&infoPtr->csWrite);
833 infoPtr->writeQueue += (*lpWritten - bufSize);
834 LeaveCriticalSection(&infoPtr->csWrite);
840 *----------------------------------------------------------------------
844 * Reads input from the IO channel into the buffer given. Returns
845 * count of how many bytes were actually read, and an error indication.
848 * A count of how many bytes were read is returned and an error
849 * indication is returned in an output argument.
852 * Reads input from the actual channel.
854 *----------------------------------------------------------------------
858 ClientData instanceData, /* Serial state. */
859 char *buf, /* Where to store data read. */
860 int bufSize, /* How much space is available
862 int *errorCode) /* Where to store error code. */
864 SerialInfo *infoPtr = (SerialInfo *) instanceData;
871 * Check if there is a CommError pending from SerialCheckProc
873 if( infoPtr->error & SERIAL_READ_ERRORS ){
878 * Look for characters already pending in windows queue.
879 * This is the mainly restored good old code from Tcl8.0
882 if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
884 * Check for errors here, but not in the evSetup/Check procedures
887 if( infoPtr->error & SERIAL_READ_ERRORS ) {
890 if( infoPtr->flags & SERIAL_ASYNC ) {
893 * Avoid blocking by reading more bytes than available
897 if( cStat.cbInQue > 0 ) {
898 if( (DWORD) bufSize > cStat.cbInQue ) {
899 bufSize = cStat.cbInQue;
902 errno = *errorCode = EAGAIN;
908 * Tcl trys to read a full buffer of 4 kBytes here
911 if( cStat.cbInQue > 0 ) {
912 if( (DWORD) bufSize > cStat.cbInQue ) {
913 bufSize = cStat.cbInQue;
922 return bytesRead = 0;
926 * Perform blocking read. Doesn't block in non-blocking mode,
927 * because we checked the number of available bytes.
929 if (blockingRead(infoPtr, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
930 &infoPtr->osRead) == FALSE) {
936 TclWinConvertError(GetLastError());
941 infoPtr->lastError = infoPtr->error; /* save last error code */
942 infoPtr->error = 0; /* reset error code */
943 *errorCode = EIO; /* to return read-error only once */
948 *----------------------------------------------------------------------
950 * SerialOutputProc --
952 * Writes the given output on the IO channel. Returns count of how
953 * many characters were actually written, and an error indication.
956 * A count of how many characters were written is returned and an
957 * error indication is returned in an output argument.
960 * Writes output on the actual channel.
962 *----------------------------------------------------------------------
967 ClientData instanceData, /* Serial state. */
968 CONST char *buf, /* The data buffer. */
969 int toWrite, /* How many bytes to write? */
970 int *errorCode) /* Where to store error code. */
972 SerialInfo *infoPtr = (SerialInfo *) instanceData;
973 DWORD bytesWritten, timeout;
978 * At EXIT Tcl trys to flush all open channels in blocking mode.
979 * We avoid blocking output after ExitProc or CloseHandler(chan)
980 * has been called by checking the corrresponding variables.
982 if( ! initialized || TclInExit() ) {
987 * Check if there is a CommError pending from SerialCheckProc
989 if( infoPtr->error & SERIAL_WRITE_ERRORS ){
990 infoPtr->lastError = infoPtr->error; /* save last error code */
991 infoPtr->error = 0; /* reset error code */
996 timeout = (infoPtr->flags & SERIAL_ASYNC) ? 0 : INFINITE;
997 if (WaitForSingleObject(infoPtr->evWritable, timeout) == WAIT_TIMEOUT) {
999 * The writer thread is blocked waiting for a write to complete
1000 * and the channel is in non-blocking mode.
1003 errno = EWOULDBLOCK;
1007 * Check for a background error on the last write.
1010 if (infoPtr->writeError) {
1011 TclWinConvertError(infoPtr->writeError);
1012 infoPtr->writeError = 0;
1017 * Remember the number of bytes in output queue
1019 EnterCriticalSection(&infoPtr->csWrite);
1020 infoPtr->writeQueue += toWrite;
1021 LeaveCriticalSection(&infoPtr->csWrite);
1023 if (infoPtr->flags & SERIAL_ASYNC) {
1025 * The serial is non-blocking, so copy the data into the output
1026 * buffer and restart the writer thread.
1029 if (toWrite > infoPtr->writeBufLen) {
1031 * Reallocate the buffer to be large enough to hold the data.
1034 if (infoPtr->writeBuf) {
1035 ckfree(infoPtr->writeBuf);
1037 infoPtr->writeBufLen = toWrite;
1038 infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
1040 memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
1041 infoPtr->toWrite = toWrite;
1042 ResetEvent(infoPtr->evWritable);
1043 SetEvent(infoPtr->evStartWriter);
1044 bytesWritten = (DWORD) toWrite;
1048 * In the blocking case, just try to write the buffer directly.
1049 * This avoids an unnecessary copy.
1051 if (! blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
1052 &bytesWritten, &infoPtr->osWrite) ) {
1055 if (bytesWritten != (DWORD) toWrite) {
1057 infoPtr->lastError |= CE_PTO;
1063 return (int) bytesWritten;
1066 TclWinConvertError(GetLastError());
1070 * Reset the output queue counter on error during blocking output
1073 EnterCriticalSection(&infoPtr->csWrite);
1074 infoPtr->writeQueue = 0;
1075 LeaveCriticalSection(&infoPtr->csWrite);
1083 *----------------------------------------------------------------------
1085 * SerialEventProc --
1087 * This function is invoked by Tcl_ServiceEvent when a file event
1088 * reaches the front of the event queue. This procedure invokes
1089 * Tcl_NotifyChannel on the serial.
1092 * Returns 1 if the event was handled, meaning it should be removed
1093 * from the queue. Returns 0 if the event was not handled, meaning
1094 * it should stay on the queue. The only time the event isn't
1095 * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1098 * Whatever the notifier callback does.
1100 *----------------------------------------------------------------------
1105 Tcl_Event *evPtr, /* Event to service. */
1106 int flags) /* Flags that indicate what events to
1107 * handle, such as TCL_FILE_EVENTS. */
1109 SerialEvent *serialEvPtr = (SerialEvent *)evPtr;
1110 SerialInfo *infoPtr;
1112 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1114 if (!(flags & TCL_FILE_EVENTS)) {
1119 * Search through the list of watched serials for the one whose handle
1120 * matches the event. We do this rather than simply dereferencing
1121 * the handle in the event so that serials can be deleted while the
1122 * event is in the queue.
1125 for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
1126 infoPtr = infoPtr->nextPtr) {
1127 if (serialEvPtr->infoPtr == infoPtr) {
1128 infoPtr->flags &= ~(SERIAL_PENDING);
1134 * Remove stale events.
1142 * Check to see if the serial is readable. Note
1143 * that we can't tell if a serial is writable, so we always report it
1144 * as being writable unless we have detected EOF.
1148 if( infoPtr->watchMask & TCL_WRITABLE ) {
1149 if( infoPtr->writable ) {
1150 mask |= TCL_WRITABLE;
1151 infoPtr->writable = 0;
1155 if( infoPtr->watchMask & TCL_READABLE ) {
1156 if( infoPtr->readable ) {
1157 mask |= TCL_READABLE;
1158 infoPtr->readable = 0;
1163 * Inform the channel of the events.
1166 Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
1171 *----------------------------------------------------------------------
1173 * SerialWatchProc --
1175 * Called by the notifier to set up to watch for events on this
1184 *----------------------------------------------------------------------
1189 ClientData instanceData, /* Serial state. */
1190 int mask) /* What events to watch for, OR-ed
1191 * combination of TCL_READABLE,
1192 * TCL_WRITABLE and TCL_EXCEPTION. */
1194 SerialInfo **nextPtrPtr, *ptr;
1195 SerialInfo *infoPtr = (SerialInfo *) instanceData;
1196 int oldMask = infoPtr->watchMask;
1197 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1200 * Since the file is always ready for events, we set the block time
1204 infoPtr->watchMask = mask & infoPtr->validMask;
1205 if (infoPtr->watchMask) {
1207 infoPtr->nextPtr = tsdPtr->firstSerialPtr;
1208 tsdPtr->firstSerialPtr = infoPtr;
1210 SerialBlockTime(infoPtr->blockTime);
1214 * Remove the serial port from the list of watched serial ports.
1217 for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr;
1219 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
1220 if (infoPtr == ptr) {
1221 *nextPtrPtr = ptr->nextPtr;
1230 *----------------------------------------------------------------------
1232 * SerialGetHandleProc --
1234 * Called from Tcl_GetChannelHandle to retrieve OS handles from
1235 * inside a command serial port based channel.
1238 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1239 * there is no handle for the specified direction.
1244 *----------------------------------------------------------------------
1248 SerialGetHandleProc(
1249 ClientData instanceData, /* The serial state. */
1250 int direction, /* TCL_READABLE or TCL_WRITABLE */
1251 ClientData *handlePtr) /* Where to store the handle. */
1253 SerialInfo *infoPtr = (SerialInfo *) instanceData;
1255 *handlePtr = (ClientData) infoPtr->handle;
1260 *----------------------------------------------------------------------
1262 * SerialWriterThread --
1264 * This function runs in a separate thread and writes data
1271 * Signals the main thread when an output operation is completed.
1272 * May cause the main thread to wake up by posting a message.
1274 *----------------------------------------------------------------------
1278 SerialWriterThread(LPVOID arg)
1281 SerialInfo *infoPtr = (SerialInfo *)arg;
1282 DWORD bytesWritten, toWrite, waitResult;
1284 OVERLAPPED myWrite; /* have an own OVERLAPPED in this thread */
1288 * The stop event takes precedence by being first in the list.
1290 wEvents[0] = infoPtr->evStopWriter;
1291 wEvents[1] = infoPtr->evStartWriter;
1295 * Wait for the main thread to signal before attempting to write.
1298 waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1300 if (waitResult != (WAIT_OBJECT_0 + 1)) {
1302 * The start event was not signaled. It might be the stop event
1303 * or an error, so exit.
1309 buf = infoPtr->writeBuf;
1310 toWrite = infoPtr->toWrite;
1312 myWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1315 * Loop until all of the bytes are written or an error occurs.
1318 while (toWrite > 0) {
1320 * Check for pending writeError
1321 * Ignore all write operations until the user has been notified
1323 if (infoPtr->writeError) {
1326 if (blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
1327 &bytesWritten, &myWrite) == FALSE) {
1328 infoPtr->writeError = GetLastError();
1331 if (bytesWritten != toWrite) {
1333 infoPtr->writeError = ERROR_WRITE_FAULT;
1336 toWrite -= bytesWritten;
1337 buf += bytesWritten;
1340 CloseHandle(myWrite.hEvent);
1342 * Signal the main thread by signalling the evWritable event and
1343 * then waking up the notifier thread.
1345 SetEvent(infoPtr->evWritable);
1348 * Alert the foreground thread. Note that we need to treat this like
1349 * a critical section so the foreground thread does not terminate
1350 * this thread while we are holding a mutex in the notifier code.
1353 Tcl_MutexLock(&serialMutex);
1354 if (infoPtr->threadId != NULL) {
1355 /* TIP #218. When in flight ignore the event, no one will receive it anyway */
1356 Tcl_ThreadAlert(infoPtr->threadId);
1358 Tcl_MutexUnlock(&serialMutex);
1366 *----------------------------------------------------------------------
1368 * TclWinSerialReopen --
1370 * Reopens the serial port with the OVERLAPPED FLAG set
1373 * Returns the new handle, or INVALID_HANDLE_VALUE
1374 * Normally there shouldn't be any error,
1375 * because the same channel has previously been succeesfully opened.
1378 * May close the original handle
1380 *----------------------------------------------------------------------
1384 TclWinSerialReopen(handle, name, access)
1389 ThreadSpecificData *tsdPtr;
1391 tsdPtr = SerialInit();
1394 * Multithreaded I/O needs the overlapped flag set
1395 * otherwise ClearCommError blocks under Windows NT/2000 until serial
1396 * output is finished
1398 if (CloseHandle(handle) == FALSE) {
1399 return INVALID_HANDLE_VALUE;
1401 handle = (*tclWinProcs->createFileProc)(name, access,
1402 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
1406 *----------------------------------------------------------------------
1408 * TclWinOpenSerialChannel --
1410 * Constructs a Serial port channel for the specified standard OS handle.
1411 * This is a helper function to break up the construction of
1412 * channels into File, Console, or Serial.
1415 * Returns the new channel, or NULL.
1418 * May open the channel
1420 *----------------------------------------------------------------------
1424 TclWinOpenSerialChannel(handle, channelName, permissions)
1429 SerialInfo *infoPtr;
1434 infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo));
1435 memset(infoPtr, 0, sizeof(SerialInfo));
1437 infoPtr->validMask = permissions;
1438 infoPtr->handle = handle;
1439 infoPtr->channel = (Tcl_Channel) NULL;
1440 infoPtr->readable = 0;
1441 infoPtr->writable = 1;
1442 infoPtr->toWrite = infoPtr->writeQueue = 0;
1443 infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME;
1444 infoPtr->lastEventTime = 0;
1445 infoPtr->lastError = infoPtr->error = 0;
1446 infoPtr->threadId = Tcl_GetCurrentThread();
1447 infoPtr->sysBufRead = 4096;
1448 infoPtr->sysBufWrite = 4096;
1451 * Use the pointer to keep the channel names unique, in case
1452 * the handles are shared between multiple channels (stdin/stdout).
1455 wsprintfA(channelName, "file%lx", (int) infoPtr);
1457 infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName,
1458 (ClientData) infoPtr, permissions);
1461 SetupComm(handle, infoPtr->sysBufRead, infoPtr->sysBufWrite);
1462 PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
1466 * default is blocking
1468 SetCommTimeouts(handle, &no_timeout);
1471 if (permissions & TCL_READABLE) {
1472 infoPtr->osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1474 if (permissions & TCL_WRITABLE) {
1476 * Initially the channel is writable
1477 * and the writeThread is idle.
1479 infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1480 infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL);
1481 infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1482 infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1483 InitializeCriticalSection(&infoPtr->csWrite);
1484 infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
1489 * Files have default translation of AUTO and ^Z eof char, which
1490 * means that a ^Z will be accepted as EOF when reading.
1493 Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1494 Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1496 return infoPtr->channel;
1500 *----------------------------------------------------------------------
1504 * Converts a Win32 serial error code to a list of readable errors
1506 *----------------------------------------------------------------------
1509 SerialErrorStr(error, dsPtr)
1510 DWORD error; /* Win32 serial error code */
1511 Tcl_DString *dsPtr; /* Where to store string */
1513 if( (error & CE_RXOVER) != 0) {
1514 Tcl_DStringAppendElement(dsPtr, "RXOVER");
1516 if( (error & CE_OVERRUN) != 0) {
1517 Tcl_DStringAppendElement(dsPtr, "OVERRUN");
1519 if( (error & CE_RXPARITY) != 0) {
1520 Tcl_DStringAppendElement(dsPtr, "RXPARITY");
1522 if( (error & CE_FRAME) != 0) {
1523 Tcl_DStringAppendElement(dsPtr, "FRAME");
1525 if( (error & CE_BREAK) != 0) {
1526 Tcl_DStringAppendElement(dsPtr, "BREAK");
1528 if( (error & CE_TXFULL) != 0) {
1529 Tcl_DStringAppendElement(dsPtr, "TXFULL");
1531 if( (error & CE_PTO) != 0) { /* PTO used to signal WRITE-TIMEOUT */
1532 Tcl_DStringAppendElement(dsPtr, "TIMEOUT");
1534 if( (error & ~((DWORD) (SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS))) != 0) {
1535 char buf[TCL_INTEGER_SPACE + 1];
1536 wsprintfA(buf, "%d", error);
1537 Tcl_DStringAppendElement(dsPtr, buf);
1541 *----------------------------------------------------------------------
1543 * SerialModemStatusStr --
1545 * Converts a Win32 modem status list of readable flags
1547 *----------------------------------------------------------------------
1550 SerialModemStatusStr(status, dsPtr)
1551 DWORD status; /* Win32 modem status */
1552 Tcl_DString *dsPtr; /* Where to store string */
1554 Tcl_DStringAppendElement(dsPtr, "CTS");
1555 Tcl_DStringAppendElement(dsPtr, (status & MS_CTS_ON) ? "1" : "0");
1556 Tcl_DStringAppendElement(dsPtr, "DSR");
1557 Tcl_DStringAppendElement(dsPtr, (status & MS_DSR_ON) ? "1" : "0");
1558 Tcl_DStringAppendElement(dsPtr, "RING");
1559 Tcl_DStringAppendElement(dsPtr, (status & MS_RING_ON) ? "1" : "0");
1560 Tcl_DStringAppendElement(dsPtr, "DCD");
1561 Tcl_DStringAppendElement(dsPtr, (status & MS_RLSD_ON) ? "1" : "0");
1565 *----------------------------------------------------------------------
1567 * SerialSetOptionProc --
1569 * Sets an option on a channel.
1572 * A standard Tcl result. Also sets the interp's result on error if
1573 * interp is not NULL.
1576 * May modify an option on a device.
1578 *----------------------------------------------------------------------
1581 SerialSetOptionProc(instanceData, interp, optionName, value)
1582 ClientData instanceData; /* File state. */
1583 Tcl_Interp *interp; /* For error reporting - can be NULL. */
1584 CONST char *optionName; /* Which option to set? */
1585 CONST char *value; /* New value for option. */
1587 SerialInfo *infoPtr;
1592 CONST TCHAR *native;
1596 infoPtr = (SerialInfo *) instanceData;
1601 len = strlen(optionName);
1602 vlen = strlen(value);
1605 * Option -mode baud,parity,databits,stopbits
1607 if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
1608 if (! GetCommState(infoPtr->handle, &dcb)) {
1610 Tcl_AppendResult(interp,
1611 "can't get comm state", (char *) NULL);
1615 native = Tcl_WinUtfToTChar(value, -1, &ds);
1616 result = (*tclWinProcs->buildCommDCBProc)(native, &dcb);
1617 Tcl_DStringFree(&ds);
1619 if (result == FALSE) {
1621 Tcl_AppendResult(interp,
1622 "bad value for -mode: should be baud,parity,data,stop",
1628 /* Default settings for serial communications */
1630 dcb.fErrorChar = FALSE;
1632 dcb.fAbortOnError = FALSE;
1634 if (! SetCommState(infoPtr->handle, &dcb) ) {
1636 Tcl_AppendResult(interp,
1637 "can't set comm state", (char *) NULL);
1645 * Option -handshake none|xonxoff|rtscts|dtrdsr
1647 if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
1648 if (! GetCommState(infoPtr->handle, &dcb)) {
1650 Tcl_AppendResult(interp,
1651 "can't get comm state", (char *) NULL);
1656 * Reset all handshake options
1657 * DTR and RTS are ON by default
1659 dcb.fOutX = dcb.fInX = FALSE;
1660 dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fDsrSensitivity = FALSE;
1661 dcb.fDtrControl = DTR_CONTROL_ENABLE;
1662 dcb.fRtsControl = RTS_CONTROL_ENABLE;
1663 dcb.fTXContinueOnXoff = FALSE;
1666 * Adjust the handshake limits.
1667 * Yes, the XonXoff limits seem to influence even hardware handshake
1669 dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
1670 dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
1672 if (strnicmp(value, "NONE", vlen) == 0) {
1673 /* leave all handshake options disabled */
1674 } else if (strnicmp(value, "XONXOFF", vlen) == 0) {
1675 dcb.fOutX = dcb.fInX = TRUE;
1676 } else if (strnicmp(value, "RTSCTS", vlen) == 0) {
1677 dcb.fOutxCtsFlow = TRUE;
1678 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
1679 } else if (strnicmp(value, "DTRDSR", vlen) == 0) {
1680 dcb.fOutxDsrFlow = TRUE;
1681 dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
1684 Tcl_AppendResult(interp, "bad value for -handshake: ",
1685 "must be one of xonxoff, rtscts, dtrdsr or none",
1691 if (! SetCommState(infoPtr->handle, &dcb)) {
1693 Tcl_AppendResult(interp,
1694 "can't set comm state", (char *) NULL);
1702 * Option -xchar {\x11 \x13}
1704 if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
1705 if (! GetCommState(infoPtr->handle, &dcb)) {
1707 Tcl_AppendResult(interp,
1708 "can't get comm state", (char *) NULL);
1713 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1717 dcb.XonChar = argv[0][0];
1718 dcb.XoffChar = argv[1][0];
1719 ckfree((char *) argv);
1722 Tcl_AppendResult(interp,
1723 "bad value for -xchar: should be a list of two elements",
1726 ckfree((char *) argv);
1730 if (! SetCommState(infoPtr->handle, &dcb)) {
1732 Tcl_AppendResult(interp,
1733 "can't set comm state", (char *) NULL);
1741 * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
1743 if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
1744 int i, result = TCL_OK;
1746 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1749 if ((argc % 2) == 1) {
1751 Tcl_AppendResult(interp,
1752 "bad value for -ttycontrol: should be a list of signal,value pairs",
1755 ckfree((char *) argv);
1758 for (i = 0; i < argc - 1; i += 2) {
1759 if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
1763 if (strnicmp(argv[i], "DTR", strlen(argv[i])) == 0) {
1764 if (!EscapeCommFunction(infoPtr->handle, flag ?
1765 (DWORD) SETDTR : (DWORD) CLRDTR)) {
1767 Tcl_AppendResult(interp,
1768 "can't set DTR signal", (char *) NULL);
1773 } else if (strnicmp(argv[i], "RTS", strlen(argv[i])) == 0) {
1774 if (!EscapeCommFunction(infoPtr->handle, flag ?
1775 (DWORD) SETRTS : (DWORD) CLRRTS)) {
1777 Tcl_AppendResult(interp,
1778 "can't set RTS signal", (char *) NULL);
1783 } else if (strnicmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
1784 if (!EscapeCommFunction(infoPtr->handle, flag ?
1785 (DWORD) SETBREAK : (DWORD) CLRBREAK)) {
1787 Tcl_AppendResult(interp,
1788 "can't set BREAK signal", (char *) NULL);
1795 Tcl_AppendResult(interp, "bad signal for -ttycontrol: ",
1796 "must be DTR, RTS or BREAK", (char *) NULL);
1803 ckfree((char *) argv);
1808 * Option -sysbuffer {read_size write_size}
1809 * Option -sysbuffer read_size
1811 if ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0)) {
1813 * -sysbuffer 4096 or -sysbuffer {64536 4096}
1815 size_t inSize = (size_t) -1, outSize = (size_t) -1;
1817 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1821 inSize = atoi(argv[0]);
1822 outSize = infoPtr->sysBufWrite;
1823 } else if (argc == 2) {
1824 inSize = atoi(argv[0]);
1825 outSize = atoi(argv[1]);
1827 ckfree((char *) argv);
1828 if ((inSize <= 0) || (outSize <= 0)) {
1830 Tcl_AppendResult(interp,
1831 "bad value for -sysbuffer: should be a list of one or two integers > 0",
1836 if (! SetupComm(infoPtr->handle, inSize, outSize)) {
1838 Tcl_AppendResult(interp,
1839 "can't setup comm buffers", (char *) NULL);
1843 infoPtr->sysBufRead = inSize;
1844 infoPtr->sysBufWrite = outSize;
1847 * Adjust the handshake limits.
1848 * Yes, the XonXoff limits seem to influence even hardware handshake
1850 if (! GetCommState(infoPtr->handle, &dcb)) {
1852 Tcl_AppendResult(interp,
1853 "can't get comm state", (char *) NULL);
1857 dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
1858 dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
1859 if (! SetCommState(infoPtr->handle, &dcb)) {
1861 Tcl_AppendResult(interp,
1862 "can't set comm state", (char *) NULL);
1870 * Option -pollinterval msec
1872 if ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0)) {
1874 if ( Tcl_GetInt(interp, value, &(infoPtr->blockTime)) != TCL_OK ) {
1881 * Option -timeout msec
1883 if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
1885 COMMTIMEOUTS tout = {0,0,0,0,0};
1887 if ( Tcl_GetInt(interp, value, &msec) != TCL_OK ) {
1890 tout.ReadTotalTimeoutConstant = msec;
1891 if (! SetCommTimeouts(infoPtr->handle, &tout)) {
1893 Tcl_AppendResult(interp,
1894 "can't set comm timeouts", (char *) NULL);
1902 return Tcl_BadChannelOption(interp, optionName,
1903 "mode handshake pollinterval sysbuffer timeout ttycontrol xchar");
1907 *----------------------------------------------------------------------
1909 * SerialGetOptionProc --
1911 * Gets a mode associated with an IO channel. If the optionName arg
1912 * is non NULL, retrieves the value of that option. If the optionName
1913 * arg is NULL, retrieves a list of alternating option names and
1914 * values for the given channel.
1917 * A standard Tcl result. Also sets the supplied DString to the
1918 * string value of the option(s) returned.
1921 * The string returned by this function is in static storage and
1922 * may be reused at any time subsequent to the call.
1924 *----------------------------------------------------------------------
1927 SerialGetOptionProc(instanceData, interp, optionName, dsPtr)
1928 ClientData instanceData; /* File state. */
1929 Tcl_Interp *interp; /* For error reporting - can be NULL. */
1930 CONST char *optionName; /* Option to get. */
1931 Tcl_DString *dsPtr; /* Where to store value(s). */
1933 SerialInfo *infoPtr;
1936 int valid = 0; /* flag if valid option parsed */
1938 infoPtr = (SerialInfo *) instanceData;
1940 if (optionName == NULL) {
1943 len = strlen(optionName);
1951 Tcl_DStringAppendElement(dsPtr, "-mode");
1954 ((len > 2) && (strncmp(optionName, "-mode", len) == 0))) {
1958 char buf[2 * TCL_INTEGER_SPACE + 16];
1960 if (! GetCommState(infoPtr->handle, &dcb)) {
1962 Tcl_AppendResult(interp,
1963 "can't get comm state", (char *) NULL);
1970 if (dcb.Parity <= 4) {
1971 parity = "noems"[dcb.Parity];
1973 stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
1974 (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
1976 wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity,
1977 dcb.ByteSize, stop);
1978 Tcl_DStringAppendElement(dsPtr, buf);
1982 * get option -pollinterval
1986 Tcl_DStringAppendElement(dsPtr, "-pollinterval");
1989 ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0))) {
1990 char buf[TCL_INTEGER_SPACE + 1];
1993 wsprintfA(buf, "%d", infoPtr->blockTime);
1994 Tcl_DStringAppendElement(dsPtr, buf);
1998 * get option -sysbuffer
2002 Tcl_DStringAppendElement(dsPtr, "-sysbuffer");
2003 Tcl_DStringStartSublist(dsPtr);
2006 ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0))) {
2008 char buf[TCL_INTEGER_SPACE + 1];
2011 wsprintfA(buf, "%d", infoPtr->sysBufRead);
2012 Tcl_DStringAppendElement(dsPtr, buf);
2013 wsprintfA(buf, "%d", infoPtr->sysBufWrite);
2014 Tcl_DStringAppendElement(dsPtr, buf);
2017 Tcl_DStringEndSublist(dsPtr);
2025 Tcl_DStringAppendElement(dsPtr, "-xchar");
2026 Tcl_DStringStartSublist(dsPtr);
2029 ((len > 1) && (strncmp(optionName, "-xchar", len) == 0))) {
2034 if (! GetCommState(infoPtr->handle, &dcb)) {
2036 Tcl_AppendResult(interp,
2037 "can't get comm state", (char *) NULL);
2041 sprintf(buf, "%c", dcb.XonChar);
2042 Tcl_DStringAppendElement(dsPtr, buf);
2043 sprintf(buf, "%c", dcb.XoffChar);
2044 Tcl_DStringAppendElement(dsPtr, buf);
2047 Tcl_DStringEndSublist(dsPtr);
2051 * get option -lasterror
2052 * option is readonly and returned by [fconfigure chan -lasterror]
2053 * but not returned by unnamed [fconfigure chan]
2056 if ( (len > 1) && (strncmp(optionName, "-lasterror", len) == 0) ) {
2058 SerialErrorStr(infoPtr->lastError, dsPtr);
2063 * option is readonly and returned by [fconfigure chan -queue]
2066 if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
2067 char buf[TCL_INTEGER_SPACE + 1];
2070 int inBuffered, outBuffered, count;
2075 * Query the pending data in Tcl's internal queues
2077 inBuffered = Tcl_InputBuffered(infoPtr->channel);
2078 outBuffered = Tcl_OutputBuffered(infoPtr->channel);
2081 * Query the number of bytes in our output queue:
2082 * 1. The bytes pending in the output thread
2083 * 2. The bytes in the system drivers buffer
2084 * The writer thread should not interfere this action.
2086 EnterCriticalSection(&infoPtr->csWrite);
2087 ClearCommError( infoPtr->handle, &error, &cStat );
2088 count = (int)cStat.cbOutQue + infoPtr->writeQueue;
2089 LeaveCriticalSection(&infoPtr->csWrite);
2091 wsprintfA(buf, "%d", inBuffered + cStat.cbInQue);
2092 Tcl_DStringAppendElement(dsPtr, buf);
2093 wsprintfA(buf, "%d", outBuffered + count);
2094 Tcl_DStringAppendElement(dsPtr, buf);
2098 * get option -ttystatus
2099 * option is readonly and returned by [fconfigure chan -ttystatus]
2100 * but not returned by unnamed [fconfigure chan]
2102 if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
2106 if (! GetCommModemStatus(infoPtr->handle, &status)) {
2108 Tcl_AppendResult(interp,
2109 "can't get tty status", (char *) NULL);
2114 SerialModemStatusStr(status, dsPtr);
2120 return Tcl_BadChannelOption(interp, optionName,
2121 "mode pollinterval lasterror queue sysbuffer ttystatus xchar");
2126 *----------------------------------------------------------------------
2128 * SerialThreadActionProc --
2130 * Insert or remove any thread local refs to this channel.
2136 * Changes thread local list of valid channels.
2138 *----------------------------------------------------------------------
2142 SerialThreadActionProc (instanceData, action)
2143 ClientData instanceData;
2146 SerialInfo *infoPtr = (SerialInfo *) instanceData;
2148 /* We do not access firstSerialPtr in the thread structures. This is
2149 * not for all serials managed by the thread, but only those we are
2150 * watching. Removal of the filevent handlers before transfer thus
2151 * takes care of this structure.
2154 Tcl_MutexLock(&serialMutex);
2155 if (action == TCL_CHANNEL_THREAD_INSERT) {
2156 /* We can't copy the thread information from the channel when
2157 * the channel is created. At this time the channel back
2158 * pointer has not been set yet. However in that case the
2159 * threadId has already been set by TclpCreateCommandChannel
2160 * itself, so the structure is still good.
2164 if (infoPtr->channel != NULL) {
2165 infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
2168 infoPtr->threadId = NULL;
2170 Tcl_MutexUnlock(&serialMutex);