ioctl.cpp

来自「这个是串口驱动程序开发包」· C++ 代码 · 共 1,202 行 · 第 1/3 页

CPP
1,202
字号
/*++

Abstract:

    This module contains the ioctl dispatcher as well as a couple
    of routines that are generally just called in response to
    ioctl calls.

--*/

#include "precomp.h"

BOOLEAN KdSerialDevice::GetStats(IN PVOID Context)

/*++

Routine Description:

    In sync with the interrupt service routine (which sets the perf stats)
    return the perf stats to the caller.


Arguments:

    Context - Pointer to a the irp.

Return Value:

    This routine always returns FALSE.

--*/

{
    PKdSerialDevice pDevice;
    pDevice = (PKdSerialDevice) GetDevice(((PKdIrp)Context)->DeviceObject());
    PSERIALPERF_STATS sp = (PSERIALPERF_STATS)((PKdIrp)Context)->SystemBuffer();

    *sp = pDevice->m_PerfStats;
    return FALSE;
}

BOOLEAN KdSerialDevice::ClearStats()

/*++

Routine Description:

    In sync with the interrpt service routine (which sets the perf stats)
    clear the perf stats.

Return Value:

    This routine always returns FALSE.

--*/

{
    RtlZeroMemory(
        &m_PerfStats,
        sizeof(SERIALPERF_STATS)
        );
    return FALSE;
}

BOOLEAN KdSerialDevice::SetChars(IN PVOID Context)

/*++

Routine Description:

    This routine is used to set the special characters for the
    driver.

Arguments:

    Context - pointer to a special characters structure.

Return Value:

    This routine always returns FALSE.

--*/

{
    m_SpecialChars = *((PSERIAL_CHARS)Context);
    return FALSE;
}

BOOLEAN KdSerialDevice::SetBaud(IN PVOID Context)
/*++
Routine Description:

    This routine is used to set the buad rate of the device.

Arguments:

    Context - Pointer to  what should be the current baud rate.

Return Value:

    This routine always returns FALSE.
--*/
{
    USHORT Appropriate = (USHORT)Context;
    WRITE_DIVISOR_LATCH(m_pController, Appropriate);
    return FALSE;
}

BOOLEAN KdSerialDevice::SetLineControl()
/*++
Routine Description:
    This routine is used to set the buad rate of the device.
--*/
{
    m_pController->WriteByte(LINE_CONTROL_REGISTER, m_LineControl);
    return FALSE;
}

BOOLEAN KdSerialDevice::GetModemUpdate(IN PVOID Context)
/*++
Routine Description:
    This routine is simply used to call the interrupt level routine
    that handles modem status update.

Arguments:
    Context - pointer to a ulong.
--*/
{
    ULONG *Result = (ULONG *) Context;
    *Result = HandleModemUpdate(FALSE);
    return FALSE;
}

BOOLEAN KdSerialDevice::GetCommStatus(IN PVOID Context)
/*++
Routine Description:
    This is used to get the current state of the serial driver.

Arguments:
    Context - pointer to a serial status record.

Return Value:
    This routine always returns FALSE.
--*/
{
    PSERIAL_STATUS Stat = (PSERIAL_STATUS) Context;

    Stat->Errors = m_ErrorWord;
    m_ErrorWord = 0;

    // BUG BUG We need to do something about eof (binary mode).
    Stat->EofReceived = FALSE;
    Stat->AmountInInQueue = m_CharsInInterruptBuffer;
    Stat->AmountInOutQueue = m_TotalCharsQueued;

    if (m_WriteLength) 
    {
        // By definition if we have a writelength the we have
        // a current write irp.
        ASSERT(m_CurrentWriteIrp);
        ASSERT(Stat->AmountInOutQueue >= m_WriteLength);
        ASSERT(m_CurrentWriteIrp.WriteLength() >= m_WriteLength);
        Stat->AmountInOutQueue -= m_CurrentWriteIrp.WriteLength() - (m_WriteLength);
    }

    Stat->WaitForImmediate = m_TransmitImmediate;
    Stat->HoldReasons = 0;
    if (m_TXHolding) 
    {
        if (m_TXHolding & SERIAL_TX_CTS)
            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
        
        if (m_TXHolding & SERIAL_TX_DSR)
            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
        
        if (m_TXHolding & SERIAL_TX_DCD)
            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
        
        if (m_TXHolding & SERIAL_TX_XOFF)
            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;
        
        if (m_TXHolding & SERIAL_TX_BREAK)
            Stat->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;
    }

    if (m_RXHolding & SERIAL_RX_DSR)
        Stat->HoldReasons |= SERIAL_RX_WAITING_FOR_DSR;

    if (m_RXHolding & SERIAL_RX_XOFF)
        Stat->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;

    return FALSE;
}

BOOLEAN KdSerialDevice::SetEscapeChar(IN PVOID Context)
/*++
Routine Description:
    This is used to set the character that will be used to escape
    line status and modem status information when the application
    has set up that line status and modem status should be passed
    back in the data stream.

Arguments:
    Context - Pointer to the irp that is specify the escape character.
              Implicitly - An escape character of 0 means no escaping
              will occur.
--*/
{
    PKdSerialDevice pDevice;
    pDevice = (PKdSerialDevice) GetDevice(((PKdIrp)Context)->DeviceObject());
            
    pDevice->m_EscapeChar = *(PUCHAR)((PKdIrp)Context)->SystemBuffer();

    return FALSE;
}

