Stream Interface API
For Device Manager to load and manage a stream driver successfully, the stream driver must export a common interface, generally referred to as the stream interface. The stream interface consists of 12 functions to initialize and open the device, read and write data, power up or down the device, and close and de-initialize the device, as summarized in Table 6-1.
Table 6-1 Stream interface functions
Function Name | Description |
---|---|
XXX_Init | Device Manager calls this function to load a driver during the boot process or in answer to calls to ActivateDeviceEx in order to initialize the hardware and any memory structures used by the device. |
XXX_PreDeinit | Device Manager calls this function before calling XXX_Deinit to give the driver a chance to wake sleeping threads and invalidate open handles in order to accelerate the de-initialization process. Applications do not call this function. |
XXX_Deinit | Device Manager calls this function to de-initialize and deallocate memory structures and other resources in response to a DeActivateDevice call after deactivating and unloading a driver. |
XXX_Open | Device Manager calls this function when an application requests access to the device by calling CreateFile for reading, writing, or both. |
XXX_PreClose | Device Manager calls this function to give the driver a chance to invalidate handles and wake sleeping threads in order to accelerate the unloading process. Applications do not call this function. |
XXX_Close | Device Manager calls this function when an application closes an open instance of the driver, such as by calling the CloseHandle function. The stream driver must de-allocate all memory and resources allocated during the previous XXX_Open call. |
XXX_PowerUp | Device Manager calls this function when the operating system comes out of a low power mode. Applications do not call this function. This function executes in kernel mode, can therefore not call external APIs, and must not be paged out because the operating system is running in single-threaded, non-paged mode. Microsoft recommends that drivers implement power management based on Power Manager and power management IOCTLs for suspend and resume functionality in a driver. |
XXX_PowerDown | Device Manager calls this function when the operating system transitions into suspend mode. Similar to XXX_PowerUp, this function executes in kernel mode, can therefore not call external APIs, and must not be paged out. Applications do not call this function. Microsoft recommends that drivers implement power management based on Power Manager and power management IOCTLs. |
In the function names, the prefix XXX is a placeholder that refers to the three-letter driver name. You need to replace this prefix with the actual name in the driver code, such as COM_Init for a driver called COM, or SPI_Init for a Serial Peripheral Interface (SPI) driver.
Device Driver Context
Device Manager supports context management based on device context and open context parameters, which Device Manager passes as DWORD values to the stream driver with each function call. Context management is vital if a driver must allocate and deallocate instance-specific resources, such as blocks of memory. It is important to keep in mind that device drivers are DLLs, which implies that global variables and other memory structures defined or allocated in the driver are shared by all driver instances. Deallocating the wrong resources in response to an XXX_Close or XXX_Deinit call can lead to memory leaks, application failures, and general system instabilities.Stream drivers can manage context information per device driver instance based on the following two levels:
1. Device context The driver initializes this context in the XXX_Init function. This context is therefore also called the Init Context. Its primary purpose is to help the driver manage resources related to hardware access. Device Manager passes this context information to the XXX_Init, XXX_Open, XXX_PowerUp, XXX_PowerDown, XXX_PreDeinit and XXX_Deinit functions.
2. Open context The driver initializes this second context in the XXX_Open function. Each time an application calls CreateFile for a stream driver, the stream driver creates a new open context. The open context then enables the stream driver to associate data pointers and other resources with each opened driver instance. Device Manager passes the device context to the stream driver in the XXX_Open function so that the driver can store a reference to the device context in the open context. In this way, the driver can retain access to the device context information in subsequent calls, such as XXX_Read, XXX_Write, XXX_Seek, XXX_IOControl, XXX_PreClose and XXX_Close. Device Manager only passes the open context to these functions in the form of a DWORD parameter.
The following code listing illustrates how to initialize a device context for a sample driver with the driver name SMP (such as SMP1:):
DWORD SMP_Init(LPCTSTR pContext, LPCVOID lpvBusContext) {
T_DRIVERINIT_STRUCTURE *pDeviceContext = (T_DRIVERINIT_STRUCTURE *)
LocalAlloc(LMEM_ZEROINIT|LMEM_FIXED, sizeof(T_DRIVERINIT_STRUCTURE));
if (pDeviceContext == NULL) {
DEBUGMSG(ZONE_ERROR,(L" SMP: ERROR: Cannot allocate memory "
+ "for sample driver's device context.\r\n"));
// Return 0 if the driver failed to initialize.
return 0;
}
// Perform system intialization...
pDeviceContext->dwOpenCount = 0;
DEBUGMSG(ZONE_INIT,(L"SMP: Sample driver initialized.\r\n"));
return (DWORD)pDeviceContext;
}
Building a Device Driver
To create a device driver, you can add a subproject for a Windows Embedded CE DLL to your OS design, but the most common way to do it is to add the device driver's source files inside the Drivers folder of the Board Support Package (BSP). For detailed information about configuring Windows Embedded CE subprojects, see Chapter 1, "Customizing the Operating System Design."
A good starting point for a device driver is A Simple Windows Embedded CE DLL Subproject, which you can select on the Auto-Generated Subproject Files page in the Windows Embedded CE Subproject Wizard. It automatically creates a source code file with a definition for the DllMain entry point for the DLL, various parameter files, such as empty module-definition (.def) and registry (.reg) files, and preconfigures the Sources file to build the target DLL. For more detailed information about parameter files and the Sources file, see Chapter 2, "Building and Deploying a Run-Time Image."