Figure 2 CDriverObject
driver.h
////////////////////////////////////////////////////////////////////////////////
// Module Name: driver.h
// Abstract: CDriverObject Interface
// Environment: Windows NT 3.51/4.0, MS Visual C++ 4.x
// Notes:
// CDriverObject encapsulates the DRIVER_OBJECT data structure.
//
// IMPORTANT:
// A CDriverObject class object MUST be statically instantiated by the
// user. Dynamic instantiation is not supported. Each driver object
// represents the image of a loaded kernel-mode driver. A pointer to
// the driver object is an input parameter to a driver's DriverEntry
// and optional Reinitialize routines and to its Unload routine, if
// any.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef driver_h
#define driver_h
#include "device.h"
class CDriverObject
{
public:
// ctor/dtor
CDriverObject( PCWSTR DriverClassName = NULL );
virtual ~CDriverObject();
// Simply returns NULL because dynamic instantiation of an
// object of this class is not supported.
PVOID operator new( size_t size );
// Cast operator for PDRIVER_OBJECT.
operator PDRIVER_OBJECT();
VOID SetDriverObject( PDRIVER_OBJECT DriverObject );
// called from within DriverEntry routine. Must call
// IoRegisterDriverReinitialization passing this pointer as Context
// argument. Then must call virtual OnInitialize() member.
NTSTATUS Initialize( PUNICODE_STRING RegistryPath );
// called from within DriverUnload() routine. This routine MUST:
// 1. Call m_DeviceObject->Unload()
// 2. Call virtual OnUnload()
static VOID Unload( PDRIVER_OBJECT DriverObject );
// Driver Entry routine exported to IOManager.
static NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath );
// Calls IoAssignResources().
NTSTATUS AssignResources(
IN PUNICODE_STRING RegistryPath,
IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources,
OUT PCM_RESOURCE_LIST* AllocatedResources );
NTSTATUS AssignSlotResources(
IN PUNICODE_STRING RegistryPath,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
OUT PCM_RESOURCE_LIST* AllocatedResources );
// calls IoReportResourceUsage().
NTSTATUS ReportResourceUsage(
IN PCM_RESOURCE_LIST DriverList,
IN ULONG DriverListSize,
IN BOOLEAN bOverRideConflict,
OUT PBOOLEAN bConflictDetected );
// set in constructor
PUNICODE_STRING GetClassName( VOID );
PUNICODE_STRING GetHardwareDatabase( VOID );
protected:
// called from Unload() member, gives derived classes an opportunity to
// cleanup before UnloadDevices is called (e.g. if driver supports a
// DEVICE_OBJECT that is not a CDeviceObject)
virtual VOID OnUnload( void );
// called by Initialize() routine, gives derived classes an opportunity to
// perform additional initialization (e.g. if driver supports a
// DEVICE_OBJECT that is not a CDeviceObject)
virtual NTSTATUS OnInitialize( PUNICODE_STRING RegistryPath );
// calls IoRegisterDriverReinitialization().
VOID RegisterDriverReinitialization(
PDRIVER_REINITIALIZE DriverReinitializationRoutine,
PVOID Context );
// createDevices must create a device object during
// CDriverObject::Initialize(). Following is an example of how a derived
// class MUST create a CDeviceObject object:
// CDeviceObject* CMyDriverObject::CreateDevice()
// {
// NTSTATUS status;
// new( *this, DeviceName, DeviceType, DeviceCharacteristics,
// bExclusive, status ) CMyDeviceObject( this );
// return status;
// }
virtual NTSTATUS CreateDevices( void ) = 0;
// setup as StartIo callback for DRIVER_OBJECT, calls OnStartIo()
static VOID StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
// calls OnReadStartIo(), OnWriteStartIo(), or OnStartIo members of
// CDeviceObject. Override to perform custom StartIo handling (e.g.
// if driver supports a DEVICE_OBJECT that is not a CDeviceObject)
virtual VOID OnStartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
// dispatch handler, calls OnIrpMjXxx
static NTSTATUS IrpMjXxx( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
// overridable dispatch handler. Override to perform custom StartIo
// handling (e.g. if driver supports a DEVICE_OBJECT that is not a
// CDeviceObject)
virtual NTSTATUS OnIrpMjXxx( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
private:
// methods
NTSTATUS InitDevices( PUNICODE_STRING RegistryPath );
VOID UnloadDevices( VOID );
// members
PDRIVER_OBJECT m_DriverObject;
// the only driver object
static CDriverObject* s_DriverObject;
// Set in constructor
UNICODE_STRING m_ClassName;
};
////////////////////////////////////////////////////////////////////////////////
// inlines
//
inline CDriverObject::operator PDRIVER_OBJECT()
{
return m_DriverObject;
}
inline VOID CDriverObject::SetDriverObject( IN PDRIVER_OBJECT DriverObject )
{
ASSERT( NULL == m_DriverObject );
ASSERT( NULL != DriverObject );
m_DriverObject = DriverObject;
return;
}
inline PUNICODE_STRING CDriverObject::GetClassName( void )
{
return &m_ClassName;
}
inline PUNICODE_STRING CDriverObject::GetHardwareDatabase( void )
{
return m_DriverObject->HardwareDatabase;
}
#endif driver_h
driver.cpp
////////////////////////////////////////////////////////////////////////////////
// Module Name: driver.cpp
// Abstract: CDriverObject Implementation
// Environment: Windows NT 3.51/4.0, MS Visual C++ 2.x
////////////////////////////////////////////////////////////////////////////////
// disable some warnings
#pragma warning( disable: 4699 ) // Note: Creating precompiled header
#pragma warning( disable: 4201 ) // nameless struct/union
#pragma warning( disable: 4514 ) // unreferenced inline function has been removed
#pragma warning( disable: 4705 ) // statement has no effect
#pragma warning( disable: 4711 ) // function 'function' selected for automatic inline expansion
#include <idrivrpp.h>
#include "cppx.h"
#include "driver.h"
#define DEFAULT_CLASS_NAME L"Other"
////////////////////////////////////////////////////////////////////////////////
// static initialization
//
CDriverObject* CDriverObject::s_DriverObject = NULL;
////////////////////////////////////////////////////////////////////////////////
// ctor/dtor
//
CDriverObject::CDriverObject( IN PCWSTR DriverClassName /* = NULL */ )
: m_DriverObject( NULL )
{
// Set the class name for the driver
//@@@ why not use our CUnicodeString class
if( NULL != DriverClassName )
{
RtlInitUnicodeString( &m_ClassName, DriverClassName );
}
else
{
RtlInitUnicodeString( &m_ClassName, DEFAULT_CLASS_NAME );
}
ASSERT( NULL != s_DriverObject );
s_DriverObject = this;
return;
}
CDriverObject::~CDriverObject()
{
s_DriverObject = NULL;
return;
}
PVOID CDriverObject::operator new( IN size_t /* size */ )
{
// Dynamic allocation not supported.
return NULL;
}
NTSTATUS CDriverObject::DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath )
{
s_DriverObject->SetDriverObject( DriverObject );
DriverObject->DriverUnload = Unload;
DriverObject->DriverStartIo = StartIo;
// Setup the entry points.
DriverObject->MajorFunction[ IRP_MJ_CLEANUP ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_CREATE ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_FLUSH_BUFFERS ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_INTERNAL_DEVICE_CONTROL ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_QUERY_INFORMATION ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_READ ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_SET_INFORMATION ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = IrpMjXxx;
DriverObject->MajorFunction[ IRP_MJ_WRITE ] = IrpMjXxx;
return s_DriverObject->Initialize( RegistryPath );
}
NTSTATUS CDriverObject::Initialize( IN PUNICODE_STRING RegistryPath )
{
// create the devices for this driver
NTSTATUS status;
if( NT_SUCCESS( status = CreateDevices() ) )
{
// If we successfully create all of the devices, finish the
// initialization of the driver object.
if( NT_SUCCESS( status = InitDevices( RegistryPath ) ) )
{
// Register for shutdown notifications for all devices
// @@@ don't we need to register for each device?
IoRegisterShutdownNotification( m_DriverObject->DeviceObject );
status = OnInitialize( RegistryPath );
// either by CreateDevices or OnInitialize, there should be at
// least one device (though not necessarily a CDeviceObject)
ASSERT( NULL != m_DriverObject->DeviceObject );
}
else
{
// InitDevices failed... unload the driver
Unload( m_DriverObject );
}
}
return status;
}
NTSTATUS CDriverObject::OnInitialize( IN PUNICODE_STRING /* RegistryPath */ )
{
return STATUS_SUCCESS;
}
NTSTATUS CDriverObject::InitDevices( PUNICODE_STRING RegistryPath )
{
// loop through list of devices (maintained by IO Manager)
PDEVICE_OBJECT pDeviceObject = m_DriverObject->DeviceObject;
while( NULL != pDeviceObject )
{
// get device object
CDeviceObject* pDevice = (CDeviceObject*) pDeviceObject->DeviceExtension;
ASSERT( NULL != pDevice );
// call the most-derived's Initialize routine
NTSTATUS status;
if( ! NT_SUCCESS( (status = pDevice->Initialize( RegistryPath )) ) )
{
// Initialize failed... return (CDriverObject::Initialize
// will cleanup)
return status;
}
// get the next device
pDeviceObject = pDeviceObject->NextDevice;
}
return STATUS_SUCCESS;
}
VOID CDriverObject::Unload( IN PDRIVER_OBJECT /* DriverObject */ )
{
ASSERT( NULL != s_DriverObject );
s_DriverObject->OnUnload();
s_DriverObject->UnloadDevices();
cppShutdown();
return;
}
VOID CDriverObject::OnUnload()
{
return;
}
VOID CDriverObject::UnloadDevices()
{
// loop through list of devices (maintained by IO Manager)
PDEVICE_OBJECT pDeviceObject = m_DriverObject->DeviceObject;
while( NULL != pDeviceObject )
{
// get device object
CDeviceObject* pDevice =
(CDeviceObject*) pDeviceObject->DeviceExtension;
ASSERT( NULL != pDevice );
// call the most-derived device object's Unload routine
pDevice->Unload();
// get the next device (before we delete this one)
pDeviceObject = pDeviceObject->NextDevice;
// delete the device object
delete pDevice;
}
return;
}
VOID CDriverObject::StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
ASSERT( NULL != s_DriverObject );
s_DriverObject->OnStartIo( DeviceObject, Irp );
return;
}
VOID CDriverObject::OnStartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
ASSERT_IRQL_EQ( DISPATCH_LEVEL );
// get device object
CDeviceObject* pObject = (CDeviceObject*) DeviceObject->DeviceExtension;
ASSERT( NULL != pObject );
// get stack location
PIO_STACK_LOCATION CurrentStackLocation = IoGetCurrentIrpStackLocation( Irp );
ASSERT( NULL != CurrentStackLocation );
// dispatch to the CDeviceObject derived handlers
switch( CurrentStackLocation->MajorFunction )
{
case IRP_MJ_READ:
pObject->OnStartReadIo( Irp );
break;
case IRP_MJ_WRITE:
pObject->OnStartWriteIo( Irp );
break;
default:
pObject->OnStartIo( Irp );
break;
}
return;
}
NTSTATUS CDriverObject::IrpMjXxx(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
{
ASSERT( NULL != s_DriverObject );
return s_DriverObject->OnIrpMjXxx( DeviceObject, Irp );
}
NTSTATUS CDriverObject::OnIrpMjXxx(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )
{
// get device object
CDeviceObject* pObject = (CDeviceObject*) DeviceObject->DeviceExtension;
ASSERT( NULL != pObject );
// get stack location
PIO_STACK_LOCATION CurrentStackLocation = IoGetCurrentIrpStackLocation(Irp );
ASSERT( NULL != CurrentStackLocation );
// dispatch to the CDeviceObject-derived handlers
// @@@ we could add parameter "crackers" to these calls... (i.e. pass as
// @@@ parameters the most commonly used members of the IO_STACK_LOCATION)
switch( CurrentStackLocation->MajorFunction )
{
case IRP_MJ_CREATE:
return pObject->OnIrpMjCreate( Irp );
case IRP_MJ_CLEANUP:
return pObject->OnIrpMjCleanup( Irp );
case IRP_MJ_CLOSE:
return pObject->OnIrpMjClose( Irp );
case IRP_MJ_READ:
return pObject->OnIrpMjRead( Irp );
case IRP_MJ_WRITE:
return pObject->OnIrpMjWrite( Irp );
case IRP_MJ_DEVICE_CONTROL:
return pObject->OnIrpMjDeviceControl( Irp );
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
return pObject->OnIrpMjInternalDeviceControl( Irp );
case IRP_MJ_QUERY_INFORMATION:
return pObject->OnIrpMjQueryInformation( Irp );
case IRP_MJ_SET_INFORMATION:
return pObject->OnIrpMjSetInformation( Irp );
case IRP_MJ_FLUSH_BUFFERS:
return pObject->OnIrpMjFlushBuffers( Irp );
case IRP_MJ_SHUTDOWN:
return pObject->OnIrpMjShutdown( Irp );
}
TRACE( "CDriverObject::OnIrpMjXxx - received an Irp that wasn't "
"setup in the dispatch table!" );
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDriverObject::ReportResourceUsage(
IN PCM_RESOURCE_LIST DriverList,
IN ULONG DriverListSize,
IN BOOLEAN OverRideConflict,
OUT PBOOLEAN ConflictDetected )
{
ASSERT( NULL != DriverList );
ASSERT( NULL != ConflictDetected );
return IoReportResourceUsage(
&m_ClassName,
m_DriverObject,
DriverList,
DriverListSize,
NULL,
NULL,
0,
OverRideConflict,
ConflictDetected );
}
NTSTATUS CDriverObject::AssignSlotResources(
IN PUNICODE_STRING RegistryPath,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
OUT PCM_RESOURCE_LIST* AllocatedResources )
{
ASSERT( NULL != RegistryPath );
ASSERT( NULL != AllocatedResources );
// If this CDeviceObject is an exclusive device object for this driver, NULL
// should be passed as the DeviceObject argument to alAssignSlotResources(),
// so that device-specific registry management is not required. If this is
// not an exclusive CDeviceObject object, then it is necessary to create and
// update the registry path passed to the DriverEntry routine in order to
// maintain device-specific resource data for each set of resources claimed
// under the driver-specific path contained in the DriverEntry's
// RegistryPath.
return HalAssignSlotResources(
RegistryPath,
&m_ClassName,
m_DriverObject,
NULL,
BusType,
BusNumber,
SlotNumber,
AllocatedResources );
}
NTSTATUS CDriverObject::AssignResources(
IN PUNICODE_STRING RegistryPath,
IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources,
OUT PCM_RESOURCE_LIST* AllocatedResources )
{
ASSERT( NULL != RegistryPath );
ASSERT( NULL != RequestedResources );
ASSERT( NULL != AllocatedResources );
return IoAssignResources(
RegistryPath,
&m_ClassName,
m_DriverObject,
NULL,
RequestedResources,
AllocatedResources );
}
VOID CDriverObject::RegisterDriverReinitialization(
IN PDRIVER_REINITIALIZE DriverReinitializationRoutine,
IN PVOID Context )
{
ASSERT_IRQL_EQ( PASSIVE_LEVEL );
ASSERT( NULL != DriverReinitializationRoutine );
IoRegisterDriverReinitialization( *this, DriverReinitializationRoutine,
Context );
return;
}
extern "C" NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath )
{
cppInit();
NTSTATUS status = CDriverObject::DriverEntry( DriverObject, RegistryPath );
// if DriverEntry does not return success, Unload is not called,
// so we need to call it for ourselves.
if( STATUS_SUCCESS != status )
{
CDriverObject::Unload( DriverObject );
}
return status;
}
Figure 3 CDeviceObject
device.h
////////////////////////////////////////////////////////////////////////////////
// Module Name: device.h
// Abstract: CDeviceObject Interface
// Environment: Windows NT 3.51/4.0, MS Visual C++ 4.x
////////////////////////////////////////////////////////////////////////////////
#ifndef device_h
#define device_h
#include "debug.h"
// forward declarations
class CDriverObject;
// CDeviceObject encapsulates the NTDDK DEVICE_OBJECT structure.
class CDeviceObject
{
public:
// ctor/dtor
CDeviceObject( IN CDriverObject* DriverObject = NULL );
virtual ~CDeviceObject();
// This operator new must return NULL.
PVOID operator new( size_t size );
// Calls IoCreateDevice() passing pointer to stack PDEVICE_OBJECT and size
// as DeviceExtensionSize. Upon return from IoCreateDevice(),
// m_DeviceObject data member will be set by casting
// pDeviceObject->DeviceExtension to CDeviceObject.
PVOID operator new(
IN size_t size,
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING DeviceName,
IN PUNICODE_STRING Win32DeviceName,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
OUT NTSTATUS& status );
// Should do nothing, since the IOManager will call IoDeleteDevice() when
// a driver is unloaded.
VOID operator delete( IN PVOID p, IN size_t size );
// Cast operator for PDEVICE_OBJECT.
operator PDEVICE_OBJECT();
// called from CDriverObject::Initialize. This call is made from the
// DriverEntry routine running at IRQL_PASSIVE_LEVEL.
// IMPORTANT:
// This routine MUST create the CInterruptObject and CDpcObject
// objects contained by value by derived CDeviceObject class to setup
// callbacks to OnInterrupt() and OnDpc(), respectively.
NTSTATUS Initialize( IN PUNICODE_STRING RegistryPath );
// Nonvirtual called by CDriverDevice::Unload() when the driver is
// unloading in order to perform cleanup.
VOID Unload( VOID );
CDriverObject* GetDriver( VOID );
// called by CInterruptObject::SynchronizeExecutionForXXX() to
// synchronize device I/O programming.
virtual BOOLEAN OnSynchronizeExecutionForRead( VOID );
virtual BOOLEAN OnSynchronizeExecutionForWrite( VOID );
// called by CInterruptObject::OnInterrupt()
virtual BOOLEAN OnInterrupt( VOID );
// called from CDpcObject::Dpc()
virtual VOID OnDpc( IN PIRP Irp, IN PVOID Context );
// overridable IO handlers
virtual VOID OnStartIo( IN PIRP Irp );
virtual VOID OnStartReadIo( IN PIRP Irp );
virtual VOID OnStartWriteIo( IN PIRP Irp );
// overridable dispatch handlers
virtual NTSTATUS OnIrpMjCleanup( IN PIRP Irp );
virtual NTSTATUS OnIrpMjClose( IN PIRP Irp );
virtual NTSTATUS OnIrpMjCreate( IN PIRP Irp );
virtual NTSTATUS OnIrpMjDeviceControl( IN PIRP Irp );
virtual NTSTATUS OnIrpMjInternalDeviceControl( IN PIRP Irp );
virtual NTSTATUS OnIrpMjFlushBuffers( IN PIRP Irp );
virtual NTSTATUS OnIrpMjQueryInformation( IN PIRP Irp );
virtual NTSTATUS OnIrpMjRead( IN PIRP Irp );
virtual NTSTATUS OnIrpMjSetInformation( IN PIRP Irp );
virtual NTSTATUS OnIrpMjShutdown( IN PIRP Irp );
virtual NTSTATUS OnIrpMjWrite( IN PIRP Irp );
virtual VOID GetDeviceDescription( OUT PDEVICE_DESCRIPTION DeviceDescription );
protected:
// called from Unload() member.
virtual VOID OnUnload( VOID );
// called by CDeviceObject::Initialize(), which is called from
// CDriverObject::Initialize() during driver initialization.
// IMPORTANT:
// A class derived from CDeviceObject MUST call
// CInterruptObject::Initialize() for its CInterruptObject member,
// if it exists.
virtual NTSTATUS OnInitialize( IN PUNICODE_STRING RegistryPath );
// called by Cancel().
virtual VOID OnCancel( IN PIRP Irp );
// calls IoStartPacket() with itself as the target device object.
VOID StartPacket( IN PIRP Irp, IN PULONG Key = NULL,
IN BOOLEAN bCancellable = TRUE );
// calls IoInitializeTimer(). A driver's IoTimer routine is called once per
// second after the driver enables the timer by calling IoStartTimer. The
// driver can disable the timer by calling StopTimer and can re-enable it
// again with StartTimer. Callers of IoInitializeTimer must be running at
// IRQL PASSIVE_LEVEL. Should be called by derived class object in its
// OnInitialize() routine.
NTSTATUS InitializeTimer( IN PIO_TIMER_ROUTINE TimerRoutine,
IN PVOID Context );
// Calls IoRaiseHardError().
VOID RaiseHardError( IN PIRP Irp, IN PVPB Vpb );
// Calls IoRequestDpc().
VOID RequestDpc( IN PIRP Irp, IN PVOID Context );
// Calls IoStartNextPacket().
VOID StartNextPacket( IN BOOLEAN bCancellable = TRUE );
// Calls IoStartNextPacketByKey().
VOID StartNextPacket( IN BOOLEAN bCancellable, ULONG Key );
// Calls IoStartTimer().
VOID StartTimer( VOID );
// Calls IoStopTimer().
VOID StopTimer( VOID );
// Calls IoUnregisterShutdownNotification().
VOID UnregisterShutdownNotification( VOID );
// Calls IoRegisterShutdownNotification().
NTSTATUS RegisterShutdownNotification( VOID );
// Calls IoCancelIrp(). IoCancelIrp() is bracketed by
// IoAquireCancelSpinLock() and IoReleaseCancelSpinLock().
BOOLEAN CancelIrp( IN PIRP Irp );
BOOLEAN PciFindDevice(
IN USHORT VendorId,
IN USHORT DeviceId,
OUT PULONG BusNumber,
OUT PPCI_SLOT_NUMBER PciSlotNumber,
OUT PPCI_COMMON_CONFIG PciCommonConfig );
// Calls HalAssignSlotResources()
NTSTATUS AssignSlotResources(
IN PUNICODE_STRING RegistryPath,
IN PUNICODE_STRING DriverClassName,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
OUT PCM_RESOURCE_LIST* AllocatedResources );
// Calls IoAssignResources().
NTSTATUS AssignResources(
IN PUNICODE_STRING RegistryPath,
IN PUNICODE_STRING DriverClassName,
IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources,
OUT PCM_RESOURCE_LIST* AllocatedResources );
NTSTATUS ReportResourceUsage(
IN PUNICODE_STRING DriverClassName,
IN PCM_RESOURCE_LIST DeviceList,
IN ULONG DeviceListSize,
IN BOOLEAN bOverRideConflict,
OUT PBOOLEAN bConflictDetected );
private:
// cancel routine.
static VOID Cancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
protected:
// members
PDEVICE_OBJECT m_DeviceObject;
CDriverObject* m_DriverObject;
UNICODE_STRING m_Win32DeviceName;
UNICODE_STRING m_RegistryPath;
};
////////////////////////////////////////////////////////////////////////////////
// inlines
//
inline CDriverObject* CDeviceObject::GetDriver( void )
{
return m_DriverObject;
}
inline NTSTATUS CDeviceObject::InitializeTimer( PIO_TIMER_ROUTINE TimerRoutine,
PVOID Context )
{
return IoInitializeTimer( *this, TimerRoutine, Context );
}
inline VOID CDeviceObject::StartTimer( void )
{
IoStartTimer( m_DeviceObject );
return;
}
inline VOID CDeviceObject::StopTimer( void )
{
IoStopTimer( m_DeviceObject );
return;
}
inline VOID CDeviceObject::RaiseHardError( PIRP Irp, PVPB Vpb )
{
IoRaiseHardError( Irp, Vpb, m_DeviceObject );
return;
}
inline VOID CDeviceObject::StartPacket(
PIRP Irp,
PULONG Key /* = NULL */,
BOOLEAN bCancellable /* = TRUE*/ )
{
ASSERT_IRQL_LE( DISPATCH_LEVEL );
IoStartPacket( m_DeviceObject, Irp, Key, bCancellable ? Cancel : NULL );
return;
}
inline VOID CDeviceObject::StartNextPacket( BOOLEAN bCancellable /* = TRUE */ )
{
IoStartNextPacket( m_DeviceObject, bCancellable );
return;
}
inline VOID CDeviceObject::StartNextPacket( BOOLEAN bCancellable, ULONG Key )
{
IoStartNextPacketByKey( m_DeviceObject, bCancellable, Key );
return;
}
inline NTSTATUS CDeviceObject::RegisterShutdownNotification( void )
{
return IoRegisterShutdownNotification( m_DeviceObject );
}
inline VOID CDeviceObject::UnregisterShutdownNotification( void )
{
IoUnregisterShutdownNotification( m_DeviceObject );
return;
}
inline VOID CDeviceObject::GetDeviceDescription( OUT PDEVICE_DESCRIPTION DeviceDescription )
{
RtlZeroMemory( DeviceDescription, sizeof DEVICE_DESCRIPTION );
return;
}
inline VOID CDeviceObject::RequestDpc( PIRP Irp, PVOID Context )
{
IoRequestDpc( m_DeviceObject, Irp, Context );
return;
}
#endif device_h
device.cpp
////////////////////////////////////////////////////////////////////////////////
// Module Name: device.cpp
// Abstract: CDeviceObject Implementation
// Environment: Windows NT 3.51/4.0, MS Visual C++ 4.x
////////////////////////////////////////////////////////////////////////////////
// disable some warnings
#pragma warning( disable: 4699 ) // Note: Creating precompiled header
#pragma warning( disable: 4201 ) // nameless struct/union
#pragma warning( disable: 4514 ) // unreferenced inline function
// has been removed
#pragma warning( disable: 4355 ) // 'this' : used in base member
// initializer list
#pragma warning( disable: 4705 ) // Statement has no effect
#pragma warning( disable: 4711 ) // function 'function' selected for
// automatic inline expansion
#include <idrivrpp.h>
#include "driver.h"
#include "device.h"
////////////////////////////////////////////////////////////////////////////////
// ctor/dtor
//
CDeviceObject::CDeviceObject( IN CDriverObject* DriverObject /* = NULL */ )
: m_DriverObject( DriverObject )
{
// initialize structures (do not initialize m_Win32Device -
// it is done in new)
m_RegistryPath.Buffer = NULL;
m_RegistryPath.Length = 0;
m_RegistryPath.MaximumLength = 0;
}
CDeviceObject::~CDeviceObject()
{
}
////////////////////////////////////////////////////////////////////////////////
// storage management
//
PVOID CDeviceObject::operator new( size_t /*size*/ )
{
return NULL;
}
PVOID CDeviceObject::operator new(
IN size_t size,
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING DeviceName,
IN PUNICODE_STRING Win32DeviceName,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
OUT NTSTATUS& status )
{
ASSERT( NULL != DriverObject );
ASSERT( NULL != DeviceName );
ASSERT( NULL != Win32DeviceName );
PDEVICE_OBJECT DeviceObject = NULL;
CDeviceObject* pThis = NULL;
// create the device, allocating space for this object
if( STATUS_SUCCESS ==
(status = IoCreateDevice(
DriverObject,
size,
DeviceName,
DeviceType,
DeviceCharacteristics,
Exclusive,
&DeviceObject )) )
{
ASSERT( NULL != DeviceObject );
pThis = (CDeviceObject*) DeviceObject->DeviceExtension;
// The device extension area will be this object's image.
// Set up the data member pointing to the DEVICE_OBJECT allocated
// by the IOManager.
ASSERT( NULL != pThis );
pThis->m_DeviceObject = DeviceObject;
// @@@ do we need to force the client to create this symbolic link?
if( STATUS_SUCCESS == (status = IoCreateSymbolicLink
( Win32DeviceName, DeviceName )) )
{
// allocate space for m_Win32DeviceName
// @@@ can we use CUnicodeString here
pThis->m_Win32DeviceName.Buffer =
PWSTR( new( PagedPool ) char[ Win32DeviceName->MaximumLength ] );
if( NULL != pThis->m_Win32DeviceName.Buffer )
{
// set maximum length to allocated length
pThis->m_Win32DeviceName.MaximumLength =
Win32DeviceName->MaximumLength;
// copy the string
RtlCopyUnicodeString( &pThis->m_Win32DeviceName,
Win32DeviceName );
}
else
{
TRACE( "CDeviceObject::operator new - Failed to allocate "
"space for m_Win32DeviceName!" );
// cleanup occurs in operator delete()
}
}
else
{
TRACE( "CDeviceObject::operator new - Failed on call to "
"IoCreateSymbolicLink!" );
// cleanup occurs in operator delete()
}
}
else
{
TRACE( "CDeviceObject::operator new - Failed on call to "
"IoCreateDevice, returning NULL!" );
}
return pThis;
}
VOID CDeviceObject::operator delete( IN PVOID p, IN size_t /*size*/ )
{
ASSERT( NULL != p );
CDeviceObject* pDevice = (CDeviceObject*) p;
// did we assign the device object member
if( NULL != pDevice->m_DeviceObject )
{
// did we allocate and assign the m_Win32DeviceNmae member
if( NULL != pDevice->m_Win32DeviceName.Buffer )
{
ASSERT( 0 != pDevice->m_Win32DeviceName.Length );
// delete the symbolic link
IoDeleteSymbolicLink( &pDevice->m_Win32DeviceName );
delete pDevice->m_Win32DeviceName.Buffer;
// reset members
pDevice->m_Win32DeviceName.Buffer = NULL;
pDevice->m_Win32DeviceName.Length = 0;
pDevice->m_Win32DeviceName.MaximumLength = 0;
}
// Free up the DEVICE_OBJECT
IoDeleteDevice( pDevice->m_DeviceObject );
}
return;
}
CDeviceObject::operator PDEVICE_OBJECT()
{
return m_DeviceObject;
}
NTSTATUS CDeviceObject::Initialize( PUNICODE_STRING RegistryPath )
{
// allocate space for m_RegistryPath
// @@@ can we use CUnicodeString here?
// @@@ do we really need to allocate MaximumLength...
// why not Length + 1, also
// @@@ aren't MaximumLength and Length in bytes not chars
// (i.e. new(...) char[ size ])
m_RegistryPath.Buffer =
new( PagedPool ) WCHAR[ RegistryPath->MaximumLength ];
if( NULL == m_RegistryPath.Buffer )
{
TRACE( "CDeviceObject::Initialize - Failed to allocate buffer "
"for RegistryPath!" );
return STATUS_NO_MEMORY;
}
m_RegistryPath.MaximumLength = RegistryPath->MaximumLength;
RtlCopyUnicodeString( &m_RegistryPath, RegistryPath );
return OnInitialize( RegistryPath );
}
NTSTATUS CDeviceObject::OnInitialize( PUNICODE_STRING /* RegistryPath */ )
{
return STATUS_SUCCESS;
}
// This object should not be deleted by CDriverObject until after this call
VOID CDeviceObject::Unload()
{
OnUnload();
// did we allocate and assign the m_RegistryPath member
if( NULL != m_RegistryPath.Buffer )
{
// free up the memory for the registry path buffer.
delete m_RegistryPath.Buffer;
// reset members
m_RegistryPath.Buffer = NULL;
m_RegistryPath.Length = 0;
m_RegistryPath.MaximumLength = 0;
}
return;
}
VOID CDeviceObject::OnUnload( void )
{
return;
}
BOOLEAN CDeviceObject::OnSynchronizeExecutionForRead( void )
{
return TRUE;
}
BOOLEAN CDeviceObject::OnSynchronizeExecutionForWrite( void )
{
return TRUE;
}
VOID CDeviceObject::OnStartIo( IN PIRP Irp )
{
ASSERT_IRQL_EQ( DISPATCH_LEVEL );
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
}
VOID CDeviceObject::OnStartReadIo( IN PIRP Irp )
{
ASSERT_IRQL_EQ( DISPATCH_LEVEL );
PIO_STACK_LOCATION CurrentStackLocation =
IoGetCurrentIrpStackLocation( Irp );
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = CurrentStackLocation->Parameters.Read.Length;
// @@@ do we want to set Information to 0 and call IoCompleteRequest
}
VOID CDeviceObject::OnStartWriteIo( IN PIRP Irp )
{
ASSERT_IRQL_EQ( DISPATCH_LEVEL );
PIO_STACK_LOCATION CurrentStackLocation = IoGetCurrentIrpStackLocation( Irp );
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = CurrentStackLocation->Parameters.Write.Length;
// @@@ do we want to set Information to 0 and call IoCompleteRequest
}
NTSTATUS CDeviceObject::OnIrpMjCleanup( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjClose( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjCreate( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjDeviceControl( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjInternalDeviceControl( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjFlushBuffers( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjQueryInformation( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjRead( IN PIRP Irp )
{
// Mark the request pending
IoMarkIrpPending( Irp );
StartPacket( Irp, NULL );
return STATUS_PENDING;
}
NTSTATUS CDeviceObject::OnIrpMjSetInformation( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjShutdown( IN PIRP Irp )
{
// Complete the request
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS CDeviceObject::OnIrpMjWrite( IN PIRP Irp )
{
// Mark the request pending
IoMarkIrpPending( Irp );
StartPacket( Irp, NULL );
return STATUS_PENDING;
}
BOOLEAN CDeviceObject::OnInterrupt( VOID )
{
return FALSE;
}
VOID CDeviceObject::OnDpc( IN PIRP /* Irp */, IN PVOID /* Context */ )
{
return;
}
BOOLEAN CDeviceObject::CancelIrp( IN PIRP Irp )
{
KIRQL Irql;
IoAcquireCancelSpinLock( &Irql );
BOOLEAN bResult = IoCancelIrp( Irp );
IoReleaseCancelSpinLock( Irql );
return bResult;
}
VOID CDeviceObject::Cancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
CDeviceObject* pDevice = (CDeviceObject*) DeviceObject->DeviceExtension;
ASSERT( NULL != pDevice );
pDevice->OnCancel( Irp );
return;
}
VOID CDeviceObject::OnCancel( IN PIRP Irp )
{
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return;
}
BOOLEAN CDeviceObject::PciFindDevice(
IN USHORT VendorId,
IN USHORT DeviceId,
OUT PULONG BusNumber,
OUT PPCI_SLOT_NUMBER PciSlotNumber,
OUT PPCI_COMMON_CONFIG PciCommonConfig )
{
// initialize parameters
*BusNumber = 0;
PciSlotNumber->u.AsULONG = 0;
BOOLEAN bFound = FALSE;
while( FALSE == bFound )
{
// get bus data
ULONG ulReturn = HalGetBusData(
PCIConfiguration,
*BusNumber,
PciSlotNumber->u.AsULONG,
PciCommonConfig,
sizeof PCI_COMMON_CONFIG );
// pci bus does not exist (assume that these start at
// 0 and go up with no gaps)
if( 0 == ulReturn )
{
break;
}
else if( sizeof PCI_COMMON_CONFIG == ulReturn )
{
// found a card... see if it is ours
if( VendorId == PciCommonConfig->VendorID &&
DeviceId == PciCommonConfig->DeviceID )
{
// found our card... get its information
bFound = TRUE;
break;
}
}
//@@@ it appears that only the DeviceNumber of the SlotNumber is used...
//@@@ maybe if you have two or more of the same card it is used
//@@@ if it is never used or if it should be stepped first we could
//@@@ simplify the code
// step our BusNumber and/or SlotNumber
if( 31 == PciSlotNumber->u.bits.DeviceNumber &&
7 == PciSlotNumber->u.bits.FunctionNumber )
{
// try the next bus
*BusNumber++;
PciSlotNumber->u.AsULONG = 0;
}
else if( 7 == PciSlotNumber->u.bits.FunctionNumber )
{
// try the next device number
PciSlotNumber->u.bits.DeviceNumber++;
PciSlotNumber->u.bits.FunctionNumber = 0;
}
else
{
// try the next function number
PciSlotNumber->u.bits.FunctionNumber++;
}
}
#ifdef _DEBUG
//@@@ did we find a device, validate its size
if( bFound )
{
ASSERT( sizeof( PCI_COMMON_CONFIG ) ==
HalSetBusData(
PCIConfiguration,
*BusNumber,
PciSlotNumber->u.AsULONG,
PciCommonConfig,
sizeof PCI_COMMON_CONFIG ) );
}
#endif
return bFound;
}
NTSTATUS CDeviceObject::AssignSlotResources(
IN PUNICODE_STRING RegistryPath,
IN PUNICODE_STRING DriverClassName,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
OUT PCM_RESOURCE_LIST* AllocatedResources )
{
// If this CDeviceObject is an exclusive device object for this driver,
// NULL should be passed as the DeviceObject argument to
// HalAssignSlotResources(), so that device-specific registry
// management is not required. If this is not an exclusive
// CDeviceObject object, then it is necessary to create and update the
// registry path passed to the DriverEntry routine in order
// to maintain device-specific resource data for each set of
// resources claimed under the driver-
// specific path contained in the DriverEntry's RegistryPath.
return HalAssignSlotResources(
RegistryPath,
DriverClassName,
(PDRIVER_OBJECT) *m_DriverObject,
m_DeviceObject,
BusType,
BusNumber,
SlotNumber,
AllocatedResources );
}
NTSTATUS CDeviceObject::AssignResources(
IN PUNICODE_STRING RegistryPath,
IN PUNICODE_STRING DriverClassName,
IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources,
OUT PCM_RESOURCE_LIST* AllocatedResources )
{
return IoAssignResources(
RegistryPath,
DriverClassName,
(PDRIVER_OBJECT) *m_DriverObject,
m_DeviceObject,
RequestedResources,
AllocatedResources );
}
NTSTATUS CDeviceObject::ReportResourceUsage(
IN PUNICODE_STRING DriverClassName,
IN PCM_RESOURCE_LIST DeviceList,
IN ULONG DeviceListSize,
IN BOOLEAN bOverRideConflict,
OUT PBOOLEAN bConflictDetected )
{
return IoReportResourceUsage(
DriverClassName,
(PDRIVER_OBJECT) *m_DriverObject,
NULL,
0,
m_DeviceObject,
DeviceList,
DeviceListSize,
bOverRideConflict,
bConflictDetected );
}
Figure 4 Commonly Used Framework Classes
C++ Class | Cardinality | Associated DDK Data Structures |
CDpcObject | 0-1 | None-simply registers DPC routine and gets CDeviceObject pointer from DeviceExtension of PDEVICE_OBJECT argument passed into DPC routine and invokes its OnDpc member function |
CSpinLock | 0..n | KSPIN_LOCK, KIRQL |
CRegistryKey | 0..n | HANDLE, PUNICODE_STRING |
CUnicodeString | 0..n | UNICODE_STRING |
CInterruptObject | 0-1 | KINTERRUPT |
CAdapterObject | 0..n | ADAPTER_OBJECT |
CCustomDpcObject | 0..n | KDPC |
CDeviceQueue | 0..n | KDEVICE_QUEUE |
Figure 5 Miscellaneous Framework Objects
C++ Class | Cardinality | Associated DDK Data Structures |
CFile | 0..n | HANDLE |
CTimeOut | 0..n | None-uses KeQuerySystemTime to implement a passive timer object |
CObject | 0(abstract) | HANDLE, PVOID pointing to system object body returned by ObReferenceObjectByHandle |
CNamedObject | 0(abstract) | UNICODE_STRING for object name. CNamedObject is a descendant of CObject CRegistryKey is an example of an instantiable CNamedObject class type |
CFileObject | 0(abstract) | None of its own-CFileObject is a descendant of CNamedObject and is really just a more general and less complete implementation of CFile |
CLargeInteger | 0..n | LARGE_INTEGER |
CThread | 0..n | KTHREAD |
CDList | 0..n | PLIST_ENTRY for doubly linked list |
CUpperDeviceObject | 0..n | Two PDEVICE_OBJECTS: one for the object CUpperDeviceObject is attached to, and one that it owns by virtue of the fact that CUpperDeviceObject is derived from CDeviceObject |
Figure 6 CUpperDeviceObject
////////////////////////////////////////////////////////////////////////////////
// Module Name: uldevice.h
// Abstract: CUpperDeviceObject Interface
// Environment: Windows NT 3.51/4.0, MS Visual C++ 4.1
// Notes:
// CDeviceObject which attaches to a lower-level device. The destructor
// must call IoDetachDevice() in order to decrement the reference count
// for the target device object.
////////////////////////////////////////////////////////////////////////////////
#ifndef uldevice_h
#define uldevice_h
#include "device.h"
class CUpperDeviceObject : public CDeviceObject
{
public:
CUpperDeviceObject( IN CDriverObject* DriverObject );
virtual ~CUpperDeviceObject();
protected:
// attachDevice attaches the caller's device object to a named target
// device object, so that I/O requests bound for the target device are
// routed first to the caller.
NTSTATUS AttachDeviceObject( IN PUNICODE_STRING TargetDeviceName );
NTSTATUS AttachDeviceObject( IN PDEVICE_OBJECT TargetDeviceObject );
// detachDeviceObject releases an attachment between the caller's device
// object and a lower driver's device object.
VOID DetachDeviceObject( VOID );
// calls IoBuildDeviceIoControlRequest() passing m_AttachedDevice as
// target DEVICE_OBJECT.
PIRP BuildDeviceIoControlRequest(
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
IN BOOLEAN bInternalDeviceIoControl,
IN PKEVENT Event,
OUT PIO_STATUS_BLOCK StatusBlock );
// IoCallDriver returns the NTSTATUS value that a lower driver set in the
// IO status block for the given request or STATUS_PENDING if the request
// was queued for additional processing.
// Comments:
// IoCallDriver assigns the DeviceObject input parameter to the device
// object field of the IRP stack location for the next lower driver.
// An IRP passed in a call to IoCallDriver becomes inaccessible to the
// higher-level driver, unless the higher-level driver has set up its
// IoCompletion routine for the IRP with IoSetCompletionRoutine. If it
// does, the IRP input to the driver-supplied IoCompletion routine has
// its I/O status block set by the lower driver(s) and all lower-level
// driver(s)' I/O stack locations filled with zeros.
NTSTATUS CallDriver( IN PIRP Irp );
// calls IoSetHardErrorOrVerifyDevice().
// Passes m_AttachedDeviceObject as target.
VOID SetHardErrorOrVerifyDevice( IN PIRP Irp );
CCHAR GetAttachedDeviceStackSize( VOID );
private:
// lower-level driver device object that is obtained in call to Attach().
PDEVICE_OBJECT m_AttachedDeviceObject;
};
////////////////////////////////////////////////////////////////////////////////
// inlines
//
inline NTSTATUS CUpperDeviceObject::CallDriver( IN PIRP Irp )
{
ASSERT_IRQL_LE( DISPATCH_LEVEL );
return IoCallDriver( m_AttachedDeviceObject, Irp );
}
inline VOID CUpperDeviceObject::SetHardErrorOrVerifyDevice( IN PIRP Irp )
{
ASSERT_IRQL_LE( DISPATCH_LEVEL );
IoSetHardErrorOrVerifyDevice( Irp, m_AttachedDeviceObject );
return;
}
inline CCHAR CUpperDeviceObject::GetAttachedDeviceStackSize( void )
{
return (NULL != m_AttachedDeviceObject) ?
m_AttachedDeviceObject->StackSize : 0;
}
#endif uldevice_h
Figure 7 Header Files
#include "dpc.h" // CDpcOjbect
#include "mem.h" // globlal new and delete
#include "intrpt.h" // CInterruptObject
#include "uldevice.h" // CUpperDeviceObject
#include "driver.h" // CDriverObject
#include "device.h" // CDeviceObject
#include "adapter.h" // CAdapterObject
#include "bertdev.h" // CBertDevice
#include "dlist.h" // CDListEntry, CDList
#include "unicode.h" // CUnicodeString
#include "custdpc.h" // CCustomDpcObject
#include "linteger.h" // CLargeInteger
#include "file.h" // CFile
#include "timeout.h" // CTimeOut
#include "object.h" // CObject
#include "thread.h" // CThread
#include "devqueue.h" // CDeviceQueueEntry, CDeviceQueue
#include "spinlock.h" // CSpinLock
#include "regkey.h" // CRegistryKey
Figure 8 sampdev.cpp
// ******************************************************
// * Module Name: sampdev.cpp
// * Abstract: SampleDevice Device object class implementation
// * Environment:
// *
// * Notes:
// * <Assumptions>
// * <Other of interest>
// *
// ******************************************************
#pragma warning (disable : 4201 ) // nonstandard extension used :
// nameless struct/union
#pragma warning (disable : 4514 ) // unreferenced inline function has
// been removed
#include "sampdev.h"
#include "debug.h"
///////////////////////////////////////////////////////////////////////////////
// ctor/dtor
///////////////////////////////////////////////////////////////////////////////
CSampleDevice::CSampleDevice():
CPciDevice( NULL ),
m_InputDmaSize( 0 ),
m_OutputDmaSize( 0 )
{
}
CSampleDevice::CSampleDevice( IN CDriverObject *DriverObject ) :
CPciDevice( DriverObject ),
m_InputDmaSize( 256 ),
m_OutputDmaSize( 256 )
{
}
CSampleDevice::~CSampleDevice()
{
}
NTSTATUS CSampleDevice::OnInitialize( IN PUNICODE_STRING RegistryPath )
{
NTSTATUS status = CPciDevice::OnInitialize( RegistryPath );
if( status != STATUS_SUCCESS )
return status;
// Initialize the adapter objects
// Each gets a page-size, cache-enabled common buffer
DEVICE_DESCRIPTION DeviceDescription;
GetDeviceDescription( &DeviceDescription );
TRACE("OnInitialize");
if( STATUS_SUCCESS !=
(status = m_ReadAdapter.Initialize(
RegistryPath, // RegistryPath
m_InputDmaSize, // Size of common buffer
0x1000, // Alignment
FALSE, // Cache enabled common buffer
&DeviceDescription )) ) // Device description
{
TRACE("OnInitialize Failed to initialize read adapter!");
return status;
}
ASSERT( 0L == m_ReadAdapter.GetCommonBufferLogicalAddress().HighPart );
if( STATUS_SUCCESS !=
(status = m_WriteAdapter.Initialize(
RegistryPath, // RegistryPath
m_OutputDmaSize, // Size of common buffer
0x1000, // Alignment
FALSE, // Cache enabled common buffer
&DeviceDescription )) ) // Device description
{
TRACE("OnInitialize Failed to initialize write adapter!");
return status;
}
ASSERT( 0L == m_WriteAdapter.GetCommonBufferLogicalAddress().HighPart );
TRACE("Read Common Buffer Phys = %X, Virt = %X",
m_ReadAdapter.GetCommonBufferLogicalAddress().LowPart,
m_ReadAdapter.GetCommonBuffer() );
TRACE("Write Common Buffer Phys = %X, Virt = %X",
m_WriteAdapter.GetCommonBufferLogicalAddress().LowPart,
m_WriteAdapter.GetCommonBuffer() );
// Setup the custom DPCs
m_DpcForWriteIsr.InitializeDpcRequest( DpcForWriteIsr, this );
m_DpcForReadIsr.InitializeDpcRequest( DpcForReadIsr, this );
return STATUS_SUCCESS;
}
NTSTATUS CSampleDevice::OnIrpMjDeviceControl( IN PIRP Irp )
{
TRACE("OnIrpMjDeviceControl");
PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( Irp );
NTSTATUS Status = STATUS_INVALID_PARAMETER;
ULONG Information = 0;
CompleteRequest( Irp, Status, Information );
return Status;
}
NTSTATUS CSampleDevice::OnIrpMjRead( IN PIRP Irp )
{
TRACE("OnIrpMjRead");
NTSTATUS Status = STATUS_PENDING;
// If we're not busy, start the IRP
if( !m_ReadQueue.InsertData( Irp ) )
{
// Only one outstanding write request supported
if( NULL != m_ReadRequest.Irp )
{
TRACE("OnIrpMjRead - Invalid Device Request!!!");
Status = STATUS_INVALID_DEVICE_REQUEST;
}
else
{
PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( Irp );
// Cache the Irp
m_ReadRequest.SpinLock.Acquire();
m_ReadRequest.Irp = Irp;
m_ReadRequest.Buffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
m_ReadRequest.Length = request->Parameters.Read.Length;
m_ReadRequest.SpinLock.Release();
// Synchronize device manipulation for reading from device
m_Interrupt.SynchronizeExecutionForRead();
}
}
if( STATUS_PENDING != Status )
CompleteRequest( Irp, Status );
else
IoMarkIrpPending( Irp );
return Status;
}
NTSTATUS CSampleDevice::OnIrpMjWrite( IN PIRP Irp )
{
TRACE("OnIrpMjWrite");
NTSTATUS Status = STATUS_PENDING;
// If we're not busy, start the IRP
if( !m_WriteQueue.InsertData( Irp ) )
{
// Only one outstanding write request supported
if( NULL != m_WriteRequest.Irp )
{
TRACE("OnIrpMjWrite - Invalid Device Request!!!");
Status = STATUS_INVALID_DEVICE_REQUEST;
}
else
{
PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( Irp );
// Cache the Irp
m_WriteRequest.SpinLock.Acquire();
m_WriteRequest.Irp = Irp;
m_WriteRequest.Buffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
m_WriteRequest.Length = request->Parameters.Write.Length;
m_WriteRequest.SpinLock.Release();
// Synchronize device manipulation for writing to device
m_Interrupt.SynchronizeExecutionForWrite();
}
}
if( STATUS_PENDING != Status )
CompleteRequest( Irp, Status );
else
IoMarkIrpPending( Irp );
return Status;
}
VOID CSampleDevice::OnUnload()
{
TRACE("OnUnload");
// Don't EVER forget to call CAdapterObject::Unload()
m_ReadAdapter.Unload();
m_WriteAdapter.Unload();
DisconnectInterrupt();
CPciDevice::OnUnload();
}
BOOLEAN CSampleDevice::OnInterrupt()
{
TRACE("OnInterrupt");
NTSTATUS Status = STATUS_SUCCESS;
// if( !OurInterrupt )
// return FALSE;
// Has a write happened?
// if( IsWriteInterrupt )
// {
// Queue DPC for write ISR
if( !m_DpcForWriteIsr.InsertQueue( (PVOID)Status ) )
{
TRACE( "OnInterrupt - Failed to queue Write DPC!" );
}
// }
// Has a read happened?
// if( IsReadInterrupt )
// {
// Queue DPC for read ISR
if( !m_DpcForReadIsr.InsertQueue( (PVOID)Status ) )
{
TRACE( "OnInterrupt - Failed to queue Read DPC!" );
}
// }
TRACE("Leaving OnInterrupt");
return TRUE;
}
VOID CSampleDevice::GetDeviceDescription( OUT PDEVICE_DESCRIPTION DeviceDescription )
{
DeviceDescription->Version = DEVICE_DESCRIPTION_VERSION;
DeviceDescription->Master = TRUE, // Busmaster Device
DeviceDescription->ScatterGather = FALSE, // No scatter/
// gather
DeviceDescription->DemandMode = FALSE, // Don't use system
// DMA controller
DeviceDescription->AutoInitialize = FALSE, // Don't use system
// DMA controller
DeviceDescription->Dma32BitAddresses = TRUE, // 32 bit addresses
DeviceDescription->IgnoreCount = FALSE, // IgnoreCount
DeviceDescription->Reserved1 = FALSE, // Reserved1
DeviceDescription->Reserved2 = FALSE, // Reserved2
DeviceDescription->BusNumber = m_ulBusNumber, // BusNumber
DeviceDescription->DmaChannel = 0, // No system DMA
// channel involved
DeviceDescription->InterfaceType = PCIBus, // BusType
DeviceDescription->DmaWidth = Width32Bits, // 32-bit wide Dma
DeviceDescription->DmaSpeed = MaximumDmaSpeed,
DeviceDescription->MaximumLength = 8196, // MaximumLength
DeviceDescription->DmaPort = 0; // DmaPort
}
BOOLEAN CSampleDevice::OnSynchronizeExecutionForRead()
{
// This is where you setup the device for a read from the device
return TRUE;
}
BOOLEAN CSampleDevice::OnSynchronizeExecutionForWrite()
{
// This is where you setup the device for a write to the device
return TRUE;
}
VOID CSampleDevice::OnDpcForWriteIsr( IN NTSTATUS Status )
{
TRACE("Entering OnDpcForWriteIsr");
ASSERT( NULL != m_WriteRequest.Irp );
// Get the next IRP in the queue
PIRP NextIrp = PIRP(m_WriteQueue.RemoveData());
PIRP CurrentIrp;
ULONG Information;
// Update the IO Request
m_WriteRequest.SpinLock.AcquireAtDpcLevel();
CurrentIrp = m_WriteRequest.Irp;
Information = STATUS_SUCCESS == Status ? m_WriteRequest.Length : 0;
m_WriteRequest.Irp = NextIrp;
m_WriteRequest.SpinLock.ReleaseFromDpcLevel();
// Complete the current WriteIrp
CompleteRequest(CurrentIrp, Status, Information );
if( NextIrp )
{
PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( NextIrp );
m_WriteRequest.Buffer = MmGetSystemAddressForMdl( NextIrp->MdlAddress );
m_WriteRequest.Length = request->Parameters.Write.Length;
// This is where you'd perform the setup for the next write
// to the device
}
TRACE("Leaving OnDpcForWriteIsr");
}
VOID CSampleDevice::OnDpcForReadIsr( IN NTSTATUS Status )
{
TRACE("Entering OnDpcForReadIsr");
ASSERT( NULL != m_ReadRequest.Irp );
ASSERT( NULL != m_ReadRequest.Buffer );
if( STATUS_SUCCESS == Status )
{
// Copy the data from the device read buffer to the user
// buffer.
RtlMoveMemory( m_ReadRequest.Buffer,
m_ReadAdapter.GetCommonBuffer(),
m_ReadRequest.Length );
}
// Get the next IRP in the queue
PIRP NextIrp = PIRP(m_ReadQueue.RemoveData());
PIRP CurrentIrp;
ULONG Information;
// Update the IO Request
m_ReadRequest.SpinLock.AcquireAtDpcLevel();
CurrentIrp = m_ReadRequest.Irp;
Information = STATUS_SUCCESS == Status ? m_ReadRequest.Length : 0;
m_ReadRequest.Irp = NextIrp;
m_ReadRequest.SpinLock.ReleaseFromDpcLevel();
// Complete the request
CompleteRequest(CurrentIrp, Status, Information );
if( NextIrp )
{
PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( NextIrp );
m_ReadRequest.Buffer = MmGetSystemAddressForMdl( NextIrp->MdlAddress );
m_ReadRequest.Length = request->Parameters.Read.Length;
// This is where you'd perform the setup for
// the next read from the device
}
TRACE("Leaving OnDpcForReadIsr");
}
BOOLEAN CSampleDevice::OnErrorInterrupt()
{
TRACE( "OnErrorInterrupt Entered" );
// Queue up DPCs to complete the outstanding Irps
if( NULL != m_WriteRequest.Irp )
m_DpcForWriteIsr.InsertQueue( (PVOID)STATUS_DEVICE_DATA_ERROR );
if( NULL != m_ReadRequest.Irp )
m_DpcForReadIsr.InsertQueue( (PVOID)STATUS_DEVICE_DATA_ERROR );
return TRUE;
}
///////////////////////////////////////
// Class statics
///////////////////////////////////////
/* static */
VOID CSampleDevice::DpcForWriteIsr(
IN PKDPC /* Dpc */,
IN PVOID DeferredContext,
IN PVOID Status,
IN PVOID /* SystemArgument2 */ )
{
((CSampleDevice*)DeferredContext)->OnDpcForWriteIsr( (NTSTATUS)Status );
}
/* static */
VOID CSampleDevice::DpcForReadIsr(
IN PKDPC /* Dpc */,
IN PVOID DeferredContext,
IN PVOID Status,
IN PVOID /* SystemArgument2 */)
{
((CSampleDevice*)DeferredContext)->OnDpcForReadIsr( (NTSTATUS)Status );
}
///////////////////////////////////////
// Utility functions
///////////////////////////////////////
VOID CompleteRequest(
IN PIRP Irp,
IN NTSTATUS Status,
IN ULONG Information /* = 0 */,
IN CCHAR PriorityBoost /* = IO_NO_INCREMENT */ )
{
TRACE( "CompletRequest Entered" );
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = Information;
IoCompleteRequest( Irp, PriorityBoost );
}
sampdev.h
// ******************************************************
// * Module Name: sampdev.h
// * Abstract: CSampleDevice Device object class declaration
// * Environment:
// *
// * Notes:
// * <Assumptions>
// * <Other of interest>
// *
// ******************************************************
#ifndef SAMPDEV_H
#define SAMPDEV_H
#include <idrivrpp.h>
#include "pcidev.h"
// ****************************
// * Predeclarations
// ****************************
// ****************************
// * Types
// ****************************
struct CIoRequest
{
// ctor/dtor
CIoRequest();
PIRP Irp;
PVOID Buffer;
ULONG Length;
CSpinLock SpinLock;
};
//*****************************************************************
//* class CIoRequest Inlines
//*****************************************************************
inline CIoRequest::CIoRequest():
Irp( NULL ),
Buffer( NULL ),
Length( Length )
{
}
#define PIO_REQUEST CIoRequest*
// ****************************
// * Macros
// ****************************
#define min( a, b ) (ULONG)a < (ULONG)b ? a : b
// ****************************
// * Class: CSampleDevice
// ****************************
class CSampleDevice : public CPciDevice
{
public:
CSampleDevice( IN CDriverObject *DriverObject );
virtual ~CSampleDevice();
protected:
CSampleDevice();
CSampleDevice( IN const CSampleDevice &right );
// Virtual from CDeviceObject
virtual NTSTATUS OnInitialize( IN PUNICODE_STRING RegistryPath );
// Virtual from CDeviceObject
virtual NTSTATUS OnIrpMjDeviceControl( IN PIRP Irp );
virtual NTSTATUS OnIrpMjRead( IN PIRP Irp );
virtual NTSTATUS OnIrpMjWrite( IN PIRP Irp );
virtual VOID OnUnload(VOID);
virtual BOOLEAN OnInterrupt(VOID);
virtual VOID GetDeviceDescription
(OUT PDEVICE_DESCRIPTION DeviceDescription );
// called by CInterruptObject::SynchronizeExecutionForXXX() to
// synchronize device I/O programming.
virtual BOOLEAN OnSynchronizeExecutionForRead( VOID );
virtual BOOLEAN OnSynchronizeExecutionForWrite( VOID );
#ifdef NOTDEF
virtual NTSTATUS OnIrpMjCreate( IN PIRP Irp );
virtual NTSTATUS OnIrpMjClose( IN PIRP Irp );
virtual NTSTATUS OnIrpMjCleanup( IN PIRP Irp );
virtual NTSTATUS OnIrpMjFlushBuffers( IN PIRP Irp );
virtual NTSTATUS OnIrpMjInternalDeviceControl( IN PIRP Irp );
virtual NTSTATUS OnIrpMjQueryInformation( IN PIRP Irp );
virtual NTSTATUS OnIrpMjSetInformation( IN PIRP Irp );
virtual VOID OnCancel( IN PIRP Irp );
#endif
/ The Dpc handlers
VOID OnDpcForWriteIsr( IN NTSTATUS Status );
VOID OnDpcForReadIsr( IN NTSTATUS Status );
// The error interrupt handler
BOOLEAN OnErrorInterrupt(VOID);
////////////////////////////
// The custom DPC routines
////////////////////////////
static VOID DpcForWriteIsr(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID Status,
IN PVOID SystemArgument2 );
static VOID DpcForReadIsr(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID Status,
IN PVOID SystemArgument2 );
private:
CIoRequest m_ReadRequest;
CIoRequest m_WriteRequest;
CAdapterObject m_ReadAdapter;
CAdapterObject m_WriteAdapter;
CDeviceQueue m_ReadQueue;
CDeviceQueue m_WriteQueue;
ULONG m_InputDmaSize;
ULONG m_OutputDmaSize;
// The custom DPCs for setting up for device reads and writes
CCustomDpcObject m_DpcForWriteIsr;
CCustomDpcObject m_DpcForReadIsr;
};
//*****************************************************************
//* C routine prototypes
//*****************************************************************
VOID CompleteRequest(
IN PIRP Irp,
IN NTSTATUS Status,
IN ULONG Information = 0,
IN CCHAR PriorityBoost = IO_NO_INCREMENT);
#endif
sampdvr.h
// ******************************************************
// * Module Name: sampdrv.h
// * Abstract: CSample DriverObject class declaration
// * Environment:
// *
// * Notes:
// * <Assumptions>
// * <Other of interest>
// *
// ******************************************************
#ifndef SAMPDRV_H
#define SAMPDRV_H
#include <idrivrpp.h>
class SampleDriver : public CDriverObject
{
public:
SampleDriver();
virtual ~SampleDriver();
protected:
SampleDriver(const SampleDriver &right);
// Virtual from CDriverObject
virtual NTSTATUS OnInitialize(PUNICODE_STRING RegistryPath);
// Virtual from CDriverObject
virtual VOID OnUnload();
// Pure virtual from CDriverObject to create a derived
// CDeviceObject object.
virtual NTSTATUS CreateDevices(VOID);
};
#endif
sampdvr.cpp
// ******************************************************
// * Module Name: sampdrv.cpp
// * Abstract: CSampleDriver class implementation
// * Environment:
// *
// * Notes:
// * <Assumptions>
// * <Other of interest>
// *
// ******************************************************
#pragma warning (disable : 4201 )
#pragma warning (disable : 4514 )
#pragma warning (disable : 4699 )
#include "sampdev.h"
// driver
#include "sampdrv.h"
#include "debug.h"
#define DEVICE_NAME L"\\Device\\SAMPLE"
#define WIN32DEVICE_NAME L"\\DosDevices\\SAMPLE"
#define DRIVER_CLASSNAME L"AudioAccelerator"
SampleDriver g_Driver;
#ifdef _DEBUG
char szDebugName[] = "SampleDevice";
#endif
// Class SampleDriver
SampleDriver::SampleDriver()
: CDriverObject( DRIVER_CLASSNAME )
{
}
SampleDriver::~SampleDriver()
{
}
NTSTATUS SampleDriver::OnInitialize(PUNICODE_STRING RegistryPath)
{
TRACE("SampleDriver::OnInitialize");
return CDriverObject::OnInitialize( RegistryPath );
}
VOID SampleDriver::OnUnload()
{
TRACE("SampleDriver::OnUnLoad");
CDriverObject::OnUnload();
}
NTSTATUS SampleDriver::CreateDevices()
{
UNICODE_STRING DeviceName,
Win32DeviceName;
NTSTATUS status;
TRACE("SampleDriver::CreateDevice entered");
RtlInitUnicodeString( &DeviceName, DEVICE_NAME );
RtlInitUnicodeString( &Win32DeviceName, WIN32DEVICE_NAME );
// Call new for each CDeviceObject that needs to be instantiated.
new( *this,
&DeviceName,
&Win32DeviceName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
status ) CSampleDevice(this);
return status;
}