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