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 ); } } } }
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; }
VOID SerialPutChar( IN PSERIAL_DEVICE_EXTENSION Extension, IN UCHAR CharToPut ) /*++ Routine Description: This routine, which only runs at device level, takes care of placing a character into the typeahead (receive) buffer. Arguments: Extension - The serial device extension. Return Value: None. --*/ { PREQUEST_CONTEXT reqContext = NULL; // // If we have dsr sensitivity enabled then // we need to check the modem status register // to see if it has changed. // if (Extension->HandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) { SerialHandleModemUpdate( Extension, FALSE ); if (Extension->RXHolding & SERIAL_RX_DSR) { // // We simply act as if we haven't // seen the character if we have dsr // sensitivity and the dsr line is low. // return; } } // // If the xoff counter is non-zero then decrement it. // If the counter then goes to zero, complete that request. // if (Extension->CountSinceXoff) { Extension->CountSinceXoff--; if (!Extension->CountSinceXoff) { reqContext = SerialGetRequestContext(Extension->CurrentXoffRequest); reqContext->Status = STATUS_SUCCESS; reqContext->Information = 0; SerialInsertQueueDpc( Extension->XoffCountCompleteDpc ); } } // // Check to see if we are copying into the // users buffer or into the interrupt buffer. // // If we are copying into the user buffer // then we know there is always room for one more. // (We know this because if there wasn't room // then that read would have completed and we // would be using the interrupt buffer.) // // If we are copying into the interrupt buffer // then we will need to check if we have enough // room. // if (Extension->ReadBufferBase != Extension->InterruptReadBuffer) { // // Increment the following value so // that the interval timer (if one exists // for this read) can know that a character // has been read. // Extension->ReadByIsr++; // // We are in the user buffer. Place the // character into the buffer. See if the // read is complete. // *Extension->CurrentCharSlot = CharToPut; if (Extension->CurrentCharSlot == Extension->LastCharSlot) { // // We've filled up the users buffer. // Switch back to the interrupt buffer // and send off a DPC to Complete the read. // // It is inherent that when we were using // a user buffer that the interrupt buffer // was empty. // Extension->ReadBufferBase = Extension->InterruptReadBuffer; Extension->CurrentCharSlot = Extension->InterruptReadBuffer; Extension->FirstReadableChar = Extension->InterruptReadBuffer; Extension->LastCharSlot = Extension->InterruptReadBuffer + (Extension->BufferSize - 1); Extension->CharsInInterruptBuffer = 0; reqContext = SerialGetRequestContext(Extension->CurrentReadRequest); reqContext->Information = reqContext->Length; SerialInsertQueueDpc( Extension->CompleteReadDpc ); } else { // // Not done with the users read. // Extension->CurrentCharSlot++; } } else { // // We need to see if we reached our flow // control threshold. If we have then // we turn on whatever flow control the // owner has specified. If no flow // control was specified, well..., we keep // trying to receive characters and hope that // we have enough room. Note that no matter // what flow control protocol we are using, it // will not prevent us from reading whatever // characters are available. // if ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_HANDSHAKE) { // // If we are already doing a // dtr hold then we don't have // to do anything else. // if (!(Extension->RXHolding & SERIAL_RX_DTR)) { if ((Extension->BufferSize - Extension->HandFlow.XoffLimit) <= (Extension->CharsInInterruptBuffer+1)) { Extension->RXHolding |= SERIAL_RX_DTR; SerialClrDTR(Extension->WdfInterrupt, Extension); } } } if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_RTS_HANDSHAKE) { // // If we are already doing a // rts hold then we don't have // to do anything else. // if (!(Extension->RXHolding & SERIAL_RX_RTS)) { if ((Extension->BufferSize - Extension->HandFlow.XoffLimit) <= (Extension->CharsInInterruptBuffer+1)) { Extension->RXHolding |= SERIAL_RX_RTS; SerialClrRTS(Extension->WdfInterrupt, Extension); } } } if (Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) { // // If we are already doing a // xoff hold then we don't have // to do anything else. // if (!(Extension->RXHolding & SERIAL_RX_XOFF)) { if ((Extension->BufferSize - Extension->HandFlow.XoffLimit) <= (Extension->CharsInInterruptBuffer+1)) { Extension->RXHolding |= SERIAL_RX_XOFF; // // If necessary cause an // off to be sent. // SerialProdXonXoff( Extension, FALSE ); } } } if (Extension->CharsInInterruptBuffer < Extension->BufferSize) { *Extension->CurrentCharSlot = CharToPut; Extension->CharsInInterruptBuffer++; // // If we've become 80% full on this character // and this is an interesting event, note it. // if (Extension->CharsInInterruptBuffer == Extension->BufferSizePt8) { if (Extension->IsrWaitMask & SERIAL_EV_RX80FULL) { Extension->HistoryMask |= SERIAL_EV_RX80FULL; if (Extension->IrpMaskLocation) { *Extension->IrpMaskLocation = Extension->HistoryMask; Extension->IrpMaskLocation = NULL; Extension->HistoryMask = 0; reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest); reqContext->Information = sizeof(ULONG); SerialInsertQueueDpc( Extension->CommWaitDpc ); } } } // // Point to the next available space // for a received character. Make sure // that we wrap around to the beginning // of the buffer if this last character // received was placed at the last slot // in the buffer. // if (Extension->CurrentCharSlot == Extension->LastCharSlot) { Extension->CurrentCharSlot = Extension->InterruptReadBuffer; } else { Extension->CurrentCharSlot++; } } else { // // We have a new character but no room for it. // Extension->PerfStats.BufferOverrunErrorCount++; Extension->WmiPerfData.BufferOverrunErrorCount++; Extension->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN; if (Extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR) { // // Place the error character into the last // valid place for a character. Be careful!, // that place might not be the previous location! // if (Extension->CurrentCharSlot == Extension->InterruptReadBuffer) { *(Extension->InterruptReadBuffer+ (Extension->BufferSize-1)) = Extension->SpecialChars.ErrorChar; } else { *(Extension->CurrentCharSlot-1) = Extension->SpecialChars.ErrorChar; } } // // If the application has requested it, abort all reads // and writes on an error. // if (Extension->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT) { SerialInsertQueueDpc( Extension->CommErrorDpc ); } } } }