NTSTATUS KdSerialDevice::DispatchDeviceControl(IN KdIrp &Irp)
/*++

Routine Description:

    This routine provides the initial processing for all of the
    Ioctls for the serial device.

Arguments:

    Irp - Pointer to the IRP for the current request

Return Value:

    The function value is the final status of the call

--*/

{
    // The status that gets returned to the caller and
    // set in the Irp.
    NTSTATUS Status;

    DebugDump(DBG_DIAG6, ("Dispatch entry for: %x\n",Irp) );

    if (CompleteIfError(Irp) != STATUS_SUCCESS) 
        return STATUS_CANCELLED;

    Irp.Information() = 0L;
    Status = STATUS_SUCCESS;
    switch (Irp.IoctlCode()) 
    {
        case IOCTL_SERIAL_SET_BAUD_RATE : 
            ULONG BaudRate;
            // Will hold the value of the appropriate divisor for
            // the requested baud rate.  If the baudrate is invalid
            // (because the device won't support that baud rate) then
            // this value is undefined.
            //
            // Note: in one sense the concept of a valid baud rate
            // is cloudy.  We could allow the user to request any
            // baud rate.  We could then calculate the divisor needed
            // for that baud rate.  As long as the divisor wasn't less
            // than one we would be "ok".  (The percentage difference
            // between the "true" divisor and the "rounded" value given
            // to the hardware might make it unusable, but... )  It would
            // really be up to the user to "Know" whether the baud rate
            // is suitable.  So much for theory, *We* only support a given
            // set of baud rates.
            SHORT AppropriateDivisor;

            if (Irp.IoctlInputBufferLength() < sizeof(SERIAL_BAUD_RATE)) 
            {
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            } 
            else 
                BaudRate = ((PSERIAL_BAUD_RATE) Irp.SystemBuffer())->BaudRate;

            // Get the baud rate from the irp.  We pass it
            // to a routine which will set the correct divisor.

            Status = GetDivisorFromBaud(
                         BaudRate,
                         &AppropriateDivisor
                         );

            m_ControlLock.Lock();
            if (NT_SUCCESS(Status)) 
            {
                m_CurrentBaud = BaudRate;
                m_KdInterrupt.SynchronizeExecution((KDIRQ_SYNC_CALLBACK_PARM)SetBaud, (PVOID)AppropriateDivisor);
            }
            m_ControlLock.Release();
            break;

        case IOCTL_SERIAL_GET_BAUD_RATE:
            {
                PSERIAL_BAUD_RATE Br = (PSERIAL_BAUD_RATE)Irp.SystemBuffer();
                if (Irp.IoctlOutputBufferLength() < sizeof(SERIAL_BAUD_RATE)) 
                {
                    Status = STATUS_BUFFER_TOO_SMALL;
                    break;
                }

                m_ControlLock.Lock();
                Br->BaudRate = m_CurrentBaud;
                m_ControlLock.Release();

                Irp.Information() = sizeof(SERIAL_BAUD_RATE);
                break;
            }

        case IOCTL_SERIAL_SET_LINE_CONTROL:
            {
                // Points to the line control record in the Irp.
                PSERIAL_LINE_CONTROL Lc =
                    ((PSERIAL_LINE_CONTROL)(Irp.SystemBuffer()));

                UCHAR LData;
                UCHAR LStop;
                UCHAR LParity;
                UCHAR Mask = 0xff;

                if (Irp.IoctlInputBufferLength() < sizeof(SERIAL_LINE_CONTROL)) 
                {
                    Status = STATUS_BUFFER_TOO_SMALL;
                    break;
                }
                switch (Lc->WordLength) 
                {
                    case 5:
                        LData = SERIAL_5_DATA;
                        Mask = 0x1f;
                        break;
                    case 6:
                        LData = SERIAL_6_DATA;
                        Mask = 0x3f;
                        break;
                    case 7:
                        LData = SERIAL_7_DATA;
                        Mask = 0x7f;
                        break;
                    case 8:
                        LData = SERIAL_8_DATA;
                        break;
                    default:
                        Status = STATUS_INVALID_PARAMETER;
                        goto DoneWithIoctl;
                }

                switch (Lc->Parity) 
                {
                    case NO_PARITY:
                        LParity = SERIAL_NONE_PARITY;
                        break;
                    case EVEN_PARITY:
                        LParity = SERIAL_EVEN_PARITY;
                        break;
                    case ODD_PARITY:
                        LParity = SERIAL_ODD_PARITY;
                        break;
                    case SPACE_PARITY:
                        LParity = SERIAL_SPACE_PARITY;
                        break;
                    case MARK_PARITY:
                        LParity = SERIAL_MARK_PARITY;
                        break;
                    default:
                        Status = STATUS_INVALID_PARAMETER;
                        goto DoneWithIoctl;
                        break;
                }

                switch (Lc->StopBits) 
                {
                    case STOP_BIT_1:
                        LStop = SERIAL_1_STOP;
                        break;
                    case STOP_BITS_1_5:
                        if (LData != SERIAL_5_DATA) 
                        {
                            Status = STATUS_INVALID_PARAMETER;
                            goto DoneWithIoctl;
                        }
                        LStop = SERIAL_1_5_STOP;
                        break;

                    case STOP_BITS_2:
                        if (LData == SERIAL_5_DATA) 
                        {
                            Status = STATUS_INVALID_PARAMETER;
                            goto DoneWithIoctl;
                        }
                        LStop = SERIAL_2_STOP;
                        break;

                    default:
                        Status = STATUS_INVALID_PARAMETER;
                        goto DoneWithIoctl;
                }

                m_ControlLock.Lock();
                m_LineControl =
                    (UCHAR)((m_LineControl & SERIAL_LCR_BREAK) |

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?