/[dtapublic]/projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclwinserial.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclwinserial.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

to_be_filed/sf_code/esrgpcpj/shared/tcl_base/tclwinserial.c revision 29 by dashley, Sat Oct 8 07:08:47 2016 UTC projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclwinserial.c revision 220 by dashley, Sun Jul 22 15:58:07 2018 UTC
# Line 1  Line 1 
 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tcl_base/tclwinserial.c,v 1.1.1.1 2001/06/13 04:50:16 dtashley Exp $ */  
   
 /*  
  * Tclwinserial.c --  
  *  
  *  This file implements the Windows-specific serial port functions,  
  *  and the "serial" channel driver.  
  *  
  * Copyright (c) 1999 by Scriptics Corp.  
  *  
  * See the file "license.terms" for information on usage and redistribution  
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.  
  * Changes by Rolf.Schroedter@dlr.de June 25-27, 1999  
  *  
  * RCS: @(#) $Id: tclwinserial.c,v 1.1.1.1 2001/06/13 04:50:16 dtashley Exp $  
  */  
   
 #include "tclWinInt.h"  
   
 #include <dos.h>  
 #include <fcntl.h>  
 #include <io.h>  
 #include <sys/stat.h>  
   
 /*  
  * The following variable is used to tell whether this module has been  
  * initialized.  
  */  
   
 static int initialized = 0;  
   
 /*  
  * Bit masks used in the flags field of the SerialInfo structure below.  
  */  
   
 #define SERIAL_PENDING  (1<<0)  /* Message is pending in the queue. */  
 #define SERIAL_ASYNC    (1<<1)  /* Channel is non-blocking. */  
   
 /*  
  * Bit masks used in the sharedFlags field of the SerialInfo structure below.  
  */  
   
 #define SERIAL_EOF      (1<<2)  /* Serial has reached EOF. */  
 #define SERIAL_ERROR    (1<<4)  
 #define SERIAL_WRITE    (1<<5)  /* enables fileevent writable  
                  * one time after write operation */  
   
 /*  
  * Default time to block between checking status on the serial port.  
  */  
 #define SERIAL_DEFAULT_BLOCKTIME    10  /* 10 msec */  
   
 /*  
  * Define Win32 read/write error masks returned by ClearCommError()  
  */  
 #define SERIAL_READ_ERRORS      ( CE_RXOVER | CE_OVERRUN | CE_RXPARITY \  
                                 | CE_FRAME  | CE_BREAK )  
 #define SERIAL_WRITE_ERRORS     ( CE_TXFULL )  
   
 /*  
  * This structure describes per-instance data for a serial based channel.  
  */  
   
 typedef struct SerialInfo {  
     HANDLE handle;  
     struct SerialInfo *nextPtr; /* Pointer to next registered serial. */  
     Tcl_Channel channel;        /* Pointer to channel structure. */  
     int validMask;              /* OR'ed combination of TCL_READABLE,  
                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates  
                                  * which operations are valid on the file. */  
     int watchMask;              /* OR'ed combination of TCL_READABLE,  
                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates  
                                  * which events should be reported. */  
     int flags;                  /* State flags, see above for a list. */  
     int writable;               /* flag that the channel is readable */  
     int readable;               /* flag that the channel is readable */  
     int blockTime;              /* max. blocktime in msec */  
     DWORD error;                /* pending error code returned by  
                                  * ClearCommError() */  
     DWORD lastError;            /* last error code, can be fetched with  
                                  * fconfigure chan -lasterror */  
 } SerialInfo;  
   
 typedef struct ThreadSpecificData {  
     /*  
      * The following pointer refers to the head of the list of serials  
      * that are being watched for file events.  
      */  
   
     SerialInfo *firstSerialPtr;  
 } ThreadSpecificData;  
   
 static Tcl_ThreadDataKey dataKey;  
   
 /*  
  * The following structure is what is added to the Tcl event queue when  
  * serial events are generated.  
  */  
   
 typedef struct SerialEvent {  
     Tcl_Event header;       /* Information that is standard for  
                              * all events. */  
     SerialInfo *infoPtr;    /* Pointer to serial info structure.  Note  
                              * that we still have to verify that the  
                              * serial exists before dereferencing this  
                              * pointer. */  
 } SerialEvent;  
   
 COMMTIMEOUTS timeout_sync  = {   /* Timouts for blocking mode */  
     MAXDWORD,        /* ReadIntervalTimeout */  
     MAXDWORD,        /* ReadTotalTimeoutMultiplier */  
     MAXDWORD-1,      /* ReadTotalTimeoutConstant,  
             MAXDWORD-1 works for both Win95/NT */  
     0,               /* WriteTotalTimeoutMultiplier */  
     0,               /* WriteTotalTimeoutConstant */  
 };  
   
 COMMTIMEOUTS timeout_async  = {   /* Timouts for non-blocking mode */  
     0,               /* ReadIntervalTimeout */  
     0,               /* ReadTotalTimeoutMultiplier */  
     1,               /* ReadTotalTimeoutConstant */  
     0,               /* WriteTotalTimeoutMultiplier */  
     0,               /* WriteTotalTimeoutConstant */  
 };  
   
 /*  
  * Declarations for functions used only in this file.  
  */  
   
 static int      SerialBlockProc(ClientData instanceData, int mode);  
 static void     SerialCheckProc(ClientData clientData, int flags);  
 static int      SerialCloseProc(ClientData instanceData,  
                 Tcl_Interp *interp);  
 static int      SerialEventProc(Tcl_Event *evPtr, int flags);  
 static void     SerialExitHandler(ClientData clientData);  
 static int      SerialGetHandleProc(ClientData instanceData,  
                 int direction, ClientData *handlePtr);  
 static ThreadSpecificData *SerialInit(void);  
 static int      SerialInputProc(ClientData instanceData, char *buf,  
                 int toRead, int *errorCode);  
 static int      SerialOutputProc(ClientData instanceData, char *buf,  
                 int toWrite, int *errorCode);  
 static void     SerialSetupProc(ClientData clientData, int flags);  
 static void     SerialWatchProc(ClientData instanceData, int mask);  
 static void     ProcExitHandler(ClientData clientData);  
 static int       SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData,  
                 Tcl_Interp *interp, char *optionName,  
                 Tcl_DString *dsPtr));  
 static int       SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData,  
                 Tcl_Interp *interp, char *optionName,  
                 char *value));  
   
 /*  
  * This structure describes the channel type structure for command serial  
  * based IO.  
  */  
   
 static Tcl_ChannelType serialChannelType = {  
     "serial",               /* Type name. */  
     SerialBlockProc,        /* Set blocking or non-blocking mode.*/  
     SerialCloseProc,        /* Close proc. */  
     SerialInputProc,        /* Input proc. */  
     SerialOutputProc,       /* Output proc. */  
     NULL,                   /* Seek proc. */  
     SerialSetOptionProc,    /* Set option proc. */  
     SerialGetOptionProc,    /* Get option proc. */  
     SerialWatchProc,        /* Set up notifier to watch the channel. */  
     SerialGetHandleProc,    /* Get an OS handle from channel. */  
 };  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialInit --  
  *  
  *  This function initializes the static variables for this file.  
  *  
  * Results:  
  *  None.  
  *  
  * Side effects:  
  *  Creates a new event source.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static ThreadSpecificData *  
 SerialInit()  
 {  
     ThreadSpecificData *tsdPtr;  
   
     /*  
      * Check the initialized flag first, then check it again in the mutex.  
      * This is a speed enhancement.  
      */  
   
     if (!initialized) {  
         if (!initialized) {  
             initialized = 1;  
             Tcl_CreateExitHandler(ProcExitHandler, NULL);  
         }  
     }  
   
     tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);  
     if (tsdPtr == NULL) {  
         tsdPtr = TCL_TSD_INIT(&dataKey);  
         tsdPtr->firstSerialPtr = NULL;  
         Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);  
         Tcl_CreateThreadExitHandler(SerialExitHandler, NULL);  
     }  
     return tsdPtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialExitHandler --  
  *  
  *  This function is called to cleanup the serial module before  
  *  Tcl is unloaded.  
  *  
  * Results:  
  *  None.  
  *  
  * Side effects:  
  *  Removes the serial event source.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 SerialExitHandler(  
     ClientData clientData)  /* Old window proc */  
 {  
     Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * ProcExitHandler --  
  *  
  *  This function is called to cleanup the process list before  
  *  Tcl is unloaded.  
  *  
  * Results:  
  *  None.  
  *  
  * Side effects:  
  *  Resets the process list.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 ProcExitHandler(  
     ClientData clientData)  /* Old window proc */  
 {  
     initialized = 0;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialBlockTime --  
  *  
  *  Wrapper to set Tcl's block time in msec  
  *  
  * Results:  
  *  None.  
  *----------------------------------------------------------------------  
  */  
   
 void  
 SerialBlockTime(  
     int msec)          /* milli-seconds */  
 {  
     Tcl_Time blockTime;  
   
     blockTime.sec  =  msec / 1000;  
     blockTime.usec = (msec % 1000) * 1000;  
     Tcl_SetMaxBlockTime(&blockTime);  
 }  
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialSetupProc --  
  *  
  *  This procedure is invoked before Tcl_DoOneEvent blocks waiting  
  *  for an event.  
  *  
  * Results:  
  *  None.  
  *  
  * Side effects:  
  *  Adjusts the block time if needed.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 SerialSetupProc(  
     ClientData data,    /* Not used. */  
     int flags)          /* Event flags as passed to Tcl_DoOneEvent. */  
 {  
     SerialInfo *infoPtr;  
     int block = 1;  
     int msec = INT_MAX; /* min. found block time */  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     if (!(flags & TCL_FILE_EVENTS)) {  
         return;  
     }  
   
     /*  
      * Look to see if any events handlers installed. If they are, do not block.  
      */  
   
     for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;  
             infoPtr = infoPtr->nextPtr) {  
   
         if( infoPtr->watchMask & (TCL_WRITABLE | TCL_READABLE) ) {  
             block = 0;  
             msec = min( msec, infoPtr->blockTime );  
         }  
     }  
   
     if (!block) {  
         SerialBlockTime(msec);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialCheckProc --  
  *  
  *  This procedure is called by Tcl_DoOneEvent to check the serial  
  *  event source for events.  
  *  
  * Results:  
  *  None.  
  *  
  * Side effects:  
  *  May queue an event.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 SerialCheckProc(  
     ClientData data,    /* Not used. */  
     int flags)          /* Event flags as passed to Tcl_DoOneEvent. */  
 {  
     SerialInfo *infoPtr;  
     SerialEvent *evPtr;  
     int needEvent;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
     COMSTAT cStat;  
   
     if (!(flags & TCL_FILE_EVENTS)) {  
         return;  
     }  
   
     /*  
      * Queue events for any ready serials that don't already have events  
      * queued.  
      */  
   
     for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;  
             infoPtr = infoPtr->nextPtr) {  
         if (infoPtr->flags & SERIAL_PENDING) {  
             continue;  
         }  
   
         needEvent = 0;  
   
         /*  
          * If any READABLE or WRITABLE watch mask is set  
          * call ClearCommError to poll cbInQue,cbOutQue  
          * Window errors are ignored here  
          */  
   
         if( infoPtr->watchMask & (TCL_WRITABLE | TCL_READABLE) ) {  
             if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {  
                 /*  
                  * Look for empty output buffer.  If empty, poll.  
                  */  
   
                 if( infoPtr->watchMask & TCL_WRITABLE ) {  
                     /*  
                      * force fileevent after serial write error  
                      */  
                     if (((infoPtr->flags & SERIAL_WRITE) != 0) &&  
                             ((cStat.cbOutQue == 0) ||  
                                     (infoPtr->error & SERIAL_WRITE_ERRORS))) {  
                         /*  
                          * allow only one fileevent after each callback  
                          */  
   
                         infoPtr->flags &= ~SERIAL_WRITE;  
                         infoPtr->writable = 1;  
                         needEvent = 1;  
                     }  
                 }  
                   
                 /*  
                  * Look for characters already pending in windows queue.  
                  * If they are, poll.  
                  */  
   
                 if( infoPtr->watchMask & TCL_READABLE ) {  
                     /*  
                      * force fileevent after serial read error  
                      */  
                     if( (cStat.cbInQue > 0) ||  
                             (infoPtr->error & SERIAL_READ_ERRORS) ) {  
                         infoPtr->readable = 1;  
                         needEvent = 1;  
                     }  
                 }  
             }  
         }  
   
         /*  
          * Queue an event if the serial is signaled for reading or writing.  
          */  
   
         if (needEvent) {  
             infoPtr->flags |= SERIAL_PENDING;  
             evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent));  
             evPtr->header.proc = SerialEventProc;  
             evPtr->infoPtr = infoPtr;  
             Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);  
         }  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialBlockProc --  
  *  
  *  Set blocking or non-blocking mode on channel.  
  *  
  * Results:  
  *  0 if successful, errno when failed.  
  *  
  * Side effects:  
  *  Sets the device into blocking or non-blocking mode.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 SerialBlockProc(  
     ClientData instanceData,    /* Instance data for channel. */  
     int mode)                   /* TCL_MODE_BLOCKING or  
                                  * TCL_MODE_NONBLOCKING. */  
 {  
     COMMTIMEOUTS *timeout;  
     int errorCode = 0;  
   
     SerialInfo *infoPtr = (SerialInfo *) instanceData;  
   
     /*  
      * Serial IO on Windows can not be switched between blocking & nonblocking,  
      * hence we have to emulate the behavior. This is done in the input  
      * function by checking against a bit in the state. We set or unset the  
      * bit here to cause the input function to emulate the correct behavior.  
      */  
   
     if (mode == TCL_MODE_NONBLOCKING) {  
         infoPtr->flags |= SERIAL_ASYNC;  
         timeout = &timeout_async;  
     } else {  
         infoPtr->flags &= ~(SERIAL_ASYNC);  
         timeout = &timeout_sync;  
     }  
     if (SetCommTimeouts(infoPtr->handle, timeout) == FALSE) {  
         TclWinConvertError(GetLastError());  
         errorCode = errno;  
     }  
     return errorCode;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialCloseProc --  
  *  
  *  Closes a serial based IO channel.  
  *  
  * Results:  
  *  0 on success, errno otherwise.  
  *  
  * Side effects:  
  *  Closes the physical channel.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 SerialCloseProc(  
     ClientData instanceData,    /* Pointer to SerialInfo structure. */  
     Tcl_Interp *interp)         /* For error reporting. */  
 {  
     SerialInfo *serialPtr = (SerialInfo *) instanceData;  
     int errorCode, result = 0;  
     SerialInfo *infoPtr, **nextPtrPtr;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     errorCode = 0;  
     serialPtr->validMask &= ~TCL_READABLE;  
     serialPtr->validMask &= ~TCL_WRITABLE;  
   
     /*  
      * Don't close the Win32 handle if the handle is a standard channel  
      * during the exit process.  Otherwise, one thread may kill the stdio  
      * of another.  
      */  
   
     if (!TclInExit()  
         || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)  
         && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)  
         && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {  
     if (CloseHandle(serialPtr->handle) == FALSE) {  
         TclWinConvertError(GetLastError());  
         errorCode = errno;  
     }  
     }  
   
     serialPtr->watchMask &= serialPtr->validMask;  
   
     /*  
      * Remove the file from the list of watched files.  
      */  
   
     for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr;  
             infoPtr != NULL;  
                     nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {  
         if (infoPtr == (SerialInfo *)serialPtr) {  
             *nextPtrPtr = infoPtr->nextPtr;  
             break;  
         }  
     }  
   
     /*  
      * Wrap the error file into a channel and give it to the cleanup  
      * routine.  
      */  
   
     ckfree((char*) serialPtr);  
   
     if (errorCode == 0) {  
         return result;  
     }  
     return errorCode;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialInputProc --  
  *  
  *  Reads input from the IO channel into the buffer given. Returns  
  *  count of how many bytes were actually read, and an error indication.  
  *  
  * Results:  
  *  A count of how many bytes were read is returned and an error  
  *  indication is returned in an output argument.  
  *  
  * Side effects:  
  *  Reads input from the actual channel.  
  *  
  *----------------------------------------------------------------------  
  */  
 static int  
 SerialInputProc(  
     ClientData instanceData,    /* Serial state. */  
     char *buf,                  /* Where to store data read. */  
     int bufSize,                /* How much space is available  
                                  * in the buffer? */  
     int *errorCode)             /* Where to store error code. */  
 {  
     SerialInfo *infoPtr = (SerialInfo *) instanceData;  
     DWORD bytesRead = 0;  
     DWORD err;  
     COMSTAT cStat;  
   
     *errorCode = 0;  
   
     /*  
      * Check if there is a CommError pending from SerialCheckProc  
      */  
     if( infoPtr->error & SERIAL_READ_ERRORS ){  
         goto commError;  
     }  
   
     /*  
      * Look for characters already pending in windows queue.  
      * This is the mainly restored good old code from Tcl8.0  
      */  
   
     if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {  
         /*  
          * Check for errors here, but not in the evSetup/Check procedures  
          */  
   
         if( infoPtr->error & SERIAL_READ_ERRORS ) {  
             goto commError;  
         }  
         if( infoPtr->flags & SERIAL_ASYNC ) {  
             /*  
              * NON_BLOCKING mode:  
              * Avoid blocking by reading more bytes than available  
              * in input buffer  
              */  
   
             if( cStat.cbInQue > 0 ) {  
                 if( (DWORD) bufSize > cStat.cbInQue ) {  
                     bufSize = cStat.cbInQue;  
                 }  
             } else {  
                 errno = *errorCode = EAGAIN;  
                 return -1;  
             }  
         } else {  
             /*  
              * BLOCKING mode:  
              * Tcl trys to read a full buffer of 4 kBytes here  
              */  
   
             if( cStat.cbInQue > 0 ) {  
                 if( (DWORD) bufSize > cStat.cbInQue ) {  
                     bufSize = cStat.cbInQue;  
                 }  
             } else {  
                 bufSize = 1;  
             }  
         }  
     }  
   
     if( bufSize == 0 ) {  
         return bytesRead = 0;  
     }  
   
     if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,  
         NULL) == FALSE) {  
         err = GetLastError();  
         if (err != ERROR_IO_PENDING) {  
             goto error;  
         }  
     }  
     return bytesRead;  
   
     error:  
     TclWinConvertError(GetLastError());  
     *errorCode = errno;  
     return -1;  
   
     commError:  
     infoPtr->lastError = infoPtr->error;  /* save last error code */  
     infoPtr->error = 0;                   /* reset error code */  
     *errorCode = EIO;                     /* to return read-error only once */  
     return -1;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialOutputProc --  
  *  
  *  Writes the given output on the IO channel. Returns count of how  
  *  many characters were actually written, and an error indication.  
  *  
  * Results:  
  *  A count of how many characters were written is returned and an  
  *  error indication is returned in an output argument.  
  *  
  * Side effects:  
  *  Writes output on the actual channel.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 SerialOutputProc(  
     ClientData instanceData,    /* Serial state. */  
     char *buf,                  /* The data buffer. */  
     int toWrite,                /* How many bytes to write? */  
     int *errorCode)             /* Where to store error code. */  
 {  
     SerialInfo *infoPtr = (SerialInfo *) instanceData;  
     DWORD bytesWritten, err;  
   
     *errorCode = 0;  
   
     /*  
      * Check if there is a CommError pending from SerialCheckProc  
      */  
     if( infoPtr->error & SERIAL_WRITE_ERRORS ){  
         infoPtr->lastError = infoPtr->error;  /* save last error code */  
         infoPtr->error = 0;                   /* reset error code */  
         *errorCode = EIO;               /* to return read-error only once */  
         return -1;  
     }  
   
     /*  
      * Check for a background error on the last write.  
      * Allow one write-fileevent after each callback  
      */  
   
     if( toWrite ) {  
         infoPtr->flags |= SERIAL_WRITE;  
     }  
   
     if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,  
             &bytesWritten, NULL) == FALSE) {  
         err = GetLastError();  
         if (err != ERROR_IO_PENDING) {  
             TclWinConvertError(GetLastError());  
             goto error;  
         }  
     }  
   
     return bytesWritten;  
   
 error:  
     *errorCode = errno;  
     return -1;  
   
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialEventProc --  
  *  
  *  This function is invoked by Tcl_ServiceEvent when a file event  
  *  reaches the front of the event queue.  This procedure invokes  
  *  Tcl_NotifyChannel on the serial.  
  *  
  * Results:  
  *  Returns 1 if the event was handled, meaning it should be removed  
  *  from the queue.  Returns 0 if the event was not handled, meaning  
  *  it should stay on the queue.  The only time the event isn't  
  *  handled is if the TCL_FILE_EVENTS flag bit isn't set.  
  *  
  * Side effects:  
  *  Whatever the notifier callback does.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 SerialEventProc(  
     Tcl_Event *evPtr,   /* Event to service. */  
     int flags)          /* Flags that indicate what events to  
                          * handle, such as TCL_FILE_EVENTS. */  
 {  
     SerialEvent *serialEvPtr = (SerialEvent *)evPtr;  
     SerialInfo *infoPtr;  
     int mask;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     if (!(flags & TCL_FILE_EVENTS)) {  
         return 0;  
     }  
   
     /*  
      * Search through the list of watched serials for the one whose handle  
      * matches the event.  We do this rather than simply dereferencing  
      * the handle in the event so that serials can be deleted while the  
      * event is in the queue.  
      */  
   
     for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;  
             infoPtr = infoPtr->nextPtr) {  
         if (serialEvPtr->infoPtr == infoPtr) {  
             infoPtr->flags &= ~(SERIAL_PENDING);  
             break;  
         }  
     }  
   
     /*  
      * Remove stale events.  
      */  
   
     if (!infoPtr) {  
         return 1;  
     }  
   
     /*  
      * Check to see if the serial is readable.  Note  
      * that we can't tell if a serial is writable, so we always report it  
      * as being writable unless we have detected EOF.  
      */  
   
     mask = 0;  
     if( infoPtr->watchMask & TCL_WRITABLE ) {  
         if( infoPtr->writable ) {  
             mask |= TCL_WRITABLE;  
             infoPtr->writable = 0;  
         }  
     }  
   
     if( infoPtr->watchMask & TCL_READABLE ) {  
         if( infoPtr->readable ) {  
             mask |= TCL_READABLE;  
             infoPtr->readable = 0;  
         }  
     }  
   
     /*  
      * Inform the channel of the events.  
      */  
   
     Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);  
     return 1;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialWatchProc --  
  *  
  *  Called by the notifier to set up to watch for events on this  
  *  channel.  
  *  
  * Results:  
  *  None.  
  *  
  * Side effects:  
  *  None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 SerialWatchProc(  
     ClientData instanceData,     /* Serial state. */  
     int mask)                    /* What events to watch for, OR-ed  
                                   * combination of TCL_READABLE,  
                                   * TCL_WRITABLE and TCL_EXCEPTION. */  
 {  
     SerialInfo **nextPtrPtr, *ptr;  
     SerialInfo *infoPtr = (SerialInfo *) instanceData;  
     int oldMask = infoPtr->watchMask;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     /*  
      * Since the file is always ready for events, we set the block time  
      * so we will poll.  
      */  
   
     infoPtr->watchMask = mask & infoPtr->validMask;  
     if (infoPtr->watchMask) {  
         if (!oldMask) {  
             infoPtr->nextPtr = tsdPtr->firstSerialPtr;  
             tsdPtr->firstSerialPtr = infoPtr;  
         }  
         SerialBlockTime(infoPtr->blockTime);  
     } else {  
         if (oldMask) {  
             /*  
              * Remove the serial port from the list of watched serial ports.  
              */  
   
             for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr;  
                     ptr != NULL;  
                     nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {  
                 if (infoPtr == ptr) {  
                     *nextPtrPtr = ptr->nextPtr;  
                     break;  
                 }  
             }  
         }  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialGetHandleProc --  
  *  
  *  Called from Tcl_GetChannelHandle to retrieve OS handles from  
  *  inside a command serial port based channel.  
  *  
  * Results:  
  *  Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if  
  *  there is no handle for the specified direction.  
  *  
  * Side effects:  
  *  None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 SerialGetHandleProc(  
     ClientData instanceData,    /* The serial state. */  
     int direction,              /* TCL_READABLE or TCL_WRITABLE */  
     ClientData *handlePtr)      /* Where to store the handle.  */  
 {  
     SerialInfo *infoPtr = (SerialInfo *) instanceData;  
   
     *handlePtr = (ClientData) infoPtr->handle;  
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclWinOpenSerialChannel --  
  *  
  *  Constructs a Serial port channel for the specified standard OS handle.  
  *      This is a helper function to break up the construction of  
  *      channels into File, Console, or Serial.  
  *  
  * Results:  
  *  Returns the new channel, or NULL.  
  *  
  * Side effects:  
  *  May open the channel  
  *  
  *----------------------------------------------------------------------  
  */  
   
 Tcl_Channel  
 TclWinOpenSerialChannel(handle, channelName, permissions)  
     HANDLE handle;  
     char *channelName;  
     int permissions;  
 {  
     SerialInfo *infoPtr;  
     ThreadSpecificData *tsdPtr;  
   
     tsdPtr = SerialInit();  
   
     SetupComm(handle, 4096, 4096);  
     PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR  
           | PURGE_RXCLEAR);  
   
     /*  
      * default is blocking  
      */  
   
     SetCommTimeouts(handle, &timeout_sync);  
   
     infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo));  
     memset(infoPtr, 0, sizeof(SerialInfo));  
   
     infoPtr->validMask = permissions;  
     infoPtr->handle = handle;  
   
     /*  
      * Use the pointer to keep the channel names unique, in case  
      * the handles are shared between multiple channels (stdin/stdout).  
      */  
   
     wsprintfA(channelName, "file%lx", (int) infoPtr);  
   
     infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName,  
             (ClientData) infoPtr, permissions);  
   
   
     infoPtr->readable = infoPtr->writable = 0;  
     infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME;  
     infoPtr->lastError = infoPtr->error = 0;  
   
     /*  
      * Files have default translation of AUTO and ^Z eof char, which  
      * means that a ^Z will be accepted as EOF when reading.  
      */  
   
     Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");  
     Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");  
   
     return infoPtr->channel;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialErrorStr --  
  *  
  *  Converts a Win32 serial error code to a list of readable errors  
  *  
  *----------------------------------------------------------------------  
  */  
 static void  
 SerialErrorStr(error, dsPtr)  
     DWORD error;           /* Win32 serial error code */  
     Tcl_DString *dsPtr;    /* Where to store string */  
 {  
     if( (error & CE_RXOVER) != 0) {  
         Tcl_DStringAppendElement(dsPtr, "RXOVER");  
     }  
     if( (error & CE_OVERRUN) != 0) {  
         Tcl_DStringAppendElement(dsPtr, "OVERRUN");  
     }  
     if( (error & CE_RXPARITY) != 0) {  
         Tcl_DStringAppendElement(dsPtr, "RXPARITY");  
     }  
     if( (error & CE_FRAME) != 0) {  
         Tcl_DStringAppendElement(dsPtr, "FRAME");  
     }  
     if( (error & CE_BREAK) != 0) {  
         Tcl_DStringAppendElement(dsPtr, "BREAK");  
     }  
     if( (error & CE_TXFULL) != 0) {  
         Tcl_DStringAppendElement(dsPtr, "TXFULL");  
     }  
     if( (error & ~(SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS)) != 0) {  
         char buf[TCL_INTEGER_SPACE + 1];  
         wsprintfA(buf, "%d", error);  
         Tcl_DStringAppendElement(dsPtr, buf);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialSetOptionProc --  
  *  
  *  Sets an option on a channel.  
  *  
  * Results:  
  *  A standard Tcl result. Also sets the interp's result on error if  
  *  interp is not NULL.  
  *  
  * Side effects:  
  *  May modify an option on a device.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 SerialSetOptionProc(instanceData, interp, optionName, value)  
     ClientData instanceData;    /* File state. */  
     Tcl_Interp *interp;         /* For error reporting - can be NULL. */  
     char *optionName;           /* Which option to set? */  
     char *value;                /* New value for option. */  
 {  
     SerialInfo *infoPtr;  
     DCB dcb;  
     int len;  
     BOOL result;  
     Tcl_DString ds;  
     TCHAR *native;  
       
     infoPtr = (SerialInfo *) instanceData;  
       
     len = strlen(optionName);  
     if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) {  
         if (GetCommState(infoPtr->handle, &dcb)) {  
             native = Tcl_WinUtfToTChar(value, -1, &ds);  
             result = (*tclWinProcs->buildCommDCBProc)(native, &dcb);  
             Tcl_DStringFree(&ds);  
               
             if ((result == FALSE) ||  
                     (SetCommState(infoPtr->handle, &dcb) == FALSE)) {  
                 /*  
                  * one should separate the 2 errors...  
                  */  
                   
                 if (interp) {  
                     Tcl_AppendResult(interp,  
                             "bad value for -mode: should be ",  
                             "baud,parity,data,stop", NULL);  
                 }  
                 return TCL_ERROR;  
             } else {  
                 return TCL_OK;  
             }  
         } else {  
             if (interp) {  
                 Tcl_AppendResult(interp, "can't get comm state", NULL);  
             }  
             return TCL_ERROR;  
         }  
     } else if ((len > 1) &&  
             (strncmp(optionName, "-pollinterval", len) == 0)) {  
         if ( Tcl_GetInt(interp, value, &(infoPtr->blockTime)) != TCL_OK ) {  
             return TCL_ERROR;  
         }  
     } else {  
         return Tcl_BadChannelOption(interp, optionName,  
                 "mode pollinterval");  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * SerialGetOptionProc --  
  *  
  *  Gets a mode associated with an IO channel. If the optionName arg  
  *  is non NULL, retrieves the value of that option. If the optionName  
  *  arg is NULL, retrieves a list of alternating option names and  
  *  values for the given channel.  
  *  
  * Results:  
  *  A standard Tcl result. Also sets the supplied DString to the  
  *  string value of the option(s) returned.  
  *  
  * Side effects:  
  *  The string returned by this function is in static storage and  
  *  may be reused at any time subsequent to the call.  
  *  
  *----------------------------------------------------------------------  
  */  
 static int  
 SerialGetOptionProc(instanceData, interp, optionName, dsPtr)  
     ClientData instanceData;    /* File state. */  
     Tcl_Interp *interp;         /* For error reporting - can be NULL. */  
     char *optionName;           /* Option to get. */  
     Tcl_DString *dsPtr;         /* Where to store value(s). */  
 {  
     SerialInfo *infoPtr;  
     DCB dcb;  
     int len;  
     int valid = 0;  /* flag if valid option parsed */  
   
     infoPtr = (SerialInfo *) instanceData;  
   
     if (optionName == NULL) {  
         len = 0;  
     } else {  
         len = strlen(optionName);  
     }  
   
     /*  
      * get option -mode  
      */  
   
     if (len == 0) {  
         Tcl_DStringAppendElement(dsPtr, "-mode");  
     }  
     if ((len == 0) ||  
         ((len > 1) && (strncmp(optionName, "-mode", len) == 0))) {  
         valid = 1;  
         if (GetCommState(infoPtr->handle, &dcb) == 0) {  
             /*  
              * shouldn't we flag an error instead ?  
              */  
               
             Tcl_DStringAppendElement(dsPtr, "");  
   
         } else {  
             char parity;  
             char *stop;  
             char buf[2 * TCL_INTEGER_SPACE + 16];  
   
             parity = 'n';  
             if (dcb.Parity < 4) {  
                 parity = "noems"[dcb.Parity];  
             }  
   
             stop = (dcb.StopBits == ONESTOPBIT) ? "1" :  
             (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";  
   
             wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity,  
             dcb.ByteSize, stop);  
             Tcl_DStringAppendElement(dsPtr, buf);  
         }  
     }  
   
     /*  
      * get option -pollinterval  
      */  
       
     if (len == 0) {  
         Tcl_DStringAppendElement(dsPtr, "-pollinterval");  
     }  
     if ((len == 0) ||  
         ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0))) {  
         char buf[TCL_INTEGER_SPACE + 1];  
   
         valid = 1;  
         wsprintfA(buf, "%d", infoPtr->blockTime);  
         Tcl_DStringAppendElement(dsPtr, buf);  
     }  
   
     /*  
      * get option -lasterror  
      * option is readonly and returned by [fconfigure chan -lasterror]  
      * but not returned by unnamed [fconfigure chan]  
      */  
   
     if ( (len > 1) && (strncmp(optionName, "-lasterror", len) == 0) ) {  
         valid = 1;  
         SerialErrorStr(infoPtr->lastError, dsPtr);  
     }  
   
     if (valid) {  
         return TCL_OK;  
     } else {  
         return Tcl_BadChannelOption(interp, optionName,  
                 "mode pollinterval lasterror");  
     }  
 }  
   
   
 /* $History: tclwinserial.c $  
  *  
  * *****************  Version 1  *****************  
  * User: Dtashley     Date: 1/02/01    Time: 12:27a  
  * Created in $/IjuScripter, IjuConsole/Source/Tcl Base  
  * Initial check-in.  
  */  
   
 /* End of TCLWINSERIAL.C */  
1    /* $Header$ */
2    /*
3     * Tclwinserial.c --
4     *
5     *  This file implements the Windows-specific serial port functions,
6     *  and the "serial" channel driver.
7     *
8     * Copyright (c) 1999 by Scriptics Corp.
9     *
10     * See the file "license.terms" for information on usage and redistribution
11     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12     * Changes by Rolf.Schroedter@dlr.de June 25-27, 1999
13     *
14     * RCS: @(#) $Id: tclwinserial.c,v 1.1.1.1 2001/06/13 04:50:16 dtashley Exp $
15     */
16    
17    #include "tclWinInt.h"
18    
19    #include <dos.h>
20    #include <fcntl.h>
21    #include <io.h>
22    #include <sys/stat.h>
23    
24    /*
25     * The following variable is used to tell whether this module has been
26     * initialized.
27     */
28    
29    static int initialized = 0;
30    
31    /*
32     * Bit masks used in the flags field of the SerialInfo structure below.
33     */
34    
35    #define SERIAL_PENDING  (1<<0)  /* Message is pending in the queue. */
36    #define SERIAL_ASYNC    (1<<1)  /* Channel is non-blocking. */
37    
38    /*
39     * Bit masks used in the sharedFlags field of the SerialInfo structure below.
40     */
41    
42    #define SERIAL_EOF      (1<<2)  /* Serial has reached EOF. */
43    #define SERIAL_ERROR    (1<<4)
44    #define SERIAL_WRITE    (1<<5)  /* enables fileevent writable
45                     * one time after write operation */
46    
47    /*
48     * Default time to block between checking status on the serial port.
49     */
50    #define SERIAL_DEFAULT_BLOCKTIME    10  /* 10 msec */
51    
52    /*
53     * Define Win32 read/write error masks returned by ClearCommError()
54     */
55    #define SERIAL_READ_ERRORS      ( CE_RXOVER | CE_OVERRUN | CE_RXPARITY \
56                                    | CE_FRAME  | CE_BREAK )
57    #define SERIAL_WRITE_ERRORS     ( CE_TXFULL )
58    
59    /*
60     * This structure describes per-instance data for a serial based channel.
61     */
62    
63    typedef struct SerialInfo {
64        HANDLE handle;
65        struct SerialInfo *nextPtr; /* Pointer to next registered serial. */
66        Tcl_Channel channel;        /* Pointer to channel structure. */
67        int validMask;              /* OR'ed combination of TCL_READABLE,
68                                     * TCL_WRITABLE, or TCL_EXCEPTION: indicates
69                                     * which operations are valid on the file. */
70        int watchMask;              /* OR'ed combination of TCL_READABLE,
71                                     * TCL_WRITABLE, or TCL_EXCEPTION: indicates
72                                     * which events should be reported. */
73        int flags;                  /* State flags, see above for a list. */
74        int writable;               /* flag that the channel is readable */
75        int readable;               /* flag that the channel is readable */
76        int blockTime;              /* max. blocktime in msec */
77        DWORD error;                /* pending error code returned by
78                                     * ClearCommError() */
79        DWORD lastError;            /* last error code, can be fetched with
80                                     * fconfigure chan -lasterror */
81    } SerialInfo;
82    
83    typedef struct ThreadSpecificData {
84        /*
85         * The following pointer refers to the head of the list of serials
86         * that are being watched for file events.
87         */
88    
89        SerialInfo *firstSerialPtr;
90    } ThreadSpecificData;
91    
92    static Tcl_ThreadDataKey dataKey;
93    
94    /*
95     * The following structure is what is added to the Tcl event queue when
96     * serial events are generated.
97     */
98    
99    typedef struct SerialEvent {
100        Tcl_Event header;       /* Information that is standard for
101                                 * all events. */
102        SerialInfo *infoPtr;    /* Pointer to serial info structure.  Note
103                                 * that we still have to verify that the
104                                 * serial exists before dereferencing this
105                                 * pointer. */
106    } SerialEvent;
107    
108    COMMTIMEOUTS timeout_sync  = {   /* Timouts for blocking mode */
109        MAXDWORD,        /* ReadIntervalTimeout */
110        MAXDWORD,        /* ReadTotalTimeoutMultiplier */
111        MAXDWORD-1,      /* ReadTotalTimeoutConstant,
112                MAXDWORD-1 works for both Win95/NT */
113        0,               /* WriteTotalTimeoutMultiplier */
114        0,               /* WriteTotalTimeoutConstant */
115    };
116    
117    COMMTIMEOUTS timeout_async  = {   /* Timouts for non-blocking mode */
118        0,               /* ReadIntervalTimeout */
119        0,               /* ReadTotalTimeoutMultiplier */
120        1,               /* ReadTotalTimeoutConstant */
121        0,               /* WriteTotalTimeoutMultiplier */
122        0,               /* WriteTotalTimeoutConstant */
123    };
124    
125    /*
126     * Declarations for functions used only in this file.
127     */
128    
129    static int      SerialBlockProc(ClientData instanceData, int mode);
130    static void     SerialCheckProc(ClientData clientData, int flags);
131    static int      SerialCloseProc(ClientData instanceData,
132                    Tcl_Interp *interp);
133    static int      SerialEventProc(Tcl_Event *evPtr, int flags);
134    static void     SerialExitHandler(ClientData clientData);
135    static int      SerialGetHandleProc(ClientData instanceData,
136                    int direction, ClientData *handlePtr);
137    static ThreadSpecificData *SerialInit(void);
138    static int      SerialInputProc(ClientData instanceData, char *buf,
139                    int toRead, int *errorCode);
140    static int      SerialOutputProc(ClientData instanceData, char *buf,
141                    int toWrite, int *errorCode);
142    static void     SerialSetupProc(ClientData clientData, int flags);
143    static void     SerialWatchProc(ClientData instanceData, int mask);
144    static void     ProcExitHandler(ClientData clientData);
145    static int       SerialGetOptionProc _ANSI_ARGS_((ClientData instanceData,
146                    Tcl_Interp *interp, char *optionName,
147                    Tcl_DString *dsPtr));
148    static int       SerialSetOptionProc _ANSI_ARGS_((ClientData instanceData,
149                    Tcl_Interp *interp, char *optionName,
150                    char *value));
151    
152    /*
153     * This structure describes the channel type structure for command serial
154     * based IO.
155     */
156    
157    static Tcl_ChannelType serialChannelType = {
158        "serial",               /* Type name. */
159        SerialBlockProc,        /* Set blocking or non-blocking mode.*/
160        SerialCloseProc,        /* Close proc. */
161        SerialInputProc,        /* Input proc. */
162        SerialOutputProc,       /* Output proc. */
163        NULL,                   /* Seek proc. */
164        SerialSetOptionProc,    /* Set option proc. */
165        SerialGetOptionProc,    /* Get option proc. */
166        SerialWatchProc,        /* Set up notifier to watch the channel. */
167        SerialGetHandleProc,    /* Get an OS handle from channel. */
168    };
169    
170    /*
171     *----------------------------------------------------------------------
172     *
173     * SerialInit --
174     *
175     *  This function initializes the static variables for this file.
176     *
177     * Results:
178     *  None.
179     *
180     * Side effects:
181     *  Creates a new event source.
182     *
183     *----------------------------------------------------------------------
184     */
185    
186    static ThreadSpecificData *
187    SerialInit()
188    {
189        ThreadSpecificData *tsdPtr;
190    
191        /*
192         * Check the initialized flag first, then check it again in the mutex.
193         * This is a speed enhancement.
194         */
195    
196        if (!initialized) {
197            if (!initialized) {
198                initialized = 1;
199                Tcl_CreateExitHandler(ProcExitHandler, NULL);
200            }
201        }
202    
203        tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
204        if (tsdPtr == NULL) {
205            tsdPtr = TCL_TSD_INIT(&dataKey);
206            tsdPtr->firstSerialPtr = NULL;
207            Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
208            Tcl_CreateThreadExitHandler(SerialExitHandler, NULL);
209        }
210        return tsdPtr;
211    }
212    
213    /*
214     *----------------------------------------------------------------------
215     *
216     * SerialExitHandler --
217     *
218     *  This function is called to cleanup the serial module before
219     *  Tcl is unloaded.
220     *
221     * Results:
222     *  None.
223     *
224     * Side effects:
225     *  Removes the serial event source.
226     *
227     *----------------------------------------------------------------------
228     */
229    
230    static void
231    SerialExitHandler(
232        ClientData clientData)  /* Old window proc */
233    {
234        Tcl_DeleteEventSource(SerialSetupProc, SerialCheckProc, NULL);
235    }
236    
237    /*
238     *----------------------------------------------------------------------
239     *
240     * ProcExitHandler --
241     *
242     *  This function is called to cleanup the process list before
243     *  Tcl is unloaded.
244     *
245     * Results:
246     *  None.
247     *
248     * Side effects:
249     *  Resets the process list.
250     *
251     *----------------------------------------------------------------------
252     */
253    
254    static void
255    ProcExitHandler(
256        ClientData clientData)  /* Old window proc */
257    {
258        initialized = 0;
259    }
260    
261    /*
262     *----------------------------------------------------------------------
263     *
264     * SerialBlockTime --
265     *
266     *  Wrapper to set Tcl's block time in msec
267     *
268     * Results:
269     *  None.
270     *----------------------------------------------------------------------
271     */
272    
273    void
274    SerialBlockTime(
275        int msec)          /* milli-seconds */
276    {
277        Tcl_Time blockTime;
278    
279        blockTime.sec  =  msec / 1000;
280        blockTime.usec = (msec % 1000) * 1000;
281        Tcl_SetMaxBlockTime(&blockTime);
282    }
283    /*
284     *----------------------------------------------------------------------
285     *
286     * SerialSetupProc --
287     *
288     *  This procedure is invoked before Tcl_DoOneEvent blocks waiting
289     *  for an event.
290     *
291     * Results:
292     *  None.
293     *
294     * Side effects:
295     *  Adjusts the block time if needed.
296     *
297     *----------------------------------------------------------------------
298     */
299    
300    void
301    SerialSetupProc(
302        ClientData data,    /* Not used. */
303        int flags)          /* Event flags as passed to Tcl_DoOneEvent. */
304    {
305        SerialInfo *infoPtr;
306        int block = 1;
307        int msec = INT_MAX; /* min. found block time */
308        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
309    
310        if (!(flags & TCL_FILE_EVENTS)) {
311            return;
312        }
313    
314        /*
315         * Look to see if any events handlers installed. If they are, do not block.
316         */
317    
318        for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
319                infoPtr = infoPtr->nextPtr) {
320    
321            if( infoPtr->watchMask & (TCL_WRITABLE | TCL_READABLE) ) {
322                block = 0;
323                msec = min( msec, infoPtr->blockTime );
324            }
325        }
326    
327        if (!block) {
328            SerialBlockTime(msec);
329        }
330    }
331    
332    /*
333     *----------------------------------------------------------------------
334     *
335     * SerialCheckProc --
336     *
337     *  This procedure is called by Tcl_DoOneEvent to check the serial
338     *  event source for events.
339     *
340     * Results:
341     *  None.
342     *
343     * Side effects:
344     *  May queue an event.
345     *
346     *----------------------------------------------------------------------
347     */
348    
349    static void
350    SerialCheckProc(
351        ClientData data,    /* Not used. */
352        int flags)          /* Event flags as passed to Tcl_DoOneEvent. */
353    {
354        SerialInfo *infoPtr;
355        SerialEvent *evPtr;
356        int needEvent;
357        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
358        COMSTAT cStat;
359    
360        if (!(flags & TCL_FILE_EVENTS)) {
361            return;
362        }
363    
364        /*
365         * Queue events for any ready serials that don't already have events
366         * queued.
367         */
368    
369        for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
370                infoPtr = infoPtr->nextPtr) {
371            if (infoPtr->flags & SERIAL_PENDING) {
372                continue;
373            }
374    
375            needEvent = 0;
376    
377            /*
378             * If any READABLE or WRITABLE watch mask is set
379             * call ClearCommError to poll cbInQue,cbOutQue
380             * Window errors are ignored here
381             */
382    
383            if( infoPtr->watchMask & (TCL_WRITABLE | TCL_READABLE) ) {
384                if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
385                    /*
386                     * Look for empty output buffer.  If empty, poll.
387                     */
388    
389                    if( infoPtr->watchMask & TCL_WRITABLE ) {
390                        /*
391                         * force fileevent after serial write error
392                         */
393                        if (((infoPtr->flags & SERIAL_WRITE) != 0) &&
394                                ((cStat.cbOutQue == 0) ||
395                                        (infoPtr->error & SERIAL_WRITE_ERRORS))) {
396                            /*
397                             * allow only one fileevent after each callback
398                             */
399    
400                            infoPtr->flags &= ~SERIAL_WRITE;
401                            infoPtr->writable = 1;
402                            needEvent = 1;
403                        }
404                    }
405                    
406                    /*
407                     * Look for characters already pending in windows queue.
408                     * If they are, poll.
409                     */
410    
411                    if( infoPtr->watchMask & TCL_READABLE ) {
412                        /*
413                         * force fileevent after serial read error
414                         */
415                        if( (cStat.cbInQue > 0) ||
416                                (infoPtr->error & SERIAL_READ_ERRORS) ) {
417                            infoPtr->readable = 1;
418                            needEvent = 1;
419                        }
420                    }
421                }
422            }
423    
424            /*
425             * Queue an event if the serial is signaled for reading or writing.
426             */
427    
428            if (needEvent) {
429                infoPtr->flags |= SERIAL_PENDING;
430                evPtr = (SerialEvent *) ckalloc(sizeof(SerialEvent));
431                evPtr->header.proc = SerialEventProc;
432                evPtr->infoPtr = infoPtr;
433                Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
434            }
435        }
436    }
437    
438    /*
439     *----------------------------------------------------------------------
440     *
441     * SerialBlockProc --
442     *
443     *  Set blocking or non-blocking mode on channel.
444     *
445     * Results:
446     *  0 if successful, errno when failed.
447     *
448     * Side effects:
449     *  Sets the device into blocking or non-blocking mode.
450     *
451     *----------------------------------------------------------------------
452     */
453    
454    static int
455    SerialBlockProc(
456        ClientData instanceData,    /* Instance data for channel. */
457        int mode)                   /* TCL_MODE_BLOCKING or
458                                     * TCL_MODE_NONBLOCKING. */
459    {
460        COMMTIMEOUTS *timeout;
461        int errorCode = 0;
462    
463        SerialInfo *infoPtr = (SerialInfo *) instanceData;
464    
465        /*
466         * Serial IO on Windows can not be switched between blocking & nonblocking,
467         * hence we have to emulate the behavior. This is done in the input
468         * function by checking against a bit in the state. We set or unset the
469         * bit here to cause the input function to emulate the correct behavior.
470         */
471    
472        if (mode == TCL_MODE_NONBLOCKING) {
473            infoPtr->flags |= SERIAL_ASYNC;
474            timeout = &timeout_async;
475        } else {
476            infoPtr->flags &= ~(SERIAL_ASYNC);
477            timeout = &timeout_sync;
478        }
479        if (SetCommTimeouts(infoPtr->handle, timeout) == FALSE) {
480            TclWinConvertError(GetLastError());
481            errorCode = errno;
482        }
483        return errorCode;
484    }
485    
486    /*
487     *----------------------------------------------------------------------
488     *
489     * SerialCloseProc --
490     *
491     *  Closes a serial based IO channel.
492     *
493     * Results:
494     *  0 on success, errno otherwise.
495     *
496     * Side effects:
497     *  Closes the physical channel.
498     *
499     *----------------------------------------------------------------------
500     */
501    
502    static int
503    SerialCloseProc(
504        ClientData instanceData,    /* Pointer to SerialInfo structure. */
505        Tcl_Interp *interp)         /* For error reporting. */
506    {
507        SerialInfo *serialPtr = (SerialInfo *) instanceData;
508        int errorCode, result = 0;
509        SerialInfo *infoPtr, **nextPtrPtr;
510        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
511    
512        errorCode = 0;
513        serialPtr->validMask &= ~TCL_READABLE;
514        serialPtr->validMask &= ~TCL_WRITABLE;
515    
516        /*
517         * Don't close the Win32 handle if the handle is a standard channel
518         * during the exit process.  Otherwise, one thread may kill the stdio
519         * of another.
520         */
521    
522        if (!TclInExit()
523            || ((GetStdHandle(STD_INPUT_HANDLE) != serialPtr->handle)
524            && (GetStdHandle(STD_OUTPUT_HANDLE) != serialPtr->handle)
525            && (GetStdHandle(STD_ERROR_HANDLE) != serialPtr->handle))) {
526        if (CloseHandle(serialPtr->handle) == FALSE) {
527            TclWinConvertError(GetLastError());
528            errorCode = errno;
529        }
530        }
531    
532        serialPtr->watchMask &= serialPtr->validMask;
533    
534        /*
535         * Remove the file from the list of watched files.
536         */
537    
538        for (nextPtrPtr = &(tsdPtr->firstSerialPtr), infoPtr = *nextPtrPtr;
539                infoPtr != NULL;
540                        nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
541            if (infoPtr == (SerialInfo *)serialPtr) {
542                *nextPtrPtr = infoPtr->nextPtr;
543                break;
544            }
545        }
546    
547        /*
548         * Wrap the error file into a channel and give it to the cleanup
549         * routine.
550         */
551    
552        ckfree((char*) serialPtr);
553    
554        if (errorCode == 0) {
555            return result;
556        }
557        return errorCode;
558    }
559    
560    /*
561     *----------------------------------------------------------------------
562     *
563     * SerialInputProc --
564     *
565     *  Reads input from the IO channel into the buffer given. Returns
566     *  count of how many bytes were actually read, and an error indication.
567     *
568     * Results:
569     *  A count of how many bytes were read is returned and an error
570     *  indication is returned in an output argument.
571     *
572     * Side effects:
573     *  Reads input from the actual channel.
574     *
575     *----------------------------------------------------------------------
576     */
577    static int
578    SerialInputProc(
579        ClientData instanceData,    /* Serial state. */
580        char *buf,                  /* Where to store data read. */
581        int bufSize,                /* How much space is available
582                                     * in the buffer? */
583        int *errorCode)             /* Where to store error code. */
584    {
585        SerialInfo *infoPtr = (SerialInfo *) instanceData;
586        DWORD bytesRead = 0;
587        DWORD err;
588        COMSTAT cStat;
589    
590        *errorCode = 0;
591    
592        /*
593         * Check if there is a CommError pending from SerialCheckProc
594         */
595        if( infoPtr->error & SERIAL_READ_ERRORS ){
596            goto commError;
597        }
598    
599        /*
600         * Look for characters already pending in windows queue.
601         * This is the mainly restored good old code from Tcl8.0
602         */
603    
604        if( ClearCommError( infoPtr->handle, &infoPtr->error, &cStat ) ) {
605            /*
606             * Check for errors here, but not in the evSetup/Check procedures
607             */
608    
609            if( infoPtr->error & SERIAL_READ_ERRORS ) {
610                goto commError;
611            }
612            if( infoPtr->flags & SERIAL_ASYNC ) {
613                /*
614                 * NON_BLOCKING mode:
615                 * Avoid blocking by reading more bytes than available
616                 * in input buffer
617                 */
618    
619                if( cStat.cbInQue > 0 ) {
620                    if( (DWORD) bufSize > cStat.cbInQue ) {
621                        bufSize = cStat.cbInQue;
622                    }
623                } else {
624                    errno = *errorCode = EAGAIN;
625                    return -1;
626                }
627            } else {
628                /*
629                 * BLOCKING mode:
630                 * Tcl trys to read a full buffer of 4 kBytes here
631                 */
632    
633                if( cStat.cbInQue > 0 ) {
634                    if( (DWORD) bufSize > cStat.cbInQue ) {
635                        bufSize = cStat.cbInQue;
636                    }
637                } else {
638                    bufSize = 1;
639                }
640            }
641        }
642    
643        if( bufSize == 0 ) {
644            return bytesRead = 0;
645        }
646    
647        if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
648            NULL) == FALSE) {
649            err = GetLastError();
650            if (err != ERROR_IO_PENDING) {
651                goto error;
652            }
653        }
654        return bytesRead;
655    
656        error:
657        TclWinConvertError(GetLastError());
658        *errorCode = errno;
659        return -1;
660    
661        commError:
662        infoPtr->lastError = infoPtr->error;  /* save last error code */
663        infoPtr->error = 0;                   /* reset error code */
664        *errorCode = EIO;                     /* to return read-error only once */
665        return -1;
666    }
667    
668    /*
669     *----------------------------------------------------------------------
670     *
671     * SerialOutputProc --
672     *
673     *  Writes the given output on the IO channel. Returns count of how
674     *  many characters were actually written, and an error indication.
675     *
676     * Results:
677     *  A count of how many characters were written is returned and an
678     *  error indication is returned in an output argument.
679     *
680     * Side effects:
681     *  Writes output on the actual channel.
682     *
683     *----------------------------------------------------------------------
684     */
685    
686    static int
687    SerialOutputProc(
688        ClientData instanceData,    /* Serial state. */
689        char *buf,                  /* The data buffer. */
690        int toWrite,                /* How many bytes to write? */
691        int *errorCode)             /* Where to store error code. */
692    {
693        SerialInfo *infoPtr = (SerialInfo *) instanceData;
694        DWORD bytesWritten, err;
695    
696        *errorCode = 0;
697    
698        /*
699         * Check if there is a CommError pending from SerialCheckProc
700         */
701        if( infoPtr->error & SERIAL_WRITE_ERRORS ){
702            infoPtr->lastError = infoPtr->error;  /* save last error code */
703            infoPtr->error = 0;                   /* reset error code */
704            *errorCode = EIO;               /* to return read-error only once */
705            return -1;
706        }
707    
708        /*
709         * Check for a background error on the last write.
710         * Allow one write-fileevent after each callback
711         */
712    
713        if( toWrite ) {
714            infoPtr->flags |= SERIAL_WRITE;
715        }
716    
717        if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
718                &bytesWritten, NULL) == FALSE) {
719            err = GetLastError();
720            if (err != ERROR_IO_PENDING) {
721                TclWinConvertError(GetLastError());
722                goto error;
723            }
724        }
725    
726        return bytesWritten;
727    
728    error:
729        *errorCode = errno;
730        return -1;
731    
732    }
733    
734    /*
735     *----------------------------------------------------------------------
736     *
737     * SerialEventProc --
738     *
739     *  This function is invoked by Tcl_ServiceEvent when a file event
740     *  reaches the front of the event queue.  This procedure invokes
741     *  Tcl_NotifyChannel on the serial.
742     *
743     * Results:
744     *  Returns 1 if the event was handled, meaning it should be removed
745     *  from the queue.  Returns 0 if the event was not handled, meaning
746     *  it should stay on the queue.  The only time the event isn't
747     *  handled is if the TCL_FILE_EVENTS flag bit isn't set.
748     *
749     * Side effects:
750     *  Whatever the notifier callback does.
751     *
752     *----------------------------------------------------------------------
753     */
754    
755    static int
756    SerialEventProc(
757        Tcl_Event *evPtr,   /* Event to service. */
758        int flags)          /* Flags that indicate what events to
759                             * handle, such as TCL_FILE_EVENTS. */
760    {
761        SerialEvent *serialEvPtr = (SerialEvent *)evPtr;
762        SerialInfo *infoPtr;
763        int mask;
764        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
765    
766        if (!(flags & TCL_FILE_EVENTS)) {
767            return 0;
768        }
769    
770        /*
771         * Search through the list of watched serials for the one whose handle
772         * matches the event.  We do this rather than simply dereferencing
773         * the handle in the event so that serials can be deleted while the
774         * event is in the queue.
775         */
776    
777        for (infoPtr = tsdPtr->firstSerialPtr; infoPtr != NULL;
778                infoPtr = infoPtr->nextPtr) {
779            if (serialEvPtr->infoPtr == infoPtr) {
780                infoPtr->flags &= ~(SERIAL_PENDING);
781                break;
782            }
783        }
784    
785        /*
786         * Remove stale events.
787         */
788    
789        if (!infoPtr) {
790            return 1;
791        }
792    
793        /*
794         * Check to see if the serial is readable.  Note
795         * that we can't tell if a serial is writable, so we always report it
796         * as being writable unless we have detected EOF.
797         */
798    
799        mask = 0;
800        if( infoPtr->watchMask & TCL_WRITABLE ) {
801            if( infoPtr->writable ) {
802                mask |= TCL_WRITABLE;
803                infoPtr->writable = 0;
804            }
805        }
806    
807        if( infoPtr->watchMask & TCL_READABLE ) {
808            if( infoPtr->readable ) {
809                mask |= TCL_READABLE;
810                infoPtr->readable = 0;
811            }
812        }
813    
814        /*
815         * Inform the channel of the events.
816         */
817    
818        Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
819        return 1;
820    }
821    
822    /*
823     *----------------------------------------------------------------------
824     *
825     * SerialWatchProc --
826     *
827     *  Called by the notifier to set up to watch for events on this
828     *  channel.
829     *
830     * Results:
831     *  None.
832     *
833     * Side effects:
834     *  None.
835     *
836     *----------------------------------------------------------------------
837     */
838    
839    static void
840    SerialWatchProc(
841        ClientData instanceData,     /* Serial state. */
842        int mask)                    /* What events to watch for, OR-ed
843                                      * combination of TCL_READABLE,
844                                      * TCL_WRITABLE and TCL_EXCEPTION. */
845    {
846        SerialInfo **nextPtrPtr, *ptr;
847        SerialInfo *infoPtr = (SerialInfo *) instanceData;
848        int oldMask = infoPtr->watchMask;
849        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
850    
851        /*
852         * Since the file is always ready for events, we set the block time
853         * so we will poll.
854         */
855    
856        infoPtr->watchMask = mask & infoPtr->validMask;
857        if (infoPtr->watchMask) {
858            if (!oldMask) {
859                infoPtr->nextPtr = tsdPtr->firstSerialPtr;
860                tsdPtr->firstSerialPtr = infoPtr;
861            }
862            SerialBlockTime(infoPtr->blockTime);
863        } else {
864            if (oldMask) {
865                /*
866                 * Remove the serial port from the list of watched serial ports.
867                 */
868    
869                for (nextPtrPtr = &(tsdPtr->firstSerialPtr), ptr = *nextPtrPtr;
870                        ptr != NULL;
871                        nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
872                    if (infoPtr == ptr) {
873                        *nextPtrPtr = ptr->nextPtr;
874                        break;
875                    }
876                }
877            }
878        }
879    }
880    
881    /*
882     *----------------------------------------------------------------------
883     *
884     * SerialGetHandleProc --
885     *
886     *  Called from Tcl_GetChannelHandle to retrieve OS handles from
887     *  inside a command serial port based channel.
888     *
889     * Results:
890     *  Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
891     *  there is no handle for the specified direction.
892     *
893     * Side effects:
894     *  None.
895     *
896     *----------------------------------------------------------------------
897     */
898    
899    static int
900    SerialGetHandleProc(
901        ClientData instanceData,    /* The serial state. */
902        int direction,              /* TCL_READABLE or TCL_WRITABLE */
903        ClientData *handlePtr)      /* Where to store the handle.  */
904    {
905        SerialInfo *infoPtr = (SerialInfo *) instanceData;
906    
907        *handlePtr = (ClientData) infoPtr->handle;
908        return TCL_OK;
909    }
910    
911    /*
912     *----------------------------------------------------------------------
913     *
914     * TclWinOpenSerialChannel --
915     *
916     *  Constructs a Serial port channel for the specified standard OS handle.
917     *      This is a helper function to break up the construction of
918     *      channels into File, Console, or Serial.
919     *
920     * Results:
921     *  Returns the new channel, or NULL.
922     *
923     * Side effects:
924     *  May open the channel
925     *
926     *----------------------------------------------------------------------
927     */
928    
929    Tcl_Channel
930    TclWinOpenSerialChannel(handle, channelName, permissions)
931        HANDLE handle;
932        char *channelName;
933        int permissions;
934    {
935        SerialInfo *infoPtr;
936        ThreadSpecificData *tsdPtr;
937    
938        tsdPtr = SerialInit();
939    
940        SetupComm(handle, 4096, 4096);
941        PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
942              | PURGE_RXCLEAR);
943    
944        /*
945         * default is blocking
946         */
947    
948        SetCommTimeouts(handle, &timeout_sync);
949    
950        infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo));
951        memset(infoPtr, 0, sizeof(SerialInfo));
952    
953        infoPtr->validMask = permissions;
954        infoPtr->handle = handle;
955    
956        /*
957         * Use the pointer to keep the channel names unique, in case
958         * the handles are shared between multiple channels (stdin/stdout).
959         */
960    
961        wsprintfA(channelName, "file%lx", (int) infoPtr);
962    
963        infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName,
964                (ClientData) infoPtr, permissions);
965    
966    
967        infoPtr->readable = infoPtr->writable = 0;
968        infoPtr->blockTime = SERIAL_DEFAULT_BLOCKTIME;
969        infoPtr->lastError = infoPtr->error = 0;
970    
971        /*
972         * Files have default translation of AUTO and ^Z eof char, which
973         * means that a ^Z will be accepted as EOF when reading.
974         */
975    
976        Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
977        Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
978    
979        return infoPtr->channel;
980    }
981    
982    /*
983     *----------------------------------------------------------------------
984     *
985     * SerialErrorStr --
986     *
987     *  Converts a Win32 serial error code to a list of readable errors
988     *
989     *----------------------------------------------------------------------
990     */
991    static void
992    SerialErrorStr(error, dsPtr)
993        DWORD error;           /* Win32 serial error code */
994        Tcl_DString *dsPtr;    /* Where to store string */
995    {
996        if( (error & CE_RXOVER) != 0) {
997            Tcl_DStringAppendElement(dsPtr, "RXOVER");
998        }
999        if( (error & CE_OVERRUN) != 0) {
1000            Tcl_DStringAppendElement(dsPtr, "OVERRUN");
1001        }
1002        if( (error & CE_RXPARITY) != 0) {
1003            Tcl_DStringAppendElement(dsPtr, "RXPARITY");
1004        }
1005        if( (error & CE_FRAME) != 0) {
1006            Tcl_DStringAppendElement(dsPtr, "FRAME");
1007        }
1008        if( (error & CE_BREAK) != 0) {
1009            Tcl_DStringAppendElement(dsPtr, "BREAK");
1010        }
1011        if( (error & CE_TXFULL) != 0) {
1012            Tcl_DStringAppendElement(dsPtr, "TXFULL");
1013        }
1014        if( (error & ~(SERIAL_READ_ERRORS | SERIAL_WRITE_ERRORS)) != 0) {
1015            char buf[TCL_INTEGER_SPACE + 1];
1016            wsprintfA(buf, "%d", error);
1017            Tcl_DStringAppendElement(dsPtr, buf);
1018        }
1019    }
1020    
1021    /*
1022     *----------------------------------------------------------------------
1023     *
1024     * SerialSetOptionProc --
1025     *
1026     *  Sets an option on a channel.
1027     *
1028     * Results:
1029     *  A standard Tcl result. Also sets the interp's result on error if
1030     *  interp is not NULL.
1031     *
1032     * Side effects:
1033     *  May modify an option on a device.
1034     *
1035     *----------------------------------------------------------------------
1036     */
1037    
1038    static int
1039    SerialSetOptionProc(instanceData, interp, optionName, value)
1040        ClientData instanceData;    /* File state. */
1041        Tcl_Interp *interp;         /* For error reporting - can be NULL. */
1042        char *optionName;           /* Which option to set? */
1043        char *value;                /* New value for option. */
1044    {
1045        SerialInfo *infoPtr;
1046        DCB dcb;
1047        int len;
1048        BOOL result;
1049        Tcl_DString ds;
1050        TCHAR *native;
1051        
1052        infoPtr = (SerialInfo *) instanceData;
1053        
1054        len = strlen(optionName);
1055        if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) {
1056            if (GetCommState(infoPtr->handle, &dcb)) {
1057                native = Tcl_WinUtfToTChar(value, -1, &ds);
1058                result = (*tclWinProcs->buildCommDCBProc)(native, &dcb);
1059                Tcl_DStringFree(&ds);
1060                
1061                if ((result == FALSE) ||
1062                        (SetCommState(infoPtr->handle, &dcb) == FALSE)) {
1063                    /*
1064                     * one should separate the 2 errors...
1065                     */
1066                    
1067                    if (interp) {
1068                        Tcl_AppendResult(interp,
1069                                "bad value for -mode: should be ",
1070                                "baud,parity,data,stop", NULL);
1071                    }
1072                    return TCL_ERROR;
1073                } else {
1074                    return TCL_OK;
1075                }
1076            } else {
1077                if (interp) {
1078                    Tcl_AppendResult(interp, "can't get comm state", NULL);
1079                }
1080                return TCL_ERROR;
1081            }
1082        } else if ((len > 1) &&
1083                (strncmp(optionName, "-pollinterval", len) == 0)) {
1084            if ( Tcl_GetInt(interp, value, &(infoPtr->blockTime)) != TCL_OK ) {
1085                return TCL_ERROR;
1086            }
1087        } else {
1088            return Tcl_BadChannelOption(interp, optionName,
1089                    "mode pollinterval");
1090        }
1091        return TCL_OK;
1092    }
1093    
1094    /*
1095     *----------------------------------------------------------------------
1096     *
1097     * SerialGetOptionProc --
1098     *
1099     *  Gets a mode associated with an IO channel. If the optionName arg
1100     *  is non NULL, retrieves the value of that option. If the optionName
1101     *  arg is NULL, retrieves a list of alternating option names and
1102     *  values for the given channel.
1103     *
1104     * Results:
1105     *  A standard Tcl result. Also sets the supplied DString to the
1106     *  string value of the option(s) returned.
1107     *
1108     * Side effects:
1109     *  The string returned by this function is in static storage and
1110     *  may be reused at any time subsequent to the call.
1111     *
1112     *----------------------------------------------------------------------
1113     */
1114    static int
1115    SerialGetOptionProc(instanceData, interp, optionName, dsPtr)
1116        ClientData instanceData;    /* File state. */
1117        Tcl_Interp *interp;         /* For error reporting - can be NULL. */
1118        char *optionName;           /* Option to get. */
1119        Tcl_DString *dsPtr;         /* Where to store value(s). */
1120    {
1121        SerialInfo *infoPtr;
1122        DCB dcb;
1123        int len;
1124        int valid = 0;  /* flag if valid option parsed */
1125    
1126        infoPtr = (SerialInfo *) instanceData;
1127    
1128        if (optionName == NULL) {
1129            len = 0;
1130        } else {
1131            len = strlen(optionName);
1132        }
1133    
1134        /*
1135         * get option -mode
1136         */
1137    
1138        if (len == 0) {
1139            Tcl_DStringAppendElement(dsPtr, "-mode");
1140        }
1141        if ((len == 0) ||
1142            ((len > 1) && (strncmp(optionName, "-mode", len) == 0))) {
1143            valid = 1;
1144            if (GetCommState(infoPtr->handle, &dcb) == 0) {
1145                /*
1146                 * shouldn't we flag an error instead ?
1147                 */
1148                
1149                Tcl_DStringAppendElement(dsPtr, "");
1150    
1151            } else {
1152                char parity;
1153                char *stop;
1154                char buf[2 * TCL_INTEGER_SPACE + 16];
1155    
1156                parity = 'n';
1157                if (dcb.Parity < 4) {
1158                    parity = "noems"[dcb.Parity];
1159                }
1160    
1161                stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
1162                (dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
1163    
1164                wsprintfA(buf, "%d,%c,%d,%s", dcb.BaudRate, parity,
1165                dcb.ByteSize, stop);
1166                Tcl_DStringAppendElement(dsPtr, buf);
1167            }
1168        }
1169    
1170        /*
1171         * get option -pollinterval
1172         */
1173        
1174        if (len == 0) {
1175            Tcl_DStringAppendElement(dsPtr, "-pollinterval");
1176        }
1177        if ((len == 0) ||
1178            ((len > 1) && (strncmp(optionName, "-pollinterval", len) == 0))) {
1179            char buf[TCL_INTEGER_SPACE + 1];
1180    
1181            valid = 1;
1182            wsprintfA(buf, "%d", infoPtr->blockTime);
1183            Tcl_DStringAppendElement(dsPtr, buf);
1184        }
1185    
1186        /*
1187         * get option -lasterror
1188         * option is readonly and returned by [fconfigure chan -lasterror]
1189         * but not returned by unnamed [fconfigure chan]
1190         */
1191    
1192        if ( (len > 1) && (strncmp(optionName, "-lasterror", len) == 0) ) {
1193            valid = 1;
1194            SerialErrorStr(infoPtr->lastError, dsPtr);
1195        }
1196    
1197        if (valid) {
1198            return TCL_OK;
1199        } else {
1200            return Tcl_BadChannelOption(interp, optionName,
1201                    "mode pollinterval lasterror");
1202        }
1203    }
1204    
1205    /* End of tclwinserial.c */

Legend:
Removed from v.29  
changed lines
  Added in v.220

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25