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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25