📄 modmflow.c
字号:
/*++
Copyright (c) 1991, 1992, 1993 Microsoft Corporation
Module Name:
modmflow.c
Abstract:
This module contains *MOST* of the code used to manipulate
the modem control and status registers. The vast majority
of the remainder of flow control is concentrated in the
Interrupt service routine. A very small amount resides
in the read code that pull characters out of the interrupt
buffer.
Author:
Anthony V. Ercolano 26-Sep-1991
Environment:
Kernel mode
Revision History :
--*/
#include "precomp.h"
BOOLEAN
SerialDecrementRTSCounter(
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#if 0
#pragma alloc_text(PAGESER,SerialHandleReducedIntBuffer)
#pragma alloc_text(PAGESER,SerialProdXonXoff)
#pragma alloc_text(PAGESER,SerialHandleModemUpdate)
#pragma alloc_text(PAGESER,SerialPerhapsLowerRTS)
#pragma alloc_text(PAGESER,SerialStartTimerLowerRTS)
#pragma alloc_text(PAGESER,SerialInvokePerhapsLowerRTS)
#pragma alloc_text(PAGESER,SerialSetDTR)
//#pragma alloc_text(PAGESER,SerialClrDTR)
#pragma alloc_text(PAGESER,SerialSetRTS)
//#pragma alloc_text(PAGESER,SerialClrRTS)
//#pragma alloc_text(PAGESER,SerialSetupNewHandFlow)
#pragma alloc_text(PAGESER,SerialSetHandFlow)
#pragma alloc_text(PAGESER,SerialTurnOnBreak)
#pragma alloc_text(PAGESER,SerialTurnOffBreak)
#pragma alloc_text(PAGESER,SerialPretendXoff)
#pragma alloc_text(PAGESER,SerialPretendXon)
#pragma alloc_text(PAGESER,SerialDecrementRTSCounter)
#endif
#endif
BOOLEAN
SerialSetDTR(
IN PVOID Context
)
/*++
Routine Description:
This routine which is only called at interrupt level is used
to set the DTR in the modem control register.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
UCHAR ModemControl;
ModemControl = READ_MODEM_CONTROL(Extension->Controller);
ModemControl |= SERIAL_MCR_DTR;
SerialDbgPrintEx(SERFLOW, "Setting DTR for %x\n", Extension->Controller);
WRITE_MODEM_CONTROL(Extension->Controller, ModemControl);
return FALSE;
}
BOOLEAN
SerialClrDTR(
IN PVOID Context
)
/*++
Routine Description:
This routine which is only called at interrupt level is used
to clear the DTR in the modem control register.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
UCHAR ModemControl;
ModemControl = READ_MODEM_CONTROL(Extension->Controller);
ModemControl &= ~SERIAL_MCR_DTR;
SerialDbgPrintEx(SERFLOW, "Clearing DTR for %x\n", Extension->Controller);
WRITE_MODEM_CONTROL(Extension->Controller, ModemControl);
return FALSE;
}
BOOLEAN
SerialSetRTS(
IN PVOID Context
)
/*++
Routine Description:
This routine which is only called at interrupt level is used
to set the RTS in the modem control register.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
UCHAR ModemControl;
ModemControl = READ_MODEM_CONTROL(Extension->Controller);
ModemControl |= SERIAL_MCR_RTS;
SerialDbgPrintEx(SERFLOW, "Setting Rts for %x\n", Extension->Controller);
WRITE_MODEM_CONTROL(Extension->Controller, ModemControl);
return FALSE;
}
BOOLEAN
SerialClrRTS(
IN PVOID Context
)
/*++
Routine Description:
This routine which is only called at interrupt level is used
to clear the RTS in the modem control register.
Arguments:
Context - Really a pointer to the device extension.
Return Value:
This routine always returns FALSE.
--*/
{
PSERIAL_DEVICE_EXTENSION Extension = Context;
UCHAR ModemControl;
ModemControl = READ_MODEM_CONTROL(Extension->Controller);
ModemControl &= ~SERIAL_MCR_RTS;
SerialDbgPrintEx(SERFLOW, "Clearing Rts for %x\n", Extension->Controller);
WRITE_MODEM_CONTROL(Extension->Controller, ModemControl);
return FALSE;
}
BOOLEAN
SerialSetupNewHandFlow(
IN PSERIAL_DEVICE_EXTENSION Extension,
IN PSERIAL_HANDFLOW NewHandFlow
)
/*++
Routine Description:
This routine adjusts the flow control based on new
control flow.
Arguments:
Extension - A pointer to the serial device extension.
NewHandFlow - A pointer to a serial handflow structure
that is to become the new setup for flow
control.
Return Value:
This routine always returns FALSE.
--*/
{
SERIAL_HANDFLOW New = *NewHandFlow;
//
// If the Extension->DeviceIsOpened is FALSE that means
// we are entering this routine in response to an open request.
// If that is so, then we always proceed with the work regardless
// of whether things have changed.
//
//
// First we take care of the DTR flow control. We only
// do work if something has changed.
//
if ((!Extension->DeviceIsOpened) ||
((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK) !=
(New.ControlHandShake & SERIAL_DTR_MASK))) {
SerialDbgPrintEx(SERFLOW, "Processing DTR flow for %x\n",
Extension->Controller);
if (New.ControlHandShake & SERIAL_DTR_MASK) {
//
// Well we might want to set DTR.
//
// Before we do, we need to check whether we are doing
// dtr flow control. If we are then we need to check
// if then number of characters in the interrupt buffer
// exceeds the XoffLimit. If it does then we don't
// enable DTR AND we set the RXHolding to record that
// we are holding because of the dtr.
//
if ((New.ControlHandShake & SERIAL_DTR_MASK)
== SERIAL_DTR_HANDSHAKE) {
if ((Extension->BufferSize - New.XoffLimit) >
Extension->CharsInInterruptBuffer) {
//
// However if we are already holding we don't want
// to turn it back on unless we exceed the Xon
// limit.
//
if (Extension->RXHolding & SERIAL_RX_DTR) {
//
// We can assume that its DTR line is already low.
//
if (Extension->CharsInInterruptBuffer >
(ULONG)New.XonLimit) {
SerialDbgPrintEx(SERFLOW, "Removing DTR block on "
"reception for %x\n",
Extension->Controller);
Extension->RXHolding &= ~SERIAL_RX_DTR;
SerialSetDTR(Extension);
}
} else {
SerialSetDTR(Extension);
}
} else {
SerialDbgPrintEx(SERFLOW, "Setting DTR block on reception "
"for %x\n", Extension->Controller);
Extension->RXHolding |= SERIAL_RX_DTR;
SerialClrDTR(Extension);
}
} else {
//
// Note that if we aren't currently doing dtr flow control then
// we MIGHT have been. So even if we aren't currently doing
// DTR flow control, we should still check if RX is holding
// because of DTR. If it is, then we should clear the holding
// of this bit.
//
if (Extension->RXHolding & SERIAL_RX_DTR) {
SerialDbgPrintEx(SERFLOW, "Removing dtr block of reception "
"for %x\n", Extension->Controller);
Extension->RXHolding &= ~SERIAL_RX_DTR;
}
SerialSetDTR(Extension);
}
} else {
//
// The end result here will be that DTR is cleared.
//
// We first need to check whether reception is being held
// up because of previous DTR flow control. If it is then
// we should clear that reason in the RXHolding mask.
//
if (Extension->RXHolding & SERIAL_RX_DTR) {
SerialDbgPrintEx(SERFLOW, "removing dtr block of reception for"
" %x\n", Extension->Controller);
Extension->RXHolding &= ~SERIAL_RX_DTR;
}
SerialClrDTR(Extension);
}
}
//
// Time to take care of the RTS Flow control.
//
// First we only do work if something has changed.
//
if ((!Extension->DeviceIsOpened) ||
((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) !=
(New.FlowReplace & SERIAL_RTS_MASK))) {
SerialDbgPrintEx(SERFLOW, "Processing RTS flow\n",
Extension->Controller);
if ((New.FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_RTS_HANDSHAKE) {
//
// Well we might want to set RTS.
//
// Before we do, we need to check whether we are doing
// rts flow control. If we are then we need to check
// if then number of characters in the interrupt buffer
// exceeds the XoffLimit. If it does then we don't
// enable RTS AND we set the RXHolding to record that
// we are holding because of the rts.
//
if ((Extension->BufferSize - New.XoffLimit) >
Extension->CharsInInterruptBuffer) {
//
// However if we are already holding we don't want
// to turn it back on unless we exceed the Xon
// limit.
//
if (Extension->RXHolding & SERIAL_RX_RTS) {
//
// We can assume that its RTS line is already low.
//
if (Extension->CharsInInterruptBuffer >
(ULONG)New.XonLimit) {
SerialDbgPrintEx(SERFLOW, "Removing rts block of "
"reception for %x\n",
Extension->Controller);
Extension->RXHolding &= ~SERIAL_RX_RTS;
SerialSetRTS(Extension);
}
} else {
SerialSetRTS(Extension);
}
} else {
SerialDbgPrintEx(SERFLOW, "Setting rts block of reception for "
"%x\n", Extension->Controller);
Extension->RXHolding |= SERIAL_RX_RTS;
SerialClrRTS(Extension);
}
} else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_RTS_CONTROL) {
//
// Note that if we aren't currently doing rts flow control then
// we MIGHT have been. So even if we aren't currently doing
// RTS flow control, we should still check if RX is holding
// because of RTS. If it is, then we should clear the holding
// of this bit.
//
if (Extension->RXHolding & SERIAL_RX_RTS) {
SerialDbgPrintEx(SERFLOW, "Clearing rts block of reception for "
"%x\n", Extension->Controller);
Extension->RXHolding &= ~SERIAL_RX_RTS;
}
SerialSetRTS(Extension);
} else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
SERIAL_TRANSMIT_TOGGLE) {
//
// We first need to check whether reception is being held
// up because of previous RTS flow control. If it is then
// we should clear that reason in the RXHolding mask.
//
if (Extension->RXHolding & SERIAL_RX_RTS) {
SerialDbgPrintEx(SERFLOW, "TOGGLE Clearing rts block of "
"reception for %x\n", Extension->Controller);
Extension->RXHolding &= ~SERIAL_RX_RTS;
}
//
// We have to place the rts value into the Extension
// now so that the code that tests whether the
// rts line should be lowered will find that we
// are "still" doing transmit toggling. The code
// for lowering can be invoked later by a timer so
// it has to test whether it still needs to do its
// work.
//
Extension->HandFlow.FlowReplace &= ~SERIAL_RTS_MASK;
Extension->HandFlow.FlowReplace |= SERIAL_TRANSMIT_TOGGLE;
//
// The order of the tests is very important below.
//
// If there is a break then we should turn on the RTS.
//
// If there isn't a break but there are characters in
// the hardware, then turn on the RTS.
//
// If there are writes pending that aren't being held
// up, then turn on the RTS.
//
if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
((SerialProcessLSR(Extension) & (SERIAL_LSR_THRE |
SERIAL_LSR_TEMT)) !=
(SERIAL_LSR_THRE |
SERIAL_LSR_TEMT)) ||
(Extension->CurrentWriteIrp || Extension->TransmitImmediate ||
(!IsListEmpty(&Extension->WriteQueue)) &&
(!Extension->TXHolding))) {
SerialSetRTS(Extension);
} else {
//
// This routine will check to see if it is time
// to lower the RTS because of transmit toggle
// being on. If it is ok to lower it, it will,
// if it isn't ok, it will schedule things so
// that it will get lowered later.
//
Extension->CountOfTryingToLowerRTS++;
SerialPerhapsLowerRTS(Extension);
}
} else {
//
// The end result here will be that RTS is cleared.
//
// We first need to check whether reception is being held
// up because of previous RTS flow control. If it is then
// we should clear that reason in the RXHolding mask.
//
if (Extension->RXHolding & SERIAL_RX_RTS) {
SerialDbgPrintEx(SERFLOW, "Clearing rts block of reception for"
" %x\n", Extension->Controller);
Extension->RXHolding &= ~SERIAL_RX_RTS;
}
SerialClrRTS(Extension);
}
}
//
// We now take care of automatic receive flow control.
// We only do work if things have changed.
//
if ((!Extension->DeviceIsOpened) ||
((Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) !=
(New.FlowReplace & SERIAL_AUTO_RECEIVE))) {
if (New.FlowReplace & SERIAL_AUTO_RECEIVE) {
//
// We wouldn't be here if it had been on before.
//
// We should check to see whether we exceed the turn
// off limits.
//
// Note that since we are following the OS/2 flow
// control rules we will never send an xon if
// when enabling xon/xoff flow control we discover that
// we could receive characters but we are held up do
// to a previous Xoff.
//
if ((Extension->BufferSize - New.XoffLimit) <=
Extension->CharsInInterruptBuffer) {
//
// Cause the Xoff to be sent.
//
Extension->RXHolding |= SERIAL_RX_XOFF;
SerialProdXonXoff(
Extension,
FALSE
);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -