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.
911 lines
25 KiB
911 lines
25 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"
|
|
|
|
// Prototypes
|
|
BOOLEAN SerialDecrementRTSCounter(IN PVOID Context);
|
|
// End of prototypes.
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
DWORD ModemSignals = UL_MC_DTR;
|
|
|
|
SpxDbgMsg(SERFLOW, ("%s: Setting DTR for port %d\n", PRODUCT_NAME, pPort->PortNumber));
|
|
pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_SET);
|
|
pPort->DTR_Set = TRUE;
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
DWORD ModemSignals = UL_MC_DTR;
|
|
|
|
SpxDbgMsg(SERFLOW, ("%s: Clearing DTR for port %d\n", PRODUCT_NAME, pPort->PortNumber));
|
|
pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_CLEAR);
|
|
pPort->DTR_Set = FALSE;
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
DWORD ModemSignals = UL_MC_RTS;
|
|
|
|
SpxDbgMsg(SERFLOW, ("%s: Setting RTS for port %d\n", PRODUCT_NAME, pPort->PortNumber));
|
|
pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_SET);
|
|
pPort->RTS_Set = TRUE;
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
DWORD ModemSignals = UL_MC_RTS;
|
|
|
|
SpxDbgMsg(SERFLOW, ("%s: Clearing RTS for port %d\n", PRODUCT_NAME, pPort->PortNumber));
|
|
pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_CLEAR);
|
|
pPort->RTS_Set = FALSE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
SerialSetupNewHandFlow(IN PPORT_DEVICE_EXTENSION pPort, 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->DeviceIsOpen 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.
|
|
|
|
|
|
if((!pPort->DeviceIsOpen)
|
|
|| (pPort->HandFlow.ControlHandShake != New.ControlHandShake)
|
|
|| (pPort->HandFlow.FlowReplace != New.FlowReplace))
|
|
{
|
|
|
|
// First we take care of the DTR flow control. We only do work if something has changed.
|
|
SerialDump(SERFLOW, ("Processing DTR flow for %x\n", pPort->Controller));
|
|
|
|
switch(New.ControlHandShake & SERIAL_DTR_MASK)
|
|
{
|
|
case SERIAL_DTR_HANDSHAKE:
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DTR_FLOW_MASK) | UC_FLWC_DTR_HS;
|
|
break;
|
|
|
|
case SERIAL_DTR_CONTROL:
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DTR_FLOW_MASK) | UC_FLWC_NO_DTR_FLOW;
|
|
SerialSetDTR(pPort);
|
|
break;
|
|
|
|
default:
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DTR_FLOW_MASK) | UC_FLWC_NO_DTR_FLOW;
|
|
SerialClrDTR(pPort);
|
|
break;
|
|
}
|
|
|
|
|
|
// Time to take care of the RTS Flow control.
|
|
SerialDump(SERFLOW,("Processing RTS flow for %x\n", pPort->Controller));
|
|
|
|
switch(New.FlowReplace & SERIAL_RTS_MASK)
|
|
{
|
|
case SERIAL_RTS_HANDSHAKE:
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_RTS_HS;
|
|
break;
|
|
|
|
case SERIAL_RTS_CONTROL:
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_NO_RTS_FLOW;
|
|
SerialSetRTS(pPort);
|
|
break;
|
|
|
|
case SERIAL_TRANSMIT_TOGGLE:
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_RTS_TOGGLE;
|
|
break;
|
|
|
|
default:
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_NO_RTS_FLOW;
|
|
SerialClrRTS(pPort);
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
if(New.ControlHandShake & SERIAL_CTS_HANDSHAKE)
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_CTS_FLOW_MASK) | UC_FLWC_CTS_HS;
|
|
else
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_CTS_FLOW_MASK) | UC_FLWC_NO_CTS_FLOW;
|
|
|
|
if(New.ControlHandShake & SERIAL_DSR_HANDSHAKE)
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DSR_FLOW_MASK) | UC_FLWC_DSR_HS;
|
|
else
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DSR_FLOW_MASK) | UC_FLWC_NO_DSR_FLOW;
|
|
|
|
//if(New.ControlHandShake & SERIAL_DCD_HANDSHAKE)
|
|
|
|
if(New.FlowReplace & SERIAL_NULL_STRIPPING)
|
|
pPort->UartConfig.SpecialMode |= UC_SM_DO_NULL_STRIPPING;
|
|
else
|
|
pPort->UartConfig.SpecialMode &= ~UC_SM_DO_NULL_STRIPPING;
|
|
|
|
|
|
//
|
|
// We now take care of automatic receive flow control.
|
|
//
|
|
|
|
if(New.FlowReplace & SERIAL_AUTO_RECEIVE)
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RX_XON_XOFF_FLOW_MASK) | UC_FLWC_RX_XON_XOFF_FLOW;
|
|
else
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RX_XON_XOFF_FLOW_MASK) | UC_FLWC_RX_NO_XON_XOFF_FLOW;
|
|
|
|
//
|
|
// We now take care of automatic transmit flow control.
|
|
//
|
|
|
|
if(New.FlowReplace & SERIAL_AUTO_TRANSMIT)
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_TX_XON_XOFF_FLOW_MASK) | UC_FLWC_TX_XON_XOFF_FLOW;
|
|
else
|
|
pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_TX_XON_XOFF_FLOW_MASK) | UC_FLWC_TX_NO_XON_XOFF_FLOW;
|
|
|
|
|
|
pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_FLOW_CTRL_MASK | UC_SPECIAL_MODE_MASK);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// At this point we can simply make sure that entire
|
|
// handflow structure in the extension is updated.
|
|
//
|
|
|
|
pPort->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;
|
|
PPORT_DEVICE_EXTENSION pPort = S->pPort;
|
|
PSERIAL_HANDFLOW HandFlow = S->Data;
|
|
|
|
SerialSetupNewHandFlow(pPort, HandFlow);
|
|
|
|
SerialHandleModemUpdate(pPort, 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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
|
|
if((pPort->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE)
|
|
SerialSetRTS(pPort);
|
|
|
|
// Set break.
|
|
pPort->UartConfig.SpecialMode |= UC_SM_TX_BREAK;
|
|
pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_SPECIAL_MODE_MASK);
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
|
|
// Clear break.
|
|
pPort->UartConfig.SpecialMode &= ~UC_SM_TX_BREAK;
|
|
pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_SPECIAL_MODE_MASK);
|
|
|
|
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
|
|
will still 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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
|
|
pPort->TXHolding |= SERIAL_TX_XOFF;
|
|
|
|
if((pPort->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE)
|
|
{
|
|
KeInsertQueueDpc(&pPort->StartTimerLowerRTSDpc, NULL, NULL) ? pPort->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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
|
|
if(pPort->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.
|
|
|
|
pPort->TXHolding &= ~SERIAL_TX_XOFF;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
SerialHandleReducedIntBuffer(IN PPORT_DEVICE_EXTENSION pPort)
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
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(pPort->RXHolding)
|
|
{
|
|
if(pPort->CharsInInterruptBuffer <= (ULONG)pPort->HandFlow.XonLimit)
|
|
{
|
|
if(pPort->RXHolding & SERIAL_RX_DTR)
|
|
{
|
|
pPort->RXHolding &= ~SERIAL_RX_DTR;
|
|
SerialSetDTR(pPort);
|
|
}
|
|
|
|
if(pPort->RXHolding & SERIAL_RX_RTS)
|
|
{
|
|
pPort->RXHolding &= ~SERIAL_RX_RTS;
|
|
SerialSetRTS(pPort);
|
|
}
|
|
|
|
if(pPort->RXHolding & SERIAL_RX_XOFF)
|
|
{
|
|
// Prod the transmit code to send xon.
|
|
SerialProdXonXoff(pPort, TRUE);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
SerialProdXonXoff(IN PPORT_DEVICE_EXTENSION pPort, 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(SendXon)
|
|
{
|
|
pPort->SendXonChar = TRUE;
|
|
pPort->SendXoffChar = FALSE;
|
|
}
|
|
else
|
|
{
|
|
pPort->SendXonChar = FALSE;
|
|
pPort->SendXoffChar = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
SerialHandleModemUpdate(IN PPORT_DEVICE_EXTENSION pPort, 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 = pPort->TXHolding;
|
|
|
|
// Holds the value in the mode status register.
|
|
UCHAR ModemStatus = 0;
|
|
DWORD ModemSignals = 0;
|
|
|
|
pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_STATUS);
|
|
|
|
|
|
// Put data in 16x5x format.
|
|
if(ModemSignals & UL_MC_DELTA_CTS)
|
|
ModemStatus |= SERIAL_MSR_DCTS;
|
|
|
|
if(ModemSignals & UL_MC_DELTA_DSR)
|
|
ModemStatus |= SERIAL_MSR_DDSR;
|
|
|
|
if(ModemSignals & UL_MC_TRAILING_RI_EDGE)
|
|
ModemStatus |= SERIAL_MSR_TERI;
|
|
|
|
if(ModemSignals & UL_MC_DELTA_DCD)
|
|
ModemStatus |= SERIAL_MSR_DDCD;
|
|
|
|
if(ModemSignals & UL_MC_CTS)
|
|
ModemStatus |= SERIAL_MSR_CTS;
|
|
|
|
if(ModemSignals & UL_MC_DSR)
|
|
ModemStatus |= SERIAL_MSR_DSR;
|
|
|
|
if(ModemSignals & UL_MC_RI)
|
|
ModemStatus |= SERIAL_MSR_RI;
|
|
|
|
if(ModemSignals & UL_MC_DCD)
|
|
ModemStatus |= SERIAL_MSR_DCD;
|
|
|
|
|
|
// If we are placing the modem status into the data stream
|
|
// on every change, we should do it now.
|
|
if(pPort->EscapeChar)
|
|
{
|
|
// If a signal changed...
|
|
if(ModemStatus & (SERIAL_MSR_DCTS | SERIAL_MSR_DDSR | SERIAL_MSR_TERI | SERIAL_MSR_DDCD))
|
|
{
|
|
BYTE TmpByte;
|
|
|
|
TmpByte = pPort->EscapeChar;
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
|
|
TmpByte = SERIAL_LSRMST_MST;
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
|
|
TmpByte = ModemStatus;
|
|
pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
|
|
}
|
|
}
|
|
|
|
|
|
// 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(pPort->IsrWaitMask)
|
|
{
|
|
if((pPort->IsrWaitMask & SERIAL_EV_CTS) && (ModemStatus & SERIAL_MSR_DCTS))
|
|
pPort->HistoryMask |= SERIAL_EV_CTS;
|
|
|
|
if((pPort->IsrWaitMask & SERIAL_EV_DSR) && (ModemStatus & SERIAL_MSR_DDSR))
|
|
pPort->HistoryMask |= SERIAL_EV_DSR;
|
|
|
|
if((pPort->IsrWaitMask & SERIAL_EV_RING) && (ModemStatus & SERIAL_MSR_TERI))
|
|
pPort->HistoryMask |= SERIAL_EV_RING;
|
|
|
|
if((pPort->IsrWaitMask & SERIAL_EV_RLSD) && (ModemStatus & SERIAL_MSR_DDCD))
|
|
pPort->HistoryMask |= SERIAL_EV_RLSD;
|
|
|
|
if(pPort->IrpMaskLocation && pPort->HistoryMask)
|
|
{
|
|
*pPort->IrpMaskLocation = pPort->HistoryMask;
|
|
pPort->IrpMaskLocation = NULL;
|
|
pPort->HistoryMask = 0;
|
|
|
|
pPort->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
// Mark IRP as about to complete normally to prevent cancel & timer DPCs
|
|
// from doing so before DPC is allowed to run.
|
|
//SERIAL_SET_REFERENCE(pPort->CurrentWaitIrp, SERIAL_REF_COMPLETING);
|
|
|
|
KeInsertQueueDpc(&pPort->CommWaitDpc, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
|
|
PPORT_DEVICE_EXTENSION pPort = DeferredContext;
|
|
LARGE_INTEGER CharTime;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(SystemContext1);
|
|
UNREFERENCED_PARAMETER(SystemContext2);
|
|
|
|
|
|
// Take out the lock to prevent the line control
|
|
// from changing out from under us while we calculate
|
|
// a character time.
|
|
|
|
KeAcquireSpinLock(&pPort->ControlLock, &OldIrql);
|
|
|
|
CharTime = SerialGetCharTime(pPort);
|
|
|
|
KeReleaseSpinLock(&pPort->ControlLock, OldIrql);
|
|
|
|
CharTime.QuadPart = -CharTime.QuadPart;
|
|
|
|
if(KeSetTimer(&pPort->LowerRTSTimer, CharTime, &pPort->PerhapsLowerRTSDpc))
|
|
{
|
|
// 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(pPort->Interrupt, SerialDecrementRTSCounter, pPort);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = DeferredContext;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(SystemContext1);
|
|
UNREFERENCED_PARAMETER(SystemContext2);
|
|
|
|
KeSynchronizeExecution(pPort->Interrupt, SerialPerhapsLowerRTS, pPort);
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
{
|
|
PPORT_DEVICE_EXTENSION pPort = Context;
|
|
|
|
pPort->CountOfTryingToLowerRTS--;
|
|
|
|
return FALSE;
|
|
}
|