4 * This file implements the Windows-specific serial port functions, and
5 * the "serial" channel driver.
7 * Copyright (c) 1999 by Scriptics Corp.
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 * Serial functionality implemented by Rolf.Schroedter@dlr.de
15 #include "tclWinInt.h"
18 * The following variable is used to tell whether this module has been
22 static int initialized = 0;
25 * The serialMutex locks around access to the initialized variable, and it is
26 * used to protect background threads from being terminated while they are
27 * using APIs that hold locks.
30 TCL_DECLARE_MUTEX(serialMutex)
33 * Bit masks used in the flags field of the SerialInfo structure below.
36 #define SERIAL_PENDING (1<<0) /* Message is pending in the queue. */
37 #define SERIAL_ASYNC (1<<1) /* Channel is non-blocking. */
40 * Bit masks used in the sharedFlags field of the SerialInfo structure below.
43 #define SERIAL_EOF (1<<2) /* Serial has reached EOF. */
44 #define SERIAL_ERROR (1<<4)
47 * Default time to block between checking status on the serial port.
50 #define SERIAL_DEFAULT_BLOCKTIME 10 /* 10 msec */
53 * Define Win32 read/write error masks returned by ClearCommError()
56 #define SERIAL_READ_ERRORS \
57 (CE_RXOVER | CE_OVERRUN | CE_RXPARITY | CE_FRAME | CE_BREAK)
58 #define SERIAL_WRITE_ERRORS \
62 * This structure describes per-instance data for a serial based channel.
65 typedef struct SerialInfo {
67 struct SerialInfo *nextPtr; /* Pointer to next registered serial. */
68 Tcl_Channel channel; /* Pointer to channel structure. */
69 int validMask; /* OR'ed combination of TCL_READABLE,
70 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
71 * which operations are valid on the file. */
72 int watchMask; /* OR'ed combination of TCL_READABLE,
73 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
74 * which events should be reported. */
75 int flags; /* State flags, see above for a list. */
76 int readable; /* Flag that the channel is readable. */
77 int writable; /* Flag that the channel is writable. */
78 int blockTime; /* Maximum blocktime in msec. */
79 unsigned int lastEventTime; /* Time in milliseconds since last readable
81 /* Next readable event only after blockTime */
82 DWORD error; /* pending error code returned by
84 DWORD lastError; /* last error code, can be fetched with
85 * fconfigure chan -lasterror */
86 DWORD sysBufRead; /* Win32 system buffer size for read ops,
88 DWORD sysBufWrite; /* Win32 system buffer size for write ops,
91 Tcl_ThreadId threadId; /* Thread to which events should be reported.
92 * This value is used by the reader/writer
94 OVERLAPPED osRead; /* OVERLAPPED structure for read operations. */
95 OVERLAPPED osWrite; /* OVERLAPPED structure for write operations */
96 TclPipeThreadInfo *writeTI; /* Thread info structure of writer worker. */
97 HANDLE writeThread; /* Handle to writer thread. */
98 CRITICAL_SECTION csWrite; /* Writer thread synchronisation. */
99 HANDLE evWritable; /* Manual-reset event to signal when the
100 * writer thread has finished waiting for the
101 * current buffer to be written. */
102 DWORD writeError; /* An error caused by the last background
103 * write. Set to 0 if no error has been
104 * detected. This word is shared with the
105 * writer thread so access must be
106 * synchronized with the evWritable object. */
107 char *writeBuf; /* Current background output buffer. Access is
108 * synchronized with the evWritable object. */
109 int writeBufLen; /* Size of write buffer. Access is
110 * synchronized with the evWritable object. */
111 int toWrite; /* Current amount to be written. Access is
112 * synchronized with the evWritable object. */
113 int writeQueue; /* Number of bytes pending in output queue.
114 * Offset to DCB.cbInQue. Used to query
115 * [fconfigure -queue] */
118 typedef struct ThreadSpecificData {
120 * The following pointer refers to the head of the list of serials that
121 * are being watched for file events.
124 SerialInfo *firstSerialPtr;
125 } ThreadSpecificData;
127 static Tcl_ThreadDataKey dataKey;
130 * The following structure is what is added to the Tcl event queue when serial
131 * events are generated.
134 typedef struct SerialEvent {
135 Tcl_Event header; /* Information that is standard for all
137 SerialInfo *infoPtr; /* Pointer to serial info structure. Note that
138 * we still have to verify that the serial
139 * exists before dereferencing this
144 * We don't use timeouts.
147 static COMMTIMEOUTS no_timeout = {
148 0, /* ReadIntervalTimeout */
149 0, /* ReadTotalTimeoutMultiplier */
150 0, /* ReadTotalTimeoutConstant */
151 0, /* WriteTotalTimeoutMultiplier */
152 0, /* WriteTotalTimeoutConstant */
156 * Declarations for functions used only in this file.
159 static int SerialBlockProc(ClientData instanceData, int mode);
160 static void SerialCheckProc(ClientData clientData, int flags);
161 static int SerialCloseProc(ClientData instanceData,
163 static int SerialClose2Proc(ClientData instanceData,
164 Tcl_Interp *interp, int flags);
165 static int SerialEventProc(Tcl_Event *evPtr, int flags);
166 static void SerialExitHandler(ClientData clientData);
167 static int SerialGetHandleProc(ClientData instanceData,
168 int direction, ClientData *handlePtr);
169 static ThreadSpecificData *SerialInit(void);
170 static int SerialInputProc(ClientData instanceData, char *buf,
171 int toRead, int *errorCode);
172 static int SerialOutputProc(ClientData instanceData,
173 const char *buf, int toWrite, int *errorCode);
174 static void SerialSetupProc(ClientData clientData, int flags);
175 static void SerialWatchProc(ClientData instanceData, int mask);
176 static void ProcExitHandler(ClientData clientData);
177 static int SerialGetOptionProc(ClientData instanceData,
178 Tcl_Interp *interp, const char *optionName,
180 static int SerialSetOptionProc(ClientData instanceData,
181 Tcl_Interp *interp, const char *optionName,
183 static DWORD WINAPI SerialWriterThread(LPVOID arg);
184 static void SerialThreadActionProc(ClientData instanceData,
186 static int SerialBlockingRead(SerialInfo *infoPtr, LPVOID buf,
187 DWORD bufSize, LPDWORD lpRead, LPOVERLAPPED osPtr);
188 static int SerialBlockingWrite(SerialInfo *infoPtr, LPVOID buf,
189 DWORD bufSize, LPDWORD lpWritten,
193 * This structure describes the channel type structure for command serial
197 static const Tcl_ChannelType serialChannelType = {
198 "serial", /* Type name. */
199 TCL_CHANNEL_VERSION_5, /* v5 channel */
200 SerialCloseProc, /* Close proc. */
201 SerialInputProc, /* Input proc. */
202 SerialOutputProc, /* Output proc. */
203 NULL, /* Seek proc. */
204 SerialSetOptionProc, /* Set option proc. */
205 SerialGetOptionProc, /* Get option proc. */
206 SerialWatchProc, /* Set up notifier to watch the channel. */
207 SerialGetHandleProc, /* Get an OS handle from channel. */
208 SerialClose2Proc, /* close2proc. */
209 SerialBlockProc, /* Set blocking or non-blocking mode.*/
210 NULL, /* flush proc. */
211 NULL, /* handler proc. */
212 NULL, /* wide seek proc */
213 SerialThreadActionProc, /* thread action proc */
218 *----------------------------------------------------------------------
222 * This function initializes the static variables for this file.
228 * Creates a new event source.
230 *----------------------------------------------------------------------
233 static ThreadSpecificData *
236 ThreadSpecificData *tsdPtr;
239 * Check the initialized flag first, then check it again in the mutex.
240 * This is a speed enhancement.
244 Tcl_MutexLock(&serialMutex);
247 Tcl_CreateExitHandler(ProcExitHandler, NULL);
249 Tcl_MutexUnlock(&serialMutex);
252 tsdPtr = (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
253 if (tsdPtr == NULL) {
254 tsdPtr = TCL_TSD_INIT(&dataKey);
255 tsdPtr->firstSerialPtr = NULL;
256 Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
257 Tcl_CreateThreadExitHandler(SerialExitHandler, NULL);
263 *----------------------------------------------------------------------
265 * SerialExitHandler --
267 * This function is called to cleanup the serial module before Tcl is
274 * Removes the serial event source.
276 *----------------------------------------------------------------------
281 ClientData clientData) /* Old window proc */
283 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
287 * Clear all eventually pending output. Otherwise Tcl's exit could totally
288 * block, because it performs a blocking flush on all open channels. Note
289 * that serial write operations may be blocked due to handshake.
292 for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
293 infoPtr = infoPtr->nextPtr) {
294 PurgeComm(infoPtr->handle,
295 PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
297 Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);
301 *----------------------------------------------------------------------
305 * This function is called to cleanup the process list before Tcl is
312 * Resets the process list.
314 *----------------------------------------------------------------------
319 ClientData clientData) /* Old window proc */
321 Tcl_MutexLock(&serialMutex);
323 Tcl_MutexUnlock(&serialMutex);
327 *----------------------------------------------------------------------
331 * Wrapper to set Tcl's block time in msec
337 * Updates the maximum blocking time.
339 *----------------------------------------------------------------------
344 int msec) /* milli-seconds */
348 blockTime.sec = msec / 1000;
349 blockTime.usec = (msec % 1000) * 1000;
350 Tcl_SetMaxBlockTime(&blockTime);
354 *----------------------------------------------------------------------
356 * SerialGetMilliseconds --
358 * Get current time in milliseconds,ignoring integer overruns.
366 *----------------------------------------------------------------------
370 SerialGetMilliseconds(void)
376 return (time.sec * 1000 + time.usec / 1000);
380 *----------------------------------------------------------------------
384 * This procedure is invoked before Tcl_DoOneEvent blocks waiting for an
391 * Adjusts the block time if needed.
393 *----------------------------------------------------------------------
398 ClientData data, /* Not used. */
399 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
403 int msec = INT_MAX; /* min. found block time */
404 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
406 if (!(flags & TCL_FILE_EVENTS)) {
411 * Look to see if any events handlers installed. If they are, do not
415 for (infoPtr=tsdPtr->firstSerialPtr ; infoPtr!=NULL ;
416 infoPtr=infoPtr->nextPtr) {
417 if (infoPtr->watchMask & TCL_WRITABLE) {
418 if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
420 msec = min(msec, infoPtr->blockTime);
423 if (infoPtr->watchMask & TCL_READABLE) {
425 msec = min(msec, infoPtr->blockTime);
430 SerialBlockTime(msec);
435 *----------------------------------------------------------------------
439 * This procedure is called by Tcl_DoOneEvent to check the serial event
446 * May queue an event.
448 *----------------------------------------------------------------------
453 ClientData data, /* Not used. */
454 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
459 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
463 if (!(flags & TCL_FILE_EVENTS)) {
468 * Queue events for any ready serials that don't already have events
472 for (infoPtr=tsdPtr->firstSerialPtr ; infoPtr!=NULL ;
473 infoPtr=infoPtr->nextPtr) {
474 if (infoPtr->flags & SERIAL_PENDING) {
481 * If WRITABLE watch mask is set look for infoPtr->evWritable object.
484 if (infoPtr->watchMask & TCL_WRITABLE &&
485 WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
486 infoPtr->writable = 1;
491 * If READABLE watch mask is set call ClearCommError to poll cbInQue.
492 * Window errors are ignored here.
495 if (infoPtr->watchMask & TCL_READABLE) {
496 if (ClearCommError(infoPtr->handle, &infoPtr->error, &cStat)) {
498 * Look for characters already pending in windows queue. If
502 if (infoPtr->watchMask & TCL_READABLE) {
504 * Force fileevent after serial read error.
507 if ((cStat.cbInQue > 0) ||
508 (infoPtr->error & SERIAL_READ_ERRORS)) {
509 infoPtr->readable = 1;
510 time = SerialGetMilliseconds();
511 if ((unsigned int) (time - infoPtr->lastEventTime)
512 >= (unsigned int) infoPtr->blockTime) {
514 infoPtr->lastEventTime = time;
522 * Queue an event if the serial is signaled for reading or writing.
526 infoPtr->flags |= SERIAL_PENDING;
527 evPtr = ckalloc(sizeof(SerialEvent));
528 evPtr->header.proc = SerialEventProc;
529 evPtr->infoPtr = infoPtr;
530 Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
536 *----------------------------------------------------------------------
540 * Set blocking or non-blocking mode on channel.
543 * 0 if successful, errno when failed.
546 * Sets the device into blocking or non-blocking mode.
548 *----------------------------------------------------------------------
553 ClientData instanceData, /* Instance data for channel. */
554 int mode) /* TCL_MODE_BLOCKING or
555 * TCL_MODE_NONBLOCKING. */
558 SerialInfo *infoPtr = (SerialInfo *) instanceData;
561 * Only serial READ can be switched between blocking & nonblocking using
562 * COMMTIMEOUTS. Serial write emulates blocking & nonblocking by the
563 * SerialWriterThread.
566 if (mode == TCL_MODE_NONBLOCKING) {
567 infoPtr->flags |= SERIAL_ASYNC;
569 infoPtr->flags &= ~(SERIAL_ASYNC);
575 *----------------------------------------------------------------------
577 * SerialCloseProc/SerialClose2Proc --
579 * Closes a serial based IO channel.
582 * 0 on success, errno otherwise.
585 * Closes the physical channel.
587 *----------------------------------------------------------------------
592 ClientData instanceData, /* Pointer to SerialInfo structure. */
593 Tcl_Interp *interp) /* For error reporting. */
595 SerialInfo *serialPtr = (SerialInfo *) instanceData;
596 int errorCode, result = 0;
597 SerialInfo *infoPtr, **nextPtrPtr;
598 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
602 if (serialPtr->validMask & TCL_READABLE) {
603 PurgeComm(serialPtr->handle, PURGE_RXABORT | PURGE_RXCLEAR);
604 CloseHandle(serialPtr->osRead.hEvent);
606 serialPtr->validMask &= ~TCL_READABLE;
608 if (serialPtr->writeThread) {
610 TclPipeThreadStop(&serialPtr->writeTI, serialPtr->writeThread);
612 CloseHandle(serialPtr->osWrite.hEvent);
613 CloseHandle(serialPtr->evWritable);
614 CloseHandle(serialPtr->writeThread);
615 serialPtr->writeThread = NULL;
617 PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR);
619 serialPtr->validMask &= ~TCL_WRITABLE;
621 DeleteCriticalSection(&serialPtr->csWrite);
624 * Don't close the Win32 handle if the handle is a standard channel during
625 * the thread exit process. Otherwise, one thread may kill the stdio of
629 if (!TclInThreadExit()
630 || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
631 && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
632 && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
633 if (CloseHandle(serialPtr->handle) == FALSE) {
634 TclWinConvertError(GetLastError());
639 serialPtr->watchMask &= serialPtr->validMask;
642 * Remove the file from the list of watched files.
645 for (nextPtrPtr=&(tsdPtr->firstSerialPtr), infoPtr=*nextPtrPtr;
647 nextPtrPtr=&infoPtr->nextPtr, infoPtr=*nextPtrPtr) {
648 if (infoPtr == (SerialInfo *)serialPtr) {
649 *nextPtrPtr = infoPtr->nextPtr;
655 * Wrap the error file into a channel and give it to the cleanup routine.
658 if (serialPtr->writeBuf != NULL) {
659 ckfree(serialPtr->writeBuf);
660 serialPtr->writeBuf = NULL;
664 if (errorCode == 0) {
672 ClientData instanceData, /* Pointer to SerialInfo structure. */
673 Tcl_Interp *interp, /* For error reporting. */
676 if ((flags & (TCL_CLOSE_READ | TCL_CLOSE_WRITE)) == 0) {
677 return SerialCloseProc(instanceData, interp);
683 *----------------------------------------------------------------------
685 * SerialBlockingRead --
687 * Perform a blocking read into the buffer given. Returns count of how
688 * many bytes were actually read, and an error indication.
691 * A count of how many bytes were read is returned and an error
692 * indication is returned.
695 * Reads input from the actual channel.
697 *----------------------------------------------------------------------
702 SerialInfo *infoPtr, /* Serial info structure */
703 LPVOID buf, /* The input buffer pointer */
704 DWORD bufSize, /* The number of bytes to read */
705 LPDWORD lpRead, /* Returns number of bytes read */
706 LPOVERLAPPED osPtr) /* OVERLAPPED structure */
709 * Perform overlapped blocking read.
710 * 1. Reset the overlapped event
711 * 2. Start overlapped read operation
712 * 3. Wait for completion
716 * Set Offset to ZERO, otherwise NT4.0 may report an error.
719 osPtr->Offset = osPtr->OffsetHigh = 0;
720 ResetEvent(osPtr->hEvent);
721 if (!ReadFile(infoPtr->handle, buf, bufSize, lpRead, osPtr)) {
722 if (GetLastError() != ERROR_IO_PENDING) {
724 * ReadFile failed, but it isn't delayed. Report error.
730 * Read is pending, wait for completion, timeout?
733 if (!GetOverlappedResult(infoPtr->handle, osPtr, lpRead, TRUE)) {
739 * ReadFile completed immediately.
746 *----------------------------------------------------------------------
748 * SerialBlockingWrite --
750 * Perform a blocking write from the buffer given. Returns count of how
751 * many bytes were actually written, and an error indication.
754 * A count of how many bytes were written is returned and an error
755 * indication is returned.
758 * Writes output to the actual channel.
760 *----------------------------------------------------------------------
765 SerialInfo *infoPtr, /* Serial info structure */
766 LPVOID buf, /* The output buffer pointer */
767 DWORD bufSize, /* The number of bytes to write */
768 LPDWORD lpWritten, /* Returns number of bytes written */
769 LPOVERLAPPED osPtr) /* OVERLAPPED structure */
774 * Perform overlapped blocking write.
775 * 1. Reset the overlapped event
776 * 2. Remove these bytes from the output queue counter
777 * 3. Start overlapped write operation
778 * 3. Remove these bytes from the output queue counter
779 * 4. Wait for completion
780 * 5. Adjust the output queue counter
783 ResetEvent(osPtr->hEvent);
785 EnterCriticalSection(&infoPtr->csWrite);
786 infoPtr->writeQueue -= bufSize;
789 * Set Offset to ZERO, otherwise NT4.0 may report an error
792 osPtr->Offset = osPtr->OffsetHigh = 0;
793 result = WriteFile(infoPtr->handle, buf, bufSize, lpWritten, osPtr);
794 LeaveCriticalSection(&infoPtr->csWrite);
796 if (result == FALSE) {
797 int err = GetLastError();
800 case ERROR_IO_PENDING:
802 * Write is pending, wait for completion.
805 if (!GetOverlappedResult(infoPtr->handle, osPtr, lpWritten,
810 case ERROR_COUNTER_TIMEOUT:
812 * Write timeout handled in SerialOutputProc.
818 * WriteFile failed, but it isn't delayed. Report error.
825 * WriteFile completed immediately.
829 EnterCriticalSection(&infoPtr->csWrite);
830 infoPtr->writeQueue += (*lpWritten - bufSize);
831 LeaveCriticalSection(&infoPtr->csWrite);
837 *----------------------------------------------------------------------
841 * Reads input from the IO channel into the buffer given. Returns count
842 * of how many bytes were actually read, and an error indication.
845 * A count of how many bytes were read is returned and an error
846 * indication is returned in an output argument.
849 * Reads input from the actual channel.
851 *----------------------------------------------------------------------
856 ClientData instanceData, /* Serial state. */
857 char *buf, /* Where to store data read. */
858 int bufSize, /* How much space is available in the
860 int *errorCode) /* Where to store error code. */
862 SerialInfo *infoPtr = (SerialInfo *) instanceData;
869 * Check if there is a CommError pending from SerialCheckProc
872 if (infoPtr->error & SERIAL_READ_ERRORS) {
877 * Look for characters already pending in windows queue. This is the
878 * mainly restored good old code from Tcl8.0
881 if (ClearCommError(infoPtr->handle, &infoPtr->error, &cStat)) {
883 * Check for errors here, but not in the evSetup/Check procedures.
886 if (infoPtr->error & SERIAL_READ_ERRORS) {
889 if (infoPtr->flags & SERIAL_ASYNC) {
891 * NON_BLOCKING mode: Avoid blocking by reading more bytes than
892 * available in input buffer.
895 if (cStat.cbInQue > 0) {
896 if ((DWORD) bufSize > cStat.cbInQue) {
897 bufSize = cStat.cbInQue;
900 errno = *errorCode = EWOULDBLOCK;
905 * BLOCKING mode: Tcl trys to read a full buffer of 4 kBytes here.
908 if (cStat.cbInQue > 0) {
909 if ((DWORD) bufSize > cStat.cbInQue) {
910 bufSize = cStat.cbInQue;
919 return bytesRead = 0;
923 * Perform blocking read. Doesn't block in non-blocking mode, because we
924 * checked the number of available bytes.
927 if (SerialBlockingRead(infoPtr, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
928 &infoPtr->osRead) == FALSE) {
929 TclWinConvertError(GetLastError());
936 infoPtr->lastError = infoPtr->error;
937 /* save last error code */
938 infoPtr->error = 0; /* reset error code */
939 *errorCode = EIO; /* to return read-error only once */
944 *----------------------------------------------------------------------
946 * SerialOutputProc --
948 * Writes the given output on the IO channel. Returns count of how many
949 * characters were actually written, and an error indication.
952 * A count of how many characters were written is returned and an error
953 * indication is returned in an output argument.
956 * Writes output on the actual channel.
958 *----------------------------------------------------------------------
963 ClientData instanceData, /* Serial state. */
964 const char *buf, /* The data buffer. */
965 int toWrite, /* How many bytes to write? */
966 int *errorCode) /* Where to store error code. */
968 SerialInfo *infoPtr = (SerialInfo *) instanceData;
969 DWORD bytesWritten, timeout;
974 * At EXIT Tcl trys to flush all open channels in blocking mode. We avoid
975 * blocking output after ExitProc or CloseHandler(chan) has been called by
976 * checking the corrresponding variables.
979 if (!initialized || TclInExit()) {
984 * Check if there is a CommError pending from SerialCheckProc
987 if (infoPtr->error & SERIAL_WRITE_ERRORS) {
988 infoPtr->lastError = infoPtr->error;
989 /* save last error code */
990 infoPtr->error = 0; /* reset error code */
995 timeout = (infoPtr->flags & SERIAL_ASYNC) ? 0 : INFINITE;
996 if (WaitForSingleObject(infoPtr->evWritable, timeout) == WAIT_TIMEOUT) {
998 * The writer thread is blocked waiting for a write to complete and
999 * the channel is in non-blocking mode.
1002 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
1020 EnterCriticalSection(&infoPtr->csWrite);
1021 infoPtr->writeQueue += toWrite;
1022 LeaveCriticalSection(&infoPtr->csWrite);
1024 if (infoPtr->flags & SERIAL_ASYNC) {
1026 * The serial is non-blocking, so copy the data into the output buffer
1027 * and restart the writer thread.
1030 if (toWrite > infoPtr->writeBufLen) {
1032 * Reallocate the buffer to be large enough to hold the data.
1035 if (infoPtr->writeBuf) {
1036 ckfree(infoPtr->writeBuf);
1038 infoPtr->writeBufLen = toWrite;
1039 infoPtr->writeBuf = ckalloc(toWrite);
1041 memcpy(infoPtr->writeBuf, buf, toWrite);
1042 infoPtr->toWrite = toWrite;
1043 ResetEvent(infoPtr->evWritable);
1044 TclPipeThreadSignal(&infoPtr->writeTI);
1045 bytesWritten = (DWORD) toWrite;
1049 * In the blocking case, just try to write the buffer directly. This
1050 * avoids an unnecessary copy.
1053 if (!SerialBlockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
1054 &bytesWritten, &infoPtr->osWrite)) {
1057 if (bytesWritten != (DWORD) toWrite) {
1061 infoPtr->lastError |= CE_PTO;
1067 return (int) bytesWritten;
1070 TclWinConvertError(GetLastError());
1074 * Reset the output queue counter on error during blocking output
1078 * EnterCriticalSection(&infoPtr->csWrite);
1079 * infoPtr->writeQueue = 0;
1080 * LeaveCriticalSection(&infoPtr->csWrite);
1088 *----------------------------------------------------------------------
1090 * SerialEventProc --
1092 * This function is invoked by Tcl_ServiceEvent when a file event reaches
1093 * the front of the event queue. This procedure invokes Tcl_NotifyChannel
1097 * Returns 1 if the event was handled, meaning it should be removed from
1098 * the queue. Returns 0 if the event was not handled, meaning it should
1099 * stay on the queue. The only time the event isn't handled is if the
1100 * TCL_FILE_EVENTS flag bit isn't set.
1103 * Whatever the notifier callback does.
1105 *----------------------------------------------------------------------
1110 Tcl_Event *evPtr, /* Event to service. */
1111 int flags) /* Flags that indicate what events to handle,
1112 * such as TCL_FILE_EVENTS. */
1114 SerialEvent *serialEvPtr = (SerialEvent *)evPtr;
1115 SerialInfo *infoPtr;
1117 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1119 if (!(flags & TCL_FILE_EVENTS)) {
1124 * Search through the list of watched serials for the one whose handle
1125 * matches the event. We do this rather than simply dereferencing the
1126 * handle in the event so that serials can be deleted while the event is
1130 for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
1131 infoPtr = infoPtr->nextPtr) {
1132 if (serialEvPtr->infoPtr == infoPtr) {
1133 infoPtr->flags &= ~(SERIAL_PENDING);
1139 * Remove stale events.
1147 * Check to see if the serial is readable. Note that we can't tell if a
1148 * serial is writable, so we always report it as being writable unless we
1149 * have detected EOF.
1153 if (infoPtr->watchMask & TCL_WRITABLE) {
1154 if (infoPtr->writable) {
1155 mask |= TCL_WRITABLE;
1156 infoPtr->writable = 0;
1160 if (infoPtr->watchMask & TCL_READABLE) {
1161 if (infoPtr->readable) {
1162 mask |= TCL_READABLE;
1163 infoPtr->readable = 0;
1168 * Inform the channel of the events.
1171 Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
1176 *----------------------------------------------------------------------
1178 * SerialWatchProc --
1180 * Called by the notifier to set up to watch for events on this channel.
1188 *----------------------------------------------------------------------
1193 ClientData instanceData, /* Serial state. */
1194 int mask) /* What events to watch for, OR-ed combination
1195 * of TCL_READABLE, TCL_WRITABLE and
1198 SerialInfo **nextPtrPtr, *ptr;
1199 SerialInfo *infoPtr = (SerialInfo *) instanceData;
1200 int oldMask = infoPtr->watchMask;
1201 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1204 * Since the file is always ready for events, we set the block time so we
1208 infoPtr->watchMask = mask & infoPtr->validMask;
1209 if (infoPtr->watchMask) {
1211 infoPtr->nextPtr = tsdPtr->firstSerialPtr;
1212 tsdPtr->firstSerialPtr = infoPtr;
1214 SerialBlockTime(infoPtr->blockTime);
1215 } else if (oldMask) {
1217 * Remove the serial port from the list of watched serial ports.
1220 for (nextPtrPtr=&(tsdPtr->firstSerialPtr), ptr=*nextPtrPtr; ptr!=NULL;
1221 nextPtrPtr=&ptr->nextPtr, ptr=*nextPtrPtr) {
1222 if (infoPtr == ptr) {
1223 *nextPtrPtr = ptr->nextPtr;
1231 *----------------------------------------------------------------------
1233 * SerialGetHandleProc --
1235 * Called from Tcl_GetChannelHandle to retrieve OS handles from inside a
1236 * command serial port based channel.
1239 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
1240 * handle for the specified direction.
1245 *----------------------------------------------------------------------
1249 SerialGetHandleProc(
1250 ClientData instanceData, /* The serial state. */
1251 int direction, /* TCL_READABLE or TCL_WRITABLE */
1252 ClientData *handlePtr) /* Where to store the handle. */
1254 SerialInfo *infoPtr = (SerialInfo *) instanceData;
1256 *handlePtr = (ClientData) infoPtr->handle;
1261 *----------------------------------------------------------------------
1263 * SerialWriterThread --
1265 * This function runs in a separate thread and writes data onto a serial.
1271 * Signals the main thread when an output operation is completed. May
1272 * cause the main thread to wake up by posting a message.
1274 *----------------------------------------------------------------------
1281 TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg;
1282 SerialInfo *infoPtr = NULL; /* access info only after success init/wait */
1283 DWORD bytesWritten, toWrite;
1285 OVERLAPPED myWrite; /* Have an own OVERLAPPED in this thread. */
1289 * Wait for the main thread to signal before attempting to write.
1291 if (!TclPipeThreadWaitForSignal(&pipeTI)) {
1295 infoPtr = (SerialInfo *)pipeTI->clientData;
1297 buf = infoPtr->writeBuf;
1298 toWrite = infoPtr->toWrite;
1300 myWrite.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
1303 * Loop until all of the bytes are written or an error occurs.
1306 while (toWrite > 0) {
1308 * Check for pending writeError. Ignore all write operations until
1309 * the user has been notified.
1312 if (infoPtr->writeError) {
1315 if (SerialBlockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
1316 &bytesWritten, &myWrite) == FALSE) {
1317 infoPtr->writeError = GetLastError();
1320 if (bytesWritten != toWrite) {
1325 infoPtr->writeError = ERROR_WRITE_FAULT;
1328 toWrite -= bytesWritten;
1329 buf += bytesWritten;
1332 CloseHandle(myWrite.hEvent);
1335 * Signal the main thread by signalling the evWritable event and then
1336 * waking up the notifier thread.
1339 SetEvent(infoPtr->evWritable);
1342 * Alert the foreground thread. Note that we need to treat this like a
1343 * critical section so the foreground thread does not terminate this
1344 * thread while we are holding a mutex in the notifier code.
1347 Tcl_MutexLock(&serialMutex);
1348 if (infoPtr->threadId != NULL) {
1350 * TIP #218: When in flight ignore the event, no one will receive
1354 Tcl_ThreadAlert(infoPtr->threadId);
1356 Tcl_MutexUnlock(&serialMutex);
1359 /* Worker exit, so inform the main thread or free TI-structure (if owned) */
1360 TclPipeThreadExit(&pipeTI);
1366 *----------------------------------------------------------------------
1368 * TclWinSerialOpen --
1370 * Opens or Reopens the serial port with the OVERLAPPED FLAG set
1373 * Returns the new handle, or INVALID_HANDLE_VALUE.
1374 * If an existing channel is specified it is closed and reopened.
1377 * May close/reopen the original handle
1379 *----------------------------------------------------------------------
1391 * If an open channel is specified, close it
1394 if ( handle != INVALID_HANDLE_VALUE && CloseHandle(handle) == FALSE) {
1395 return INVALID_HANDLE_VALUE;
1399 * Multithreaded I/O needs the overlapped flag set otherwise
1400 * ClearCommError blocks under Windows NT/2000 until serial output is
1404 handle = CreateFileW(name, access, 0, 0, OPEN_EXISTING,
1405 FILE_FLAG_OVERLAPPED, 0);
1411 *----------------------------------------------------------------------
1413 * TclWinOpenSerialChannel --
1415 * Constructs a Serial port channel for the specified standard OS handle.
1416 * This is a helper function to break up the construction of channels
1417 * into File, Console, or Serial.
1420 * Returns the new channel, or NULL.
1423 * May open the channel
1425 *----------------------------------------------------------------------
1429 TclWinOpenSerialChannel(
1434 SerialInfo *infoPtr;
1438 infoPtr = ckalloc(sizeof(SerialInfo));
1439 memset(infoPtr, 0, sizeof(SerialInfo));
1441 infoPtr->validMask = permissions;
1442 infoPtr->handle = handle;
1443 infoPtr->channel = (Tcl_Channel) NULL;
1444 infoPtr->readable = 0;
1445 infoPtr->writable = 1;
1446 infoPtr->toWrite = infoPtr->writeQueue = 0;
1447 infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME;
1448 infoPtr->lastEventTime = 0;
1449 infoPtr->lastError = infoPtr->error = 0;
1450 infoPtr->threadId = Tcl_GetCurrentThread();
1451 infoPtr->sysBufRead = 4096;
1452 infoPtr->sysBufWrite = 4096;
1455 * Use the pointer to keep the channel names unique, in case the handles
1456 * are shared between multiple channels (stdin/stdout).
1459 sprintf(channelName, "file%" TCL_Z_MODIFIER "x", (size_t) infoPtr);
1461 infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName,
1462 infoPtr, permissions);
1465 SetupComm(handle, infoPtr->sysBufRead, infoPtr->sysBufWrite);
1467 PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
1470 * Default is blocking.
1473 SetCommTimeouts(handle, &no_timeout);
1475 InitializeCriticalSection(&infoPtr->csWrite);
1476 if (permissions & TCL_READABLE) {
1477 infoPtr->osRead.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
1479 if (permissions & TCL_WRITABLE) {
1481 * Initially the channel is writable and the writeThread is idle.
1484 infoPtr->osWrite.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
1485 infoPtr->evWritable = CreateEventW(NULL, TRUE, TRUE, NULL);
1486 infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
1487 TclPipeThreadCreateTI(&infoPtr->writeTI, infoPtr,
1488 infoPtr->evWritable), 0, NULL);
1492 * Files have default translation of AUTO and ^Z eof char, which means
1493 * that a ^Z will be accepted as EOF when reading.
1496 Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1497 Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1499 return infoPtr->channel;
1503 *----------------------------------------------------------------------
1507 * Converts a Win32 serial error code to a list of readable errors.
1513 * Generates readable errors in the supplied DString.
1515 *----------------------------------------------------------------------
1520 DWORD error, /* Win32 serial error code. */
1521 Tcl_DString *dsPtr) /* Where to store string. */
1523 if (error & CE_RXOVER) {
1524 Tcl_DStringAppendElement(dsPtr, "RXOVER");
1526 if (error & CE_OVERRUN) {
1527 Tcl_DStringAppendElement(dsPtr, "OVERRUN");
1529 if (error & CE_RXPARITY) {
1530 Tcl_DStringAppendElement(dsPtr, "RXPARITY");
1532 if (error & CE_FRAME) {
1533 Tcl_DStringAppendElement(dsPtr, "FRAME");
1535 if (error & CE_BREAK) {
1536 Tcl_DStringAppendElement(dsPtr, "BREAK");
1538 if (error & CE_TXFULL) {
1539 Tcl_DStringAppendElement(dsPtr, "TXFULL");
1541 if (error & CE_PTO) { /* PTO used to signal WRITE-TIMEOUT */
1542 Tcl_DStringAppendElement(dsPtr, "TIMEOUT");
1544 if (error & ~((DWORD) (SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS))) {
1545 char buf[TCL_INTEGER_SPACE + 1];
1547 wsprintfA(buf, "%d", error);
1548 Tcl_DStringAppendElement(dsPtr, buf);
1553 *----------------------------------------------------------------------
1555 * SerialModemStatusStr --
1557 * Converts a Win32 modem status list of readable flags
1563 * Appends modem status flag strings to the given DString.
1565 *----------------------------------------------------------------------
1569 SerialModemStatusStr(
1570 DWORD status, /* Win32 modem status. */
1571 Tcl_DString *dsPtr) /* Where to store string. */
1573 Tcl_DStringAppendElement(dsPtr, "CTS");
1574 Tcl_DStringAppendElement(dsPtr, (status & MS_CTS_ON) ? "1" : "0");
1575 Tcl_DStringAppendElement(dsPtr, "DSR");
1576 Tcl_DStringAppendElement(dsPtr, (status & MS_DSR_ON) ? "1" : "0");
1577 Tcl_DStringAppendElement(dsPtr, "RING");
1578 Tcl_DStringAppendElement(dsPtr, (status & MS_RING_ON) ? "1" : "0");
1579 Tcl_DStringAppendElement(dsPtr, "DCD");
1580 Tcl_DStringAppendElement(dsPtr, (status & MS_RLSD_ON) ? "1" : "0");
1584 *----------------------------------------------------------------------
1586 * SerialSetOptionProc --
1588 * Sets an option on a channel.
1591 * A standard Tcl result. Also sets the interp's result on error if
1592 * interp is not NULL.
1595 * May modify an option on a device.
1597 *----------------------------------------------------------------------
1601 SerialSetOptionProc(
1602 ClientData instanceData, /* File state. */
1603 Tcl_Interp *interp, /* For error reporting - can be NULL. */
1604 const char *optionName, /* Which option to set? */
1605 const char *value) /* New value for option. */
1607 SerialInfo *infoPtr;
1612 const WCHAR *native;
1616 infoPtr = (SerialInfo *) instanceData;
1619 * Parse options. This would be far easier if we had Tcl_Objs to work with
1620 * as that would let us use Tcl_GetIndexFromObj()...
1623 len = strlen(optionName);
1624 vlen = strlen(value);
1627 * Option -mode baud,parity,databits,stopbits
1630 if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
1631 if (!GetCommState(infoPtr->handle, &dcb)) {
1632 goto getStateFailed;
1634 native = (const WCHAR *)Tcl_WinUtfToTChar(value, -1, &ds);
1635 result = BuildCommDCBW(native, &dcb);
1636 Tcl_DStringFree(&ds);
1638 if (result == FALSE) {
1639 if (interp != NULL) {
1640 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1641 "bad value \"%s\" for -mode: should be baud,parity,data,stop",
1643 Tcl_SetErrorCode(interp, "TCL", "VALUE", "SERIALMODE", NULL);
1649 * Default settings for serial communications.
1653 dcb.fErrorChar = FALSE;
1655 dcb.fAbortOnError = FALSE;
1657 if (!SetCommState(infoPtr->handle, &dcb)) {
1658 goto setStateFailed;
1664 * Option -handshake none|xonxoff|rtscts|dtrdsr
1667 if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
1668 if (!GetCommState(infoPtr->handle, &dcb)) {
1669 goto getStateFailed;
1673 * Reset all handshake options. DTR and RTS are ON by default.
1676 dcb.fOutX = dcb.fInX = FALSE;
1677 dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fDsrSensitivity = FALSE;
1678 dcb.fDtrControl = DTR_CONTROL_ENABLE;
1679 dcb.fRtsControl = RTS_CONTROL_ENABLE;
1680 dcb.fTXContinueOnXoff = FALSE;
1683 * Adjust the handshake limits. Yes, the XonXoff limits seem to
1684 * influence even hardware handshake.
1687 dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
1688 dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
1690 if (strncasecmp(value, "NONE", vlen) == 0) {
1692 * Leave all handshake options disabled.
1694 } else if (strncasecmp(value, "XONXOFF", vlen) == 0) {
1695 dcb.fOutX = dcb.fInX = TRUE;
1696 } else if (strncasecmp(value, "RTSCTS", vlen) == 0) {
1697 dcb.fOutxCtsFlow = TRUE;
1698 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
1699 } else if (strncasecmp(value, "DTRDSR", vlen) == 0) {
1700 dcb.fOutxDsrFlow = TRUE;
1701 dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
1703 if (interp != NULL) {
1704 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1705 "bad value \"%s\" for -handshake: must be one of"
1706 " xonxoff, rtscts, dtrdsr or none", value));
1707 Tcl_SetErrorCode(interp, "TCL", "VALUE", "HANDSHAKE", NULL);
1712 if (!SetCommState(infoPtr->handle, &dcb)) {
1713 goto setStateFailed;
1719 * Option -xchar {\x11 \x13}
1722 if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
1723 if (!GetCommState(infoPtr->handle, &dcb)) {
1724 goto getStateFailed;
1727 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1732 if (interp != NULL) {
1733 Tcl_SetObjResult(interp, Tcl_NewStringObj(
1734 "bad value for -xchar: should be a list of"
1735 " two elements with each a single character", -1));
1736 Tcl_SetErrorCode(interp, "TCL", "VALUE", "XCHAR", NULL);
1743 * These dereferences are safe, even in the zero-length string cases,
1744 * because that just makes the xon/xoff character into NUL. When the
1745 * character looks like it is UTF-8 encoded, decode it before casting
1746 * into the format required for the Win guts. Note that this does not
1747 * convert character sets; it is expected that when people set the
1748 * control characters to something large and custom, they'll know the
1749 * hex/octal value rather than the printable form.
1752 dcb.XonChar = argv[0][0];
1753 dcb.XoffChar = argv[1][0];
1754 if (argv[0][0] & 0x80 || argv[1][0] & 0x80) {
1758 charLen = TclUtfToUCS4(argv[0], &character);
1759 if ((character & ~0xFF) || argv[0][charLen]) {
1762 dcb.XonChar = (char) character;
1763 charLen = TclUtfToUCS4(argv[1], &character);
1764 if ((character & ~0xFF) || argv[1][charLen]) {
1767 dcb.XoffChar = (char) character;
1771 if (!SetCommState(infoPtr->handle, &dcb)) {
1772 goto setStateFailed;
1778 * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
1781 if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
1782 int i, res = TCL_OK;
1784 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1787 if ((argc % 2) == 1) {
1788 if (interp != NULL) {
1789 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1790 "bad value \"%s\" for -ttycontrol: should be "
1791 "a list of signal,value pairs", value));
1792 Tcl_SetErrorCode(interp, "TCL", "VALUE", "TTYCONTROL", NULL);
1798 for (i = 0; i < argc - 1; i += 2) {
1799 if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
1803 if (strncasecmp(argv[i], "DTR", strlen(argv[i])) == 0) {
1804 if (!EscapeCommFunction(infoPtr->handle,
1805 (DWORD) (flag ? SETDTR : CLRDTR))) {
1806 if (interp != NULL) {
1807 Tcl_SetObjResult(interp, Tcl_NewStringObj(
1808 "can't set DTR signal", -1));
1809 Tcl_SetErrorCode(interp, "TCL", "OPERATION",
1810 "FCONFIGURE", "TTY_SIGNAL", NULL);
1815 } else if (strncasecmp(argv[i], "RTS", strlen(argv[i])) == 0) {
1816 if (!EscapeCommFunction(infoPtr->handle,
1817 (DWORD) (flag ? SETRTS : CLRRTS))) {
1818 if (interp != NULL) {
1819 Tcl_SetObjResult(interp, Tcl_NewStringObj(
1820 "can't set RTS signal", -1));
1821 Tcl_SetErrorCode(interp, "TCL", "OPERATION",
1822 "FCONFIGURE", "TTY_SIGNAL", NULL);
1827 } else if (strncasecmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
1828 if (!EscapeCommFunction(infoPtr->handle,
1829 (DWORD) (flag ? SETBREAK : CLRBREAK))) {
1830 if (interp != NULL) {
1831 Tcl_SetObjResult(interp, Tcl_NewStringObj(
1832 "can't set BREAK signal", -1));
1833 Tcl_SetErrorCode(interp, "TCL", "OPERATION",
1834 "FCONFIGURE", "TTY_SIGNAL", NULL);
1840 if (interp != NULL) {
1841 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1842 "bad signal name \"%s\" for -ttycontrol: must be"
1843 " DTR, RTS or BREAK", argv[i]));
1844 Tcl_SetErrorCode(interp, "TCL", "VALUE", "TTY_SIGNAL",
1857 * Option -sysbuffer {read_size write_size}
1858 * Option -sysbuffer read_size
1861 if ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0)) {
1863 * -sysbuffer 4096 or -sysbuffer {64536 4096}
1866 size_t inSize = (size_t) -1, outSize = (size_t) -1;
1868 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1872 inSize = atoi(argv[0]);
1873 outSize = infoPtr->sysBufWrite;
1874 } else if (argc == 2) {
1875 inSize = atoi(argv[0]);
1876 outSize = atoi(argv[1]);
1880 if ((argc < 1) || (argc > 2) || (inSize <= 0) || (outSize <= 0)) {
1881 if (interp != NULL) {
1882 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1883 "bad value \"%s\" for -sysbuffer: should be "
1884 "a list of one or two integers > 0", value));
1885 Tcl_SetErrorCode(interp, "TCL", "VALUE", "SYS_BUFFER", NULL);
1890 if (!SetupComm(infoPtr->handle, inSize, outSize)) {
1891 if (interp != NULL) {
1892 TclWinConvertError(GetLastError());
1893 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1894 "can't setup comm buffers: %s",
1895 Tcl_PosixError(interp)));
1899 infoPtr->sysBufRead = inSize;
1900 infoPtr->sysBufWrite = outSize;
1903 * Adjust the handshake limits. Yes, the XonXoff limits seem to
1904 * influence even hardware handshake.
1907 if (!GetCommState(infoPtr->handle, &dcb)) {
1908 goto getStateFailed;
1910 dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
1911 dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
1912 if (!SetCommState(infoPtr->handle, &dcb)) {
1913 goto setStateFailed;
1919 * Option -pollinterval msec
1922 if ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0)) {
1923 if (Tcl_GetInt(interp, value, &(infoPtr->blockTime)) != TCL_OK) {
1930 * Option -timeout msec
1933 if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
1935 COMMTIMEOUTS tout = {0,0,0,0,0};
1937 if (Tcl_GetInt(interp, value, &msec) != TCL_OK) {
1940 tout.ReadTotalTimeoutConstant = msec;
1941 if (!SetCommTimeouts(infoPtr->handle, &tout)) {
1942 if (interp != NULL) {
1943 TclWinConvertError(GetLastError());
1944 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1945 "can't set comm timeouts: %s",
1946 Tcl_PosixError(interp)));
1954 return Tcl_BadChannelOption(interp, optionName,
1955 "mode handshake pollinterval sysbuffer timeout ttycontrol xchar");
1958 if (interp != NULL) {
1959 TclWinConvertError(GetLastError());
1960 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1961 "can't get comm state: %s", Tcl_PosixError(interp)));
1966 if (interp != NULL) {
1967 TclWinConvertError(GetLastError());
1968 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1969 "can't set comm state: %s", Tcl_PosixError(interp)));
1975 *----------------------------------------------------------------------
1977 * SerialGetOptionProc --
1979 * Gets a mode associated with an IO channel. If the optionName arg is
1980 * non NULL, retrieves the value of that option. If the optionName arg is
1981 * NULL, retrieves a list of alternating option names and values for the
1985 * A standard Tcl result. Also sets the supplied DString to the string
1986 * value of the option(s) returned.
1989 * The string returned by this function is in static storage and may be
1990 * reused at any time subsequent to the call.
1992 *----------------------------------------------------------------------
1996 SerialGetOptionProc(
1997 ClientData instanceData, /* File state. */
1998 Tcl_Interp *interp, /* For error reporting - can be NULL. */
1999 const char *optionName, /* Option to get. */
2000 Tcl_DString *dsPtr) /* Where to store value(s). */
2002 SerialInfo *infoPtr;
2005 int valid = 0; /* Flag if valid option parsed. */
2007 infoPtr = (SerialInfo *) instanceData;
2009 if (optionName == NULL) {
2012 len = strlen(optionName);
2020 Tcl_DStringAppendElement(dsPtr, "-mode");
2022 if (len==0 || (len>2 && (strncmp(optionName, "-mode", len) == 0))) {
2025 char buf[2 * TCL_INTEGER_SPACE + 16];
2027 if (!GetCommState(infoPtr->handle, &dcb)) {
2028 if (interp != NULL) {
2029 TclWinConvertError(GetLastError());
2030 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2031 "can't get comm state: %s", Tcl_PosixError(interp)));
2038 if (dcb.Parity <= 4) {
2039 parity = "noems"[dcb.Parity];
2041 stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
2042 (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
2044 wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity,
2045 dcb.ByteSize, stop);
2046 Tcl_DStringAppendElement(dsPtr, buf);
2050 * Get option -pollinterval
2054 Tcl_DStringAppendElement(dsPtr, "-pollinterval");
2056 if (len==0 || (len>1 && strncmp(optionName, "-pollinterval", len)==0)) {
2057 char buf[TCL_INTEGER_SPACE + 1];
2060 wsprintfA(buf, "%d", infoPtr->blockTime);
2061 Tcl_DStringAppendElement(dsPtr, buf);
2065 * Get option -sysbuffer
2069 Tcl_DStringAppendElement(dsPtr, "-sysbuffer");
2070 Tcl_DStringStartSublist(dsPtr);
2072 if (len==0 || (len>1 && strncmp(optionName, "-sysbuffer", len) == 0)) {
2073 char buf[TCL_INTEGER_SPACE + 1];
2076 wsprintfA(buf, "%d", infoPtr->sysBufRead);
2077 Tcl_DStringAppendElement(dsPtr, buf);
2078 wsprintfA(buf, "%d", infoPtr->sysBufWrite);
2079 Tcl_DStringAppendElement(dsPtr, buf);
2082 Tcl_DStringEndSublist(dsPtr);
2090 Tcl_DStringAppendElement(dsPtr, "-xchar");
2091 Tcl_DStringStartSublist(dsPtr);
2093 if (len==0 || (len>1 && strncmp(optionName, "-xchar", len) == 0)) {
2097 if (!GetCommState(infoPtr->handle, &dcb)) {
2098 if (interp != NULL) {
2099 TclWinConvertError(GetLastError());
2100 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2101 "can't get comm state: %s", Tcl_PosixError(interp)));
2105 sprintf(buf, "%c", dcb.XonChar);
2106 Tcl_DStringAppendElement(dsPtr, buf);
2107 sprintf(buf, "%c", dcb.XoffChar);
2108 Tcl_DStringAppendElement(dsPtr, buf);
2111 Tcl_DStringEndSublist(dsPtr);
2115 * Get option -lasterror
2117 * Option is readonly and returned by [fconfigure chan -lasterror] but not
2118 * returned by unnamed [fconfigure chan].
2121 if (len>1 && strncmp(optionName, "-lasterror", len)==0) {
2123 SerialErrorStr(infoPtr->lastError, dsPtr);
2129 * Option is readonly and returned by [fconfigure chan -queue].
2132 if (len>1 && strncmp(optionName, "-queue", len)==0) {
2133 char buf[TCL_INTEGER_SPACE + 1];
2136 int inBuffered, outBuffered, count;
2141 * Query the pending data in Tcl's internal queues.
2144 inBuffered = Tcl_InputBuffered(infoPtr->channel);
2145 outBuffered = Tcl_OutputBuffered(infoPtr->channel);
2148 * Query the number of bytes in our output queue:
2149 * 1. The bytes pending in the output thread
2150 * 2. The bytes in the system drivers buffer
2151 * The writer thread should not interfere this action.
2154 EnterCriticalSection(&infoPtr->csWrite);
2155 ClearCommError(infoPtr->handle, &error, &cStat);
2156 count = (int) cStat.cbOutQue + infoPtr->writeQueue;
2157 LeaveCriticalSection(&infoPtr->csWrite);
2159 wsprintfA(buf, "%d", inBuffered + cStat.cbInQue);
2160 Tcl_DStringAppendElement(dsPtr, buf);
2161 wsprintfA(buf, "%d", outBuffered + count);
2162 Tcl_DStringAppendElement(dsPtr, buf);
2166 * get option -ttystatus
2168 * Option is readonly and returned by [fconfigure chan -ttystatus] but not
2169 * returned by unnamed [fconfigure chan].
2172 if (len>4 && strncmp(optionName, "-ttystatus", len)==0) {
2175 if (!GetCommModemStatus(infoPtr->handle, &status)) {
2176 if (interp != NULL) {
2177 TclWinConvertError(GetLastError());
2178 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2179 "can't get tty status: %s", Tcl_PosixError(interp)));
2184 SerialModemStatusStr(status, dsPtr);
2190 return Tcl_BadChannelOption(interp, optionName,
2191 "mode pollinterval lasterror queue sysbuffer ttystatus xchar");
2195 *----------------------------------------------------------------------
2197 * SerialThreadActionProc --
2199 * Insert or remove any thread local refs to this channel.
2205 * Changes thread local list of valid channels.
2207 *----------------------------------------------------------------------
2211 SerialThreadActionProc(
2212 ClientData instanceData,
2215 SerialInfo *infoPtr = (SerialInfo *) instanceData;
2218 * We do not access firstSerialPtr in the thread structures. This is not
2219 * for all serials managed by the thread, but only those we are watching.
2220 * Removal of the filevent handlers before transfer thus takes care of
2224 Tcl_MutexLock(&serialMutex);
2225 if (action == TCL_CHANNEL_THREAD_INSERT) {
2227 * We can't copy the thread information from the channel when the
2228 * channel is created. At this time the channel back pointer has not
2229 * been set yet. However in that case the threadId has already been
2230 * set by TclpCreateCommandChannel itself, so the structure is still
2235 if (infoPtr->channel != NULL) {
2236 infoPtr->threadId = Tcl_GetChannelThread(infoPtr->channel);
2239 infoPtr->threadId = NULL;
2241 Tcl_MutexUnlock(&serialMutex);