Windows Device Driver Development

1. Setup Driver Development Environment
Step 1: Install following packages on the development computer
  1. Platform Software Development Kit (SDK),
  2. Microsoft Visual C (must use the version specified in DDK, newer version might not be working),
  3. Device Driver Kits (DDK).
Step 2: Test driver development system
To compile a driver, one uses “build.exe” program from DDK. BUILD reads inputs from files “dirs” or “sources” and generates outputs “build.log”. Suppose DDK is installed in c:\ntddk directory. Open a command window and type
c:\NTDDK\bin\setenv.bat c:\NTDDK free
This setup an environment to build drivers of free version (no debug). In this command window, type the following commands to build a sample driver,
cd c:\NTDDK\src\general\portio
build
If everything goes well, “genport.sys” should appear in
“C:\NTDDK\src\general\portio\sys\objfre\i386”
folder and “gpdread.exe” and “gpdwrite.exe” are also generated.
2. Setup Driver Debug Environment
Install Checked Build (Debug Build) Windows on a driver test system. Add a line in [operation systems] section of “boot.ini” file.
For example
multi(0)disk(0)rdisk(0)partition(1)\WINNT=”Debug Windows 2000″ /debug \
/debugport=com1
/baudrate=192000
On the development system, create a dedicated debugging directory “c:\Symbols” and copy OS symbol files from Windows CD in “\support\debug\i386\Symbols”, especially “hal.dbg” and “ntoskrnl.dbg” into this directory. Also, put the newly created driver (e.g., genport.sys) in this directory because it symbol information for the driver to be tested.
On the development system, start “WinDbg.exe”.
From View->Option, select Kernel Debugger tab to enable kernel debugger, set communication port and baud rate. Note, the baud rate must match that specified in the test machine. Select Symbols tab and set the symbol search path to “c:\Symbols”. Connect the development machine and test machine with a null modem cable. Copy the newly created driver (e.g., genport.sys) to the test system (normally in %SystemRoot%\System32\Drivers). Create necessary registry entries for this driver (under \HKLM\System\CurrentControlSet\Servics\driver_name). It might be easier to install/uninstall a device driver using a setup program.
DbgPrint() is a macro in Kernel mode equivalent of C’s printf(). Often encloded in #if DBG and #endif conditionals. ASSERT() macro only works in checked build.
Start value in registry:
(1) 0x00 — Boot,

(2) 0x01 – System,

(3) 0x02 – Automatic,

(4) 0x03 – Manual,

(5) 0x04 -Disabled.

Example:
—————FileName: MyDriver.c —————-

#include <ntddk.h>

 

#define NativeDriverName L”\\Device\\MyDriver”

#define DosDriverName L”\\DosDevices\\MyDriver”

//===================================================================

// I/O Manager calls this routine to unload the device driver

//===================================================================

VOID MyUnload (IN PDRIVER_OBJECT pDriverObject) {

UNICODE_STRING DeviceLinkUnicodeString;

NTSTATUS status;

//————————————————————–

// “IoDeleteSymbolicLink” removes a symbolic link

//————————————————————–

RtlInitUnicodeString (&DeviceLinkUnicodeString, DosDriverName);

status = IoDeleteSymbolicLink (&DeviceLinkUnicodeString);

//————————————————————–

// “IoDeleteDevice” removes a device object

//————————————————————–

if (NT_SUCCESS(status))

IoDeleteDevice(pDriverObject->DeviceObject);

} // End of DriverUnload

//==========================================================

// Handles “CreateFile” Win32 call from user program

//==========================================================

NTSTATUS MyCreate (IN PDEVICE_OBJECT pDriverObject, IN PIRP pIrp) {

pIrp->IoStatus.Status = STATUS_SUCCESS;

pIrp->IoStatus.Information = 0;

// no bytes xfered

IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return STATUS_SUCCESS;

} //End of MyCreate

//===================================================================

// Handles “CloseHandle” Win32 call from user program

//===================================================================

NTSTATUS MyClose (IN PDEVICE_OBJECT pDriverObject, IN PIRP pIrp) {

pIrp->IoStatus.Status = STATUS_SUCCESS;

pIrp->IoStatus.Information = 0;

// no bytes xfered

IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return STATUS_SUCCESS;

} //End of MyClose

