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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25