|
|
1.1 root 1: /*++
2:
3: Copyright (c) 1991, 1992, 1993 Microsoft Corporation
4:
5: Module Name:
6:
7: modmflow.c
8:
9: Abstract:
10:
11: This module contains *MOST* of the code used to manipulate
12: the modem control and status registers. The vast majority
13: of the remainder of flow control is concentrated in the
14: Interrupt service routine. A very small amount resides
15: in the read code that pull characters out of the interrupt
16: buffer.
17:
18: Author:
19:
20: Anthony V. Ercolano 26-Sep-1991
21:
22: Environment:
23:
24: Kernel mode
25:
26: Revision History :
27:
28: --*/
29:
30: #include <stddef.h>
31: #include "ntddk.h"
32: #include "ntddser.h"
33: #include "serial.h"
34: #include "serialp.h"
35:
36: BOOLEAN
37: SerialDecrementRTSCounter(
38: IN PVOID Context
39: );
40:
41:
42: BOOLEAN
43: SerialSetDTR(
44: IN PVOID Context
45: )
46:
47: /*++
48:
49: Routine Description:
50:
51: This routine which is only called at interrupt level is used
52: to set the DTR in the modem control register.
53:
54: Arguments:
55:
56: Context - Really a pointer to the device extension.
57:
58: Return Value:
59:
60: This routine always returns FALSE.
61:
62: --*/
63:
64: {
65:
66: PSERIAL_DEVICE_EXTENSION Extension = Context;
67: UCHAR ModemControl;
68:
69: ModemControl = READ_MODEM_CONTROL(Extension->Controller);
70:
71: ModemControl |= SERIAL_MCR_DTR;
72:
73: SerialDump(
74: SERFLOW,
75: ("SERIAL: Setting DTR for %x\n",
76: Extension->Controller)
77: );
78: WRITE_MODEM_CONTROL(
79: Extension->Controller,
80: ModemControl
81: );
82:
83: return FALSE;
84:
85: }
86:
87: BOOLEAN
88: SerialClrDTR(
89: IN PVOID Context
90: )
91:
92: /*++
93:
94: Routine Description:
95:
96: This routine which is only called at interrupt level is used
97: to clear the DTR in the modem control register.
98:
99: Arguments:
100:
101: Context - Really a pointer to the device extension.
102:
103: Return Value:
104:
105: This routine always returns FALSE.
106:
107: --*/
108:
109: {
110:
111: PSERIAL_DEVICE_EXTENSION Extension = Context;
112: UCHAR ModemControl;
113:
114: ModemControl = READ_MODEM_CONTROL(Extension->Controller);
115:
116: ModemControl &= ~SERIAL_MCR_DTR;
117:
118: SerialDump(
119: SERFLOW,
120: ("SERIAL: Clearing DTR for %x\n",
121: Extension->Controller)
122: );
123: WRITE_MODEM_CONTROL(
124: Extension->Controller,
125: ModemControl
126: );
127:
128: return FALSE;
129:
130: }
131:
132: BOOLEAN
133: SerialSetRTS(
134: IN PVOID Context
135: )
136:
137: /*++
138:
139: Routine Description:
140:
141: This routine which is only called at interrupt level is used
142: to set the RTS in the modem control register.
143:
144: Arguments:
145:
146: Context - Really a pointer to the device extension.
147:
148: Return Value:
149:
150: This routine always returns FALSE.
151:
152: --*/
153:
154: {
155:
156: PSERIAL_DEVICE_EXTENSION Extension = Context;
157: UCHAR ModemControl;
158:
159: ModemControl = READ_MODEM_CONTROL(Extension->Controller);
160:
161: ModemControl |= SERIAL_MCR_RTS;
162:
163: SerialDump(
164: SERFLOW,
165: ("SERIAL: Setting Rts for %x\n",
166: Extension->Controller)
167: );
168: WRITE_MODEM_CONTROL(
169: Extension->Controller,
170: ModemControl
171: );
172:
173: return FALSE;
174:
175: }
176:
177: BOOLEAN
178: SerialClrRTS(
179: IN PVOID Context
180: )
181:
182: /*++
183:
184: Routine Description:
185:
186: This routine which is only called at interrupt level is used
187: to clear the RTS in the modem control register.
188:
189: Arguments:
190:
191: Context - Really a pointer to the device extension.
192:
193: Return Value:
194:
195: This routine always returns FALSE.
196:
197: --*/
198:
199: {
200:
201: PSERIAL_DEVICE_EXTENSION Extension = Context;
202: UCHAR ModemControl;
203:
204: ModemControl = READ_MODEM_CONTROL(Extension->Controller);
205:
206: ModemControl &= ~SERIAL_MCR_RTS;
207:
208: SerialDump(
209: SERFLOW,
210: ("SERIAL: Clearing Rts for %x\n",
211: Extension->Controller)
212: );
213: WRITE_MODEM_CONTROL(
214: Extension->Controller,
215: ModemControl
216: );
217:
218: return FALSE;
219:
220: }
221:
222: BOOLEAN
223: SerialSetupNewHandFlow(
224: IN PSERIAL_DEVICE_EXTENSION Extension,
225: IN PSERIAL_HANDFLOW NewHandFlow
226: )
227:
228: /*++
229:
230: Routine Description:
231:
232: This routine adjusts the flow control based on new
233: control flow.
234:
235: Arguments:
236:
237: Extension - A pointer to the serial device extension.
238:
239: NewHandFlow - A pointer to a serial handflow structure
240: that is to become the new setup for flow
241: control.
242:
243: Return Value:
244:
245: This routine always returns FALSE.
246:
247: --*/
248:
249: {
250:
251: SERIAL_HANDFLOW New = *NewHandFlow;
252:
253: //
254: // If the Extension->DeviceIsOpened is FALSE that means
255: // we are entering this routine in response to an open request.
256: // If that is so, then we always proceed with the work regardless
257: // of whether things have changed.
258: //
259:
260: //
261: // First we take care of the DTR flow control. We only
262: // do work if something has changed.
263: //
264:
265: if ((!Extension->DeviceIsOpened) ||
266: ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK) !=
267: (New.ControlHandShake & SERIAL_DTR_MASK))) {
268:
269: SerialDump(
270: SERFLOW,
271: ("SERIAL: Processing DTR flow for %x\n",
272: Extension->Controller)
273: );
274:
275: if (New.ControlHandShake & SERIAL_DTR_MASK) {
276:
277: //
278: // Well we might want to set DTR.
279: //
280: // Before we do, we need to check whether we are doing
281: // dtr flow control. If we are then we need to check
282: // if then number of characters in the interrupt buffer
283: // exceeds the XoffLimit. If it does then we don't
284: // enable DTR AND we set the RXHolding to record that
285: // we are holding because of the dtr.
286: //
287:
288: if ((New.ControlHandShake & SERIAL_DTR_MASK)
289: == SERIAL_DTR_HANDSHAKE) {
290:
291: if ((Extension->BufferSize - New.XoffLimit) >
292: Extension->CharsInInterruptBuffer) {
293:
294: //
295: // However if we are already holding we don't want
296: // to turn it back on unless we exceed the Xon
297: // limit.
298: //
299:
300: if (Extension->RXHolding & SERIAL_RX_DTR) {
301:
302: //
303: // We can assume that its DTR line is already low.
304: //
305:
306: if (Extension->CharsInInterruptBuffer >
307: (ULONG)New.XonLimit) {
308:
309: SerialDump(
310: SERFLOW,
311: ("SERIAL: Removing DTR block on reception for %x\n",
312: Extension->Controller)
313: );
314: Extension->RXHolding &= ~SERIAL_RX_DTR;
315: SerialSetDTR(Extension);
316:
317: }
318:
319: } else {
320:
321: SerialSetDTR(Extension);
322:
323: }
324:
325: } else {
326:
327: SerialDump(
328: SERFLOW,
329: ("SERIAL: Setting DTR block on reception for %x\n",
330: Extension->Controller)
331: );
332: Extension->RXHolding |= SERIAL_RX_DTR;
333: SerialClrDTR(Extension);
334:
335: }
336:
337: } else {
338:
339: //
340: // Note that if we aren't currently doing dtr flow control then
341: // we MIGHT have been. So even if we aren't currently doing
342: // DTR flow control, we should still check if RX is holding
343: // because of DTR. If it is, then we should clear the holding
344: // of this bit.
345: //
346:
347: if (Extension->RXHolding & SERIAL_RX_DTR) {
348:
349: SerialDump(
350: SERFLOW,
351: ("SERIAL: Removing dtr block of reception for %x\n",
352: Extension->Controller)
353: );
354: Extension->RXHolding &= ~SERIAL_RX_DTR;
355:
356: }
357:
358: SerialSetDTR(Extension);
359:
360: }
361:
362: } else {
363:
364: //
365: // The end result here will be that DTR is cleared.
366: //
367: // We first need to check whether reception is being held
368: // up because of previous DTR flow control. If it is then
369: // we should clear that reason in the RXHolding mask.
370: //
371:
372: if (Extension->RXHolding & SERIAL_RX_DTR) {
373:
374: SerialDump(
375: SERFLOW,
376: ("SERIAL: removing dtr block of reception for %x\n",
377: Extension->Controller)
378: );
379: Extension->RXHolding &= ~SERIAL_RX_DTR;
380:
381: }
382:
383: SerialClrDTR(Extension);
384:
385: }
386:
387: }
388:
389: //
390: // Time to take care of the RTS Flow control.
391: //
392: // First we only do work if something has changed.
393: //
394:
395: if ((!Extension->DeviceIsOpened) ||
396: ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) !=
397: (New.FlowReplace & SERIAL_RTS_MASK))) {
398:
399: SerialDump(
400: SERFLOW,
401: ("SERIAL: Processing RTS flow\n",
402: Extension->Controller)
403: );
404:
405: if ((New.FlowReplace & SERIAL_RTS_MASK) ==
406: SERIAL_RTS_HANDSHAKE) {
407:
408: //
409: // Well we might want to set RTS.
410: //
411: // Before we do, we need to check whether we are doing
412: // rts flow control. If we are then we need to check
413: // if then number of characters in the interrupt buffer
414: // exceeds the XoffLimit. If it does then we don't
415: // enable RTS AND we set the RXHolding to record that
416: // we are holding because of the rts.
417: //
418:
419: if ((Extension->BufferSize - New.XoffLimit) >
420: Extension->CharsInInterruptBuffer) {
421:
422: //
423: // However if we are already holding we don't want
424: // to turn it back on unless we exceed the Xon
425: // limit.
426: //
427:
428: if (Extension->RXHolding & SERIAL_RX_RTS) {
429:
430: //
431: // We can assume that its RTS line is already low.
432: //
433:
434: if (Extension->CharsInInterruptBuffer >
435: (ULONG)New.XonLimit) {
436:
437: SerialDump(
438: SERFLOW,
439: ("SERIAL: Removing rts block of reception for %x\n",
440: Extension->Controller)
441: );
442: Extension->RXHolding &= ~SERIAL_RX_RTS;
443: SerialSetRTS(Extension);
444:
445: }
446:
447: } else {
448:
449: SerialSetRTS(Extension);
450:
451: }
452:
453: } else {
454:
455: SerialDump(
456: SERFLOW,
457: ("SERIAL: Setting rts block of reception for %x\n",
458: Extension->Controller)
459: );
460: Extension->RXHolding |= SERIAL_RX_RTS;
461: SerialClrRTS(Extension);
462:
463: }
464:
465: } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
466: SERIAL_RTS_CONTROL) {
467:
468: //
469: // Note that if we aren't currently doing rts flow control then
470: // we MIGHT have been. So even if we aren't currently doing
471: // RTS flow control, we should still check if RX is holding
472: // because of RTS. If it is, then we should clear the holding
473: // of this bit.
474: //
475:
476: if (Extension->RXHolding & SERIAL_RX_RTS) {
477:
478: SerialDump(
479: SERFLOW,
480: ("SERIAL: Clearing rts block of reception for %x\n",
481: Extension->Controller)
482: );
483: Extension->RXHolding &= ~SERIAL_RX_RTS;
484:
485: }
486:
487: SerialSetRTS(Extension);
488:
489: } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
490: SERIAL_TRANSMIT_TOGGLE) {
491:
492: //
493: // We first need to check whether reception is being held
494: // up because of previous RTS flow control. If it is then
495: // we should clear that reason in the RXHolding mask.
496: //
497:
498: if (Extension->RXHolding & SERIAL_RX_RTS) {
499:
500: SerialDump(
501: SERFLOW,
502: ("SERIAL: TOGGLE Clearing rts block of reception for %x\n",
503: Extension->Controller)
504: );
505: Extension->RXHolding &= ~SERIAL_RX_RTS;
506:
507: }
508:
509: //
510: // We have to place the rts value into the Extension
511: // now so that the code that tests whether the
512: // rts line should be lowered will find that we
513: // are "still" doing transmit toggling. The code
514: // for lowering can be invoked later by a timer so
515: // it has to test whether it still needs to do its
516: // work.
517: //
518:
519: Extension->HandFlow.FlowReplace &= ~SERIAL_RTS_MASK;
520: Extension->HandFlow.FlowReplace |= SERIAL_TRANSMIT_TOGGLE;
521:
522: //
523: // The order of the tests is very important below.
524: //
525: // If there is a break then we should turn on the RTS.
526: //
527: // If there isn't a break but there are characters in
528: // the hardware, then turn on the RTS.
529: //
530: // If there are writes pending that aren't being held
531: // up, then turn on the RTS.
532: //
533:
534: if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
535: ((SerialProcessLSR(Extension) & (SERIAL_LSR_THRE |
536: SERIAL_LSR_TEMT)) !=
537: (SERIAL_LSR_THRE |
538: SERIAL_LSR_TEMT)) ||
539: (Extension->CurrentWriteIrp || Extension->TransmitImmediate ||
540: (!IsListEmpty(&Extension->WriteQueue)) &&
541: (!Extension->TXHolding))) {
542:
543: SerialSetRTS(Extension);
544:
545: } else {
546:
547: //
548: // This routine will check to see if it is time
549: // to lower the RTS because of transmit toggle
550: // being on. If it is ok to lower it, it will,
551: // if it isn't ok, it will schedule things so
552: // that it will get lowered later.
553: //
554:
555: Extension->CountOfTryingToLowerRTS++;
556: SerialPerhapsLowerRTS(Extension);
557:
558: }
559:
560: } else {
561:
562: //
563: // The end result here will be that RTS is cleared.
564: //
565: // We first need to check whether reception is being held
566: // up because of previous RTS flow control. If it is then
567: // we should clear that reason in the RXHolding mask.
568: //
569:
570: if (Extension->RXHolding & SERIAL_RX_RTS) {
571:
572: SerialDump(
573: SERFLOW,
574: ("SERIAL: Clearing rts block of reception for %x\n",
575: Extension->Controller)
576: );
577: Extension->RXHolding &= ~SERIAL_RX_RTS;
578:
579: }
580:
581: SerialClrRTS(Extension);
582:
583: }
584:
585: }
586:
587: //
588: // We now take care of automatic receive flow control.
589: // We only do work if things have changed.
590: //
591:
592: if ((!Extension->DeviceIsOpened) ||
593: ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) !=
594: (New.FlowReplace & SERIAL_AUTO_RECEIVE))) {
595:
596: if (New.FlowReplace & SERIAL_AUTO_RECEIVE) {
597:
598: //
599: // We wouldn't be here if it had been on before.
600: //
601: // We should check to see whether we exceed the turn
602: // off limits.
603: //
604: // Note that since we are following the OS/2 flow
605: // control rules we will never send an xon if
606: // when enabling xon/xoff flow control we discover that
607: // we could receive characters but we are held up do
608: // to a previous Xoff.
609: //
610:
611: if ((Extension->BufferSize - New.XoffLimit) <=
612: Extension->CharsInInterruptBuffer) {
613:
614: //
615: // Cause the Xoff to be sent.
616: //
617:
618: Extension->RXHolding |= SERIAL_RX_XOFF;
619:
620: SerialProdXonXoff(
621: Extension,
622: FALSE
623: );
624:
625: }
626:
627: } else {
628:
629: //
630: // The app has disabled automatic receive flow control.
631: //
632: // If transmission was being held up because of
633: // an automatic receive Xoff, then we should
634: // cause an Xon to be sent.
635: //
636:
637: if (Extension->RXHolding & SERIAL_RX_XOFF) {
638:
639: Extension->RXHolding &= ~SERIAL_RX_XOFF;
640:
641: //
642: // Cause the Xon to be sent.
643: //
644:
645: SerialProdXonXoff(
646: Extension,
647: TRUE
648: );
649:
650: }
651:
652: }
653:
654: }
655:
656: //
657: // We now take care of automatic transmit flow control.
658: // We only do work if things have changed.
659: //
660:
661: if ((!Extension->DeviceIsOpened) ||
662: ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) !=
663: (New.FlowReplace & SERIAL_AUTO_TRANSMIT))) {
664:
665: if (New.FlowReplace & SERIAL_AUTO_TRANSMIT) {
666:
667: //
668: // We wouldn't be here if it had been on before.
669: //
670: // BUG BUG ??? There is some belief that if autotransmit
671: // was just enabled, I should go look in what we
672: // already received, and if we find the xoff character
673: // then we should stop transmitting. I think this
674: // is an application bug. For now we just care about
675: // what we see in the future.
676: //
677:
678: ;
679:
680: } else {
681:
682: //
683: // The app has disabled automatic transmit flow control.
684: //
685: // If transmission was being held up because of
686: // an automatic transmit Xoff, then we should
687: // cause an Xon to be sent.
688: //
689:
690: if (Extension->TXHolding & SERIAL_TX_XOFF) {
691:
692: Extension->TXHolding &= ~SERIAL_TX_XOFF;
693:
694: //
695: // Cause the Xon to be sent.
696: //
697:
698: SerialProdXonXoff(
699: Extension,
700: TRUE
701: );
702:
703: }
704:
705: }
706:
707: }
708:
709: //
710: // At this point we can simply make sure that entire
711: // handflow structure in the extension is updated.
712: //
713:
714: Extension->HandFlow = New;
715:
716: return FALSE;
717:
718: }
719:
720: BOOLEAN
721: SerialSetHandFlow(
722: IN PVOID Context
723: )
724:
725: /*++
726:
727: Routine Description:
728:
729: This routine is used to set the handshake and control
730: flow in the device extension.
731:
732: Arguments:
733:
734: Context - Pointer to a structure that contains a pointer to
735: the device extension and a pointer to a handflow
736: structure..
737:
738: Return Value:
739:
740: This routine always returns FALSE.
741:
742: --*/
743:
744: {
745:
746: PSERIAL_IOCTL_SYNC S = Context;
747: PSERIAL_DEVICE_EXTENSION Extension = S->Extension;
748: PSERIAL_HANDFLOW HandFlow = S->Data;
749:
750: SerialSetupNewHandFlow(
751: Extension,
752: HandFlow
753: );
754:
755: SerialHandleModemUpdate(
756: Extension,
757: FALSE
758: );
759:
760: return FALSE;
761:
762: }
763:
764: BOOLEAN
765: SerialTurnOnBreak(
766: IN PVOID Context
767: )
768:
769: /*++
770:
771: Routine Description:
772:
773: This routine will turn on break in the hardware and
774: record the fact the break is on, in the extension variable
775: that holds reasons that transmission is stopped.
776:
777: Arguments:
778:
779: Context - Really a pointer to the device extension.
780:
781: Return Value:
782:
783: This routine always returns FALSE.
784:
785: --*/
786:
787: {
788:
789: PSERIAL_DEVICE_EXTENSION Extension = Context;
790:
791: UCHAR OldLineControl;
792:
793: if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
794: SERIAL_TRANSMIT_TOGGLE) {
795:
796: SerialSetRTS(Extension);
797:
798: }
799:
800: OldLineControl = READ_LINE_CONTROL(Extension->Controller);
801:
802: OldLineControl |= SERIAL_LCR_BREAK;
803:
804: WRITE_LINE_CONTROL(
805: Extension->Controller,
806: OldLineControl
807: );
808:
809: Extension->TXHolding |= SERIAL_TX_BREAK;
810:
811: return FALSE;
812:
813: }
814:
815: BOOLEAN
816: SerialTurnOffBreak(
817: IN PVOID Context
818: )
819:
820: /*++
821:
822: Routine Description:
823:
824: This routine will turn off break in the hardware and
825: record the fact the break is off, in the extension variable
826: that holds reasons that transmission is stopped.
827:
828: Arguments:
829:
830: Context - Really a pointer to the device extension.
831:
832: Return Value:
833:
834: This routine always returns FALSE.
835:
836: --*/
837:
838: {
839:
840: PSERIAL_DEVICE_EXTENSION Extension = Context;
841:
842: UCHAR OldLineControl;
843:
844: if (Extension->TXHolding & SERIAL_TX_BREAK) {
845:
846: //
847: // We actually have a good reason for testing if transmission
848: // is holding instead of blindly clearing the bit.
849: //
850: // If transmission actually was holding and the result of
851: // clearing the bit is that we should restart transmission
852: // then we will poke the interrupt enable bit, which will
853: // cause an actual interrupt and transmission will then
854: // restart on its own.
855: //
856: // If transmission wasn't holding and we poked the bit
857: // then we would interrupt before a character actually made
858: // it out and we could end up over writing a character in
859: // the transmission hardware.
860:
861: OldLineControl = READ_LINE_CONTROL(Extension->Controller);
862:
863: OldLineControl &= ~SERIAL_LCR_BREAK;
864:
865: WRITE_LINE_CONTROL(
866: Extension->Controller,
867: OldLineControl
868: );
869:
870: Extension->TXHolding &= ~SERIAL_TX_BREAK;
871:
872: if (!Extension->TXHolding &&
873: (Extension->TransmitImmediate ||
874: Extension->WriteLength) &&
875: Extension->HoldingEmpty) {
876:
877: DISABLE_ALL_INTERRUPTS(Extension->Controller);
878: ENABLE_ALL_INTERRUPTS(Extension->Controller);
879:
880: } else {
881:
882: //
883: // The following routine will lower the rts if we
884: // are doing transmit toggleing and there is no
885: // reason to keep it up.
886: //
887:
888: Extension->CountOfTryingToLowerRTS++;
889: SerialPerhapsLowerRTS(Extension);
890:
891: }
892:
893: }
894:
895: return FALSE;
896:
897: }
898:
899: BOOLEAN
900: SerialPretendXoff(
901: IN PVOID Context
902: )
903:
904: /*++
905:
906: Routine Description:
907:
908: This routine is used to process the Ioctl that request the
909: driver to act as if an Xoff was received. Even if the
910: driver does not have automatic Xoff/Xon flowcontrol - This
911: still will stop the transmission. This is the OS/2 behavior
912: and is not well specified for Windows. Therefore we adopt
913: the OS/2 behavior.
914:
915: Note: If the driver does not have automatic Xoff/Xon enabled
916: then the only way to restart transmission is for the
917: application to request we "act" as if we saw the xon.
918:
919: Arguments:
920:
921: Context - Really a pointer to the device extension.
922:
923: Return Value:
924:
925: This routine always returns FALSE.
926:
927: --*/
928:
929: {
930:
931: PSERIAL_DEVICE_EXTENSION Extension = Context;
932:
933: Extension->TXHolding |= SERIAL_TX_XOFF;
934:
935: if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
936: SERIAL_TRANSMIT_TOGGLE) {
937:
938: KeInsertQueueDpc(
939: &Extension->StartTimerLowerRTSDpc,
940: NULL,
941: NULL
942: )?Extension->CountOfTryingToLowerRTS++:0;
943:
944: }
945:
946: return FALSE;
947:
948: }
949:
950: BOOLEAN
951: SerialPretendXon(
952: IN PVOID Context
953: )
954:
955: /*++
956:
957: Routine Description:
958:
959: This routine is used to process the Ioctl that request the
960: driver to act as if an Xon was received.
961:
962: Note: If the driver does not have automatic Xoff/Xon enabled
963: then the only way to restart transmission is for the
964: application to request we "act" as if we saw the xon.
965:
966: Arguments:
967:
968: Context - Really a pointer to the device extension.
969:
970: Return Value:
971:
972: This routine always returns FALSE.
973:
974: --*/
975:
976: {
977:
978: PSERIAL_DEVICE_EXTENSION Extension = Context;
979:
980: if (Extension->TXHolding) {
981:
982: //
983: // We actually have a good reason for testing if transmission
984: // is holding instead of blindly clearing the bit.
985: //
986: // If transmission actually was holding and the result of
987: // clearing the bit is that we should restart transmission
988: // then we will poke the interrupt enable bit, which will
989: // cause an actual interrupt and transmission will then
990: // restart on its own.
991: //
992: // If transmission wasn't holding and we poked the bit
993: // then we would interrupt before a character actually made
994: // it out and we could end up over writing a character in
995: // the transmission hardware.
996:
997: Extension->TXHolding &= ~SERIAL_TX_XOFF;
998:
999: if (!Extension->TXHolding &&
1000: (Extension->TransmitImmediate ||
1001: Extension->WriteLength) &&
1002: Extension->HoldingEmpty) {
1003:
1004: DISABLE_ALL_INTERRUPTS(Extension->Controller);
1005: ENABLE_ALL_INTERRUPTS(Extension->Controller);
1006:
1007: }
1008:
1009: }
1010:
1011: return FALSE;
1012:
1013: }
1014:
1015: VOID
1016: SerialHandleReducedIntBuffer(
1017: IN PSERIAL_DEVICE_EXTENSION Extension
1018: )
1019:
1020: /*++
1021:
1022: Routine Description:
1023:
1024: This routine is called to handle a reduction in the number
1025: of characters in the interrupt (typeahead) buffer. It
1026: will check the current output flow control and re-enable transmission
1027: as needed.
1028:
1029: NOTE: This routine assumes that it is working at interrupt level.
1030:
1031: Arguments:
1032:
1033: Extension - A pointer to the device extension.
1034:
1035: Return Value:
1036:
1037: None.
1038:
1039: --*/
1040:
1041: {
1042:
1043:
1044: //
1045: // If we are doing receive side flow control and we are
1046: // currently "holding" then because we've emptied out
1047: // some characters from the interrupt buffer we need to
1048: // see if we can "re-enable" reception.
1049: //
1050:
1051: if (Extension->RXHolding) {
1052:
1053: if (Extension->CharsInInterruptBuffer <=
1054: (ULONG)Extension->HandFlow.XonLimit) {
1055:
1056: if (Extension->RXHolding & SERIAL_RX_DTR) {
1057:
1058: Extension->RXHolding &= ~SERIAL_RX_DTR;
1059: SerialSetDTR(Extension);
1060:
1061: }
1062:
1063: if (Extension->RXHolding & SERIAL_RX_RTS) {
1064:
1065: Extension->RXHolding &= ~SERIAL_RX_RTS;
1066: SerialSetRTS(Extension);
1067:
1068: }
1069:
1070: if (Extension->RXHolding & SERIAL_RX_XOFF) {
1071:
1072: //
1073: // Prod the transmit code to send xon.
1074: //
1075:
1076: SerialProdXonXoff(
1077: Extension,
1078: TRUE
1079: );
1080:
1081: }
1082:
1083: }
1084:
1085: }
1086:
1087: }
1088:
1089: VOID
1090: SerialProdXonXoff(
1091: IN PSERIAL_DEVICE_EXTENSION Extension,
1092: IN BOOLEAN SendXon
1093: )
1094:
1095: /*++
1096:
1097: Routine Description:
1098:
1099: This routine will set up the SendXxxxChar variables if
1100: necessary and determine if we are going to be interrupting
1101: because of current transmission state. It will cause an
1102: interrupt to occur if neccessary, to send the xon/xoff char.
1103:
1104: NOTE: This routine assumes that it is called at interrupt
1105: level.
1106:
1107: Arguments:
1108:
1109: Extension - A pointer to the serial device extension.
1110:
1111: SendXon - If a character is to be send, this indicates whether
1112: it should be an Xon or an Xoff.
1113:
1114: Return Value:
1115:
1116: None.
1117:
1118: --*/
1119:
1120: {
1121:
1122: //
1123: // We assume that if the prodding is called more than
1124: // once that the last prod has set things up appropriately.
1125: //
1126: // We could get called before the character is sent out
1127: // because the send of the character was blocked because
1128: // of hardware flow control (or break).
1129: //
1130:
1131: if (!Extension->SendXonChar && !Extension->SendXoffChar
1132: && Extension->HoldingEmpty) {
1133:
1134: DISABLE_ALL_INTERRUPTS(Extension->Controller);
1135: ENABLE_ALL_INTERRUPTS(Extension->Controller);
1136:
1137: }
1138:
1139: if (SendXon) {
1140:
1141: Extension->SendXonChar = TRUE;
1142: Extension->SendXoffChar = FALSE;
1143:
1144: } else {
1145:
1146: Extension->SendXonChar = FALSE;
1147: Extension->SendXoffChar = TRUE;
1148:
1149: }
1150:
1151: }
1152:
1153: ULONG
1154: SerialHandleModemUpdate(
1155: IN PSERIAL_DEVICE_EXTENSION Extension,
1156: IN BOOLEAN DoingTX
1157: )
1158:
1159: /*++
1160:
1161: Routine Description:
1162:
1163: This routine will be to check on the modem status, and
1164: handle any appropriate event notification as well as
1165: any flow control appropriate to modem status lines.
1166:
1167: NOTE: This routine assumes that it is called at interrupt
1168: level.
1169:
1170: Arguments:
1171:
1172: Extension - A pointer to the serial device extension.
1173:
1174: DoingTX - This boolean is used to indicate that this call
1175: came from the transmit processing code. If this
1176: is true then there is no need to cause a new interrupt
1177: since the code will be trying to send the next
1178: character as soon as this call finishes.
1179:
1180: Return Value:
1181:
1182: This returns the old value of the modem status register
1183: (extended into a ULONG).
1184:
1185: --*/
1186:
1187: {
1188:
1189: //
1190: // We keep this local so that after we are done
1191: // examining the modem status and we've updated
1192: // the transmission holding value, we know whether
1193: // we've changed from needing to hold up transmission
1194: // to transmission being able to proceed.
1195: //
1196: ULONG OldTXHolding = Extension->TXHolding;
1197:
1198: //
1199: // Holds the value in the mode status register.
1200: //
1201: UCHAR ModemStatus;
1202:
1203: ModemStatus =
1204: READ_MODEM_STATUS(Extension->Controller);
1205:
1206: //
1207: // If we are placeing the modem status into the data stream
1208: // on every change, we should do it now.
1209: //
1210:
1211: if (Extension->EscapeChar) {
1212:
1213: if (ModemStatus & (SERIAL_MSR_DCTS |
1214: SERIAL_MSR_DDSR |
1215: SERIAL_MSR_TERI |
1216: SERIAL_MSR_DDCD)) {
1217:
1218: SerialPutChar(
1219: Extension,
1220: Extension->EscapeChar
1221: );
1222: SerialPutChar(
1223: Extension,
1224: SERIAL_LSRMST_MST
1225: );
1226: SerialPutChar(
1227: Extension,
1228: ModemStatus
1229: );
1230:
1231: }
1232:
1233: }
1234:
1235:
1236: //
1237: // Take care of input flow control based on sensitivity
1238: // to the DSR. This is done so that the application won't
1239: // see spurious data generated by odd devices.
1240: //
1241: // Basically, if we are doing dsr sensitivity then the
1242: // driver should only accept data when the dsr bit is
1243: // set.
1244: //
1245:
1246: if (Extension->HandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) {
1247:
1248: if (ModemStatus & SERIAL_MSR_DSR) {
1249:
1250: //
1251: // The line is high. Simply make sure that
1252: // RXHolding does't have the DSR bit.
1253: //
1254:
1255: Extension->RXHolding &= ~SERIAL_RX_DSR;
1256:
1257: } else {
1258:
1259: Extension->RXHolding |= SERIAL_RX_DSR;
1260:
1261: }
1262:
1263: } else {
1264:
1265: //
1266: // We don't have sensitivity due to DSR. Make sure we
1267: // arn't holding. (We might have been, but the app just
1268: // asked that we don't hold for this reason any more.)
1269: //
1270:
1271: Extension->RXHolding &= ~SERIAL_RX_DSR;
1272:
1273: }
1274:
1275: //
1276: // Check to see if we have a wait
1277: // pending on the modem status events. If we
1278: // do then we schedule a dpc to satisfy
1279: // that wait.
1280: //
1281:
1282: if (Extension->IsrWaitMask) {
1283:
1284: if ((Extension->IsrWaitMask & SERIAL_EV_CTS) &&
1285: (ModemStatus & SERIAL_MSR_DCTS)) {
1286:
1287: Extension->HistoryMask |= SERIAL_EV_CTS;
1288:
1289: }
1290:
1291: if ((Extension->IsrWaitMask & SERIAL_EV_DSR) &&
1292: (ModemStatus & SERIAL_MSR_DDSR)) {
1293:
1294: Extension->HistoryMask |= SERIAL_EV_DSR;
1295:
1296: }
1297:
1298: if ((Extension->IsrWaitMask & SERIAL_EV_RING) &&
1299: (ModemStatus & SERIAL_MSR_TERI)) {
1300:
1301: Extension->HistoryMask |= SERIAL_EV_RING;
1302:
1303: }
1304:
1305: if ((Extension->IsrWaitMask & SERIAL_EV_RLSD) &&
1306: (ModemStatus & SERIAL_MSR_DDCD)) {
1307:
1308: Extension->HistoryMask |= SERIAL_EV_RLSD;
1309:
1310: }
1311:
1312: if (Extension->IrpMaskLocation &&
1313: Extension->HistoryMask) {
1314:
1315: *Extension->IrpMaskLocation =
1316: Extension->HistoryMask;
1317: Extension->IrpMaskLocation = NULL;
1318: Extension->HistoryMask = 0;
1319:
1320: Extension->CurrentWaitIrp->
1321: IoStatus.Information = sizeof(ULONG);
1322: KeInsertQueueDpc(
1323: &Extension->CommWaitDpc,
1324: NULL,
1325: NULL
1326: );
1327:
1328: }
1329:
1330: }
1331:
1332: //
1333: // If the app has modem line flow control then
1334: // we check to see if we have to hold up transmission.
1335: //
1336:
1337: if (Extension->HandFlow.ControlHandShake &
1338: SERIAL_OUT_HANDSHAKEMASK) {
1339:
1340: if (Extension->HandFlow.ControlHandShake &
1341: SERIAL_CTS_HANDSHAKE) {
1342:
1343: if (ModemStatus & SERIAL_MSR_CTS) {
1344:
1345: Extension->TXHolding &= ~SERIAL_TX_CTS;
1346:
1347: } else {
1348:
1349: Extension->TXHolding |= SERIAL_TX_CTS;
1350:
1351: }
1352:
1353: } else {
1354:
1355: Extension->TXHolding &= ~SERIAL_TX_CTS;
1356:
1357: }
1358:
1359: if (Extension->HandFlow.ControlHandShake &
1360: SERIAL_DSR_HANDSHAKE) {
1361:
1362: if (ModemStatus & SERIAL_MSR_DSR) {
1363:
1364: Extension->TXHolding &= ~SERIAL_TX_DSR;
1365:
1366: } else {
1367:
1368: Extension->TXHolding |= SERIAL_TX_DSR;
1369:
1370: }
1371:
1372: } else {
1373:
1374: Extension->TXHolding &= ~SERIAL_TX_DSR;
1375:
1376: }
1377:
1378: if (Extension->HandFlow.ControlHandShake &
1379: SERIAL_DCD_HANDSHAKE) {
1380:
1381: if (ModemStatus & SERIAL_MSR_DCD) {
1382:
1383: Extension->TXHolding &= ~SERIAL_TX_DCD;
1384:
1385: } else {
1386:
1387: Extension->TXHolding |= SERIAL_TX_DCD;
1388:
1389: }
1390:
1391: } else {
1392:
1393: Extension->TXHolding &= ~SERIAL_TX_DCD;
1394:
1395: }
1396:
1397: //
1398: // If we hadn't been holding, and now we are then
1399: // queue off a dpc that will lower the RTS line
1400: // if we are doing transmit toggling.
1401: //
1402:
1403: if (!OldTXHolding && Extension->TXHolding &&
1404: ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
1405: SERIAL_TRANSMIT_TOGGLE)) {
1406:
1407: KeInsertQueueDpc(
1408: &Extension->StartTimerLowerRTSDpc,
1409: NULL,
1410: NULL
1411: )?Extension->CountOfTryingToLowerRTS++:0;
1412:
1413: }
1414:
1415: //
1416: // We've done any adjusting that needed to be
1417: // done to the holding mask given updates
1418: // to the modem status. If the Holding mask
1419: // is clear (and it wasn't clear to start)
1420: // and we have "write" work to do set things
1421: // up so that the transmission code gets invoked.
1422: //
1423:
1424: if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
1425:
1426: if (!Extension->TXHolding &&
1427: (Extension->TransmitImmediate ||
1428: Extension->WriteLength) &&
1429: Extension->HoldingEmpty) {
1430:
1431: DISABLE_ALL_INTERRUPTS(Extension->Controller);
1432: ENABLE_ALL_INTERRUPTS(Extension->Controller);
1433:
1434: }
1435:
1436: }
1437:
1438: } else {
1439:
1440: //
1441: // We need to check if transmission is holding
1442: // up because of modem status lines. What
1443: // could have occured is that for some strange
1444: // reason, the app has asked that we no longer
1445: // stop doing output flow control based on
1446: // the modem status lines. If however, we
1447: // *had* been held up because of the status lines
1448: // then we need to clear up those reasons.
1449: //
1450:
1451: if (Extension->TXHolding & (SERIAL_TX_DCD |
1452: SERIAL_TX_DSR |
1453: SERIAL_TX_CTS)) {
1454:
1455: Extension->TXHolding &= ~(SERIAL_TX_DCD |
1456: SERIAL_TX_DSR |
1457: SERIAL_TX_CTS);
1458:
1459:
1460: if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
1461:
1462: if (!Extension->TXHolding &&
1463: (Extension->TransmitImmediate ||
1464: Extension->WriteLength) &&
1465: Extension->HoldingEmpty) {
1466:
1467: DISABLE_ALL_INTERRUPTS(Extension->Controller);
1468: ENABLE_ALL_INTERRUPTS(Extension->Controller);
1469:
1470: }
1471:
1472: }
1473:
1474: }
1475:
1476: }
1477:
1478: return ((ULONG)ModemStatus);
1479: }
1480:
1481: BOOLEAN
1482: SerialPerhapsLowerRTS(
1483: IN PVOID Context
1484: )
1485:
1486: /*++
1487:
1488: Routine Description:
1489:
1490: This routine checks that the software reasons for lowering
1491: the RTS lines are present. If so, it will then cause the
1492: line status register to be read (and any needed processing
1493: implied by the status register to be done), and if the
1494: shift register is empty it will lower the line. If the
1495: shift register isn't empty, this routine will queue off
1496: a dpc that will start a timer, that will basically call
1497: us back to try again.
1498:
1499: NOTE: This routine assumes that it is called at interrupt
1500: level.
1501:
1502: Arguments:
1503:
1504: Context - Really a pointer to the device extension.
1505:
1506: Return Value:
1507:
1508: Always FALSE.
1509:
1510: --*/
1511:
1512: {
1513:
1514: PSERIAL_DEVICE_EXTENSION Extension = Context;
1515:
1516:
1517: //
1518: // We first need to test if we are actually still doing
1519: // transmit toggle flow control. If we aren't then
1520: // we have no reason to try be here.
1521: //
1522:
1523: if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
1524: SERIAL_TRANSMIT_TOGGLE) {
1525:
1526: //
1527: // The order of the tests is very important below.
1528: //
1529: // If there is a break then we should leave on the RTS,
1530: // because when the break is turned off, it will submit
1531: // the code to shut down the RTS.
1532: //
1533: // If there are writes pending that aren't being held
1534: // up, then leave on the RTS, because the end of the write
1535: // code will cause this code to be reinvoked. If the writes
1536: // are being held up, its ok to lower the RTS because the
1537: // upon trying to write the first character after transmission
1538: // is restarted, we will raise the RTS line.
1539: //
1540:
1541: if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
1542: (Extension->CurrentWriteIrp || Extension->TransmitImmediate ||
1543: (!IsListEmpty(&Extension->WriteQueue)) &&
1544: (!Extension->TXHolding))) {
1545:
1546: NOTHING;
1547:
1548: } else {
1549:
1550: //
1551: // Looks good so far. Call the line status check and processing
1552: // code, it will return the "current" line status value. If
1553: // the holding and shift register are clear, lower the RTS line,
1554: // if they aren't clear, queue of a dpc that will cause a timer
1555: // to reinvoke us later. We do this code here because no one
1556: // but this routine cares about the characters in the hardware,
1557: // so no routine by this routine will bother invoking to test
1558: // if the hardware is empty.
1559: //
1560:
1561: if ((SerialProcessLSR(Extension) &
1562: (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
1563: (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
1564:
1565: //
1566: // Well it's not empty, try again later.
1567: //
1568:
1569: KeInsertQueueDpc(
1570: &Extension->StartTimerLowerRTSDpc,
1571: NULL,
1572: NULL
1573: )?Extension->CountOfTryingToLowerRTS++:0;
1574:
1575:
1576: } else {
1577:
1578: //
1579: // Nothing in the hardware, Lower the RTS.
1580: //
1581:
1582: SerialClrRTS(Extension);
1583:
1584:
1585: }
1586:
1587: }
1588:
1589: }
1590:
1591: //
1592: // We decement the counter to indicate that we've reached
1593: // the end of the execution path that is trying to push
1594: // down the RTS line.
1595: //
1596:
1597: Extension->CountOfTryingToLowerRTS--;
1598:
1599: return FALSE;
1600: }
1601:
1602: VOID
1603: SerialStartTimerLowerRTS(
1604: IN PKDPC Dpc,
1605: IN PVOID DeferredContext,
1606: IN PVOID SystemContext1,
1607: IN PVOID SystemContext2
1608: )
1609:
1610: /*++
1611:
1612: Routine Description:
1613:
1614: This routine starts a timer that when it expires will start
1615: a dpc that will check if it can lower the rts line because
1616: there are no characters in the hardware.
1617:
1618: Arguments:
1619:
1620: Dpc - Not Used.
1621:
1622: DeferredContext - Really points to the device extension.
1623:
1624: SystemContext1 - Not Used.
1625:
1626: SystemContext2 - Not Used.
1627:
1628: Return Value:
1629:
1630: None.
1631:
1632: --*/
1633:
1634: {
1635:
1636: PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
1637: LARGE_INTEGER CharTime;
1638: KIRQL OldIrql;
1639:
1640: UNREFERENCED_PARAMETER(Dpc);
1641: UNREFERENCED_PARAMETER(SystemContext1);
1642: UNREFERENCED_PARAMETER(SystemContext2);
1643:
1644:
1645: //
1646: // Take out the lock to prevent the line control
1647: // from changing out from under us while we calculate
1648: // a character time.
1649: //
1650:
1651: KeAcquireSpinLock(
1652: &Extension->ControlLock,
1653: &OldIrql
1654: );
1655:
1656: CharTime = SerialGetCharTime(Extension);
1657:
1658: KeReleaseSpinLock(
1659: &Extension->ControlLock,
1660: OldIrql
1661: );
1662:
1663: CharTime = RtlLargeIntegerNegate(CharTime);
1664:
1665: if (KeSetTimer(
1666: &Extension->LowerRTSTimer,
1667: CharTime,
1668: &Extension->PerhapsLowerRTSDpc
1669: )) {
1670:
1671: //
1672: // The timer was already in the timer queue. This implies
1673: // that one path of execution that was trying to lower
1674: // the RTS has "died". Synchronize with the ISR so that
1675: // we can lower the count.
1676: //
1677:
1678: KeSynchronizeExecution(
1679: Extension->Interrupt,
1680: SerialDecrementRTSCounter,
1681: Extension
1682: );
1683:
1684: }
1685:
1686: }
1687:
1688: VOID
1689: SerialInvokePerhapsLowerRTS(
1690: IN PKDPC Dpc,
1691: IN PVOID DeferredContext,
1692: IN PVOID SystemContext1,
1693: IN PVOID SystemContext2
1694: )
1695:
1696: /*++
1697:
1698: Routine Description:
1699:
1700: This dpc routine exists solely to call the code that
1701: tests if the rts line should be lowered when TRANSMIT
1702: TOGGLE flow control is being used.
1703:
1704: Arguments:
1705:
1706: Dpc - Not Used.
1707:
1708: DeferredContext - Really points to the device extension.
1709:
1710: SystemContext1 - Not Used.
1711:
1712: SystemContext2 - Not Used.
1713:
1714: Return Value:
1715:
1716: None.
1717:
1718: --*/
1719:
1720: {
1721:
1722: PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
1723:
1724: UNREFERENCED_PARAMETER(Dpc);
1725: UNREFERENCED_PARAMETER(SystemContext1);
1726: UNREFERENCED_PARAMETER(SystemContext2);
1727:
1728: KeSynchronizeExecution(
1729: Extension->Interrupt,
1730: SerialPerhapsLowerRTS,
1731: Extension
1732: );
1733:
1734: }
1735:
1736: BOOLEAN
1737: SerialDecrementRTSCounter(
1738: IN PVOID Context
1739: )
1740:
1741: /*++
1742:
1743: Routine Description:
1744:
1745: This routine checks that the software reasons for lowering
1746: the RTS lines are present. If so, it will then cause the
1747: line status register to be read (and any needed processing
1748: implied by the status register to be done), and if the
1749: shift register is empty it will lower the line. If the
1750: shift register isn't empty, this routine will queue off
1751: a dpc that will start a timer, that will basically call
1752: us back to try again.
1753:
1754: NOTE: This routine assumes that it is called at interrupt
1755: level.
1756:
1757: Arguments:
1758:
1759: Context - Really a pointer to the device extension.
1760:
1761: Return Value:
1762:
1763: Always FALSE.
1764:
1765: --*/
1766:
1767: {
1768:
1769: PSERIAL_DEVICE_EXTENSION Extension = Context;
1770:
1771: Extension->CountOfTryingToLowerRTS--;
1772:
1773: return FALSE;
1774:
1775: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.