The following example shows how a basic kernel-mode device driver initializes itself. The code for this example is in chapter 6 directory on the disk that accompanies this book. This first minimal driver must be manually loaded. It does not touch any hardware, but instead creates an internal device name (MINIMAL0) and a symbolic link name (MIN1). It consists of a single source module, Driver.cpp. A header file, Driver.h, declares driver-specific information about our nonhardware device, such as the DEVICE_EXTENSION. DRIVERENTRYIn our first non-WDM driver example, the DriverEntry routine is small and straightforward. The responsibilities include
//++ // Function: DriverEntry // // Description: // Initializes the driver, locating and claiming // hardware resources. Creates the kernel objects // needed to process I/O requests. // // Arguments: // pDriverObject - Passed from I/O Manager // pRegistryPath - UNICODE_STRING pointer to // registry info (service key) // for this driver // // Return value: // NTSTATUS signaling success or failure // NTSTATUS DriverEntry ( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath ) { ULONG ulDeviceNumber = 0; NTSTATUS status; // If this driver controlled real hardware, // code would be placed here to locate it. // Using IoReportResourceUsage, the ports, // IRQs, and DMA channels would be "marked" // as "in use" and under the control of this driver. // This minimal driver has no HW, so... // Announce other driver entry points pDriverObject->DriverUnload = DriverUnload; // Over time, the MajorFunction array will be filled // For each physical or logical device detected // that will be under this Driver's control, // a new Device object must be created. status = CreateDevice(pDriverObject, ulDeviceNumber); // This call would be repeated until // all devices are created. E.g., // ulDeviceNumber++; // status = // CreateDevice(pDriverObject, ulDeviceNumber); return status; } CREATEDEVICEThe work of actually creating the device object is delegated to a module-private (static) routine called CreateDevice. Although this routine doesn't do much, modularizing this work is appropriate as this driver evolves into a full WDM driver. Its responsibilities include
The CreateDevice routine relies on the C++ class, CUString, discussed in the last chapter. The use of CUString makes it simple to convert numbers into Unicode strings and append them onto device names. //++ // Function: CreateDevice // // Description: // Adds a new device // // Arguments: // pDriverObject - Passed from I/O Manager // ulDeviceNumber - Logical device number (zero-based) // // Return value: // None // NTSTATUS CreateDevice ( IN PDRIVER_OBJECT pDriverObject, IN ULONG ulDeviceNumber ) { NTSTATUS status; PDEVICE_OBJECT pDevObj; PDEVICE_EXTENSION pDevExt; // Form the internal Device Name CUString devName("\\Device\\MINIMAL"); // for "minimal" device devName += CUString(ulDeviceNumber); // Now create the device status = IoCreateDevice( pDriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj ); if (!NT_SUCCESS(status)) return status; // Initialize the Device Extension pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; pDevExt->pDevice = pDevObj; // back pointer pDevExt->DeviceNumber = ulDeviceNumber; pDevExt->ustrDeviceName = devName; // Form the symbolic link name CUString symLinkName("\\??\\MIN"); symLinkName += CUString(ulDeviceNumber+1); // 1 based pDevExt->ustrSymLinkName = symLinkName; // Now create the link name status = IoCreateSymbolicLink( &(UNICODE_STRING)symLinkName, &(UNICODE_STRING)devName ); if (!NT_SUCCESS(status)) { // if it fails now, must delete Device object IoDeleteDevice( pDevObj ); return status; } // Made it return STATUS_SUCCESS; }
|