📄 device.cpp
字号:
//
// File: device.cpp
// Description: The implementation of class Device
//
// Created: Wed. Jan 15, 2003
//
//
// Copyright and Disclaimer:
//
// ---------------------------------------------------------------
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// IN NO EVENT SHALL CONEXANT BE LIABLE TO ANY PARTY FOR DIRECT,
// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE
// AND ITS DOCUMENTATION, EVEN IF CONEXANT HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2000-2001 Conexant Systems, Inc.
//
// All Rights Reserved.
// ---------------------------------------------------------------
//
// Module Revision Id:
//
//
#include "common.h"
#include "device.h"
/////////////////////////////////////////////////////////////////////////////////////////
//Device::Device (constructor)
//
// This just initializes the member variables. Most of the real initialization is done
// when we receive IRP_MN_START_DEVICE (Device::start)
//
Device::Device(PDEVICE_OBJECT p_device_object):
_device_state(DEVICE_STATE_NOT_STARTED),
_p_interrupt(NULL),
_p_ir_control(NULL),
_p_registers(NULL),
_p_device_object(p_device_object),
_power_state(PowerDeviceD0) //Always start out powered up
{
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::~Device (destructor)
//
// Clean up things initialized if the device was started.
//
Device::~Device()
{
ULONG old_device_state = _device_state;
_device_state = DEVICE_STATE_REMOVING;
// Cancel any outstanding IRPs if the device was running
if(old_device_state == DEVICE_STATE_RUNNING)
{
releaseResources();
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::static_add
//
// This is the AddDevice DDK entry point. The purpose of this function is to allocate
// a new Device object. The pointer to the device is stored in the device extension.
//
// The object is deleted when IRP_MN_REMOVE_DEVICE is received.
//
// The system calls the AddDevice handler when it detects a new device which the driver
// is supposed to handle. We are not supposed to touch the device until after it
// sends IRP_MN_START_DEVICE, handled by Device::start(). That is where most device
// initialization is done.
//
NTSTATUS Device::static_add(
PDRIVER_OBJECT p_driver_object,
PDEVICE_OBJECT p_device_object)
{
dbgLogInfo(("Device::static_add\n"));
PDEVICE_EXTENSION deviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(p_device_object);
deviceExtension->p_device = new Device(p_device_object);
if(!deviceExtension->p_device)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
return STATUS_SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::static_pnp
//
// This is the DDK entry point for IRP_MJ_PNP. In general, this function will call some
// non-static member of Device based on the minor function of the IRP.
//
// The exception is IRP_MN_REMOVE_DEVICE where we must delete the Device object. So the
// destructor is the remove handler.
//
// The driver currently only handles IRP_MN_START_DEVICE, IRP_MN_STOP_DEVICE and
// IRP_MN_REMOVE_DEVICE
//
NTSTATUS Device::static_pnp(
PDEVICE_OBJECT p_device_object,
PIRP p_irp)
{
// Get a pointer to the device extension
PDEVICE_EXTENSION p_device_extension = GET_MINIDRIVER_DEVICE_EXTENSION(p_device_object);
//If there's no device, it must have already been removed. Fail the IRP.
if(!p_device_extension->p_device)
{
p_irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
IoCompleteRequest(p_irp, IO_NO_INCREMENT);
return STATUS_NO_SUCH_DEVICE;
}
// Get a pointer to the current location in the Irp
PIO_STACK_LOCATION p_irp_stack = IoGetCurrentIrpStackLocation(p_irp);
NTSTATUS status = STATUS_SUCCESS;
switch (p_irp_stack->MinorFunction)
{
case IRP_MN_START_DEVICE:
status = p_device_extension->p_device->start(p_irp);
break;
case IRP_MN_STOP_DEVICE:
dbgLogTrace(("IRP_MN_STOP_DEVICE\n"));
status = p_device_extension->p_device->stop(p_irp);
break;
case IRP_MN_REMOVE_DEVICE:
delete p_device_extension->p_device;
p_device_extension->p_device = NULL;
status = static_passIrpDown(
GET_NEXT_DEVICE_OBJECT(p_device_object),
p_irp);
break;
default:
//If we don't handle the IRP just pass it down to the lower device
dbgLogTrace(("Unknown PNP IRP Parameter (%lx)\n", p_irp_stack->MinorFunction));
status = static_passIrpDown(
GET_NEXT_DEVICE_OBJECT(p_device_object),
p_irp);
break;
}
dbgLogInfo(("HidMiniPlugnPlay Exit = %x\n", status));
return status;
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::static_passIrpDown
//
// This function passes an IRP down to the next lower device object. The static version
// is there for those static functions that might need to pass an IRP down outside of the
// Device class. (For instance, we need to pass on the IRP_MN_REMOVE_DEVICE IRP after
// we delete the Device object.
//
// Most users should use the non-static version of this function.
//
NTSTATUS
Device::static_passIrpDown(PDEVICE_OBJECT p_lower_device, PIRP p_irp)
{
IoSkipCurrentIrpStackLocation(p_irp);
return IoCallDriver(p_lower_device, p_irp);
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::start
//
// This is the handler for IRP_MJ_PNP, IRP_MN_START_DEVICE. This is the main IRP for
// device initialization. We receive our hardware resources here.
//
// The start IRP should always be passed down to the lower device first to give it a
// chance to start the hardware before we try to access it.
//
// Most of our initialization is done in the init() function, an internal helper function
// called after the start IRP has been processed by the lower device.
//
NTSTATUS Device::start(PIRP p_irp)
{
_device_state = DEVICE_STATE_STARTING;
//First pass the IRP down to the lower device
NTSTATUS status = passIrpDownSync(p_irp);
if(!NT_SUCCESS(status))
{
return status;
}
status = init(p_irp);
if (NT_SUCCESS(status))
{
dbgLogTrace(("Device was configured!\n"));
_device_state = DEVICE_STATE_RUNNING;
}
else
{
dbgLogError(("Device configuration failed!\n"));
_device_state = DEVICE_STATE_NOT_STARTED;
}
p_irp->IoStatus.Status = status;
IoCompleteRequest(p_irp, IO_NO_INCREMENT);
return status;
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::init
//
// This is where we do most of the device initialization. This is called from
// Device::start after it passes the IRP down to the lower device.
//
// This function does the following:
// 1) Get hardware resources from the IRP.
// 2) Allocate the registers object using the memory range
// 3) Allocate the IR_Control object. This object needs access to the registers object,
// and will handle any interrupts generated, so it should be allocated before
// connecting the interrupt.
// 4) Connect the interrupt.
//
// If the return status is a failure, none of these things were done. So, we need to
// clean up in the destructor only if the device was successfully started.
//
NTSTATUS Device::init(PIRP p_irp)
{
NTSTATUS status = STATUS_SUCCESS;
//Get our hardware resources
PCM_PARTIAL_RESOURCE_DESCRIPTOR p_memory = NULL;
PCM_PARTIAL_RESOURCE_DESCRIPTOR p_interrupt = NULL;
getResources(p_irp, &p_memory, &p_interrupt);
if(!p_memory || !p_interrupt)
{
dbgLogError(("Problem getting device resources"));
status = STATUS_DEVICE_CONFIGURATION_ERROR;
}
//Initialize the memory range and registers object
if(NT_SUCCESS(status))
{
//Map the memory range to a virtual address
PBYTE p_memory_base = (PBYTE) MmMapIoSpace(
p_memory->u.Memory.Start,
p_memory->u.Memory.Length,
MmNonCached);
//Initialize the RegisterIo object
if(p_memory_base)
{
_p_registers = new RegisterIo(p_memory_base);
}
//Oops, allocation failed
if(!_p_registers)
{
//We couldn't allocate memory for the registers
dbgLogError(("Could not allocate memory for register object\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
//Initialize the IR_Control object
if(NT_SUCCESS(status))
{
//Read the value of the IR standard to use from the the registry
DWORD standard = (DWORD)IR_STANDARD_RC5;
readRegistryDword("IR_Standard", &standard);
DWORD table = 0;
readRegistryDword("IR_Command_table", &table);
_p_ir_control = new IR_Control(_p_registers, &_hid, (IR_STANDARD)standard, table);
if(!_p_ir_control)
{
dbgLogError(("Could not allocate memory for IR Control object\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
//Connect the interrupt
if(NT_SUCCESS(status))
{
status = IoConnectInterrupt(
&_p_interrupt, //Interrupt object
(PKSERVICE_ROUTINE) static_interruptRoutine, //ISR
this, //context
NULL, //spinlock
p_interrupt->u.Interrupt.Vector, //vector
(KIRQL) p_interrupt->u.Interrupt.Level, //IRQL
(KIRQL) p_interrupt->u.Interrupt.Level, //Synchronize IRQL
(p_interrupt->Flags == CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive,
TRUE, //Shared
p_interrupt->u.Interrupt.Affinity,
FALSE); //Floating save - must be false
if(!NT_SUCCESS(status))
{
dbgLogError(("Error connecting interrupt\n"));
}
else
{
//This actually enables interrupts on the device.
_p_ir_control->initialize();
}
}
//Something failed. Clean up.
if(!NT_SUCCESS(status))
{
delete _p_ir_control;
delete _p_registers;
_p_registers = NULL;
_p_ir_control = NULL;
}
return status;
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::stop
//
// This is the handler for IRP_MJ_PNP, IRP_MN_STOP. Note that IRP_MN_REMOVE is a better
// indication of when the device is going away. IRP_MN_STOP usually is not called before
// IRP_MN_REMOVE.
//
// Anyway, we mark the device state as stopped so that we do not queue any further read
// report requests. We also release all of our hardware resources.
//
// Processing should be done before passing the IRP to the lower device.
//
NTSTATUS Device::stop(PIRP p_irp)
{
_device_state = DEVICE_STATE_STOPPING;
//We should release any system resources here
releaseResources();
//Cancel any pending requests in the HID class
_hid.cancelOutstandingRequests();
NTSTATUS status = passIrpDownSync(p_irp);
if(!NT_SUCCESS(status))
{
return status;
}
_device_state = DEVICE_STATE_STOPPED;
p_irp->IoStatus.Status = status;
IoCompleteRequest(p_irp, IO_NO_INCREMENT);
return status;
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::static_interruptRoutine
//
// This is the interrupt handler, connected to the interrupt in Device::init. Currently,
// all interrupts are generated and handled by the IR_Control class, so we just call the
// IR_Control class with the interrupt.
//
// Since the interrupt is only connected when the IR_Control class is allocated, there
// should never be a problem of the pointer being NULL.
//
BOOLEAN Device::static_interruptRoutine(
PKINTERRUPT interrupt,
PVOID p_context)
{
Device* p_device = (Device*) p_context;
if(!p_device || !p_device->_p_ir_control)
{
//Should never get here. . .
return FALSE;
}
//Don't try to handle the interrupt if we aren't powered up.
if(p_device->_power_state == PowerDeviceD3)
{
return FALSE;
}
return p_device->_p_ir_control->interruptHandler();
}
/////////////////////////////////////////////////////////////////////////////////////////
//Device::static_power
//
// This is the DDK entry point for power IRPs. Since we are not the power manager for the
// device, we just pass them through to the lower device.
//
// TODO: intercept the power up and down messages so that we can restore the state correctly
// after a hibernate.
//
NTSTATUS Device::static_power(
PDEVICE_OBJECT p_device_object,
PIRP p_irp)
{
//Check for a device set power IRP. These are the only type we process
PIO_STACK_LOCATION p_irp_stack = IoGetCurrentIrpStackLocation(p_irp);
if((p_irp_stack->MinorFunction == IRP_MN_SET_POWER) &&
(p_irp_stack->Parameters.Power.Type == DevicePowerState))
{
//If it's a power down, we should handle it first, and then pass it to the lower
// device.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -