Device Drivers in Win2k

Device Drivers in Win2k

Device Drivers in Win2K

Windows NT software executes in one of two modes.

User mode

(restricted to authorized activities only, no I/O or memory r/w)

Kernel mode

(able to execute any instruction the processor is capable of)

Schematically,

In response to a user-mode API call, the I/O Manager sends an I/O Request Packet (IRP) to the device driver entry point. As an example, suppose a user program executed the API ReadFile function. The system calls NtReadFile in the system DLL NTDLL.DLL, in response to which the I/O Manager sends an IRP with major function code IRP_MJ_READ (a constant defined in the DDK header file). When the device driver handles the IRP_MJ_READ function code, it makes a call to the HAL, perhaps READ_PORT_UCHAR to read a single byte from an I/O port. When done, the driver completes the IRP by calling a particular kernel-mode service routine.

Types of drivers

Kernel-mode Drivers

File System Drivers Implement standard PC file system on hard disks or over network connections

Legacy Drivers (NT-style) are kernel-mode drivers that directly control a hardware device without help from other drivers

PnP Drivers understand Plug and Play protocols

WDM Drivers also understand Power Management

Class Drivers manage well-defined classes of devices
Mini-Drivers supply vendor-specific help to a class driver

Video Drivers are kernel-mode drivers for displays and printers

The I/O Manager uses driver object and device object data structures to represent each device driver. From the ntddk.h file:

//

// Device Object structure definition – stores all needed state information for a particular piece of real or virtual hardware

//

typedef struct _DEVICE_OBJECT {

CSHORT Type;

USHORT Size;

LONG ReferenceCount;

struct _DRIVER_OBJECT *DriverObject;

struct _DEVICE_OBJECT *NextDevice;

struct _DEVICE_OBJECT *AttachedDevice;

struct _IRP *CurrentIrp;

PIO_TIMER Timer;

ULONG Flags; // See above: DO_...

ULONG Characteristics; // See ntioapi: FILE_...

PVPB Vpb;

PVOID DeviceExtension;

DEVICE_TYPE DeviceType;

CCHAR StackSize;

union {

LIST_ENTRY ListEntry;

WAIT_CONTEXT_BLOCK Wcb;

} Queue;

ULONG AlignmentRequirement;

KDEVICE_QUEUE DeviceQueue;

KDPC Dpc;

//

// The following field is for exclusive use by the filesystem to keep

// track of the number of Fsp threads currently using the device

//

ULONG ActiveThreadCount;

PSECURITY_DESCRIPTOR SecurityDescriptor;

KEVENT DeviceLock;

USHORT SectorSize;

USHORT Spare1;

struct _DEVOBJ_EXTENSION *DeviceObjectExtension;

PVOID Reserved;

} DEVICE_OBJECT;

// Driver Object structure definition – provides entry points for operating system

typedef struct _DRIVER_OBJECT {

CSHORT Type;

CSHORT Size;

//

// The following links all of the devices created by a single driver

// together on a list, and the Flags word provides an extensible flag

// location for driver objects.

//

PDEVICE_OBJECT DeviceObject;

ULONG Flags;

//

// The following section describes where the driver is loaded. The count

// field is used to count the number of times the driver has had its

// registered reinitialization routine invoked.

//

PVOID DriverStart;

ULONG DriverSize;

PVOID DriverSection;

PDRIVER_EXTENSION DriverExtension;

//

// The driver name field is used by the error log thread

// determine the name of the driver that an I/O request is/was bound.

//

UNICODE_STRING DriverName;

//

// The following section is for registry support. Thise is a pointer

// to the path to the hardware information in the registry

//

PUNICODE_STRING HardwareDatabase;

//

// The following section contains the optional pointer to an array of

// alternate entry points to a driver for "fast I/O" support. Fast I/O

// is performed by invoking the driver routine directly with separate

// parameters, rather than using the standard IRP call mechanism. Note

// that these functions may only be used for synchronous I/O, and when

// the file is cached.

//

PFAST_IO_DISPATCH FastIoDispatch;

//

// The following section describes the entry points to this particular

// driver. Note that the major function dispatch table must be the last

// field in the object so that it remains extensible.

//

PDRIVER_INITIALIZE DriverInit;

PDRIVER_STARTIO DriverStartIo;

PDRIVER_UNLOAD DriverUnload;

PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];

} DRIVER_OBJECT;

Both these structures are defined in ntddk.h

A simple example

The following source files implement an example driver that does nothing but output debug messages and handles only CreateFile and CloseHandle Win32 API functions.

The header file

//////////////////////////////////////////////////////////////////////////////

//2002

//James A. Reising

//

//Drvr1 example

/////////////////////////////////////////////////////////////////////////////

//Drvr1.hCommon header

/////////////////////////////////////////////////////////////////////////////

//Version history

//4-Nov-021.0.0JARcreation

//

//

/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////

//Include NT DDK standard header with C linkage

#ifdef __cplusplus

extern "C"

{

#endif

#include <ntddk.h>

#ifdef __cplusplus

}

#endif

/////////////////////////////////////////////////////////////////////////////

//DebugPrint header

#include "DebugPrint.h"

/////////////////////////////////////////////////////////////////////////////

//Our device extension -- extremely simple in this case

typedef struct _DRVR_DEVICE_EXTENSION

{

PDEVICE_OBJECTdrvrdo;

} DRVR_DEVICE_EXTENSION, *PDRVR_DEVICE_EXTENSION;

/////////////////////////////////////////////////////////////////////////////

// Forward declarations of global functions

NTSTATUS DrvrCreate(IN PDEVICE_OBJECT drvrdo,

IN PIRP Irp);

NTSTATUS DrvrClose(IN PDEVICE_OBJECT drvrdo,

IN PIRP Irp);

/////////////////////////////////////////////////////////////////////////////

The source file

//////////////////////////////////////////////////////////////////////////////

//2002 James A. Reising

//

//

//Drvr1 example -- does nothing but handle IRP_MJ_CREATE and IRP_MJ_CLOSE

//

//Simply returns after outputting debug messages, indicating success

//

/////////////////////////////////////////////////////////////////////////////

//Drvr1.cpp:Driver code

/////////////////////////////////////////////////////////////////////////////

//DriverEntryInitialisation entry point

//DrvrCreateDeviceCode to create device, called by DriverEntry

//DrvrCloseCode to handle IRP_MJ_CLOSE

//DrvrCreateCode to handle IRP_MJ_CREATE

//

/////////////////////////////////////////////////////////////////////////////

//Version history

//4-Nov-021.0.0JARcreation

//

//

//

/////////////////////////////////////////////////////////////////////////////

#include "Drvr1.h"

/////////////////////////////////////////////////////////////////////////////

PDEVICE_OBJECT drvrdo = NULL;

NTSTATUS DrvrCreateDevice( IN PDRIVER_OBJECT DriverObject);

void DrvrDeleteDevice();

/////////////////////////////////////////////////////////////////////////////

#pragma code_seg("INIT") // start INIT section

/////////////////////////////////////////////////////////////////////////////

//DriverEntry:

//

//Description:

//This function initializes the driver, and creates

//any objects needed to process I/O requests.

//

//Arguments:

//Pointer to the Driver object

//Registry path string for driver service key

//

//Return Value:

//This function returns STATUS_XXX

extern "C"

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,

IN PUNICODE_STRING RegistryPath)

{

NTSTATUS status = STATUS_SUCCESS;

#if DBG

DebugPrintInit("Drvr1 checked");

#else

DebugPrintInit("Drvr1 free");

#endif

DebugPrint("RegistryPath is %T",RegistryPath);

// Export other driver entry points...

DriverObject->MajorFunction[IRP_MJ_CREATE] = DrvrCreate;

DriverObject->MajorFunction[IRP_MJ_CLOSE] = DrvrClose;

status = DrvrCreateDevice(DriverObject);

DebugPrintMsg("DriverEntry completed");

return status;

}

//////////////////////////////////////////////////////////////////////////////

#defineNT_DEVICE_NAMEL"\\Device\\Drvr1"

#defineSYM_LINK_NAMEL"\\DosDevices\\Drvr1"

