mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1803 lines
43 KiB
1803 lines
43 KiB
/*++
|
|
|
|
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;
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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;
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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;
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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;
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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))) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: Removing DTR block on reception for %x\n",
|
|
Extension->Controller)
|
|
);
|
|
Extension->RXHolding &= ~SERIAL_RX_DTR;
|
|
SerialSetDTR(Extension);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SerialSetDTR(Extension);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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))) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: Removing rts block of reception for %x\n",
|
|
Extension->Controller)
|
|
);
|
|
Extension->RXHolding &= ~SERIAL_RX_RTS;
|
|
SerialSetRTS(Extension);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SerialSetRTS(Extension);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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) {
|
|
|
|
SerialDump(
|
|
SERFLOW,
|
|
("SERIAL: 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
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The app has disabled automatic receive flow control.
|
|
//
|
|
// If transmission was being held up because of
|
|
// an automatic receive Xoff, then we should
|
|
// cause an Xon to be sent.
|
|
//
|
|
|
|
if (Extension->RXHolding & SERIAL_RX_XOFF) {
|
|
|
|
Extension->RXHolding &= ~SERIAL_RX_XOFF;
|
|
|
|
//
|
|
// Cause the Xon to be sent.
|
|
//
|
|
|
|
SerialProdXonXoff(
|
|
Extension,
|
|
TRUE
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We now take care of automatic transmit flow control.
|
|
// We only do work if things have changed.
|
|
//
|
|
|
|
if ((!Extension->DeviceIsOpened) ||
|
|
((Extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) !=
|
|
(New.FlowReplace & SERIAL_AUTO_TRANSMIT))) {
|
|
|
|
if (New.FlowReplace & SERIAL_AUTO_TRANSMIT) {
|
|
|
|
//
|
|
// We wouldn't be here if it had been on before.
|
|
//
|
|
// BUG BUG ??? There is some belief that if autotransmit
|
|
// was just enabled, I should go look in what we
|
|
// already received, and if we find the xoff character
|
|
// then we should stop transmitting. I think this
|
|
// is an application bug. For now we just care about
|
|
// what we see in the future.
|
|
//
|
|
|
|
;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The app has disabled automatic transmit flow control.
|
|
//
|
|
// If transmission was being held up because of
|
|
// an automatic transmit Xoff, then we should
|
|
// cause an Xon to be sent.
|
|
//
|
|
|
|
if (Extension->TXHolding & SERIAL_TX_XOFF) {
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_XOFF;
|
|
|
|
//
|
|
// Cause the Xon to be sent.
|
|
//
|
|
|
|
SerialProdXonXoff(
|
|
Extension,
|
|
TRUE
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// At this point we can simply make sure that entire
|
|
// handflow structure in the extension is updated.
|
|
//
|
|
|
|
Extension->HandFlow = New;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SerialSetHandFlow(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to set the handshake and control
|
|
flow in the device extension.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to a structure that contains a pointer to
|
|
the device extension and a pointer to a handflow
|
|
structure..
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_IOCTL_SYNC S = Context;
|
|
PSERIAL_DEVICE_EXTENSION Extension = S->Extension;
|
|
PSERIAL_HANDFLOW HandFlow = S->Data;
|
|
|
|
SerialSetupNewHandFlow(
|
|
Extension,
|
|
HandFlow
|
|
);
|
|
|
|
SerialHandleModemUpdate(
|
|
Extension,
|
|
FALSE
|
|
);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SerialTurnOnBreak(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will turn on break in the hardware and
|
|
record the fact the break is on, in the extension variable
|
|
that holds reasons that transmission is stopped.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = Context;
|
|
|
|
UCHAR OldLineControl;
|
|
|
|
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
SerialSetRTS(Extension);
|
|
|
|
}
|
|
|
|
OldLineControl = READ_LINE_CONTROL(Extension->Controller);
|
|
|
|
OldLineControl |= SERIAL_LCR_BREAK;
|
|
|
|
WRITE_LINE_CONTROL(
|
|
Extension->Controller,
|
|
OldLineControl
|
|
);
|
|
|
|
Extension->TXHolding |= SERIAL_TX_BREAK;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SerialTurnOffBreak(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will turn off break in the hardware and
|
|
record the fact the break is off, in the extension variable
|
|
that holds reasons that transmission is stopped.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = Context;
|
|
|
|
UCHAR OldLineControl;
|
|
|
|
if (Extension->TXHolding & SERIAL_TX_BREAK) {
|
|
|
|
//
|
|
// We actually have a good reason for testing if transmission
|
|
// is holding instead of blindly clearing the bit.
|
|
//
|
|
// If transmission actually was holding and the result of
|
|
// clearing the bit is that we should restart transmission
|
|
// then we will poke the interrupt enable bit, which will
|
|
// cause an actual interrupt and transmission will then
|
|
// restart on its own.
|
|
//
|
|
// If transmission wasn't holding and we poked the bit
|
|
// then we would interrupt before a character actually made
|
|
// it out and we could end up over writing a character in
|
|
// the transmission hardware.
|
|
|
|
OldLineControl = READ_LINE_CONTROL(Extension->Controller);
|
|
|
|
OldLineControl &= ~SERIAL_LCR_BREAK;
|
|
|
|
WRITE_LINE_CONTROL(
|
|
Extension->Controller,
|
|
OldLineControl
|
|
);
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_BREAK;
|
|
|
|
if (!Extension->TXHolding &&
|
|
(Extension->TransmitImmediate ||
|
|
Extension->WriteLength) &&
|
|
Extension->HoldingEmpty) {
|
|
|
|
DISABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
ENABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The following routine will lower the rts if we
|
|
// are doing transmit toggleing and there is no
|
|
// reason to keep it up.
|
|
//
|
|
|
|
Extension->CountOfTryingToLowerRTS++;
|
|
SerialPerhapsLowerRTS(Extension);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SerialPretendXoff(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to process the Ioctl that request the
|
|
driver to act as if an Xoff was received. Even if the
|
|
driver does not have automatic Xoff/Xon flowcontrol - This
|
|
still will stop the transmission. This is the OS/2 behavior
|
|
and is not well specified for Windows. Therefore we adopt
|
|
the OS/2 behavior.
|
|
|
|
Note: If the driver does not have automatic Xoff/Xon enabled
|
|
then the only way to restart transmission is for the
|
|
application to request we "act" as if we saw the xon.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = Context;
|
|
|
|
Extension->TXHolding |= SERIAL_TX_XOFF;
|
|
|
|
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
SerialInsertQueueDpc(
|
|
&Extension->StartTimerLowerRTSDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
)?Extension->CountOfTryingToLowerRTS++:0;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SerialPretendXon(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to process the Ioctl that request the
|
|
driver to act as if an Xon was received.
|
|
|
|
Note: If the driver does not have automatic Xoff/Xon enabled
|
|
then the only way to restart transmission is for the
|
|
application to request we "act" as if we saw the xon.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
This routine always returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = Context;
|
|
|
|
if (Extension->TXHolding) {
|
|
|
|
//
|
|
// We actually have a good reason for testing if transmission
|
|
// is holding instead of blindly clearing the bit.
|
|
//
|
|
// If transmission actually was holding and the result of
|
|
// clearing the bit is that we should restart transmission
|
|
// then we will poke the interrupt enable bit, which will
|
|
// cause an actual interrupt and transmission will then
|
|
// restart on its own.
|
|
//
|
|
// If transmission wasn't holding and we poked the bit
|
|
// then we would interrupt before a character actually made
|
|
// it out and we could end up over writing a character in
|
|
// the transmission hardware.
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_XOFF;
|
|
|
|
if (!Extension->TXHolding &&
|
|
(Extension->TransmitImmediate ||
|
|
Extension->WriteLength) &&
|
|
Extension->HoldingEmpty) {
|
|
|
|
DISABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
ENABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
VOID
|
|
SerialHandleReducedIntBuffer(
|
|
IN PSERIAL_DEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to handle a reduction in the number
|
|
of characters in the interrupt (typeahead) buffer. It
|
|
will check the current output flow control and re-enable transmission
|
|
as needed.
|
|
|
|
NOTE: This routine assumes that it is working at interrupt level.
|
|
|
|
Arguments:
|
|
|
|
Extension - A pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
//
|
|
// If we are doing receive side flow control and we are
|
|
// currently "holding" then because we've emptied out
|
|
// some characters from the interrupt buffer we need to
|
|
// see if we can "re-enable" reception.
|
|
//
|
|
|
|
if (Extension->RXHolding) {
|
|
|
|
if (Extension->CharsInInterruptBuffer <=
|
|
(ULONG)Extension->HandFlow.XonLimit) {
|
|
|
|
if (Extension->RXHolding & SERIAL_RX_DTR) {
|
|
|
|
Extension->RXHolding &= ~SERIAL_RX_DTR;
|
|
SerialSetDTR(Extension);
|
|
|
|
}
|
|
|
|
if (Extension->RXHolding & SERIAL_RX_RTS) {
|
|
|
|
Extension->RXHolding &= ~SERIAL_RX_RTS;
|
|
SerialSetRTS(Extension);
|
|
|
|
}
|
|
|
|
if (Extension->RXHolding & SERIAL_RX_XOFF) {
|
|
|
|
//
|
|
// Prod the transmit code to send xon.
|
|
//
|
|
|
|
SerialProdXonXoff(
|
|
Extension,
|
|
TRUE
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
SerialProdXonXoff(
|
|
IN PSERIAL_DEVICE_EXTENSION Extension,
|
|
IN BOOLEAN SendXon
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will set up the SendXxxxChar variables if
|
|
necessary and determine if we are going to be interrupting
|
|
because of current transmission state. It will cause an
|
|
interrupt to occur if neccessary, to send the xon/xoff char.
|
|
|
|
NOTE: This routine assumes that it is called at interrupt
|
|
level.
|
|
|
|
Arguments:
|
|
|
|
Extension - A pointer to the serial device extension.
|
|
|
|
SendXon - If a character is to be send, this indicates whether
|
|
it should be an Xon or an Xoff.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// We assume that if the prodding is called more than
|
|
// once that the last prod has set things up appropriately.
|
|
//
|
|
// We could get called before the character is sent out
|
|
// because the send of the character was blocked because
|
|
// of hardware flow control (or break).
|
|
//
|
|
|
|
if (!Extension->SendXonChar && !Extension->SendXoffChar
|
|
&& Extension->HoldingEmpty) {
|
|
|
|
DISABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
ENABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
|
|
}
|
|
|
|
if (SendXon) {
|
|
|
|
Extension->SendXonChar = TRUE;
|
|
Extension->SendXoffChar = FALSE;
|
|
|
|
} else {
|
|
|
|
Extension->SendXonChar = FALSE;
|
|
Extension->SendXoffChar = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ULONG
|
|
SerialHandleModemUpdate(
|
|
IN PSERIAL_DEVICE_EXTENSION Extension,
|
|
IN BOOLEAN DoingTX
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will be to check on the modem status, and
|
|
handle any appropriate event notification as well as
|
|
any flow control appropriate to modem status lines.
|
|
|
|
NOTE: This routine assumes that it is called at interrupt
|
|
level.
|
|
|
|
Arguments:
|
|
|
|
Extension - A pointer to the serial device extension.
|
|
|
|
DoingTX - This boolean is used to indicate that this call
|
|
came from the transmit processing code. If this
|
|
is true then there is no need to cause a new interrupt
|
|
since the code will be trying to send the next
|
|
character as soon as this call finishes.
|
|
|
|
Return Value:
|
|
|
|
This returns the old value of the modem status register
|
|
(extended into a ULONG).
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// We keep this local so that after we are done
|
|
// examining the modem status and we've updated
|
|
// the transmission holding value, we know whether
|
|
// we've changed from needing to hold up transmission
|
|
// to transmission being able to proceed.
|
|
//
|
|
ULONG OldTXHolding = Extension->TXHolding;
|
|
|
|
//
|
|
// Holds the value in the mode status register.
|
|
//
|
|
UCHAR ModemStatus;
|
|
|
|
ModemStatus =
|
|
READ_MODEM_STATUS(Extension->Controller);
|
|
|
|
//
|
|
// If we are placeing the modem status into the data stream
|
|
// on every change, we should do it now.
|
|
//
|
|
|
|
if (Extension->EscapeChar) {
|
|
|
|
if (ModemStatus & (SERIAL_MSR_DCTS |
|
|
SERIAL_MSR_DDSR |
|
|
SERIAL_MSR_TERI |
|
|
SERIAL_MSR_DDCD)) {
|
|
|
|
SerialPutChar(
|
|
Extension,
|
|
Extension->EscapeChar
|
|
);
|
|
SerialPutChar(
|
|
Extension,
|
|
SERIAL_LSRMST_MST
|
|
);
|
|
SerialPutChar(
|
|
Extension,
|
|
ModemStatus
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Take care of input flow control based on sensitivity
|
|
// to the DSR. This is done so that the application won't
|
|
// see spurious data generated by odd devices.
|
|
//
|
|
// Basically, if we are doing dsr sensitivity then the
|
|
// driver should only accept data when the dsr bit is
|
|
// set.
|
|
//
|
|
|
|
if (Extension->HandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) {
|
|
|
|
if (ModemStatus & SERIAL_MSR_DSR) {
|
|
|
|
//
|
|
// The line is high. Simply make sure that
|
|
// RXHolding does't have the DSR bit.
|
|
//
|
|
|
|
Extension->RXHolding &= ~SERIAL_RX_DSR;
|
|
|
|
} else {
|
|
|
|
Extension->RXHolding |= SERIAL_RX_DSR;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We don't have sensitivity due to DSR. Make sure we
|
|
// arn't holding. (We might have been, but the app just
|
|
// asked that we don't hold for this reason any more.)
|
|
//
|
|
|
|
Extension->RXHolding &= ~SERIAL_RX_DSR;
|
|
|
|
}
|
|
|
|
//
|
|
// Check to see if we have a wait
|
|
// pending on the modem status events. If we
|
|
// do then we schedule a dpc to satisfy
|
|
// that wait.
|
|
//
|
|
|
|
if (Extension->IsrWaitMask) {
|
|
|
|
if ((Extension->IsrWaitMask & SERIAL_EV_CTS) &&
|
|
(ModemStatus & SERIAL_MSR_DCTS)) {
|
|
|
|
Extension->HistoryMask |= SERIAL_EV_CTS;
|
|
|
|
}
|
|
|
|
if ((Extension->IsrWaitMask & SERIAL_EV_DSR) &&
|
|
(ModemStatus & SERIAL_MSR_DDSR)) {
|
|
|
|
Extension->HistoryMask |= SERIAL_EV_DSR;
|
|
|
|
}
|
|
|
|
if ((Extension->IsrWaitMask & SERIAL_EV_RING) &&
|
|
(ModemStatus & SERIAL_MSR_TERI)) {
|
|
|
|
Extension->HistoryMask |= SERIAL_EV_RING;
|
|
|
|
}
|
|
|
|
if ((Extension->IsrWaitMask & SERIAL_EV_RLSD) &&
|
|
(ModemStatus & SERIAL_MSR_DDCD)) {
|
|
|
|
Extension->HistoryMask |= SERIAL_EV_RLSD;
|
|
|
|
}
|
|
|
|
if (Extension->IrpMaskLocation &&
|
|
Extension->HistoryMask) {
|
|
|
|
*Extension->IrpMaskLocation =
|
|
Extension->HistoryMask;
|
|
Extension->IrpMaskLocation = NULL;
|
|
Extension->HistoryMask = 0;
|
|
|
|
Extension->CurrentWaitIrp->
|
|
IoStatus.Information = sizeof(ULONG);
|
|
SerialInsertQueueDpc(
|
|
&Extension->CommWaitDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If the app has modem line flow control then
|
|
// we check to see if we have to hold up transmission.
|
|
//
|
|
|
|
if (Extension->HandFlow.ControlHandShake &
|
|
SERIAL_OUT_HANDSHAKEMASK) {
|
|
|
|
if (Extension->HandFlow.ControlHandShake &
|
|
SERIAL_CTS_HANDSHAKE) {
|
|
|
|
if (ModemStatus & SERIAL_MSR_CTS) {
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_CTS;
|
|
|
|
} else {
|
|
|
|
Extension->TXHolding |= SERIAL_TX_CTS;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_CTS;
|
|
|
|
}
|
|
|
|
if (Extension->HandFlow.ControlHandShake &
|
|
SERIAL_DSR_HANDSHAKE) {
|
|
|
|
if (ModemStatus & SERIAL_MSR_DSR) {
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_DSR;
|
|
|
|
} else {
|
|
|
|
Extension->TXHolding |= SERIAL_TX_DSR;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_DSR;
|
|
|
|
}
|
|
|
|
if (Extension->HandFlow.ControlHandShake &
|
|
SERIAL_DCD_HANDSHAKE) {
|
|
|
|
if (ModemStatus & SERIAL_MSR_DCD) {
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_DCD;
|
|
|
|
} else {
|
|
|
|
Extension->TXHolding |= SERIAL_TX_DCD;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Extension->TXHolding &= ~SERIAL_TX_DCD;
|
|
|
|
}
|
|
|
|
//
|
|
// If we hadn't been holding, and now we are then
|
|
// queue off a dpc that will lower the RTS line
|
|
// if we are doing transmit toggling.
|
|
//
|
|
|
|
if (!OldTXHolding && Extension->TXHolding &&
|
|
((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE)) {
|
|
|
|
SerialInsertQueueDpc(
|
|
&Extension->StartTimerLowerRTSDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
)?Extension->CountOfTryingToLowerRTS++:0;
|
|
|
|
}
|
|
|
|
//
|
|
// We've done any adjusting that needed to be
|
|
// done to the holding mask given updates
|
|
// to the modem status. If the Holding mask
|
|
// is clear (and it wasn't clear to start)
|
|
// and we have "write" work to do set things
|
|
// up so that the transmission code gets invoked.
|
|
//
|
|
|
|
if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
|
|
|
|
if (!Extension->TXHolding &&
|
|
(Extension->TransmitImmediate ||
|
|
Extension->WriteLength) &&
|
|
Extension->HoldingEmpty) {
|
|
|
|
DISABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
ENABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to check if transmission is holding
|
|
// up because of modem status lines. What
|
|
// could have occured is that for some strange
|
|
// reason, the app has asked that we no longer
|
|
// stop doing output flow control based on
|
|
// the modem status lines. If however, we
|
|
// *had* been held up because of the status lines
|
|
// then we need to clear up those reasons.
|
|
//
|
|
|
|
if (Extension->TXHolding & (SERIAL_TX_DCD |
|
|
SERIAL_TX_DSR |
|
|
SERIAL_TX_CTS)) {
|
|
|
|
Extension->TXHolding &= ~(SERIAL_TX_DCD |
|
|
SERIAL_TX_DSR |
|
|
SERIAL_TX_CTS);
|
|
|
|
|
|
if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
|
|
|
|
if (!Extension->TXHolding &&
|
|
(Extension->TransmitImmediate ||
|
|
Extension->WriteLength) &&
|
|
Extension->HoldingEmpty) {
|
|
|
|
DISABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
ENABLE_ALL_INTERRUPTS(Extension->Controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ((ULONG)ModemStatus);
|
|
}
|
|
|
|
BOOLEAN
|
|
SerialPerhapsLowerRTS(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks that the software reasons for lowering
|
|
the RTS lines are present. If so, it will then cause the
|
|
line status register to be read (and any needed processing
|
|
implied by the status register to be done), and if the
|
|
shift register is empty it will lower the line. If the
|
|
shift register isn't empty, this routine will queue off
|
|
a dpc that will start a timer, that will basically call
|
|
us back to try again.
|
|
|
|
NOTE: This routine assumes that it is called at interrupt
|
|
level.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
Always FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = Context;
|
|
|
|
|
|
//
|
|
// We first need to test if we are actually still doing
|
|
// transmit toggle flow control. If we aren't then
|
|
// we have no reason to try be here.
|
|
//
|
|
|
|
if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE) {
|
|
|
|
//
|
|
// The order of the tests is very important below.
|
|
//
|
|
// If there is a break then we should leave on the RTS,
|
|
// because when the break is turned off, it will submit
|
|
// the code to shut down the RTS.
|
|
//
|
|
// If there are writes pending that aren't being held
|
|
// up, then leave on the RTS, because the end of the write
|
|
// code will cause this code to be reinvoked. If the writes
|
|
// are being held up, its ok to lower the RTS because the
|
|
// upon trying to write the first character after transmission
|
|
// is restarted, we will raise the RTS line.
|
|
//
|
|
|
|
if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
|
|
(Extension->CurrentWriteIrp || Extension->TransmitImmediate ||
|
|
(!IsListEmpty(&Extension->WriteQueue)) &&
|
|
(!Extension->TXHolding))) {
|
|
|
|
NOTHING;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Looks good so far. Call the line status check and processing
|
|
// code, it will return the "current" line status value. If
|
|
// the holding and shift register are clear, lower the RTS line,
|
|
// if they aren't clear, queue of a dpc that will cause a timer
|
|
// to reinvoke us later. We do this code here because no one
|
|
// but this routine cares about the characters in the hardware,
|
|
// so no routine by this routine will bother invoking to test
|
|
// if the hardware is empty.
|
|
//
|
|
|
|
if ((SerialProcessLSR(Extension) &
|
|
(SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
|
|
(SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
|
|
|
|
//
|
|
// Well it's not empty, try again later.
|
|
//
|
|
|
|
SerialInsertQueueDpc(
|
|
&Extension->StartTimerLowerRTSDpc,
|
|
NULL,
|
|
NULL,
|
|
Extension
|
|
)?Extension->CountOfTryingToLowerRTS++:0;
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Nothing in the hardware, Lower the RTS.
|
|
//
|
|
|
|
SerialClrRTS(Extension);
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We decement the counter to indicate that we've reached
|
|
// the end of the execution path that is trying to push
|
|
// down the RTS line.
|
|
//
|
|
|
|
Extension->CountOfTryingToLowerRTS--;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
SerialStartTimerLowerRTS(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemContext1,
|
|
IN PVOID SystemContext2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine starts a timer that when it expires will start
|
|
a dpc that will check if it can lower the rts line because
|
|
there are no characters in the hardware.
|
|
|
|
Arguments:
|
|
|
|
Dpc - Not Used.
|
|
|
|
DeferredContext - Really points to the device extension.
|
|
|
|
SystemContext1 - Not Used.
|
|
|
|
SystemContext2 - Not Used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
|
|
LARGE_INTEGER CharTime;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(SystemContext1);
|
|
UNREFERENCED_PARAMETER(SystemContext2);
|
|
|
|
|
|
SerialDump(SERTRACECALLS, ("SERIAL: SerialStartTimerLowerRTS\n"));
|
|
|
|
|
|
//
|
|
// Take out the lock to prevent the line control
|
|
// from changing out from under us while we calculate
|
|
// a character time.
|
|
//
|
|
|
|
KeAcquireSpinLock(
|
|
&Extension->ControlLock,
|
|
&OldIrql
|
|
);
|
|
|
|
CharTime = SerialGetCharTime(Extension);
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->ControlLock,
|
|
OldIrql
|
|
);
|
|
|
|
CharTime.QuadPart = -CharTime.QuadPart;
|
|
|
|
if (SerialSetTimer(
|
|
&Extension->LowerRTSTimer,
|
|
CharTime,
|
|
&Extension->PerhapsLowerRTSDpc,
|
|
Extension
|
|
)) {
|
|
|
|
//
|
|
// The timer was already in the timer queue. This implies
|
|
// that one path of execution that was trying to lower
|
|
// the RTS has "died". Synchronize with the ISR so that
|
|
// we can lower the count.
|
|
//
|
|
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
SerialDecrementRTSCounter,
|
|
Extension
|
|
);
|
|
|
|
}
|
|
|
|
SerialDpcEpilogue(Extension, Dpc);
|
|
|
|
}
|
|
|
|
VOID
|
|
SerialInvokePerhapsLowerRTS(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemContext1,
|
|
IN PVOID SystemContext2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This dpc routine exists solely to call the code that
|
|
tests if the rts line should be lowered when TRANSMIT
|
|
TOGGLE flow control is being used.
|
|
|
|
Arguments:
|
|
|
|
Dpc - Not Used.
|
|
|
|
DeferredContext - Really points to the device extension.
|
|
|
|
SystemContext1 - Not Used.
|
|
|
|
SystemContext2 - Not Used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(SystemContext1);
|
|
UNREFERENCED_PARAMETER(SystemContext2);
|
|
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
SerialPerhapsLowerRTS,
|
|
Extension
|
|
);
|
|
|
|
SerialDpcEpilogue(Extension, Dpc);
|
|
}
|
|
|
|
BOOLEAN
|
|
SerialDecrementRTSCounter(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks that the software reasons for lowering
|
|
the RTS lines are present. If so, it will then cause the
|
|
line status register to be read (and any needed processing
|
|
implied by the status register to be done), and if the
|
|
shift register is empty it will lower the line. If the
|
|
shift register isn't empty, this routine will queue off
|
|
a dpc that will start a timer, that will basically call
|
|
us back to try again.
|
|
|
|
NOTE: This routine assumes that it is called at interrupt
|
|
level.
|
|
|
|
Arguments:
|
|
|
|
Context - Really a pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
Always FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSERIAL_DEVICE_EXTENSION Extension = Context;
|
|
|
|
Extension->CountOfTryingToLowerRTS--;
|
|
|
|
return FALSE;
|
|
|
|
}
|