Example #1
0
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
                    );

            }

        }

    }

}
Example #2
0
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;

}
Example #3
0
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
                    );

            }

        }

    }

}