os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinSerial.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinSerial.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,2171 @@
1.4 +/*
1.5 + * tclWinSerial.c --
1.6 + *
1.7 + * This file implements the Windows-specific serial port functions,
1.8 + * and the "serial" channel driver.
1.9 + *
1.10 + * Copyright (c) 1999 by Scriptics Corp.
1.11 + *
1.12 + * See the file "license.terms" for information on usage and redistribution
1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.14 + *
1.15 + * Serial functionality implemented by Rolf.Schroedter@dlr.de
1.16 + *
1.17 + * RCS: @(#) $Id: tclWinSerial.c,v 1.25.2.3 2005/10/05 06:33:52 hobbs Exp $
1.18 + */
1.19 +
1.20 +#include "tclWinInt.h"
1.21 +
1.22 +#include <fcntl.h>
1.23 +#include <io.h>
1.24 +#include <sys/stat.h>
1.25 +
1.26 +/*
1.27 + * The following variable is used to tell whether this module has been
1.28 + * initialized.
1.29 + */
1.30 +
1.31 +static int initialized = 0;
1.32 +
1.33 +/*
1.34 + * The serialMutex locks around access to the initialized variable, and it is
1.35 + * used to protect background threads from being terminated while they are
1.36 + * using APIs that hold locks.
1.37 + */
1.38 +
1.39 +TCL_DECLARE_MUTEX(serialMutex)
1.40 +
1.41 +/*
1.42 + * Bit masks used in the flags field of the SerialInfo structure below.
1.43 + */
1.44 +
1.45 +#define SERIAL_PENDING (1<<0) /* Message is pending in the queue. */
1.46 +#define SERIAL_ASYNC (1<<1) /* Channel is non-blocking. */
1.47 +
1.48 +/*
1.49 + * Bit masks used in the sharedFlags field of the SerialInfo structure below.
1.50 + */
1.51 +
1.52 +#define SERIAL_EOF (1<<2) /* Serial has reached EOF. */
1.53 +#define SERIAL_ERROR (1<<4)
1.54 +
1.55 +/*
1.56 + * Default time to block between checking status on the serial port.
1.57 + */
1.58 +#define SERIAL_DEFAULT_BLOCKTIME 10 /* 10 msec */
1.59 +
1.60 +/*
1.61 + * Define Win32 read/write error masks returned by ClearCommError()
1.62 + */
1.63 +#define SERIAL_READ_ERRORS ( CE_RXOVER | CE_OVERRUN | CE_RXPARITY \
1.64 + | CE_FRAME | CE_BREAK )
1.65 +#define SERIAL_WRITE_ERRORS ( CE_TXFULL | CE_PTO )
1.66 +
1.67 +/*
1.68 + * This structure describes per-instance data for a serial based channel.
1.69 + */
1.70 +
1.71 +typedef struct SerialInfo {
1.72 + HANDLE handle;
1.73 + struct SerialInfo *nextPtr; /* Pointer to next registered serial. */
1.74 + Tcl_Channel channel; /* Pointer to channel structure. */
1.75 + int validMask; /* OR'ed combination of TCL_READABLE,
1.76 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.77 + * which operations are valid on the file. */
1.78 + int watchMask; /* OR'ed combination of TCL_READABLE,
1.79 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.80 + * which events should be reported. */
1.81 + int flags; /* State flags, see above for a list. */
1.82 + int readable; /* flag that the channel is readable */
1.83 + int writable; /* flag that the channel is writable */
1.84 + int blockTime; /* max. blocktime in msec */
1.85 + unsigned int lastEventTime; /* Time in milliseconds since last readable event */
1.86 + /* Next readable event only after blockTime */
1.87 + DWORD error; /* pending error code returned by
1.88 + * ClearCommError() */
1.89 + DWORD lastError; /* last error code, can be fetched with
1.90 + * fconfigure chan -lasterror */
1.91 + DWORD sysBufRead; /* Win32 system buffer size for read ops,
1.92 + * default=4096 */
1.93 + DWORD sysBufWrite; /* Win32 system buffer size for write ops,
1.94 + * default=4096 */
1.95 +
1.96 + Tcl_ThreadId threadId; /* Thread to which events should be reported.
1.97 + * This value is used by the reader/writer
1.98 + * threads. */
1.99 + OVERLAPPED osRead; /* OVERLAPPED structure for read operations */
1.100 + OVERLAPPED osWrite; /* OVERLAPPED structure for write operations */
1.101 + HANDLE writeThread; /* Handle to writer thread. */
1.102 + CRITICAL_SECTION csWrite; /* Writer thread synchronisation */
1.103 + HANDLE evWritable; /* Manual-reset event to signal when the
1.104 + * writer thread has finished waiting for
1.105 + * the current buffer to be written. */
1.106 + HANDLE evStartWriter; /* Auto-reset event used by the main thread to
1.107 + * signal when the writer thread should attempt
1.108 + * to write to the serial. */
1.109 + HANDLE evStopWriter; /* Auto-reset event used by the main thread to
1.110 + * signal when the writer thread should close.
1.111 + */
1.112 + DWORD writeError; /* An error caused by the last background
1.113 + * write. Set to 0 if no error has been
1.114 + * detected. This word is shared with the
1.115 + * writer thread so access must be
1.116 + * synchronized with the evWritable object.
1.117 + */
1.118 + char *writeBuf; /* Current background output buffer.
1.119 + * Access is synchronized with the evWritable
1.120 + * object. */
1.121 + int writeBufLen; /* Size of write buffer. Access is
1.122 + * synchronized with the evWritable
1.123 + * object. */
1.124 + int toWrite; /* Current amount to be written. Access is
1.125 + * synchronized with the evWritable object. */
1.126 + int writeQueue; /* Number of bytes pending in output queue.
1.127 + * Offset to DCB.cbInQue.
1.128 + * Used to query [fconfigure -queue] */
1.129 +} SerialInfo;
1.130 +
1.131 +typedef struct ThreadSpecificData {
1.132 + /*
1.133 + * The following pointer refers to the head of the list of serials
1.134 + * that are being watched for file events.
1.135 + */
1.136 +
1.137 + SerialInfo *firstSerialPtr;
1.138 +} ThreadSpecificData;
1.139 +
1.140 +static Tcl_ThreadDataKey dataKey;
1.141 +
1.142 +/*
1.143 + * The following structure is what is added to the Tcl event queue when
1.144 + * serial events are generated.
1.145 + */
1.146 +
1.147 +typedef struct SerialEvent {
1.148 + Tcl_Event header; /* Information that is standard for
1.149 + * all events. */
1.150 + SerialInfo *infoPtr; /* Pointer to serial info structure. Note
1.151 + * that we still have to verify that the
1.152 + * serial exists before dereferencing this
1.153 + * pointer. */
1.154 +} SerialEvent;
1.155 +
1.156 +/*
1.157 + * We don't use timeouts.
1.158 + */
1.159 +
1.160 +static COMMTIMEOUTS no_timeout = {
1.161 + 0, /* ReadIntervalTimeout */
1.162 + 0, /* ReadTotalTimeoutMultiplier */
1.163 + 0, /* ReadTotalTimeoutConstant */
1.164 + 0, /* WriteTotalTimeoutMultiplier */
1.165 + 0, /* WriteTotalTimeoutConstant */
1.166 +};
1.167 +
1.168 +/*
1.169 + * Declarations for functions used only in this file.
1.170 + */
1.171 +
1.172 +static int SerialBlockProc(ClientData instanceData, int mode);
1.173 +static void SerialCheckProc(ClientData clientData, int flags);
1.174 +static int SerialCloseProc(ClientData instanceData,
1.175 + Tcl_Interp *interp);
1.176 +static int SerialEventProc(Tcl_Event *evPtr, int flags);
1.177 +static void SerialExitHandler(ClientData clientData);
1.178 +static int SerialGetHandleProc(ClientData instanceData,
1.179 + int direction, ClientData *handlePtr);
1.180 +static ThreadSpecificData *SerialInit(void);
1.181 +static int SerialInputProc(ClientData instanceData, char *buf,
1.182 + int toRead, int *errorCode);
1.183 +static int SerialOutputProc(ClientData instanceData, CONST char *buf,
1.184 + int toWrite, int *errorCode);
1.185 +static void SerialSetupProc(ClientData clientData, int flags);
1.186 +static void SerialWatchProc(ClientData instanceData, int mask);
1.187 +static void ProcExitHandler(ClientData clientData);
1.188 +static int SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData,
1.189 + Tcl_Interp *interp, CONST char *optionName,
1.190 + Tcl_DString *dsPtr));
1.191 +static int SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData,
1.192 + Tcl_Interp *interp, CONST char *optionName,
1.193 + CONST char *value));
1.194 +static DWORD WINAPI SerialWriterThread(LPVOID arg);
1.195 +
1.196 +static void SerialThreadActionProc _ANSI_ARGS_ ((
1.197 + ClientData instanceData, int action));
1.198 +
1.199 +/*
1.200 + * This structure describes the channel type structure for command serial
1.201 + * based IO.
1.202 + */
1.203 +
1.204 +static Tcl_ChannelType serialChannelType = {
1.205 + "serial", /* Type name. */
1.206 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.207 + SerialCloseProc, /* Close proc. */
1.208 + SerialInputProc, /* Input proc. */
1.209 + SerialOutputProc, /* Output proc. */
1.210 + NULL, /* Seek proc. */
1.211 + SerialSetOptionProc, /* Set option proc. */
1.212 + SerialGetOptionProc, /* Get option proc. */
1.213 + SerialWatchProc, /* Set up notifier to watch the channel. */
1.214 + SerialGetHandleProc, /* Get an OS handle from channel. */
1.215 + NULL, /* close2proc. */
1.216 + SerialBlockProc, /* Set blocking or non-blocking mode.*/
1.217 + NULL, /* flush proc. */
1.218 + NULL, /* handler proc. */
1.219 + NULL, /* wide seek proc */
1.220 + SerialThreadActionProc, /* thread action proc */
1.221 +};
1.222 +
1.223 +/*
1.224 + *----------------------------------------------------------------------
1.225 + *
1.226 + * SerialInit --
1.227 + *
1.228 + * This function initializes the static variables for this file.
1.229 + *
1.230 + * Results:
1.231 + * None.
1.232 + *
1.233 + * Side effects:
1.234 + * Creates a new event source.
1.235 + *
1.236 + *----------------------------------------------------------------------
1.237 + */
1.238 +
1.239 +static ThreadSpecificData *
1.240 +SerialInit()
1.241 +{
1.242 + ThreadSpecificData *tsdPtr;
1.243 +
1.244 + /*
1.245 + * Check the initialized flag first, then check it again in the mutex.
1.246 + * This is a speed enhancement.
1.247 + */
1.248 +
1.249 + if (!initialized) {
1.250 + Tcl_MutexLock(&serialMutex);
1.251 + if (!initialized) {
1.252 + initialized = 1;
1.253 + Tcl_CreateExitHandler(ProcExitHandler, NULL);
1.254 + }
1.255 + Tcl_MutexUnlock(&serialMutex);
1.256 + }
1.257 +
1.258 + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.259 + if (tsdPtr == NULL) {
1.260 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.261 + tsdPtr->firstSerialPtr = NULL;
1.262 + Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
1.263 + Tcl_CreateThreadExitHandler(SerialExitHandler, NULL);
1.264 + }
1.265 + return tsdPtr;
1.266 +}
1.267 +
1.268 +/*
1.269 + *----------------------------------------------------------------------
1.270 + *
1.271 + * SerialExitHandler --
1.272 + *
1.273 + * This function is called to cleanup the serial module before
1.274 + * Tcl is unloaded.
1.275 + *
1.276 + * Results:
1.277 + * None.
1.278 + *
1.279 + * Side effects:
1.280 + * Removes the serial event source.
1.281 + *
1.282 + *----------------------------------------------------------------------
1.283 + */
1.284 +
1.285 +static void
1.286 +SerialExitHandler(
1.287 + ClientData clientData) /* Old window proc */
1.288 +{
1.289 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.290 + SerialInfo *infoPtr;
1.291 +
1.292 + /*
1.293 + * Clear all eventually pending output.
1.294 + * Otherwise Tcl's exit could totally block,
1.295 + * because it performs a blocking flush on all open channels.
1.296 + * Note that serial write operations may be blocked due to handshake.
1.297 + */
1.298 + for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
1.299 + infoPtr = infoPtr->nextPtr) {
1.300 + PurgeComm(infoPtr->handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
1.301 + | PURGE_RXCLEAR);
1.302 +
1.303 + }
1.304 + Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);
1.305 +}
1.306 +
1.307 +/*
1.308 + *----------------------------------------------------------------------
1.309 + *
1.310 + * ProcExitHandler --
1.311 + *
1.312 + * This function is called to cleanup the process list before
1.313 + * Tcl is unloaded.
1.314 + *
1.315 + * Results:
1.316 + * None.
1.317 + *
1.318 + * Side effects:
1.319 + * Resets the process list.
1.320 + *
1.321 + *----------------------------------------------------------------------
1.322 + */
1.323 +
1.324 +static void
1.325 +ProcExitHandler(
1.326 + ClientData clientData) /* Old window proc */
1.327 +{
1.328 + Tcl_MutexLock(&serialMutex);
1.329 + initialized = 0;
1.330 + Tcl_MutexUnlock(&serialMutex);
1.331 +}
1.332 +
1.333 +/*
1.334 + *----------------------------------------------------------------------
1.335 + *
1.336 + * SerialBlockTime --
1.337 + *
1.338 + * Wrapper to set Tcl's block time in msec
1.339 + *
1.340 + * Results:
1.341 + * None.
1.342 + *----------------------------------------------------------------------
1.343 + */
1.344 +
1.345 +static void
1.346 +SerialBlockTime(
1.347 + int msec) /* milli-seconds */
1.348 +{
1.349 + Tcl_Time blockTime;
1.350 +
1.351 + blockTime.sec = msec / 1000;
1.352 + blockTime.usec = (msec % 1000) * 1000;
1.353 + Tcl_SetMaxBlockTime(&blockTime);
1.354 +}
1.355 +/*
1.356 + *----------------------------------------------------------------------
1.357 + *
1.358 + * SerialGetMilliseconds --
1.359 + *
1.360 + * Get current time in milliseconds,
1.361 + * Don't care about integer overruns
1.362 + *
1.363 + * Results:
1.364 + * None.
1.365 + *----------------------------------------------------------------------
1.366 + */
1.367 +
1.368 +static unsigned int
1.369 +SerialGetMilliseconds(
1.370 + void)
1.371 +{
1.372 + Tcl_Time time;
1.373 +
1.374 + TclpGetTime(&time);
1.375 +
1.376 + return (time.sec * 1000 + time.usec / 1000);
1.377 +}
1.378 +/*
1.379 + *----------------------------------------------------------------------
1.380 + *
1.381 + * SerialSetupProc --
1.382 + *
1.383 + * This procedure is invoked before Tcl_DoOneEvent blocks waiting
1.384 + * for an event.
1.385 + *
1.386 + * Results:
1.387 + * None.
1.388 + *
1.389 + * Side effects:
1.390 + * Adjusts the block time if needed.
1.391 + *
1.392 + *----------------------------------------------------------------------
1.393 + */
1.394 +
1.395 +void
1.396 +SerialSetupProc(
1.397 + ClientData data, /* Not used. */
1.398 + int flags) /* Event flags as passed to Tcl_DoOneEvent. */
1.399 +{
1.400 + SerialInfo *infoPtr;
1.401 + int block = 1;
1.402 + int msec = INT_MAX; /* min. found block time */
1.403 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.404 +
1.405 + if (!(flags & TCL_FILE_EVENTS)) {
1.406 + return;
1.407 + }
1.408 +
1.409 + /*
1.410 + * Look to see if any events handlers installed. If they are, do not block.
1.411 + */
1.412 +
1.413 + for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
1.414 + infoPtr = infoPtr->nextPtr) {
1.415 +
1.416 + if (infoPtr->watchMask & TCL_WRITABLE) {
1.417 + if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
1.418 + block = 0;
1.419 + msec = min( msec, infoPtr->blockTime );
1.420 + }
1.421 + }
1.422 + if( infoPtr->watchMask & TCL_READABLE ) {
1.423 + block = 0;
1.424 + msec = min( msec, infoPtr->blockTime );
1.425 + }
1.426 + }
1.427 +
1.428 + if (!block) {
1.429 + SerialBlockTime(msec);
1.430 + }
1.431 +}
1.432 +
1.433 +/*
1.434 + *----------------------------------------------------------------------
1.435 + *
1.436 + * SerialCheckProc --
1.437 + *
1.438 + * This procedure is called by Tcl_DoOneEvent to check the serial
1.439 + * event source for events.
1.440 + *
1.441 + * Results:
1.442 + * None.
1.443 + *
1.444 + * Side effects:
1.445 + * May queue an event.
1.446 + *
1.447 + *----------------------------------------------------------------------
1.448 + */
1.449 +
1.450 +static void
1.451 +SerialCheckProc(
1.452 + ClientData data, /* Not used. */
1.453 + int flags) /* Event flags as passed to Tcl_DoOneEvent. */
1.454 +{
1.455 + SerialInfo *infoPtr;
1.456 + SerialEvent *evPtr;
1.457 + int needEvent;
1.458 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.459 + COMSTAT cStat;
1.460 + unsigned int time;
1.461 +
1.462 + if (!(flags & TCL_FILE_EVENTS)) {
1.463 + return;
1.464 + }
1.465 +
1.466 + /*
1.467 + * Queue events for any ready serials that don't already have events
1.468 + * queued.
1.469 + */
1.470 +
1.471 + for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
1.472 + infoPtr = infoPtr->nextPtr) {
1.473 + if (infoPtr->flags & SERIAL_PENDING) {
1.474 + continue;
1.475 + }
1.476 +
1.477 + needEvent = 0;
1.478 +
1.479 + /*
1.480 + * If WRITABLE watch mask is set
1.481 + * look for infoPtr->evWritable object
1.482 + */
1.483 + if (infoPtr->watchMask & TCL_WRITABLE) {
1.484 + if (WaitForSingleObject(infoPtr->evWritable, 0) != WAIT_TIMEOUT) {
1.485 + infoPtr->writable = 1;
1.486 + needEvent = 1;
1.487 + }
1.488 + }
1.489 +
1.490 + /*
1.491 + * If READABLE watch mask is set
1.492 + * call ClearCommError to poll cbInQue
1.493 + * Window errors are ignored here
1.494 + */
1.495 +
1.496 + if( infoPtr->watchMask & TCL_READABLE ) {
1.497 + if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
1.498 + /*
1.499 + * Look for characters already pending in windows queue.
1.500 + * If they are, poll.
1.501 + */
1.502 +
1.503 + if( infoPtr->watchMask & TCL_READABLE ) {
1.504 + /*
1.505 + * force fileevent after serial read error
1.506 + */
1.507 + if( (cStat.cbInQue > 0) ||
1.508 + (infoPtr->error & SERIAL_READ_ERRORS) ) {
1.509 + infoPtr->readable = 1;
1.510 + time = SerialGetMilliseconds();
1.511 + if ((unsigned int) (time - infoPtr->lastEventTime)
1.512 + >= (unsigned int) infoPtr->blockTime) {
1.513 + needEvent = 1;
1.514 + infoPtr->lastEventTime = time;
1.515 + }
1.516 + }
1.517 + }
1.518 + }
1.519 + }
1.520 +
1.521 + /*
1.522 + * Queue an event if the serial is signaled for reading or writing.
1.523 + */
1.524 + if (needEvent) {
1.525 + infoPtr->flags |= SERIAL_PENDING;
1.526 + evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent));
1.527 + evPtr->header.proc = SerialEventProc;
1.528 + evPtr->infoPtr = infoPtr;
1.529 + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
1.530 + }
1.531 + }
1.532 +}
1.533 +
1.534 +/*
1.535 + *----------------------------------------------------------------------
1.536 + *
1.537 + * SerialBlockProc --
1.538 + *
1.539 + * Set blocking or non-blocking mode on channel.
1.540 + *
1.541 + * Results:
1.542 + * 0 if successful, errno when failed.
1.543 + *
1.544 + * Side effects:
1.545 + * Sets the device into blocking or non-blocking mode.
1.546 + *
1.547 + *----------------------------------------------------------------------
1.548 + */
1.549 +
1.550 +static int
1.551 +SerialBlockProc(
1.552 + ClientData instanceData, /* Instance data for channel. */
1.553 + int mode) /* TCL_MODE_BLOCKING or
1.554 + * TCL_MODE_NONBLOCKING. */
1.555 +{
1.556 + int errorCode = 0;
1.557 +
1.558 + SerialInfo *infoPtr = (SerialInfo *) instanceData;
1.559 +
1.560 + /*
1.561 + * Only serial READ can be switched between blocking & nonblocking
1.562 + * using COMMTIMEOUTS.
1.563 + * Serial write emulates blocking & nonblocking by the SerialWriterThread.
1.564 + */
1.565 +
1.566 + if (mode == TCL_MODE_NONBLOCKING) {
1.567 + infoPtr->flags |= SERIAL_ASYNC;
1.568 + } else {
1.569 + infoPtr->flags &= ~(SERIAL_ASYNC);
1.570 + }
1.571 + return errorCode;
1.572 +}
1.573 +
1.574 +/*
1.575 + *----------------------------------------------------------------------
1.576 + *
1.577 + * SerialCloseProc --
1.578 + *
1.579 + * Closes a serial based IO channel.
1.580 + *
1.581 + * Results:
1.582 + * 0 on success, errno otherwise.
1.583 + *
1.584 + * Side effects:
1.585 + * Closes the physical channel.
1.586 + *
1.587 + *----------------------------------------------------------------------
1.588 + */
1.589 +
1.590 +static int
1.591 +SerialCloseProc(
1.592 + ClientData instanceData, /* Pointer to SerialInfo structure. */
1.593 + Tcl_Interp *interp) /* For error reporting. */
1.594 +{
1.595 + SerialInfo *serialPtr = (SerialInfo *) instanceData;
1.596 + int errorCode, result = 0;
1.597 + SerialInfo *infoPtr, **nextPtrPtr;
1.598 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.599 + DWORD exitCode;
1.600 +
1.601 + errorCode = 0;
1.602 +
1.603 + if (serialPtr->validMask & TCL_READABLE) {
1.604 + PurgeComm(serialPtr->handle, PURGE_RXABORT | PURGE_RXCLEAR);
1.605 + CloseHandle(serialPtr->osRead.hEvent);
1.606 + }
1.607 + serialPtr->validMask &= ~TCL_READABLE;
1.608 +
1.609 + if (serialPtr->validMask & TCL_WRITABLE) {
1.610 +
1.611 + /*
1.612 + * Generally we cannot wait for a pending write operation
1.613 + * because it may hang due to handshake
1.614 + * WaitForSingleObject(serialPtr->evWritable, INFINITE);
1.615 + */
1.616 +
1.617 + /*
1.618 + * The thread may have already closed on it's own. Check it's
1.619 + * exit code.
1.620 + */
1.621 +
1.622 + GetExitCodeThread(serialPtr->writeThread, &exitCode);
1.623 +
1.624 + if (exitCode == STILL_ACTIVE) {
1.625 + /*
1.626 + * Set the stop event so that if the writer thread is
1.627 + * blocked in SerialWriterThread on WaitForMultipleEvents, it
1.628 + * will exit cleanly.
1.629 + */
1.630 +
1.631 + SetEvent(serialPtr->evStopWriter);
1.632 +
1.633 + /*
1.634 + * Wait at most 20 milliseconds for the writer thread to
1.635 + * close.
1.636 + */
1.637 +
1.638 + if (WaitForSingleObject(serialPtr->writeThread, 20)
1.639 + == WAIT_TIMEOUT) {
1.640 + /*
1.641 + * Forcibly terminate the background thread as a last
1.642 + * resort. Note that we need to guard against
1.643 + * terminating the thread while it is in the middle of
1.644 + * Tcl_ThreadAlert because it won't be able to release
1.645 + * the notifier lock.
1.646 + */
1.647 +
1.648 + Tcl_MutexLock(&serialMutex);
1.649 +
1.650 + /* BUG: this leaks memory */
1.651 + TerminateThread(serialPtr->writeThread, 0);
1.652 +
1.653 + Tcl_MutexUnlock(&serialMutex);
1.654 + }
1.655 + }
1.656 +
1.657 + CloseHandle(serialPtr->writeThread);
1.658 + CloseHandle(serialPtr->osWrite.hEvent);
1.659 + DeleteCriticalSection(&serialPtr->csWrite);
1.660 + CloseHandle(serialPtr->evWritable);
1.661 + CloseHandle(serialPtr->evStartWriter);
1.662 + CloseHandle(serialPtr->evStopWriter);
1.663 + serialPtr->writeThread = NULL;
1.664 +
1.665 + PurgeComm(serialPtr->handle, PURGE_TXABORT | PURGE_TXCLEAR);
1.666 + }
1.667 + serialPtr->validMask &= ~TCL_WRITABLE;
1.668 +
1.669 + /*
1.670 + * Don't close the Win32 handle if the handle is a standard channel
1.671 + * during the thread exit process. Otherwise, one thread may kill
1.672 + * the stdio of another.
1.673 + */
1.674 +
1.675 + if (!TclInThreadExit()
1.676 + || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
1.677 + && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
1.678 + && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
1.679 + if (CloseHandle(serialPtr->handle) == FALSE) {
1.680 + TclWinConvertError(GetLastError());
1.681 + errorCode = errno;
1.682 + }
1.683 + }
1.684 +
1.685 + serialPtr->watchMask &= serialPtr->validMask;
1.686 +
1.687 + /*
1.688 + * Remove the file from the list of watched files.
1.689 + */
1.690 +
1.691 + for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr;
1.692 + infoPtr != NULL;
1.693 + nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
1.694 + if (infoPtr == (SerialInfo *)serialPtr) {
1.695 + *nextPtrPtr = infoPtr->nextPtr;
1.696 + break;
1.697 + }
1.698 + }
1.699 +
1.700 + /*
1.701 + * Wrap the error file into a channel and give it to the cleanup
1.702 + * routine.
1.703 + */
1.704 + if (serialPtr->writeBuf != NULL) {
1.705 + ckfree(serialPtr->writeBuf);
1.706 + serialPtr->writeBuf = NULL;
1.707 + }
1.708 + ckfree((char*) serialPtr);
1.709 +
1.710 + if (errorCode == 0) {
1.711 + return result;
1.712 + }
1.713 + return errorCode;
1.714 +}
1.715 +
1.716 +/*
1.717 + *----------------------------------------------------------------------
1.718 + *
1.719 + * blockingRead --
1.720 + *
1.721 + * Perform a blocking read into the buffer given. Returns
1.722 + * count of how many bytes were actually read, and an error indication.
1.723 + *
1.724 + * Results:
1.725 + * A count of how many bytes were read is returned and an error
1.726 + * indication is returned.
1.727 + *
1.728 + * Side effects:
1.729 + * Reads input from the actual channel.
1.730 + *
1.731 + *----------------------------------------------------------------------
1.732 + */
1.733 +static int
1.734 +blockingRead(
1.735 + SerialInfo *infoPtr, /* Serial info structure */
1.736 + LPVOID buf, /* The input buffer pointer */
1.737 + DWORD bufSize, /* The number of bytes to read */
1.738 + LPDWORD lpRead, /* Returns number of bytes read */
1.739 + LPOVERLAPPED osPtr ) /* OVERLAPPED structure */
1.740 +{
1.741 + /*
1.742 + * Perform overlapped blocking read.
1.743 + * 1. Reset the overlapped event
1.744 + * 2. Start overlapped read operation
1.745 + * 3. Wait for completion
1.746 + */
1.747 +
1.748 + /*
1.749 + * Set Offset to ZERO, otherwise NT4.0 may report an error.
1.750 + */
1.751 + osPtr->Offset = osPtr->OffsetHigh = 0;
1.752 + ResetEvent(osPtr->hEvent);
1.753 + if (! ReadFile(infoPtr->handle, buf, bufSize, lpRead, osPtr) ) {
1.754 + if (GetLastError() != ERROR_IO_PENDING) {
1.755 + /* ReadFile failed, but it isn't delayed. Report error. */
1.756 + return FALSE;
1.757 + } else {
1.758 + /* Read is pending, wait for completion, timeout ? */
1.759 + if (! GetOverlappedResult(infoPtr->handle, osPtr, lpRead, TRUE) ) {
1.760 + return FALSE;
1.761 + }
1.762 + }
1.763 + } else {
1.764 + /* ReadFile completed immediately. */
1.765 + }
1.766 + return TRUE;
1.767 +}
1.768 +
1.769 +/*
1.770 + *----------------------------------------------------------------------
1.771 + *
1.772 + * blockingWrite --
1.773 + *
1.774 + * Perform a blocking write from the buffer given. Returns
1.775 + * count of how many bytes were actually written, and an error indication.
1.776 + *
1.777 + * Results:
1.778 + * A count of how many bytes were written is returned and an error
1.779 + * indication is returned.
1.780 + *
1.781 + * Side effects:
1.782 + * Writes output to the actual channel.
1.783 + *
1.784 + *----------------------------------------------------------------------
1.785 + */
1.786 +static int
1.787 +blockingWrite(
1.788 + SerialInfo *infoPtr, /* Serial info structure */
1.789 + LPVOID buf, /* The output buffer pointer */
1.790 + DWORD bufSize, /* The number of bytes to write */
1.791 + LPDWORD lpWritten, /* Returns number of bytes written */
1.792 + LPOVERLAPPED osPtr ) /* OVERLAPPED structure */
1.793 +{
1.794 + int result;
1.795 + /*
1.796 + * Perform overlapped blocking write.
1.797 + * 1. Reset the overlapped event
1.798 + * 2. Remove these bytes from the output queue counter
1.799 + * 3. Start overlapped write operation
1.800 + * 3. Remove these bytes from the output queue counter
1.801 + * 4. Wait for completion
1.802 + * 5. Adjust the output queue counter
1.803 + */
1.804 + ResetEvent(osPtr->hEvent);
1.805 +
1.806 + EnterCriticalSection(&infoPtr->csWrite);
1.807 + infoPtr->writeQueue -= bufSize;
1.808 + /*
1.809 + * Set Offset to ZERO, otherwise NT4.0 may report an error
1.810 + */
1.811 + osPtr->Offset = osPtr->OffsetHigh = 0;
1.812 + result = WriteFile(infoPtr->handle, buf, bufSize, lpWritten, osPtr);
1.813 + LeaveCriticalSection(&infoPtr->csWrite);
1.814 +
1.815 + if (result == FALSE ) {
1.816 + int err = GetLastError();
1.817 + switch (err) {
1.818 + case ERROR_IO_PENDING:
1.819 + /* Write is pending, wait for completion */
1.820 + if (! GetOverlappedResult(infoPtr->handle, osPtr, lpWritten, TRUE) ) {
1.821 + return FALSE;
1.822 + }
1.823 + break;
1.824 + case ERROR_COUNTER_TIMEOUT:
1.825 + /* Write timeout handled in SerialOutputProc */
1.826 + break;
1.827 + default:
1.828 + /* WriteFile failed, but it isn't delayed. Report error */
1.829 + return FALSE;
1.830 + }
1.831 + } else {
1.832 + /* WriteFile completed immediately. */
1.833 + }
1.834 +
1.835 + EnterCriticalSection(&infoPtr->csWrite);
1.836 + infoPtr->writeQueue += (*lpWritten - bufSize);
1.837 + LeaveCriticalSection(&infoPtr->csWrite);
1.838 +
1.839 + return TRUE;
1.840 +}
1.841 +
1.842 +/*
1.843 + *----------------------------------------------------------------------
1.844 + *
1.845 + * SerialInputProc --
1.846 + *
1.847 + * Reads input from the IO channel into the buffer given. Returns
1.848 + * count of how many bytes were actually read, and an error indication.
1.849 + *
1.850 + * Results:
1.851 + * A count of how many bytes were read is returned and an error
1.852 + * indication is returned in an output argument.
1.853 + *
1.854 + * Side effects:
1.855 + * Reads input from the actual channel.
1.856 + *
1.857 + *----------------------------------------------------------------------
1.858 + */
1.859 +static int
1.860 +SerialInputProc(
1.861 + ClientData instanceData, /* Serial state. */
1.862 + char *buf, /* Where to store data read. */
1.863 + int bufSize, /* How much space is available
1.864 + * in the buffer? */
1.865 + int *errorCode) /* Where to store error code. */
1.866 +{
1.867 + SerialInfo *infoPtr = (SerialInfo *) instanceData;
1.868 + DWORD bytesRead = 0;
1.869 + COMSTAT cStat;
1.870 +
1.871 + *errorCode = 0;
1.872 +
1.873 + /*
1.874 + * Check if there is a CommError pending from SerialCheckProc
1.875 + */
1.876 + if( infoPtr->error & SERIAL_READ_ERRORS ){
1.877 + goto commError;
1.878 + }
1.879 +
1.880 + /*
1.881 + * Look for characters already pending in windows queue.
1.882 + * This is the mainly restored good old code from Tcl8.0
1.883 + */
1.884 +
1.885 + if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
1.886 + /*
1.887 + * Check for errors here, but not in the evSetup/Check procedures
1.888 + */
1.889 +
1.890 + if( infoPtr->error & SERIAL_READ_ERRORS ) {
1.891 + goto commError;
1.892 + }
1.893 + if( infoPtr->flags & SERIAL_ASYNC ) {
1.894 + /*
1.895 + * NON_BLOCKING mode:
1.896 + * Avoid blocking by reading more bytes than available
1.897 + * in input buffer
1.898 + */
1.899 +
1.900 + if( cStat.cbInQue > 0 ) {
1.901 + if( (DWORD) bufSize > cStat.cbInQue ) {
1.902 + bufSize = cStat.cbInQue;
1.903 + }
1.904 + } else {
1.905 + errno = *errorCode = EAGAIN;
1.906 + return -1;
1.907 + }
1.908 + } else {
1.909 + /*
1.910 + * BLOCKING mode:
1.911 + * Tcl trys to read a full buffer of 4 kBytes here
1.912 + */
1.913 +
1.914 + if( cStat.cbInQue > 0 ) {
1.915 + if( (DWORD) bufSize > cStat.cbInQue ) {
1.916 + bufSize = cStat.cbInQue;
1.917 + }
1.918 + } else {
1.919 + bufSize = 1;
1.920 + }
1.921 + }
1.922 + }
1.923 +
1.924 + if( bufSize == 0 ) {
1.925 + return bytesRead = 0;
1.926 + }
1.927 +
1.928 + /*
1.929 + * Perform blocking read. Doesn't block in non-blocking mode,
1.930 + * because we checked the number of available bytes.
1.931 + */
1.932 + if (blockingRead(infoPtr, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
1.933 + &infoPtr->osRead) == FALSE) {
1.934 + goto error;
1.935 + }
1.936 + return bytesRead;
1.937 +
1.938 +error:
1.939 + TclWinConvertError(GetLastError());
1.940 + *errorCode = errno;
1.941 + return -1;
1.942 +
1.943 +commError:
1.944 + infoPtr->lastError = infoPtr->error; /* save last error code */
1.945 + infoPtr->error = 0; /* reset error code */
1.946 + *errorCode = EIO; /* to return read-error only once */
1.947 + return -1;
1.948 +}
1.949 +
1.950 +/*
1.951 + *----------------------------------------------------------------------
1.952 + *
1.953 + * SerialOutputProc --
1.954 + *
1.955 + * Writes the given output on the IO channel. Returns count of how
1.956 + * many characters were actually written, and an error indication.
1.957 + *
1.958 + * Results:
1.959 + * A count of how many characters were written is returned and an
1.960 + * error indication is returned in an output argument.
1.961 + *
1.962 + * Side effects:
1.963 + * Writes output on the actual channel.
1.964 + *
1.965 + *----------------------------------------------------------------------
1.966 + */
1.967 +
1.968 +static int
1.969 +SerialOutputProc(
1.970 + ClientData instanceData, /* Serial state. */
1.971 + CONST char *buf, /* The data buffer. */
1.972 + int toWrite, /* How many bytes to write? */
1.973 + int *errorCode) /* Where to store error code. */
1.974 +{
1.975 + SerialInfo *infoPtr = (SerialInfo *) instanceData;
1.976 + DWORD bytesWritten, timeout;
1.977 +
1.978 + *errorCode = 0;
1.979 +
1.980 + /*
1.981 + * At EXIT Tcl trys to flush all open channels in blocking mode.
1.982 + * We avoid blocking output after ExitProc or CloseHandler(chan)
1.983 + * has been called by checking the corrresponding variables.
1.984 + */
1.985 + if( ! initialized || TclInExit() ) {
1.986 + return toWrite;
1.987 + }
1.988 +
1.989 + /*
1.990 + * Check if there is a CommError pending from SerialCheckProc
1.991 + */
1.992 + if( infoPtr->error & SERIAL_WRITE_ERRORS ){
1.993 + infoPtr->lastError = infoPtr->error; /* save last error code */
1.994 + infoPtr->error = 0; /* reset error code */
1.995 + errno = EIO;
1.996 + goto error;
1.997 + }
1.998 +
1.999 + timeout = (infoPtr->flags & SERIAL_ASYNC) ? 0 : INFINITE;
1.1000 + if (WaitForSingleObject(infoPtr->evWritable, timeout) == WAIT_TIMEOUT) {
1.1001 + /*
1.1002 + * The writer thread is blocked waiting for a write to complete
1.1003 + * and the channel is in non-blocking mode.
1.1004 + */
1.1005 +
1.1006 + errno = EWOULDBLOCK;
1.1007 + goto error1;
1.1008 + }
1.1009 + /*
1.1010 + * Check for a background error on the last write.
1.1011 + */
1.1012 +
1.1013 + if (infoPtr->writeError) {
1.1014 + TclWinConvertError(infoPtr->writeError);
1.1015 + infoPtr->writeError = 0;
1.1016 + goto error1;
1.1017 + }
1.1018 +
1.1019 + /*
1.1020 + * Remember the number of bytes in output queue
1.1021 + */
1.1022 + EnterCriticalSection(&infoPtr->csWrite);
1.1023 + infoPtr->writeQueue += toWrite;
1.1024 + LeaveCriticalSection(&infoPtr->csWrite);
1.1025 +
1.1026 + if (infoPtr->flags & SERIAL_ASYNC) {
1.1027 + /*
1.1028 + * The serial is non-blocking, so copy the data into the output
1.1029 + * buffer and restart the writer thread.
1.1030 + */
1.1031 +
1.1032 + if (toWrite > infoPtr->writeBufLen) {
1.1033 + /*
1.1034 + * Reallocate the buffer to be large enough to hold the data.
1.1035 + */
1.1036 +
1.1037 + if (infoPtr->writeBuf) {
1.1038 + ckfree(infoPtr->writeBuf);
1.1039 + }
1.1040 + infoPtr->writeBufLen = toWrite;
1.1041 + infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
1.1042 + }
1.1043 + memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
1.1044 + infoPtr->toWrite = toWrite;
1.1045 + ResetEvent(infoPtr->evWritable);
1.1046 + SetEvent(infoPtr->evStartWriter);
1.1047 + bytesWritten = (DWORD) toWrite;
1.1048 +
1.1049 + } else {
1.1050 + /*
1.1051 + * In the blocking case, just try to write the buffer directly.
1.1052 + * This avoids an unnecessary copy.
1.1053 + */
1.1054 + if (! blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
1.1055 + &bytesWritten, &infoPtr->osWrite) ) {
1.1056 + goto writeError;
1.1057 + }
1.1058 + if (bytesWritten != (DWORD) toWrite) {
1.1059 + /* Write timeout */
1.1060 + infoPtr->lastError |= CE_PTO;
1.1061 + errno = EIO;
1.1062 + goto error;
1.1063 + }
1.1064 + }
1.1065 +
1.1066 + return (int) bytesWritten;
1.1067 +
1.1068 +writeError:
1.1069 + TclWinConvertError(GetLastError());
1.1070 +
1.1071 +error:
1.1072 + /*
1.1073 + * Reset the output queue counter on error during blocking output
1.1074 + */
1.1075 +/*
1.1076 + EnterCriticalSection(&infoPtr->csWrite);
1.1077 + infoPtr->writeQueue = 0;
1.1078 + LeaveCriticalSection(&infoPtr->csWrite);
1.1079 +*/
1.1080 + error1:
1.1081 + *errorCode = errno;
1.1082 + return -1;
1.1083 +}
1.1084 +
1.1085 +/*
1.1086 + *----------------------------------------------------------------------
1.1087 + *
1.1088 + * SerialEventProc --
1.1089 + *
1.1090 + * This function is invoked by Tcl_ServiceEvent when a file event
1.1091 + * reaches the front of the event queue. This procedure invokes
1.1092 + * Tcl_NotifyChannel on the serial.
1.1093 + *
1.1094 + * Results:
1.1095 + * Returns 1 if the event was handled, meaning it should be removed
1.1096 + * from the queue. Returns 0 if the event was not handled, meaning
1.1097 + * it should stay on the queue. The only time the event isn't
1.1098 + * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1.1099 + *
1.1100 + * Side effects:
1.1101 + * Whatever the notifier callback does.
1.1102 + *
1.1103 + *----------------------------------------------------------------------
1.1104 + */
1.1105 +
1.1106 +static int
1.1107 +SerialEventProc(
1.1108 + Tcl_Event *evPtr, /* Event to service. */
1.1109 + int flags) /* Flags that indicate what events to
1.1110 + * handle, such as TCL_FILE_EVENTS. */
1.1111 +{
1.1112 + SerialEvent *serialEvPtr = (SerialEvent *)evPtr;
1.1113 + SerialInfo *infoPtr;
1.1114 + int mask;
1.1115 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1116 +
1.1117 + if (!(flags & TCL_FILE_EVENTS)) {
1.1118 + return 0;
1.1119 + }
1.1120 +
1.1121 + /*
1.1122 + * Search through the list of watched serials for the one whose handle
1.1123 + * matches the event. We do this rather than simply dereferencing
1.1124 + * the handle in the event so that serials can be deleted while the
1.1125 + * event is in the queue.
1.1126 + */
1.1127 +
1.1128 + for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
1.1129 + infoPtr = infoPtr->nextPtr) {
1.1130 + if (serialEvPtr->infoPtr == infoPtr) {
1.1131 + infoPtr->flags &= ~(SERIAL_PENDING);
1.1132 + break;
1.1133 + }
1.1134 + }
1.1135 +
1.1136 + /*
1.1137 + * Remove stale events.
1.1138 + */
1.1139 +
1.1140 + if (!infoPtr) {
1.1141 + return 1;
1.1142 + }
1.1143 +
1.1144 + /*
1.1145 + * Check to see if the serial is readable. Note
1.1146 + * that we can't tell if a serial is writable, so we always report it
1.1147 + * as being writable unless we have detected EOF.
1.1148 + */
1.1149 +
1.1150 + mask = 0;
1.1151 + if( infoPtr->watchMask & TCL_WRITABLE ) {
1.1152 + if( infoPtr->writable ) {
1.1153 + mask |= TCL_WRITABLE;
1.1154 + infoPtr->writable = 0;
1.1155 + }
1.1156 + }
1.1157 +
1.1158 + if( infoPtr->watchMask & TCL_READABLE ) {
1.1159 + if( infoPtr->readable ) {
1.1160 + mask |= TCL_READABLE;
1.1161 + infoPtr->readable = 0;
1.1162 + }
1.1163 + }
1.1164 +
1.1165 + /*
1.1166 + * Inform the channel of the events.
1.1167 + */
1.1168 +
1.1169 + Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
1.1170 + return 1;
1.1171 +}
1.1172 +
1.1173 +/*
1.1174 + *----------------------------------------------------------------------
1.1175 + *
1.1176 + * SerialWatchProc --
1.1177 + *
1.1178 + * Called by the notifier to set up to watch for events on this
1.1179 + * channel.
1.1180 + *
1.1181 + * Results:
1.1182 + * None.
1.1183 + *
1.1184 + * Side effects:
1.1185 + * None.
1.1186 + *
1.1187 + *----------------------------------------------------------------------
1.1188 + */
1.1189 +
1.1190 +static void
1.1191 +SerialWatchProc(
1.1192 + ClientData instanceData, /* Serial state. */
1.1193 + int mask) /* What events to watch for, OR-ed
1.1194 + * combination of TCL_READABLE,
1.1195 + * TCL_WRITABLE and TCL_EXCEPTION. */
1.1196 +{
1.1197 + SerialInfo **nextPtrPtr, *ptr;
1.1198 + SerialInfo *infoPtr = (SerialInfo *) instanceData;
1.1199 + int oldMask = infoPtr->watchMask;
1.1200 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1201 +
1.1202 + /*
1.1203 + * Since the file is always ready for events, we set the block time
1.1204 + * so we will poll.
1.1205 + */
1.1206 +
1.1207 + infoPtr->watchMask = mask & infoPtr->validMask;
1.1208 + if (infoPtr->watchMask) {
1.1209 + if (!oldMask) {
1.1210 + infoPtr->nextPtr = tsdPtr->firstSerialPtr;
1.1211 + tsdPtr->firstSerialPtr = infoPtr;
1.1212 + }
1.1213 + SerialBlockTime(infoPtr->blockTime);
1.1214 + } else {
1.1215 + if (oldMask) {
1.1216 + /*
1.1217 + * Remove the serial port from the list of watched serial ports.
1.1218 + */
1.1219 +
1.1220 + for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr;
1.1221 + ptr != NULL;
1.1222 + nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
1.1223 + if (infoPtr == ptr) {
1.1224 + *nextPtrPtr = ptr->nextPtr;
1.1225 + break;
1.1226 + }
1.1227 + }
1.1228 + }
1.1229 + }
1.1230 +}
1.1231 +
1.1232 +/*
1.1233 + *----------------------------------------------------------------------
1.1234 + *
1.1235 + * SerialGetHandleProc --
1.1236 + *
1.1237 + * Called from Tcl_GetChannelHandle to retrieve OS handles from
1.1238 + * inside a command serial port based channel.
1.1239 + *
1.1240 + * Results:
1.1241 + * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1.1242 + * there is no handle for the specified direction.
1.1243 + *
1.1244 + * Side effects:
1.1245 + * None.
1.1246 + *
1.1247 + *----------------------------------------------------------------------
1.1248 + */
1.1249 +
1.1250 +static int
1.1251 +SerialGetHandleProc(
1.1252 + ClientData instanceData, /* The serial state. */
1.1253 + int direction, /* TCL_READABLE or TCL_WRITABLE */
1.1254 + ClientData *handlePtr) /* Where to store the handle. */
1.1255 +{
1.1256 + SerialInfo *infoPtr = (SerialInfo *) instanceData;
1.1257 +
1.1258 + *handlePtr = (ClientData) infoPtr->handle;
1.1259 + return TCL_OK;
1.1260 +}
1.1261 +
1.1262 +/*
1.1263 + *----------------------------------------------------------------------
1.1264 + *
1.1265 + * SerialWriterThread --
1.1266 + *
1.1267 + * This function runs in a separate thread and writes data
1.1268 + * onto a serial.
1.1269 + *
1.1270 + * Results:
1.1271 + * Always returns 0.
1.1272 + *
1.1273 + * Side effects:
1.1274 + * Signals the main thread when an output operation is completed.
1.1275 + * May cause the main thread to wake up by posting a message.
1.1276 + *
1.1277 + *----------------------------------------------------------------------
1.1278 + */
1.1279 +
1.1280 +static DWORD WINAPI
1.1281 +SerialWriterThread(LPVOID arg)
1.1282 +{
1.1283 +
1.1284 + SerialInfo *infoPtr = (SerialInfo *)arg;
1.1285 + DWORD bytesWritten, toWrite, waitResult;
1.1286 + char *buf;
1.1287 + OVERLAPPED myWrite; /* have an own OVERLAPPED in this thread */
1.1288 + HANDLE wEvents[2];
1.1289 +
1.1290 + /*
1.1291 + * The stop event takes precedence by being first in the list.
1.1292 + */
1.1293 + wEvents[0] = infoPtr->evStopWriter;
1.1294 + wEvents[1] = infoPtr->evStartWriter;
1.1295 +
1.1296 + for (;;) {
1.1297 + /*
1.1298 + * Wait for the main thread to signal before attempting to write.
1.1299 + */
1.1300 +
1.1301 + waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1.1302 +
1.1303 + if (waitResult != (WAIT_OBJECT_0 + 1)) {
1.1304 + /*
1.1305 + * The start event was not signaled. It might be the stop event
1.1306 + * or an error, so exit.
1.1307 + */
1.1308 +
1.1309 + break;
1.1310 + }
1.1311 +
1.1312 + buf = infoPtr->writeBuf;
1.1313 + toWrite = infoPtr->toWrite;
1.1314 +
1.1315 + myWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1.1316 +
1.1317 + /*
1.1318 + * Loop until all of the bytes are written or an error occurs.
1.1319 + */
1.1320 +
1.1321 + while (toWrite > 0) {
1.1322 + /*
1.1323 + * Check for pending writeError
1.1324 + * Ignore all write operations until the user has been notified
1.1325 + */
1.1326 + if (infoPtr->writeError) {
1.1327 + break;
1.1328 + }
1.1329 + if (blockingWrite(infoPtr, (LPVOID) buf, (DWORD) toWrite,
1.1330 + &bytesWritten, &myWrite) == FALSE) {
1.1331 + infoPtr->writeError = GetLastError();
1.1332 + break;
1.1333 + }
1.1334 + if (bytesWritten != toWrite) {
1.1335 + /* Write timeout */
1.1336 + infoPtr->writeError = ERROR_WRITE_FAULT;
1.1337 + break;
1.1338 + }
1.1339 + toWrite -= bytesWritten;
1.1340 + buf += bytesWritten;
1.1341 + }
1.1342 +
1.1343 + CloseHandle(myWrite.hEvent);
1.1344 + /*
1.1345 + * Signal the main thread by signalling the evWritable event and
1.1346 + * then waking up the notifier thread.
1.1347 + */
1.1348 + SetEvent(infoPtr->evWritable);
1.1349 +
1.1350 + /*
1.1351 + * Alert the foreground thread. Note that we need to treat this like
1.1352 + * a critical section so the foreground thread does not terminate
1.1353 + * this thread while we are holding a mutex in the notifier code.
1.1354 + */
1.1355 +
1.1356 + Tcl_MutexLock(&serialMutex);
1.1357 + if (infoPtr->threadId != NULL) {
1.1358 + /* TIP #218. When in flight ignore the event, no one will receive it anyway */
1.1359 + Tcl_ThreadAlert(infoPtr->threadId);
1.1360 + }
1.1361 + Tcl_MutexUnlock(&serialMutex);
1.1362 + }
1.1363 +
1.1364 + return 0;
1.1365 +}
1.1366 +
1.1367 +
1.1368 +/*
1.1369 + *----------------------------------------------------------------------
1.1370 + *
1.1371 + * TclWinSerialReopen --
1.1372 + *
1.1373 + * Reopens the serial port with the OVERLAPPED FLAG set
1.1374 + *
1.1375 + * Results:
1.1376 + * Returns the new handle, or INVALID_HANDLE_VALUE
1.1377 + * Normally there shouldn't be any error,
1.1378 + * because the same channel has previously been succeesfully opened.
1.1379 + *
1.1380 + * Side effects:
1.1381 + * May close the original handle
1.1382 + *
1.1383 + *----------------------------------------------------------------------
1.1384 + */
1.1385 +
1.1386 +HANDLE
1.1387 +TclWinSerialReopen(handle, name, access)
1.1388 + HANDLE handle;
1.1389 + CONST TCHAR *name;
1.1390 + DWORD access;
1.1391 +{
1.1392 + ThreadSpecificData *tsdPtr;
1.1393 +
1.1394 + tsdPtr = SerialInit();
1.1395 +
1.1396 + /*
1.1397 + * Multithreaded I/O needs the overlapped flag set
1.1398 + * otherwise ClearCommError blocks under Windows NT/2000 until serial
1.1399 + * output is finished
1.1400 + */
1.1401 + if (CloseHandle(handle) == FALSE) {
1.1402 + return INVALID_HANDLE_VALUE;
1.1403 + }
1.1404 + handle = (*tclWinProcs->createFileProc)(name, access,
1.1405 + 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
1.1406 + return handle;
1.1407 +}
1.1408 +/*
1.1409 + *----------------------------------------------------------------------
1.1410 + *
1.1411 + * TclWinOpenSerialChannel --
1.1412 + *
1.1413 + * Constructs a Serial port channel for the specified standard OS handle.
1.1414 + * This is a helper function to break up the construction of
1.1415 + * channels into File, Console, or Serial.
1.1416 + *
1.1417 + * Results:
1.1418 + * Returns the new channel, or NULL.
1.1419 + *
1.1420 + * Side effects:
1.1421 + * May open the channel
1.1422 + *
1.1423 + *----------------------------------------------------------------------
1.1424 + */
1.1425 +
1.1426 +Tcl_Channel
1.1427 +TclWinOpenSerialChannel(handle, channelName, permissions)
1.1428 + HANDLE handle;
1.1429 + char *channelName;
1.1430 + int permissions;
1.1431 +{
1.1432 + SerialInfo *infoPtr;
1.1433 + DWORD id;
1.1434 +
1.1435 + SerialInit();
1.1436 +
1.1437 + infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo));
1.1438 + memset(infoPtr, 0, sizeof(SerialInfo));
1.1439 +
1.1440 + infoPtr->validMask = permissions;
1.1441 + infoPtr->handle = handle;
1.1442 + infoPtr->channel = (Tcl_Channel) NULL;
1.1443 + infoPtr->readable = 0;
1.1444 + infoPtr->writable = 1;
1.1445 + infoPtr->toWrite = infoPtr->writeQueue = 0;
1.1446 + infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME;
1.1447 + infoPtr->lastEventTime = 0;
1.1448 + infoPtr->lastError = infoPtr->error = 0;
1.1449 + infoPtr->threadId = Tcl_GetCurrentThread();
1.1450 + infoPtr->sysBufRead = 4096;
1.1451 + infoPtr->sysBufWrite = 4096;
1.1452 +
1.1453 + /*
1.1454 + * Use the pointer to keep the channel names unique, in case
1.1455 + * the handles are shared between multiple channels (stdin/stdout).
1.1456 + */
1.1457 +
1.1458 + wsprintfA(channelName, "file%lx", (int) infoPtr);
1.1459 +
1.1460 + infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName,
1.1461 + (ClientData) infoPtr, permissions);
1.1462 +
1.1463 +
1.1464 + SetupComm(handle, infoPtr->sysBufRead, infoPtr->sysBufWrite);
1.1465 + PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
1.1466 + | PURGE_RXCLEAR);
1.1467 +
1.1468 + /*
1.1469 + * default is blocking
1.1470 + */
1.1471 + SetCommTimeouts(handle, &no_timeout);
1.1472 +
1.1473 +
1.1474 + if (permissions & TCL_READABLE) {
1.1475 + infoPtr->osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1.1476 + }
1.1477 + if (permissions & TCL_WRITABLE) {
1.1478 + /*
1.1479 + * Initially the channel is writable
1.1480 + * and the writeThread is idle.
1.1481 + */
1.1482 + infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1.1483 + infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL);
1.1484 + infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1.1485 + infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1.1486 + InitializeCriticalSection(&infoPtr->csWrite);
1.1487 + infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
1.1488 + infoPtr, 0, &id);
1.1489 + }
1.1490 +
1.1491 + /*
1.1492 + * Files have default translation of AUTO and ^Z eof char, which
1.1493 + * means that a ^Z will be accepted as EOF when reading.
1.1494 + */
1.1495 +
1.1496 + Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1.1497 + Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1.1498 +
1.1499 + return infoPtr->channel;
1.1500 +}
1.1501 +
1.1502 +/*
1.1503 + *----------------------------------------------------------------------
1.1504 + *
1.1505 + * SerialErrorStr --
1.1506 + *
1.1507 + * Converts a Win32 serial error code to a list of readable errors
1.1508 + *
1.1509 + *----------------------------------------------------------------------
1.1510 + */
1.1511 +static void
1.1512 +SerialErrorStr(error, dsPtr)
1.1513 + DWORD error; /* Win32 serial error code */
1.1514 + Tcl_DString *dsPtr; /* Where to store string */
1.1515 +{
1.1516 + if( (error & CE_RXOVER) != 0) {
1.1517 + Tcl_DStringAppendElement(dsPtr, "RXOVER");
1.1518 + }
1.1519 + if( (error & CE_OVERRUN) != 0) {
1.1520 + Tcl_DStringAppendElement(dsPtr, "OVERRUN");
1.1521 + }
1.1522 + if( (error & CE_RXPARITY) != 0) {
1.1523 + Tcl_DStringAppendElement(dsPtr, "RXPARITY");
1.1524 + }
1.1525 + if( (error & CE_FRAME) != 0) {
1.1526 + Tcl_DStringAppendElement(dsPtr, "FRAME");
1.1527 + }
1.1528 + if( (error & CE_BREAK) != 0) {
1.1529 + Tcl_DStringAppendElement(dsPtr, "BREAK");
1.1530 + }
1.1531 + if( (error & CE_TXFULL) != 0) {
1.1532 + Tcl_DStringAppendElement(dsPtr, "TXFULL");
1.1533 + }
1.1534 + if( (error & CE_PTO) != 0) { /* PTO used to signal WRITE-TIMEOUT */
1.1535 + Tcl_DStringAppendElement(dsPtr, "TIMEOUT");
1.1536 + }
1.1537 + if( (error & ~((DWORD) (SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS))) != 0) {
1.1538 + char buf[TCL_INTEGER_SPACE + 1];
1.1539 + wsprintfA(buf, "%d", error);
1.1540 + Tcl_DStringAppendElement(dsPtr, buf);
1.1541 + }
1.1542 +}
1.1543 +/*
1.1544 + *----------------------------------------------------------------------
1.1545 + *
1.1546 + * SerialModemStatusStr --
1.1547 + *
1.1548 + * Converts a Win32 modem status list of readable flags
1.1549 + *
1.1550 + *----------------------------------------------------------------------
1.1551 + */
1.1552 +static void
1.1553 +SerialModemStatusStr(status, dsPtr)
1.1554 + DWORD status; /* Win32 modem status */
1.1555 + Tcl_DString *dsPtr; /* Where to store string */
1.1556 +{
1.1557 + Tcl_DStringAppendElement(dsPtr, "CTS");
1.1558 + Tcl_DStringAppendElement(dsPtr, (status & MS_CTS_ON) ? "1" : "0");
1.1559 + Tcl_DStringAppendElement(dsPtr, "DSR");
1.1560 + Tcl_DStringAppendElement(dsPtr, (status & MS_DSR_ON) ? "1" : "0");
1.1561 + Tcl_DStringAppendElement(dsPtr, "RING");
1.1562 + Tcl_DStringAppendElement(dsPtr, (status & MS_RING_ON) ? "1" : "0");
1.1563 + Tcl_DStringAppendElement(dsPtr, "DCD");
1.1564 + Tcl_DStringAppendElement(dsPtr, (status & MS_RLSD_ON) ? "1" : "0");
1.1565 +}
1.1566 +
1.1567 +/*
1.1568 + *----------------------------------------------------------------------
1.1569 + *
1.1570 + * SerialSetOptionProc --
1.1571 + *
1.1572 + * Sets an option on a channel.
1.1573 + *
1.1574 + * Results:
1.1575 + * A standard Tcl result. Also sets the interp's result on error if
1.1576 + * interp is not NULL.
1.1577 + *
1.1578 + * Side effects:
1.1579 + * May modify an option on a device.
1.1580 + *
1.1581 + *----------------------------------------------------------------------
1.1582 + */
1.1583 +static int
1.1584 +SerialSetOptionProc(instanceData, interp, optionName, value)
1.1585 + ClientData instanceData; /* File state. */
1.1586 + Tcl_Interp *interp; /* For error reporting - can be NULL. */
1.1587 + CONST char *optionName; /* Which option to set? */
1.1588 + CONST char *value; /* New value for option. */
1.1589 +{
1.1590 + SerialInfo *infoPtr;
1.1591 + DCB dcb;
1.1592 + BOOL result, flag;
1.1593 + size_t len, vlen;
1.1594 + Tcl_DString ds;
1.1595 + CONST TCHAR *native;
1.1596 + int argc;
1.1597 + CONST char **argv;
1.1598 +
1.1599 + infoPtr = (SerialInfo *) instanceData;
1.1600 +
1.1601 + /*
1.1602 + * Parse options
1.1603 + */
1.1604 + len = strlen(optionName);
1.1605 + vlen = strlen(value);
1.1606 +
1.1607 + /*
1.1608 + * Option -mode baud,parity,databits,stopbits
1.1609 + */
1.1610 + if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
1.1611 + if (! GetCommState(infoPtr->handle, &dcb)) {
1.1612 + if (interp) {
1.1613 + Tcl_AppendResult(interp,
1.1614 + "can't get comm state", (char *) NULL);
1.1615 + }
1.1616 + return TCL_ERROR;
1.1617 + }
1.1618 + native = Tcl_WinUtfToTChar(value, -1, &ds);
1.1619 + result = (*tclWinProcs->buildCommDCBProc)(native, &dcb);
1.1620 + Tcl_DStringFree(&ds);
1.1621 +
1.1622 + if (result == FALSE) {
1.1623 + if (interp) {
1.1624 + Tcl_AppendResult(interp,
1.1625 + "bad value for -mode: should be baud,parity,data,stop",
1.1626 + (char *) NULL);
1.1627 + }
1.1628 + return TCL_ERROR;
1.1629 + }
1.1630 +
1.1631 + /* Default settings for serial communications */
1.1632 + dcb.fBinary = TRUE;
1.1633 + dcb.fErrorChar = FALSE;
1.1634 + dcb.fNull = FALSE;
1.1635 + dcb.fAbortOnError = FALSE;
1.1636 +
1.1637 + if (! SetCommState(infoPtr->handle, &dcb) ) {
1.1638 + if (interp) {
1.1639 + Tcl_AppendResult(interp,
1.1640 + "can't set comm state", (char *) NULL);
1.1641 + }
1.1642 + return TCL_ERROR;
1.1643 + }
1.1644 + return TCL_OK;
1.1645 + }
1.1646 +
1.1647 + /*
1.1648 + * Option -handshake none|xonxoff|rtscts|dtrdsr
1.1649 + */
1.1650 + if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
1.1651 + if (! GetCommState(infoPtr->handle, &dcb)) {
1.1652 + if (interp) {
1.1653 + Tcl_AppendResult(interp,
1.1654 + "can't get comm state", (char *) NULL);
1.1655 + }
1.1656 + return TCL_ERROR;
1.1657 + }
1.1658 + /*
1.1659 + * Reset all handshake options
1.1660 + * DTR and RTS are ON by default
1.1661 + */
1.1662 + dcb.fOutX = dcb.fInX = FALSE;
1.1663 + dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fDsrSensitivity = FALSE;
1.1664 + dcb.fDtrControl = DTR_CONTROL_ENABLE;
1.1665 + dcb.fRtsControl = RTS_CONTROL_ENABLE;
1.1666 + dcb.fTXContinueOnXoff = FALSE;
1.1667 +
1.1668 + /*
1.1669 + * Adjust the handshake limits.
1.1670 + * Yes, the XonXoff limits seem to influence even hardware handshake
1.1671 + */
1.1672 + dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
1.1673 + dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
1.1674 +
1.1675 + if (strnicmp(value, "NONE", vlen) == 0) {
1.1676 + /* leave all handshake options disabled */
1.1677 + } else if (strnicmp(value, "XONXOFF", vlen) == 0) {
1.1678 + dcb.fOutX = dcb.fInX = TRUE;
1.1679 + } else if (strnicmp(value, "RTSCTS", vlen) == 0) {
1.1680 + dcb.fOutxCtsFlow = TRUE;
1.1681 + dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
1.1682 + } else if (strnicmp(value, "DTRDSR", vlen) == 0) {
1.1683 + dcb.fOutxDsrFlow = TRUE;
1.1684 + dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
1.1685 + } else {
1.1686 + if (interp) {
1.1687 + Tcl_AppendResult(interp, "bad value for -handshake: ",
1.1688 + "must be one of xonxoff, rtscts, dtrdsr or none",
1.1689 + (char *) NULL);
1.1690 + return TCL_ERROR;
1.1691 + }
1.1692 + }
1.1693 +
1.1694 + if (! SetCommState(infoPtr->handle, &dcb)) {
1.1695 + if (interp) {
1.1696 + Tcl_AppendResult(interp,
1.1697 + "can't set comm state", (char *) NULL);
1.1698 + }
1.1699 + return TCL_ERROR;
1.1700 + }
1.1701 + return TCL_OK;
1.1702 + }
1.1703 +
1.1704 + /*
1.1705 + * Option -xchar {\x11 \x13}
1.1706 + */
1.1707 + if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
1.1708 + if (! GetCommState(infoPtr->handle, &dcb)) {
1.1709 + if (interp) {
1.1710 + Tcl_AppendResult(interp,
1.1711 + "can't get comm state", (char *) NULL);
1.1712 + }
1.1713 + return TCL_ERROR;
1.1714 + }
1.1715 +
1.1716 + if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1.1717 + return TCL_ERROR;
1.1718 + }
1.1719 + if (argc == 2) {
1.1720 + dcb.XonChar = argv[0][0];
1.1721 + dcb.XoffChar = argv[1][0];
1.1722 + ckfree((char *) argv);
1.1723 + } else {
1.1724 + if (interp) {
1.1725 + Tcl_AppendResult(interp,
1.1726 + "bad value for -xchar: should be a list of two elements",
1.1727 + (char *) NULL);
1.1728 + }
1.1729 + ckfree((char *) argv);
1.1730 + return TCL_ERROR;
1.1731 + }
1.1732 +
1.1733 + if (! SetCommState(infoPtr->handle, &dcb)) {
1.1734 + if (interp) {
1.1735 + Tcl_AppendResult(interp,
1.1736 + "can't set comm state", (char *) NULL);
1.1737 + }
1.1738 + return TCL_ERROR;
1.1739 + }
1.1740 + return TCL_OK;
1.1741 + }
1.1742 +
1.1743 + /*
1.1744 + * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
1.1745 + */
1.1746 + if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
1.1747 + int i, result = TCL_OK;
1.1748 +
1.1749 + if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1.1750 + return TCL_ERROR;
1.1751 + }
1.1752 + if ((argc % 2) == 1) {
1.1753 + if (interp) {
1.1754 + Tcl_AppendResult(interp,
1.1755 + "bad value for -ttycontrol: should be a list of signal,value pairs",
1.1756 + (char *) NULL);
1.1757 + }
1.1758 + ckfree((char *) argv);
1.1759 + return TCL_ERROR;
1.1760 + }
1.1761 + for (i = 0; i < argc - 1; i += 2) {
1.1762 + if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
1.1763 + result = TCL_ERROR;
1.1764 + break;
1.1765 + }
1.1766 + if (strnicmp(argv[i], "DTR", strlen(argv[i])) == 0) {
1.1767 + if (!EscapeCommFunction(infoPtr->handle, flag ?
1.1768 + (DWORD) SETDTR : (DWORD) CLRDTR)) {
1.1769 + if (interp) {
1.1770 + Tcl_AppendResult(interp,
1.1771 + "can't set DTR signal", (char *) NULL);
1.1772 + }
1.1773 + result = TCL_ERROR;
1.1774 + break;
1.1775 + }
1.1776 + } else if (strnicmp(argv[i], "RTS", strlen(argv[i])) == 0) {
1.1777 + if (!EscapeCommFunction(infoPtr->handle, flag ?
1.1778 + (DWORD) SETRTS : (DWORD) CLRRTS)) {
1.1779 + if (interp) {
1.1780 + Tcl_AppendResult(interp,
1.1781 + "can't set RTS signal", (char *) NULL);
1.1782 + }
1.1783 + result = TCL_ERROR;
1.1784 + break;
1.1785 + }
1.1786 + } else if (strnicmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
1.1787 + if (!EscapeCommFunction(infoPtr->handle, flag ?
1.1788 + (DWORD) SETBREAK : (DWORD) CLRBREAK)) {
1.1789 + if (interp) {
1.1790 + Tcl_AppendResult(interp,
1.1791 + "can't set BREAK signal", (char *) NULL);
1.1792 + }
1.1793 + result = TCL_ERROR;
1.1794 + break;
1.1795 + }
1.1796 + } else {
1.1797 + if (interp) {
1.1798 + Tcl_AppendResult(interp, "bad signal for -ttycontrol: ",
1.1799 + "must be DTR, RTS or BREAK", (char *) NULL);
1.1800 + }
1.1801 + result = TCL_ERROR;
1.1802 + break;
1.1803 + }
1.1804 + }
1.1805 +
1.1806 + ckfree((char *) argv);
1.1807 + return result;
1.1808 + }
1.1809 +
1.1810 + /*
1.1811 + * Option -sysbuffer {read_size write_size}
1.1812 + * Option -sysbuffer read_size
1.1813 + */
1.1814 + if ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0)) {
1.1815 + /*
1.1816 + * -sysbuffer 4096 or -sysbuffer {64536 4096}
1.1817 + */
1.1818 + size_t inSize = (size_t) -1, outSize = (size_t) -1;
1.1819 +
1.1820 + if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1.1821 + return TCL_ERROR;
1.1822 + }
1.1823 + if (argc == 1) {
1.1824 + inSize = atoi(argv[0]);
1.1825 + outSize = infoPtr->sysBufWrite;
1.1826 + } else if (argc == 2) {
1.1827 + inSize = atoi(argv[0]);
1.1828 + outSize = atoi(argv[1]);
1.1829 + }
1.1830 + ckfree((char *) argv);
1.1831 + if ((inSize <= 0) || (outSize <= 0)) {
1.1832 + if (interp) {
1.1833 + Tcl_AppendResult(interp,
1.1834 + "bad value for -sysbuffer: should be a list of one or two integers > 0",
1.1835 + (char *) NULL);
1.1836 + }
1.1837 + return TCL_ERROR;
1.1838 + }
1.1839 + if (! SetupComm(infoPtr->handle, inSize, outSize)) {
1.1840 + if (interp) {
1.1841 + Tcl_AppendResult(interp,
1.1842 + "can't setup comm buffers", (char *) NULL);
1.1843 + }
1.1844 + return TCL_ERROR;
1.1845 + }
1.1846 + infoPtr->sysBufRead = inSize;
1.1847 + infoPtr->sysBufWrite = outSize;
1.1848 +
1.1849 + /*
1.1850 + * Adjust the handshake limits.
1.1851 + * Yes, the XonXoff limits seem to influence even hardware handshake
1.1852 + */
1.1853 + if (! GetCommState(infoPtr->handle, &dcb)) {
1.1854 + if (interp) {
1.1855 + Tcl_AppendResult(interp,
1.1856 + "can't get comm state", (char *) NULL);
1.1857 + }
1.1858 + return TCL_ERROR;
1.1859 + }
1.1860 + dcb.XonLim = (WORD) (infoPtr->sysBufRead*1/2);
1.1861 + dcb.XoffLim = (WORD) (infoPtr->sysBufRead*1/4);
1.1862 + if (! SetCommState(infoPtr->handle, &dcb)) {
1.1863 + if (interp) {
1.1864 + Tcl_AppendResult(interp,
1.1865 + "can't set comm state", (char *) NULL);
1.1866 + }
1.1867 + return TCL_ERROR;
1.1868 + }
1.1869 + return TCL_OK;
1.1870 + }
1.1871 +
1.1872 + /*
1.1873 + * Option -pollinterval msec
1.1874 + */
1.1875 + if ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0)) {
1.1876 +
1.1877 + if ( Tcl_GetInt(interp, value, &(infoPtr->blockTime)) != TCL_OK ) {
1.1878 + return TCL_ERROR;
1.1879 + }
1.1880 + return TCL_OK;
1.1881 + }
1.1882 +
1.1883 + /*
1.1884 + * Option -timeout msec
1.1885 + */
1.1886 + if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
1.1887 + int msec;
1.1888 + COMMTIMEOUTS tout = {0,0,0,0,0};
1.1889 +
1.1890 + if ( Tcl_GetInt(interp, value, &msec) != TCL_OK ) {
1.1891 + return TCL_ERROR;
1.1892 + }
1.1893 + tout.ReadTotalTimeoutConstant = msec;
1.1894 + if (! SetCommTimeouts(infoPtr->handle, &tout)) {
1.1895 + if (interp) {
1.1896 + Tcl_AppendResult(interp,
1.1897 + "can't set comm timeouts", (char *) NULL);
1.1898 + }
1.1899 + return TCL_ERROR;
1.1900 + }
1.1901 +
1.1902 + return TCL_OK;
1.1903 + }
1.1904 +
1.1905 + return Tcl_BadChannelOption(interp, optionName,
1.1906 + "mode handshake pollinterval sysbuffer timeout ttycontrol xchar");
1.1907 +}
1.1908 +
1.1909 +/*
1.1910 + *----------------------------------------------------------------------
1.1911 + *
1.1912 + * SerialGetOptionProc --
1.1913 + *
1.1914 + * Gets a mode associated with an IO channel. If the optionName arg
1.1915 + * is non NULL, retrieves the value of that option. If the optionName
1.1916 + * arg is NULL, retrieves a list of alternating option names and
1.1917 + * values for the given channel.
1.1918 + *
1.1919 + * Results:
1.1920 + * A standard Tcl result. Also sets the supplied DString to the
1.1921 + * string value of the option(s) returned.
1.1922 + *
1.1923 + * Side effects:
1.1924 + * The string returned by this function is in static storage and
1.1925 + * may be reused at any time subsequent to the call.
1.1926 + *
1.1927 + *----------------------------------------------------------------------
1.1928 + */
1.1929 +static int
1.1930 +SerialGetOptionProc(instanceData, interp, optionName, dsPtr)
1.1931 + ClientData instanceData; /* File state. */
1.1932 + Tcl_Interp *interp; /* For error reporting - can be NULL. */
1.1933 + CONST char *optionName; /* Option to get. */
1.1934 + Tcl_DString *dsPtr; /* Where to store value(s). */
1.1935 +{
1.1936 + SerialInfo *infoPtr;
1.1937 + DCB dcb;
1.1938 + size_t len;
1.1939 + int valid = 0; /* flag if valid option parsed */
1.1940 +
1.1941 + infoPtr = (SerialInfo *) instanceData;
1.1942 +
1.1943 + if (optionName == NULL) {
1.1944 + len = 0;
1.1945 + } else {
1.1946 + len = strlen(optionName);
1.1947 + }
1.1948 +
1.1949 + /*
1.1950 + * get option -mode
1.1951 + */
1.1952 +
1.1953 + if (len == 0) {
1.1954 + Tcl_DStringAppendElement(dsPtr, "-mode");
1.1955 + }
1.1956 + if ((len == 0) ||
1.1957 + ((len > 2) && (strncmp(optionName, "-mode", len) == 0))) {
1.1958 +
1.1959 + char parity;
1.1960 + char *stop;
1.1961 + char buf[2 * TCL_INTEGER_SPACE + 16];
1.1962 +
1.1963 + if (! GetCommState(infoPtr->handle, &dcb)) {
1.1964 + if (interp) {
1.1965 + Tcl_AppendResult(interp,
1.1966 + "can't get comm state", (char *) NULL);
1.1967 + }
1.1968 + return TCL_ERROR;
1.1969 + }
1.1970 +
1.1971 + valid = 1;
1.1972 + parity = 'n';
1.1973 + if (dcb.Parity <= 4) {
1.1974 + parity = "noems"[dcb.Parity];
1.1975 + }
1.1976 + stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
1.1977 + (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
1.1978 +
1.1979 + wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity,
1.1980 + dcb.ByteSize, stop);
1.1981 + Tcl_DStringAppendElement(dsPtr, buf);
1.1982 + }
1.1983 +
1.1984 + /*
1.1985 + * get option -pollinterval
1.1986 + */
1.1987 +
1.1988 + if (len == 0) {
1.1989 + Tcl_DStringAppendElement(dsPtr, "-pollinterval");
1.1990 + }
1.1991 + if ((len == 0) ||
1.1992 + ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0))) {
1.1993 + char buf[TCL_INTEGER_SPACE + 1];
1.1994 +
1.1995 + valid = 1;
1.1996 + wsprintfA(buf, "%d", infoPtr->blockTime);
1.1997 + Tcl_DStringAppendElement(dsPtr, buf);
1.1998 + }
1.1999 +
1.2000 + /*
1.2001 + * get option -sysbuffer
1.2002 + */
1.2003 +
1.2004 + if (len == 0) {
1.2005 + Tcl_DStringAppendElement(dsPtr, "-sysbuffer");
1.2006 + Tcl_DStringStartSublist(dsPtr);
1.2007 + }
1.2008 + if ((len == 0) ||
1.2009 + ((len > 1) && (strncmp(optionName, "-sysbuffer", len) == 0))) {
1.2010 +
1.2011 + char buf[TCL_INTEGER_SPACE + 1];
1.2012 + valid = 1;
1.2013 +
1.2014 + wsprintfA(buf, "%d", infoPtr->sysBufRead);
1.2015 + Tcl_DStringAppendElement(dsPtr, buf);
1.2016 + wsprintfA(buf, "%d", infoPtr->sysBufWrite);
1.2017 + Tcl_DStringAppendElement(dsPtr, buf);
1.2018 + }
1.2019 + if (len == 0) {
1.2020 + Tcl_DStringEndSublist(dsPtr);
1.2021 + }
1.2022 +
1.2023 + /*
1.2024 + * get option -xchar
1.2025 + */
1.2026 +
1.2027 + if (len == 0) {
1.2028 + Tcl_DStringAppendElement(dsPtr, "-xchar");
1.2029 + Tcl_DStringStartSublist(dsPtr);
1.2030 + }
1.2031 + if ((len == 0) ||
1.2032 + ((len > 1) && (strncmp(optionName, "-xchar", len) == 0))) {
1.2033 +
1.2034 + char buf[4];
1.2035 + valid = 1;
1.2036 +
1.2037 + if (! GetCommState(infoPtr->handle, &dcb)) {
1.2038 + if (interp) {
1.2039 + Tcl_AppendResult(interp,
1.2040 + "can't get comm state", (char *) NULL);
1.2041 + }
1.2042 + return TCL_ERROR;
1.2043 + }
1.2044 + sprintf(buf, "%c", dcb.XonChar);
1.2045 + Tcl_DStringAppendElement(dsPtr, buf);
1.2046 + sprintf(buf, "%c", dcb.XoffChar);
1.2047 + Tcl_DStringAppendElement(dsPtr, buf);
1.2048 + }
1.2049 + if (len == 0) {
1.2050 + Tcl_DStringEndSublist(dsPtr);
1.2051 + }
1.2052 +
1.2053 + /*
1.2054 + * get option -lasterror
1.2055 + * option is readonly and returned by [fconfigure chan -lasterror]
1.2056 + * but not returned by unnamed [fconfigure chan]
1.2057 + */
1.2058 +
1.2059 + if ( (len > 1) && (strncmp(optionName, "-lasterror", len) == 0) ) {
1.2060 + valid = 1;
1.2061 + SerialErrorStr(infoPtr->lastError, dsPtr);
1.2062 + }
1.2063 +
1.2064 + /*
1.2065 + * get option -queue
1.2066 + * option is readonly and returned by [fconfigure chan -queue]
1.2067 + */
1.2068 +
1.2069 + if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
1.2070 + char buf[TCL_INTEGER_SPACE + 1];
1.2071 + COMSTAT cStat;
1.2072 + DWORD error;
1.2073 + int inBuffered, outBuffered, count;
1.2074 +
1.2075 + valid = 1;
1.2076 +
1.2077 + /*
1.2078 + * Query the pending data in Tcl's internal queues
1.2079 + */
1.2080 + inBuffered = Tcl_InputBuffered(infoPtr->channel);
1.2081 + outBuffered = Tcl_OutputBuffered(infoPtr->channel);
1.2082 +
1.2083 + /*
1.2084 + * Query the number of bytes in our output queue:
1.2085 + * 1. The bytes pending in the output thread
1.2086 + * 2. The bytes in the system drivers buffer
1.2087 + * The writer thread should not interfere this action.
1.2088 + */
1.2089 + EnterCriticalSection(&infoPtr->csWrite);
1.2090 + ClearCommError( infoPtr->handle, &error, &cStat );
1.2091 + count = (int)cStat.cbOutQue + infoPtr->writeQueue;
1.2092 + LeaveCriticalSection(&infoPtr->csWrite);
1.2093 +
1.2094 + wsprintfA(buf, "%d", inBuffered + cStat.cbInQue);
1.2095 + Tcl_DStringAppendElement(dsPtr, buf);
1.2096 + wsprintfA(buf, "%d", outBuffered + count);
1.2097 + Tcl_DStringAppendElement(dsPtr, buf);
1.2098 + }
1.2099 +
1.2100 + /*
1.2101 + * get option -ttystatus
1.2102 + * option is readonly and returned by [fconfigure chan -ttystatus]
1.2103 + * but not returned by unnamed [fconfigure chan]
1.2104 + */
1.2105 + if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
1.2106 +
1.2107 + DWORD status;
1.2108 +
1.2109 + if (! GetCommModemStatus(infoPtr->handle, &status)) {
1.2110 + if (interp) {
1.2111 + Tcl_AppendResult(interp,
1.2112 + "can't get tty status", (char *) NULL);
1.2113 + }
1.2114 + return TCL_ERROR;
1.2115 + }
1.2116 + valid = 1;
1.2117 + SerialModemStatusStr(status, dsPtr);
1.2118 + }
1.2119 +
1.2120 + if (valid) {
1.2121 + return TCL_OK;
1.2122 + } else {
1.2123 + return Tcl_BadChannelOption(interp, optionName,
1.2124 + "mode pollinterval lasterror queue sysbuffer ttystatus xchar");
1.2125 + }
1.2126 +}
1.2127 +
1.2128 +/*
1.2129 + *----------------------------------------------------------------------
1.2130 + *
1.2131 + * SerialThreadActionProc --
1.2132 + *
1.2133 + * Insert or remove any thread local refs to this channel.
1.2134 + *
1.2135 + * Results:
1.2136 + * None.
1.2137 + *
1.2138 + * Side effects:
1.2139 + * Changes thread local list of valid channels.
1.2140 + *
1.2141 + *----------------------------------------------------------------------
1.2142 + */
1.2143 +
1.2144 +static void
1.2145 +SerialThreadActionProc (instanceData, action)
1.2146 + ClientData instanceData;
1.2147 + int action;
1.2148 +{
1.2149 + SerialInfo *infoPtr = (SerialInfo *) instanceData;
1.2150 +
1.2151 + /* We do not access firstSerialPtr in the thread structures. This is
1.2152 + * not for all serials managed by the thread, but only those we are
1.2153 + * watching. Removal of the filevent handlers before transfer thus
1.2154 + * takes care of this structure.
1.2155 + */
1.2156 +
1.2157 + Tcl_MutexLock(&serialMutex);
1.2158 + if (action == TCL_CHANNEL_THREAD_INSERT) {
1.2159 + /* We can't copy the thread information from the channel when
1.2160 + * the channel is created. At this time the channel back
1.2161 + * pointer has not been set yet. However in that case the
1.2162 + * threadId has already been set by TclpCreateCommandChannel
1.2163 + * itself, so the structure is still good.
1.2164 + */
1.2165 +
1.2166 + SerialInit ();
1.2167 + if (infoPtr->channel != NULL) {
1.2168 + infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
1.2169 + }
1.2170 + } else {
1.2171 + infoPtr->threadId = NULL;
1.2172 + }
1.2173 + Tcl_MutexUnlock(&serialMutex);
1.2174 +}