ActivateDeviceEx replaces the older RegisterDevice function as a method to load a driver on demand, as illustrated in the following code sample:
// Ask Device Manager to load the driver for which the definition
// is located at HKLM\Drivers\Sample in the registry.
hActiveDriver = ActivateDeviceEx(L"\\Drivers\\Sample", NULL, 0, NULL);
if (hActiveDriver == INVALID_HANDLE_VALUE) {
ERRORMSG(1, (L"Unable to load driver"));
return -1;
}
// Once the driver is lodaded, applications can open the driver
hDriver = CreateFile (L"SMP1:",
GENERIC_READ| GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDriver == INVALID_HANDLE_VALUE) {
ERRORMSG(1, (TEXT("Unable to open Sample (SMP) driver")));
return 0; //Insert code that uses the driver here
}
// Close the driver when access is no longer needed
if (hDriver != INVALID_HANDLE_VALUE) {
bRet = CloseHandle(hDriver);
if (bRet == FALSE) {
ERRORMSG(1, (TEXT("Unable to close SMP driver")));
}
}
// Manually unload the driver from the system using Device Manager
if (hActiveDriver != INVALID_HANDLE_VALUE) {
bRet = DeactivateDevice(hActiveDriver);
if (bRet == FALSE) {
ERRORMSG(1, (TEXT("Unable to unload SMP driver ")));
}
}
Calling ActivateDeviceEx to load a driver has the same result as loading the driver automatically during the boot process through parameters defined in the HKEY_LOCAL_MACHINE\Drivers\BuiltIn key. The BuiltIn registry key is covered in more detail in Lesson 3 later in this chapter.
Lesson Summary
Stream drivers are Windows Embedded CE drivers that implement the stream interface API. The stream interface enables Device Manager to load and manage these drivers, and applications can use standard file system functions to access these drivers and perform I/O operations. To present a stream driver as a file resource accessible through a CreateFile call, the name of the stream driver must follow a special naming convention that distinguishes the device resource from ordinary files. Legacy names (such as COM1:) have a limitation of ten instances per driver because they include only a single-digit instance identification. If you must support more than ten driver instances on a target device, use a device name instead (such as \$device\COM1).
Because Device Manager can load a single driver multiple times to satisfy the requests from different processes and threads, stream drivers must implement context management. Windows Embedded CE knows two context levels for device drivers, device context and open context, which the operating system passes in each appropriate function call to the driver so that the driver can associate internal resources and allocated memory regions with each caller.
The stream interface consists of 12 functions: XXX_Init, XXX_Open, XXX_Read, XXX_Write, XXX_Seek, XXX_IOControl, XXX_PowerUp, XXX_PowerDown, XXX_PreClose, XXX_Close, XXX_PreDeinit, and XXX_Deinit. Not all functions are mandatory (such as XXX_PreClose and XXX_PreDeinit), yet any functions that the stream device driver implements must be exposed from the driver DLL to Device Manager. To export these functions, you must define them in the .def file of the DLL subproject. You should also adjust the DLL subproject's Sources file to ensure that the driver DLL can make platform-dependent function calls.
Lesson 3: Configuring and Loading a Driver
In general, you have two options to load a stream driver under Windows Embedded CE 6.0. You can instruct Device Manager to load the driver automatically during the boot sequence by configuring driver settings in the HKEY_LOCAL_MACHINE\Drivers\BuiltIn registry key, or you can load the driver dynamically through a direct call to ActivateDeviceEx. Either way, Device Manager can load the device driver with the same registry flags and settings. The key difference is that you receive a handle to the driver when using ActivateDeviceEx, which you can use later in a call to DeactivateDevice. Especially during the development stage, it might be advantageous to load a driver dynamically through ActivateDeviceEx so that you can unload the driver, install an updated version, and then reload the driver without having to restart the operating system. You can also use DeactivateDevice to unload drivers loaded automatically based on entries under the BuiltIn registry key, but you cannot reload them without calling ActivateDeviceEx directly.
After this lesson, you will be able to:
■ Identify the mandatory registry settings for a device driver.
■ Access the registry settings from within a driver.
■ Load a driver at startup or on demand in an application.
■ Load a driver in user space or kernel space.
Estimated lesson time: 25 minutes.
Device Driver Load Procedure
Whether you load a device driver statically or dynamically, the ActivateDeviceEx function is always involved. A dedicated driver named the Bus Enumerator (BusEnum) calls ActivateDeviceEx for every driver registered under HKEY_LOCAL_MACHINE\Drivers\BuiltIn just as you can call ActivateDeviceEx directly, passing in an alternate registry path for the driver settings in the lpszDevKey parameter.
Device Manager uses the following procedure to load device drivers at boot time:
1. Device Manager reads the HKEY_LOCAL_MACHINE\Drivers\RootKey entry to determine the location of the device driver entries in the registry. The default value of the RootKey entry is Drivers\BuiltIn.
2. Device Manager reads the Dll registry value at the location specified in the RootKey location (HKEY_LOCAL_MACHINE\Drivers\BuiltIn) to determine the enumerator DLL to load. By default, this is the bus enumerator (BusEnum.dll). The bus enumerator is a stream driver that exports the Init and Deinit functions.
3. The bus enumerator runs at startup to scan the RootKey registry location for subkeys that refer to additional buses and devices. It can be run again later with a different RootKey to load more drivers. The bus enumerator examines the Order value in each subkey to determine the load order.
4. Starting with the lowest Order values, the bus enumerator iterates through the subkeys and calls ActivateDeviceEx passing in the current driver's registry path (that is, HKEY_LOCAL_MACHINE\Drivers\BuiltIn\<DriverName>).