/[dtapublic]/projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinserial.c
ViewVC logotype

Diff of /projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinserial.c

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

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

Legend:
Removed from v.67  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25