// modmflow.c // // // Requires DDK Only // File created on 2/2/2005 // #include "pch.h" #ifdef CPR_WMI_TRACE #include "modmflow.tmh" #endif /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialSetDTR // sets DTR bit in MCR // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialSetDTR( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { UCHAR ModemControl; ModemControl = CprUartReadMCR(&DeviceExtension->Uart); ModemControl |= CPR_UART_MCR_DTR; CprUartWriteMCR(&DeviceExtension->Uart, ModemControl); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialClrDTR // clears DTR bit in MCR // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialClrDTR( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { UCHAR ModemControl; ModemControl = CprUartReadMCR(&DeviceExtension->Uart); ModemControl &= ~CPR_UART_MCR_DTR; CprUartWriteMCR(&DeviceExtension->Uart, ModemControl); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialSetRTS // sets RTS bit in MCR // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialSetRTS( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { UCHAR ModemControl; ModemControl = CprUartReadMCR(&DeviceExtension->Uart); ModemControl |= CPR_UART_MCR_RTS; CprUartWriteMCR(&DeviceExtension->Uart, ModemControl); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialClrRTS // clears RTS bit in MCR // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialClrRTS( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { UCHAR ModemControl; ModemControl = CprUartReadMCR(&DeviceExtension->Uart); ModemControl &= ~CPR_UART_MCR_RTS; CprUartWriteMCR(&DeviceExtension->Uart, ModemControl); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialSetupNewHandFlow // sets the new handflow control // // Arguments: // IN DeviceExtension // our device extension // // IN HandFlow // new handflow structure // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialSetupNewHandFlow( IN PCPR_DEVICE_EXTENSION DeviceExtension, IN PSERIAL_HANDFLOW HandFlow ) { // setup DTR HandShake if (!DeviceExtension->IsDeviceEnabled || ((DeviceExtension->SerialHandFlow.ControlHandShake & SERIAL_DTR_MASK) != (HandFlow->ControlHandShake & SERIAL_DTR_MASK))) { // Check if DTR HandShaking is enabled if (HandFlow->ControlHandShake & SERIAL_DTR_MASK) { if ((HandFlow->ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_HANDSHAKE) { if ((DeviceExtension->QueueSize - HandFlow->XoffLimit) > DeviceExtension->ReadCount) { if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_DTR) { // check if we have more characters than Xon limit if (DeviceExtension->ReadCount > (ULONG)HandFlow->XonLimit) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_DTR; CprSerialSetDTR(DeviceExtension); } else { // leave DTR in cleared state NOTHING; } } else { // there is no holding reason CprSerialSetDTR(DeviceExtension); } } else { // number of available space is less than Xoff limit DeviceExtension->RxStopReason |= CPR_SERIAL_RX_DTR; CprSerialClrDTR(DeviceExtension); } } else { // we are not using DTR HandShaking if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_DTR) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_DTR; } CprSerialSetDTR(DeviceExtension); } } else { // we are not using DTR HandShaking if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_DTR) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_DTR; } CprSerialClrDTR(DeviceExtension); } } // take care of RTS HandShake if (!DeviceExtension->IsDeviceEnabled || ((DeviceExtension->SerialHandFlow.FlowReplace & SERIAL_RTS_MASK) != (HandFlow->FlowReplace & SERIAL_RTS_MASK))) { if ((HandFlow->FlowReplace & SERIAL_RTS_MASK) == SERIAL_RTS_HANDSHAKE) { // check Xoff limit if ((DeviceExtension->QueueSize - HandFlow->XoffLimit) > DeviceExtension->ReadCount) { if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_RTS) { if (DeviceExtension->ReadCount > (ULONG)HandFlow->XonLimit) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_RTS; CprSerialSetRTS(DeviceExtension); } else { // Xon limit stops us from setting RTS NOTHING; } } else { // there is no reason not to set RTS CprSerialSetRTS(DeviceExtension); } } else { // we have less than Xoff free space DeviceExtension->RxStopReason |= CPR_SERIAL_RX_RTS; CprSerialClrRTS(DeviceExtension); } } else if ((HandFlow->FlowReplace & SERIAL_RTS_MASK) == SERIAL_RTS_CONTROL) { // we are not really doing any RTS HandShaking if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_RTS) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_RTS; } CprSerialSetRTS(DeviceExtension); } #ifdef REMOVE_FOR_TESTING_WITH_GRID_CONNECT // GridConnect had FlowReplace = 0xC0 else if ((HandFlow->FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { // not implemented ASSERT(FALSE); } #endif else { // we are not using RTS HandShake if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_RTS) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_RTS; } CprSerialClrRTS(DeviceExtension); } } // setup automatic receive flow control if (!DeviceExtension->IsDeviceEnabled || ((DeviceExtension->SerialHandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) != (HandFlow->FlowReplace & SERIAL_AUTO_RECEIVE))) { if (HandFlow->FlowReplace & SERIAL_AUTO_RECEIVE) { if ((DeviceExtension->QueueSize - HandFlow->XoffLimit) <= DeviceExtension->ReadCount) { // Cause the Xoff to be sent. DeviceExtension->RxStopReason |= CPR_SERIAL_RX_XOFF; CprSerialProdXonXoff(DeviceExtension, FALSE); } } else { if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_XOFF) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_XOFF; // Cause the Xon to be sent. CprSerialProdXonXoff(DeviceExtension, TRUE); } } } // We now take care of automatic transmit flow control. // We only do work if things have changed. if (!DeviceExtension->IsDeviceEnabled || ((DeviceExtension->SerialHandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) != (HandFlow->FlowReplace & SERIAL_AUTO_TRANSMIT))) { if (HandFlow->FlowReplace & SERIAL_AUTO_TRANSMIT) { NOTHING; } else { if (DeviceExtension->TxStopReason & CPR_SERIAL_TX_XOFF) { DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_XOFF; // Cause the Xon to be sent. CprSerialProdXonXoff(DeviceExtension, TRUE); } } } if (DeviceExtension->UseRFC2217) { BOOLEAN b = FALSE; if ((HandFlow->FlowReplace != DeviceExtension->SerialHandFlow.FlowReplace) || (DeviceExtension->FlowSent == FALSE)) { DeviceExtension->FlowSent = TRUE; if (((HandFlow->FlowReplace & SERIAL_RTS_HANDSHAKE) == SERIAL_RTS_HANDSHAKE) || ((HandFlow->ControlHandShake & SERIAL_CTS_HANDSHAKE) == SERIAL_CTS_HANDSHAKE)) { // Outbound/Both CprRfc2217_SendCPCByteCommand(DeviceExtension, TNCAS_SET_CONTROL, TN_CONTROL_FLOW_HARDWARE); b = TRUE; } if (((HandFlow->FlowReplace & SERIAL_AUTO_TRANSMIT) == SERIAL_AUTO_TRANSMIT) || ((HandFlow->FlowReplace & SERIAL_AUTO_RECEIVE) == SERIAL_AUTO_RECEIVE)) { // Outbound CprRfc2217_SendCPCByteCommand(DeviceExtension, TNCAS_SET_CONTROL, TN_CONTROL_FLOW_XON_XOFF); b = TRUE; } if (b == FALSE) { CprRfc2217_SendCPCByteCommand(DeviceExtension, TNCAS_SET_CONTROL, TN_CONTROL_FLOW_NONE); } CprRfc2217_SendToNet(DeviceExtension, TRUE); } } // save new handflow information DeviceExtension->SerialHandFlow = *HandFlow; return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialSetHandFlow // sets the new handflow control // // Arguments: // IN DeviceExtension // our device extension // // IN HandFlow // new handflow structure // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialSetHandFlow( IN PCPR_DEVICE_EXTENSION DeviceExtension, IN PSERIAL_HANDFLOW HandFlow ) { // setup new handflow information CprSerialSetupNewHandFlow(DeviceExtension, HandFlow); // make sure that modem state is in sync with new handflow // settings CprSerialHandleModemUpdate(DeviceExtension, FALSE); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialTurnOnBreak // turns on break signal // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialTurnOnBreak( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { UCHAR OldLineControl; #ifdef REMOVE_FOR_TESTING_WITH_GRID_CONNECT // GridConnect had FlowReplace = 0xC0 if ((DeviceExtension->SerialHandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { CprSerialSetRTS(DeviceExtension); } #endif OldLineControl = CprUartReadLCR(&DeviceExtension->Uart); OldLineControl |= CPR_UART_LCR_BREAK; CprUartWriteLCR(&DeviceExtension->Uart, OldLineControl); DeviceExtension->TxStopReason |= CPR_SERIAL_TX_BREAK; return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialTurnOffBreak // turns off break signal // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialTurnOffBreak( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { UCHAR OldLineControl; if (DeviceExtension->TxStopReason & CPR_SERIAL_TX_BREAK) { OldLineControl = CprUartReadLCR(&DeviceExtension->Uart); OldLineControl &= ~CPR_UART_LCR_BREAK; CprUartWriteLCR(&DeviceExtension->Uart, OldLineControl); DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_BREAK; // we might need to restart transmit engine, which might have been // paused due to the break if (!DeviceExtension->TxStopReason && (DeviceExtension->TxImmediate || DeviceExtension->WriteLength) && DeviceExtension->TxIdle) { CprUartStartWrite(&DeviceExtension->Uart); } } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialPretendXoff // simulates receiving xoff character // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialPretendXoff( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { // record that we received xoff character DeviceExtension->TxStopReason |= CPR_SERIAL_TX_XOFF; #ifdef REMOVE_FOR_TESTING_WITH_GRID_CONNECT // GridConnect had FlowReplace = 0xC0 if ((DeviceExtension->SerialHandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { // not implemented ASSERT(FALSE); } #endif return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialPretendXon // simulates receiving xon character // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialPretendXon( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { // turn off xoff character and restart transmit engine // if it was stopped due to xoff if (DeviceExtension->TxStopReason) { DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_XOFF; if (!DeviceExtension->TxStopReason && (DeviceExtension->TxImmediate || DeviceExtension->WriteLength) && DeviceExtension->TxIdle) { CprUartStartWrite(&DeviceExtension->Uart); } } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprCheckForReceiveBufferChangedEvent // checks if any handflow information should be set in response to reducing // size of read queue // // Arguments: // IN DeviceExtension // our device extension // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprCheckForReceiveBufferChangedEvent( IN PCPR_DEVICE_EXTENSION DeviceExtension ) { // check if receive engine was stopped due to Xon limit if (DeviceExtension->RxStopReason) { if (DeviceExtension->ReadCount <= (ULONG)DeviceExtension->SerialHandFlow.XonLimit) { if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_DTR) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_DTR; CprSerialSetDTR(DeviceExtension); } if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_RTS) { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_RTS; CprSerialSetRTS(DeviceExtension); } if (DeviceExtension->RxStopReason & CPR_SERIAL_RX_XOFF) { CprSerialProdXonXoff(DeviceExtension, TRUE); } } } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialProdXonXoff // requests sending of xoff/xon character // // Arguments: // IN DeviceExtension // our device extension // // IN SendXon // if TRUE, send xon, otherwise send xoff // // Return Value: // None // // Comment: // This method is called with SerialLock held // VOID CprSerialProdXonXoff( IN PCPR_DEVICE_EXTENSION DeviceExtension, IN BOOLEAN SendXon ) { // if transmit engine is idle, restart it if (!DeviceExtension->SendXonChar && !DeviceExtension->SendXoffChar && DeviceExtension->TxIdle) { CprUartStartWrite(&DeviceExtension->Uart); } // record the fact that we need to send xon/xoff character. Our CprUartEventDpcCallback // will pick it up if (SendXon) { DeviceExtension->SendXonChar = TRUE; DeviceExtension->SendXoffChar = FALSE; } else { DeviceExtension->SendXonChar = FALSE; DeviceExtension->SendXoffChar = TRUE; } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprSerialHandleModemUpdate // handle changing of modem status // // Arguments: // IN DeviceExtension // our device extension // // IN DoingTX // if FALSE, we might need to manually restart transimit engine // // Return Value: // modem status register value, before update // // Comment: // This method is called with SerialLock held // ULONG CprSerialHandleModemUpdate( IN PCPR_DEVICE_EXTENSION DeviceExtension, IN BOOLEAN DoingTX ) { ULONG OldTxStopReason; UCHAR ModemStatus; OldTxStopReason = DeviceExtension->TxStopReason; ModemStatus = CprUartReadMSR(&DeviceExtension->Uart); // check if we need to insert modem status in read character stream if (DeviceExtension->EscapeChar) { if (ModemStatus & (CPR_UART_MSR_DCTS | CPR_UART_MSR_DDSR | CPR_UART_MSR_TERI | CPR_UART_MSR_DDCD)) { CprSerialPutChar(DeviceExtension, DeviceExtension->EscapeChar); CprSerialPutChar(DeviceExtension, SERIAL_LSRMST_MST); CprSerialPutChar(DeviceExtension, ModemStatus); } } // check if we are using dsr for handshaking if (DeviceExtension->SerialHandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) { if (ModemStatus & CPR_UART_MSR_DSR) { // dsr is up, thus enable receiver DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_DSR; } else { // dsr is down, thus disable receiver DeviceExtension->RxStopReason |= CPR_SERIAL_RX_DSR; } } else { DeviceExtension->RxStopReason &= ~CPR_SERIAL_RX_DSR; } // check if our client is interested in this event if (DeviceExtension->WaitMask) { // change of CTS if ((DeviceExtension->WaitMask & SERIAL_EV_CTS) && (ModemStatus & CPR_UART_MSR_DCTS)) { DeviceExtension->WaitEvents |= SERIAL_EV_CTS; CprLogEvent( CPR_EVENT_TYPE_WAIT_CTS, CPR_EVENT_SUB_TYPE_NONE, DeviceExtension, STATUS_SUCCESS, NULL); } // change of DSR if ((DeviceExtension->WaitMask & SERIAL_EV_DSR) && (ModemStatus & CPR_UART_MSR_DDSR)) { DeviceExtension->WaitEvents |= SERIAL_EV_DSR; CprLogEvent( CPR_EVENT_TYPE_WAIT_DSR, CPR_EVENT_SUB_TYPE_NONE, DeviceExtension, STATUS_SUCCESS, NULL); } // chage of RING if ((DeviceExtension->WaitMask & SERIAL_EV_RING) && (ModemStatus & CPR_UART_MSR_TERI)) { DeviceExtension->WaitEvents |= SERIAL_EV_RING; CprLogEvent( CPR_EVENT_TYPE_WAIT_RING, CPR_EVENT_SUB_TYPE_NONE, DeviceExtension, STATUS_SUCCESS, NULL); } // change in DCD if ((DeviceExtension->WaitMask & SERIAL_EV_RLSD) && (ModemStatus & CPR_UART_MSR_DDCD)) { DeviceExtension->WaitEvents |= SERIAL_EV_RLSD; CprLogEvent( CPR_EVENT_TYPE_WAIT_RLSD, CPR_EVENT_SUB_TYPE_NONE, DeviceExtension, STATUS_SUCCESS, NULL); } // complete pending wait on mask ioctl if (DeviceExtension->IrpWaitMask && DeviceExtension->WaitEvents) { CprDebugPrint2(DeviceExtension, DBG_IO, DBG_INFO, "CprSerialHandleModemUpdate: Releasing Wait Irp %p Events 0x%08X", DeviceExtension->WaitIrp, DeviceExtension->WaitEvents); *DeviceExtension->IrpWaitMask = DeviceExtension->WaitEvents; DeviceExtension->IrpWaitMask = NULL; DeviceExtension->WaitEvents = 0; DeviceExtension->WaitIrp->IoStatus.Information = sizeof(ULONG); KeInsertQueueDpc(&DeviceExtension->WaitCompleteDpc, NULL, NULL); } } // check if we are using handshaking to control transmitter if (DeviceExtension->SerialHandFlow.ControlHandShake & SERIAL_OUT_HANDSHAKEMASK) { // check if we are using CTS handshake if (DeviceExtension->SerialHandFlow.ControlHandShake & SERIAL_CTS_HANDSHAKE) { if (ModemStatus & CPR_UART_MSR_CTS) { // CTS is up, so enable transmitter DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_CTS; } else { // CTS is down, so disable transmitter DeviceExtension->TxStopReason |= CPR_SERIAL_TX_CTS; } } else { // we are not using CTS handshake DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_CTS; } // check if we are using DSR handshake if (DeviceExtension->SerialHandFlow.ControlHandShake & SERIAL_DSR_HANDSHAKE) { if (ModemStatus & CPR_UART_MSR_DSR) { // DSR is up, so enable transmitter DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_DSR; } else { // DSR is down, so disable transmitter DeviceExtension->TxStopReason |= CPR_SERIAL_TX_DSR; } } else { // we are not using DSR handshake DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_DSR; } // check if we are using DCD handshake if (DeviceExtension->SerialHandFlow.ControlHandShake & SERIAL_DCD_HANDSHAKE) { if (ModemStatus & CPR_UART_MSR_DCD) { // DCD is up, so enable transmitter DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_DCD; } else { // DCD is down, so disable transmitter DeviceExtension->TxStopReason |= CPR_SERIAL_TX_DCD; } } else { // we are not using DCD handshake DeviceExtension->TxStopReason &= ~CPR_SERIAL_TX_DCD; } #ifdef REMOVE_FOR_TESTING_WITH_GRID_CONNECT // GridConnect had FlowReplace = 0xC0 if (!OldTxStopReason && DeviceExtension->TxStopReason && ((DeviceExtension->SerialHandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE)) { // not implemented ASSERT(FALSE); } #endif // check if we need to restart transmitter if (!DoingTX && (OldTxStopReason != 0) && (DeviceExtension->TxStopReason == 0)) { if ((DeviceExtension->TxImmediate || DeviceExtension->WriteLength) && DeviceExtension->TxIdle) { CprUartStartWrite(&DeviceExtension->Uart); } } } else { // since we are not using any transmitter handshaking, make sure that transmitter is running if (DeviceExtension->TxStopReason & (CPR_SERIAL_TX_DCD | CPR_SERIAL_TX_DSR | CPR_SERIAL_TX_CTS)) { DeviceExtension->TxStopReason &= ~(CPR_SERIAL_TX_DCD | CPR_SERIAL_TX_DSR | CPR_SERIAL_TX_CTS); if (!DoingTX && (OldTxStopReason != 0) && (DeviceExtension->TxStopReason == 0)) { if ((DeviceExtension->TxImmediate || DeviceExtension->WriteLength) && DeviceExtension->TxIdle) { CprUartStartWrite(&DeviceExtension->Uart); } } } } return ModemStatus; }