//==============================================================

// Handles “ReadFile” Win32 call from user program

//==============================================================

NTSTATUS MyRead (IN PDEVICE_OBJECT

pDriverObject, IN PIRP pIrp) {

pIrp->IoStatus.Status = STATUS_SUCCESS;

pIrp->IoStatus.Information = 0;

//no bytes xfered

IoCompleteRequest(pIrp, IO_NO_INCREMENT );

Page 3

return STATUS_SUCCESS;

} // End of MyRead

//===================================================================

// Handles “WriteFile” Win32 call from user program

//===================================================================

NTSTATUS MyWrite (IN PDEVICE_OBJECT

pDriverObject, IN PIRP pIrp) {

pIrp->IoStatus.Status = STATUS_SUCCESS;

pIrp->IoStatus.Information = 0;

//no bytes xfered

IoCompleteRequest(pIrp, IO_NO_INCREMENT );

return STATUS_SUCCESS;

} //End of MyWrite

//===================================================================

// Handles “DeviceIoControl” Win32 call from user program

//===================================================================

NTSTATUS MyDeviceControl (IN PDEVICE_OBJECT pDriverObject, IN PIRP pIrp) {

pIrp->IoStatus.Status = STATUS_SUCCESS;

pIrp->IoStatus.Information = 0;

//no bytes xfered

IoCompleteRequest(pIrp, IO_NO_INCREMENT );

return STATUS_SUCCESS;

} //End of MyDeviceControl

//===================================================================

// Each driver must have DriverEntry routine. The I/O Manager

// calls the DriverEntry routine when it loads the driver.

//

//

pDriverObject – Point to a driver object, passed from I/O Manager

//

pRegistryPath – UNICODE_STRING pointer to

//

\HKLM\System\CurrentControlSet\Services\DriverName

//

//

Return value:

//

STATUS_SUCCESS or an appropriate error status.

//

//===================================================================

NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDriverObject,

IN PUNICODE_STRING pRegistryPath) {

PDEVICE_OBJECT pDeviceObject = NULL;

UNICODE_STRING DeviceNameUnicodeString;

UNICODE_STRING DeviceLinkUnicodeString;

NTSTATUS status;

//————————————————————–

//

“RtlInitUnicodeString” function initializes a counted

// Unicode string from a zero-terminated Unicode string.

//————————————————————–

RtlInitUnicodeString (&DeviceNameUnicodeString, NativeDriverName);

RtlInitUnicodeString (&DeviceLinkUnicodeString, DosDriverName);

//————————————————————–

// “IoCreateDevice” allocates memory for and initializes

// a device object for use by a driver.

//————————————————————–

status = IoCreateDevice(pDriverObject, 0,

&DeviceNameUnicodeString,

FILE_DEVICE_UNKNOWN,

0, FALSE,

&pDeviceObject);

Page 4

if (!NT_SUCCESS(status)) return status;

//————————————————————–

// “IoCreateSymbolicLink” sets up a symbolic link between

// an NT device object name and a user-visible name for the device.

// user program can only access devices in “\??” object directory

//————————————————————–

status = IoCreateSymbolicLink (&DeviceLinkUnicodeString, &DeviceNameUnicodeString);

if (!NT_SUCCESS(status)) {

IoDeleteDevice(pDeviceObject);

return status;

}

//————————————————————–

// Fill Dispatch routine entry points

//————————————————————–

pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDeviceControl;

pDriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;

pDriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;

pDriverObject->MajorFunction[IRP_MJ_WRITE] = MyWrite;

pDriverObject->MajorFunction[IRP_MJ_READ] = MyRead;

pDriverObject->DriverUnload = MyUnload;

return STATUS_SUCCESS;

} // End of DriverEntry

 

 

——– FileName: makefile ———–

#

# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source

# file to this component. This file merely indirects to the real make file

# that is shared by all the driver components of the Windows NT DDK

#

!INCLUDE $(NTMAKEENV)\makefile.def

——– FileName: sources —————

TARGETNAME=MyDriver

TARGETTYPE=DRIVER

TARGETPATH=.

INCLUDES= $(BASEDIR)\inc;.

SOURCES=.\MyDriver.c