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

Contents of /projs/trunk/shared_source/tcl_base/tclwinserial.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations) (download)
Fri Oct 14 01:50:00 2016 UTC (7 years, 9 months ago) by dashley
File MIME type: text/plain
File size: 34962 byte(s)
Move shared source code to commonize.
1 /* $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