NTSTATUS DrvrCreateDevice( IN PDRIVER_OBJECT DriverObject)

{

NTSTATUS status = STATUS_SUCCESS;

// Initialise NT and Symbolic link names

UNICODE_STRING deviceName, linkName;

RtlInitUnicodeString( &deviceName, NT_DEVICE_NAME);

RtlInitUnicodeString( &linkName, SYM_LINK_NAME);

// Create our device

DebugPrint("Creating device %T",&deviceName);

status = IoCreateDevice(

DriverObject,

sizeof(DRVR_DEVICE_EXTENSION),

&deviceName,

FILE_DEVICE_UNKNOWN,

0,

TRUE,// Exclusive

&drvrdo);

if( !NT_SUCCESS(status))

{

DebugPrintMsg("Could not create device");

return status;

}

drvrdo->Flags |= DO_BUFFERED_IO;

// Initialise device extension

PDRVR_DEVICE_EXTENSION dx = (PDRVR_DEVICE_EXTENSION)drvrdo->DeviceExtension;

dx->drvrdo = drvrdo;

// Create a symbolic link so our device is visible to Win32...

DebugPrint("Creating symbolic link %T",&linkName);

status = IoCreateSymbolicLink( &linkName, &deviceName);

if( !NT_SUCCESS(status))

{

DebugPrintMsg("Could not create symbolic link");

IoDeleteDevice(drvrdo);

return status;

}

return status;

}

#pragma code_seg() // end INIT section

/////////////////////////////////////////////////////////////////////////////

//DrvrClose:

//

//Description:

//Handle IRP_MJ_CLOSE requests

//Allow closes to complete if device not started

//

//Arguments:

//Pointer to our DRVR DO

//Pointer to the IRP

//

//Return Value:

//This function returns STATUS_XXX

NTSTATUS DrvrClose(IN PDEVICE_OBJECT drvrdo,

IN PIRP Irp)

{

PDRVR_DEVICE_EXTENSION dx = (PDRVR_DEVICE_EXTENSION)drvrdo->DeviceExtension;

DebugPrintMsg("Close");

// Complete successfully

return 0;// which is what STATUS_SUCCESS is defined as

}

/////////////////////////////////////////////////////////////////////////////

//DrvrCreate:

//

//Description:

//Handle IRP_MJ_CREATE requests

//

//Arguments:

//Pointer to our Drvr DO

//Pointer to the IRP

//IrpStack->Parameters.Create.xxx has create parameters

//IrpStack->FileObject->FileName has file name of device

//

//Return Value:

//This function returns STATUS_XXX

NTSTATUS DrvrCreate(IN PDEVICE_OBJECT drvrdo,

IN PIRP Irp)

// Complete

{

DebugPrintMsg("Created OK");

return STATUS_SUCCESS;

}

Source for a console application to exercise the driver

//////////////////////////////////////////////////////////////////////////////

//2002 James A. Reising

//

//

//Drvr1Test example

//////////////////////////////////////////////////////////////////////////////

//Drvr1Test.cpp:Win32 console application to exercise Drvr1 devices

/////////////////////////////////////////////////////////////////////////////

//mainProgram main line

/////////////////////////////////////////////////////////////////////////////

//Version history

//4-Nov-021.0.0JARcreation

/////////////////////////////////////////////////////////////////////////////

#include "windows.h"

#include "stdio.h"

int main(int argc, char* argv[])

{

int TestNo = 1;

printf("\nDrvrTest\n");

/////////////////////////////////////////////////////////////////////////

// Open

printf("\nTest %d\n",TestNo++);

HANDLE hDrvr = CreateFile("\\\\.\\Drvr1", GENERIC_READ|GENERIC_WRITE, 0,

NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if( hDrvr==INVALID_HANDLE_VALUE)

{

printf("XXX Could not open Drvr1 device\n");

return 1;

}

printf(" Opened OK\n");

/////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////

// Close device

printf("\nTest %d\n",TestNo++);

if( !CloseHandle(hDrvr))

printf("XXX CloseHandle failed %d\n",GetLastError());

else

printf(" CloseHandle worked\n");

return 0;

}