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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (show annotations) (download)
Sat Nov 5 11:07:06 2016 UTC (8 years, 1 month 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 /* $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