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