Writing Device Drivers for Microsoft Windows CE 3.0
Summary: This paper introduces the process of developing device drivers for Windows CE. It provides an overview of device-driver architecture, discusses the primary Windows CE device-driver models, and identifies associated APIs and design issues. (12 printed pages)
Device Driver Models
Device Driver Design
Creating Windows CE Drivers
Loading Windows CE Drivers
Stream Interface Drivers
Native Device Drivers
Developing USB Device Drivers
For More Information
A key aspect of the modular design of Windows CE is that original equipment manufacturers (OEMs) and independent hardware vendors (IHVs) can implement device drivers that support their own hardware without additional support from Microsoft. The versatility of Windows CE provides a rich opportunity to develop applications for a wide range of devices, either built-in or installable. Built-in devices are typically integral to a Windows CE-based platform and are designed specifically for low-level, built-in hardware, such as keyboards, display screens, and PC Card sockets. Installable devices are third-party peripheral devices that can be connected to a Windows CE-based platform at any time.
The Windows CE Driver Development Kit supplies documentation enabling you to create device drivers for most device classes in Windows CE. The Driver Development Kit is packaged with the Windows CE Platform Builder, which contains source code and libraries for OEMs that build custom Windows CE–based platforms, plus source code for a number of sample device drivers. OEMs are encouraged to use these sample device drivers as the basis for their own device drivers. This white paper provides an overview of device-driver architecture and discusses the primary Windows CE device driver.
All device drivers in Windows CE are dynamic-link libraries (DLLs) and primarily use the standard Windows CE APIs (Win32®-based) in their implementations. This is in contrast to drivers on Windows 2000 and Windows 98 where drivers use the Windows Driver Model (WDM) interfaces and run either in user mode or kernel mode, with special privileges based on what mode they are in. Understanding the Windows CE driver model and developing device driver for Windows CE is generally easier than for Windows 2000 and Windows 98, since the Windows CE driver architecture is significantly less complex than WDM.
Windows CE offers two primary device driver models, which are unique to the Windows CE operating system, stream interface drivers and native device drivers. These driver models are differentiated only by the software interface they support and not by the devices they serve. The model determines the software interface that any given driver exports to applications or to other operating system modules.
In addition, Windows CE supports NDIS miniport and protocol drivers for networking, and USB device drivers for USB-compliant peripherals. NDIS miniport and protocol drivers must expose specific NDIS APIs in order to work with the Windows CE networking infrastructure. Programmers can choose whether to implement a USB device driver as a stream interface driver or a native device driver. Please refer to the NDIS and USB sections of this document for more information.
A device driver is code that communicates with a hardware device. It links the device with the operating system, making the device available to the operating system and applications through an interface. Device drivers can access their devices directly if the devices are mapped into system memory, or they may need to use the services of lower-level drivers—ones that manage various data ports on the platform—in order to access devices that connect to Windows CE through some intermediate hardware connection such as a serial port or USB port.
Windows CE supports two primary device driver models: stream interface drivers and native device drivers. Stream interface drivers are ones that implement and expose the standard set of stream interface functions for use by applications. The stream interface functions closely map to the semantics of the Win32 file I/O APIs, so any device that can be treated as if it were a special file—that is, a producer or consumer of ordered streams of bytes—is a good candidate for a stream interface driver. Native device drivers are ones that implement and expose any interface other than the stream interface functions. The APIs exposed by native device drivers are most often specifically tailored to the underlying device's functionality.
The characteristics of these models are summarized in the following table.
|Device driver type||Devices supported||Functionality|
Usually loaded and called by core OS at boot time. Exposes a custom API set that is unique to the driver.
Any device whose I/O operations are similar to the operation of reading and writing to a file.
Exposes the stream interface functions. Loaded by Device Manager at system boot time, device detection time, or application load time; exposed through file system.
There is some overlap between these two categories. While most of the sample drivers included in the Windows CE Platform Builder are clearly either native device drivers or stream interface drivers, some are hybrid in the sense that they expose both a custom-purpose interface and a stream interface to the rest of the system. For example, Windows CE only sends power-up and power-down notifications—messages that the system is turning off or has just turned on—to stream interface drivers. Native device drivers that need to receive these messages can implement a minimal stream interface in addition to their primary custom-purpose interfaces.
Despite a general misconception, that there is no requirement that built-in devices use the native device driver model while installable devices use the stream interface model. In fact, some of Microsoft's sample device drivers for built-in devices expose the stream interface functions. Similarly, there is no reason why an IHV could not write a driver for an installable device that exposes a unique interface to the rest of the system, albeit at the expense of losing access to the robust support infrastructure that Windows CE provides for stream interface drivers. Previous versions of Windows CE had a greater association between driver models--whether the corresponding device was built in or installable. That association is no longer applicable in Windows CE 3.0; the driver model is completely independent of a device's status as built-in or installable.
Any device driver in Windows CE can conform to a monolithic or layered organizational model.
A monolithic device driver has the following characteristics:
- It is based on a single piece of code that exposes the hardware device's functionality directly to the operating system.
- It accesses its devices directly.
A layered device driver has the following characteristics:
- It consists of two layers:
- A model device driver (MDD) layer, or upper layer.
- A platform-dependent driver (PDD) layer, or lower layer.
Distinctions between these two layers are described in the following table.
MDD (upper layer)
PDD (lower layer)
The following diagram illustrates the integration of monolithic and layered device drivers within the Windows CE operating system.
Figure 1. Integration of monolithic and layered device drivers within the Windows CE operating system
Each device driver model has advantages in the context of the overall design considerations. Many of the sample drivers provided by Microsoft use the layered organization because it reduces the amount of code that developers must write when porting the sample drivers to new devices. In some instances, however, a monolithic driver might be a better choice than a layered driver. If performance is a critical factor, for example, using a monolithic driver avoids the overhead associated with the function calls that take place between the MDD and PDD layers. A developer might also choose to implement a monolithic driver if the capabilities of the target device correspond closely to the semantics of the APIs exposed by the MDD layer.
The Device Manager is a user-level process that is typically loaded at boot time on a Windows CE–based platform. It is an application that interacts with the kernel, registry, file system, and most device-driver DLLs. The Device Manager primarily manages stream interface drivers; because stream interface drivers all expose the same APIs to applications, they can be managed in the same way.
The Device Manager performs the following tasks:
- Loads and tracks device drivers.
- Registers special file names.
- Locates device drivers.
- Unloads device drivers from working RAM when they are no longer being used.
The Device Manager uses a variety of registry entries in the process of loading and unloading stream interface drivers. Some keys relate to drivers that should always be present, and thus must be loaded when the system boots. Other keys relate to devices that are identified when they are connected to the Windows CE-based platform through Plug and Play mechanisms. See the DDK documentation for detailed discussion of these registry entries and the ways that device drivers can be loaded.
The process of writing a Windows CE device driver will vary greatly in detail, depending on which of the two types of drivers—stream interface drivers and native device drivers—you are implementing, and whether you choose to use a layered or monolithic design. Microsoft provides many resources to support this process, including numerous sample device drivers, ample documentation, and debugging tools for use both on the Windows CE platform and on your development workstation.
Creating a device driver in Windows CE requires the following steps.
- Identify the interface between the driver and the device:
- If the device driver will be communicating directly with the hardware device, this interface will be the memory ranges, registers, and/or ports through which I/O to the device takes place. Two examples of this kind of device are a keyboard driver and a display driver.
- If the device driver will be communicating with its device via an intermediate device, this interface will be whatever APIs the driver for the intermediate device exposes. Examples of this type of driver are any drivers that communicate with their devices through data ports such as USB or serial. For instance, a GPS device driver will use the serial port driver's API—the stream interface functions—to communicate with a GPS receiver that is connected to the Windows CE platform's serial port.
- Identify the APIs that the driver must expose:
- If the device driver will be providing services to a specific component of the Windows CE kernel that already has a defined interface, the device driver will expose the native API set for that type of driver. For example, a battery driver must implement the functions defined by GWES for battery drivers. Similarly, the PDD layer of a layered driver must implement the DDSI functions defined by the driver's MDD layer. In either case, the code you will write must conform to an existing API set.
- If there is no predefined set of functions that your driver must expose to the operating system or to applications, you must design the upper interface. You are encouraged to make use of the stream interface functions whenever possible. For example, developers of USB device drivers often have to invent a way to expose the USB device to applications. Because of the flexibility of the stream interface functions and the fact that nearly all devices can be treated as special files, using the stream interface functions as the API, on which applications use a USB device, is often a good design choice.
- Identify any additional interfaces that the device driver must use, such as operating system services.
- Determine an appropriate mapping between the interfaces. This is the essence of a device driver's job: mapping between the functions that applications or the operating system will call in the driver and the functionality provided by the device itself.
- Identify any additional architectural factors that are relevant to your device driver, such as whether it must be organized as specific modules and what functions are likely to have the most time or memory constraints.
- Identify any pre-existing code that you can borrow or port.
- Implement any remaining code.
- Test and debug the device driver.
Device drivers in Windows CE are managed by a variety of different operating system modules. Drivers that are integral to a Windows CE-based platform's user interface, such as the keyboard and display, are managed by the Graphics, Windowing, and Event (GWE) module. All of the device drivers managed by GWE are native device drivers. GWE loads its drivers when the system boots. A special process called the Device Manager handles stream interface drivers. Device manager loads some stream interface drivers when the system boots, if those drivers are listed in the registry. Finally, any process has the ability to load a device driver after the system has booted, generally because a user has connected an installable device to the Windows CE-based platform. The Device Manager does this for PC Card client drivers, the USBD module does this for USB client device drivers, etc. The following table classifies the sample device drivers in terms of what interfaces they expose and what Windows CE module is responsible for loading them.
|Load Time||Native Device Driver||Native Device Driver|
Native Device Driver
Loaded by the Device Manager when Windows CE boots
PCMCIA Host Controller
Loaded as needed by the Device Manager, USBD, and so forth.
PC Card Client drivers
Windows CE uses interrupts to signal when a device needs servicing by its driver. Windows CE 2.12 and earlier did not support nested interrupts; in those versions processing of one interrupt must finish before another begins, and additional interrupts are turned off while an interrupt is being processed. Windows CE versions 3.0 and later introduce nested interrupts in conjunction with its priority scheduling system. Interrupts of a higher priority can occur while lower level interrupts are being processed; while the higher level interrupt is being processed, no interrupts of a lower priority level will be handled.
To minimize the amount of time that lower level interrupts are disabled, Windows CE divides interrupt handling into two processes: the kernel-mode interrupt service routine (ISR) and the user-mode interrupt service thread (IST). The ISR should be as small and fast as possible; it is usually a few hand-coded assembly instructions, whose sole job is to identify the hardware interrupt and map it to a logical interrupt number, which it returns to the kernel.
The kernel then identifies the IST that is registered for that logical interrupt, and signals the thread that it must service an interrupt. Because the IST is a user-mode thread, it is subject to the same pre-emption and scheduling as any other thread in the system, but also has access to the full range of operating system services available to user-mode code. ISTs can perform much longer interrupt processing tasks without preventing any interrupts from occurring, such as coping data from a hardware buffer into user memory while converting it from one format to another.
All stream interface drivers expose a generic type of interface to the operating system, a set of APIs known as the stream interface functions. The stream interface driver model can be used for any device whose functionality is similar to a file. Any device whose logical behavior is to produce or consume ordered streams of bytes can be treated as a special kind of file, one that applications can read to and write from just as they do with ordinary data files. It should come as no surprise, then, that the stream interface functions are nearly identical to the standard Win32 file I/O APIs.
Stream interface drivers are DLLs that typically are loaded, controlled, and unloaded by the Device Manager. Stream interface drivers are designed mainly for peripheral devices connected to a Windows CE–based platform. Examples of these devices are modems, printers, and digital cameras.
Stream interface drivers typically are designed by IHVs for peripheral devices. However, OEMs that customize Windows CE–based platforms by adding special built-in devices can write stream interface drivers for those additional built-in devices. For example, a Windows CE–based, point-of-sale system might have a bar-code scanner built into the hardware of the system, for which the OEM could implement a stream interface driver. Internally, the driver could respond to interrupts from the bar code scanner hardware by storing the scanned data in a buffer. Applications could then read from the buffer just as they would from a plain text file. The driver would be responsible for removing data from the buffer as applications read it.
Windows CE presents stream interface drivers to applications by presenting them as special files in the file system. The names for these special device files follow a particular naming convention that differentiates the device files from ordinary files. Device file names all consist of three upper case letters, a digit, and a colon (for example, "COM1:" or "PGR7:"). Windows CE recognizes these file names and redirects file I/O operations to the appropriate stream interface driver. Thus, a stream interface driver must convert file I/O operations into the relevant read and write actions on the driver's device.
Stream interface drivers expose their implementations of the stream interface functions by prepending the same three-letter combination used in the driver's special device file name to the base function's name. The following table lists the stream interface functions and briefly describes their purpose; you would, of course, replace XXX with whatever prefix you choose for your stream interface driver.
|Stream Interface Function||Purpose|
Release an application’s instance handle to the driver.
Uninitialize the driver.
Initialize the driver.
Perform a driver-specific I/O control action.
Get an instance handle to the driver for use by an application.
Perform any processing necessary when the Windows CE platform suspends.
Perform any processing necessary when the Windows CE platform resumes.
Read data from the driver.
Move forward or backward in the driver’s data stream.
Write data to the driver.
Sample Code for Stream Interface Drivers
Windows CE provides the following sample drivers that implement the stream interface.
ATADISK and SRAMDISK
Illustrates device drivers for a PC Card-based ATA disk and SRAM flash memory card.
Device driver for an audio device
Device driver for devices that connect to a serial port
Native device drivers are drivers that expose any API set other than the stream interface functions. Other than that, they are exactly like stream interface drivers. They can use a monolithic or layered design, they can serve devices that are built into a Windows CE-based platform or installable devices, and they can be loaded either at the time that the system boots, or at the time that the user connects a device to the Windows CE-based platform.
Many of the sample native device drivers provided in the Windows CE Platform Builder happen to serve devices that are typically built into a Windows CE-based platform. This is partly due to legacy code reasons, but is primarily because the types of devices that aren't suitable for stream interface drivers tend to be the type of device that OEMs provide as part of their platforms. For example, a display device is essentially a range of memory addresses that are wired to a 2-dimensional surface that a user can see. Addresses in that display memory can be written to in arbitrary patterns and at any time, making a display device very different than a producer or consumer of ordered streams of bytes. Display drivers are thus not good candidates for using the stream interface driver model. At the same time, display hardware is generally built into Windows CE-based platforms, and not added by end users. Similar logic applies to some other native device drivers, such as the drivers for PC Card sockets and USB controller chips.
Microsoft has already designed the APIs exposed by native device drivers that interact with certain portions of the Windows CE operating system, such as the Graphics, Windowing, and Events System (GWES). Native device drivers that interact with GWES include display drivers, notification LED drivers, battery drivers, and keyboard drivers. Further, Microsoft provides sample code for these drivers. In most cases, an OEM only needs to port Microsoft's sample native device drivers to their specific hardware. For these specific native device drivers, you do not need to define the API set that the driver will expose.
Platform Builder includes sample native device drivers for both monolithic and layered drivers. Most native device drivers, however, are configured as layered device drivers.
The sample native device drivers present a standard set of functionality for all devices of a particular class, and all representatives of a specific class conform to the same interface. This enables the Windows CE operating system to treat all instances of a particular device class similarly. For example, the operating system treats both UHCI- and OHCI-compliant USB controller hardware the same, because the USB host controller driver hides the differences from the operating system, even though those two types of USB controller are internally very different.
Sample Code for Native Device Drivers
Windows CE provides many sample native device drivers, and using code from these samples can significantly reduce development time. Provided drivers include:
- Sample Battery Driver
- Sample Display Driver
- Sample Keyboard Driver
- Sample Notification LED Driver
- Sample PC Card Socket Driver
- Sample Touch Screen Driver
- Sample USB Host Controller Driver
USB device drivers communicate with USB devices through logical communication channels called pipes. The USB system software implements APIs that USB device drivers use to open and close pipes, configure them, and read and write to them. Pipes are connected to endpoints on the USB device, which are logical producers or consumers of data. USB devices generally have several endpoints, each with a specific purpose. All USB devices have a special endpoint, endpoint number 0, which is used as a high-level control channel.
Internally, the USB system software and hardware in Windows CE handles the task of marshalling the data from all pipes going to and from the endpoints on all USB devices connected to the Windows CE-based platform onto a single USB cable.
The following illustration shows the structure of the USB implementation in Windows CE:
Figure 2. USB implementation in Windows CE
Windows CE supports the host-side of the USB architecture, which enables developers to write USB device drivers for any USB peripheral devices, and provides limited support for the device-side of the USB architecture, which allows a Windows CE-based platform to appear as a USB peripheral to other computers. The host-side exists to support the use of USB peripherals, while the device-side exists mainly to facilitate USB connectivity to desktop computers. The USB architecture implemented in Windows CE supports both device-specific USB drivers, and more generic USB drivers known as class drivers, which can control a whole class of similar devices. Microsoft supplies one sample USB driver: a class driver for human interface-class (HID class) devices.
OEMs who wish to support USB on their Windows CE-based platforms must port Microsoft's sample HCD module implementations to their hardware. Microsoft supplies sample HCD modules for OHCI- and UHCI-compliant host controller chips.
Writing USB device drivers is generally fairly easy because the USB infrastructure itself provides a rich set of services for accessing USB peripheral devices. USB device drivers can use high-level data transfer functions to send data to and read data from USB peripherals, and thus the developer can concentrate on writing the code that exposes the USB device to applications. Windows CE supports all four types of data transfer defined in the Universal Serial Bus Specification. USB device drivers can use any of the transfer types that are appropriate for their peripherals.
Windows CE 3.0 also provides some support for the device-side of the USB device/host architecture. This allows Windows CE-based platforms to communicate to other USB hosts, such as desktop PCs, through a USB connection. Microsoft provides a sample USB function controller driver that works with the Scanlogic SL11 USB function controller chip.
Choosing Design Strategies for USB Device Drivers
USB client drivers exist to make the services of peripheral devices available to applications. OEMs and IHVs can choose among the following three strategies to make the services of peripheral devices available to applications:
- Using the stream interface functions. USB device drivers can expose the stream interface functions just like any other device driver. Applications can then treat the peripheral device as a file and use standard file I/O functions to interact with the device.
- Using an existing Windows CE API. USB device drivers can indirectly expose certain types of peripherals to applications by interacting with a Windows CE API if Windows CE has an existing API appropriate to the peripheral. For example, USB drivers for mass storage devices, such as hard drives and CD-ROM drives, can expose such devices through the standard installable file system interface. The sample HID-class driver included in the DDK also uses this strategy; the driver does not expose devices directly to applications; rather, it interacts with existing Windows CE APIs in order to submit the correct input events to the system. Thus, the USB nature of HID-class devices is transparent to applications.
- Creating a native device driver. This strategy does not place any restrictions on the way a USB driver exposes a device. It allows you to create whatever API best maps to the ways in which applications are likely to use your USB device. However, you must provide appropriate documentation for application writers who will be writing applications that use the driver because application writers cannot otherwise know what APIs your driver exposes.
The NDIS is the mechanism by which Windows CE supports network connectivity. NDIS provides a pair of abstraction layers used to connect networking drivers to protocol stacks, such as TCP/IP and IrDA, and to network adapters, such as Ethernet cards. NDIS presents two sets of APIs for writers of network drivers: One set interfaces to the networking protocol stacks, and one set interfaces to Network Interface Cards (NICs).
Windows CE implements a subset of the NDIS 4.0 model used by the Windows NT® operating system, enabling OEMs and IHVs to port existing Windows NT networking drivers to Windows CE. The full NDIS supports several types of network drivers, but Windows CE supports only miniport drivers and intermediate drivers, not monolithic or full NIC drivers. In addition, note that Ethernet (802.3), Token Ring (802.5), WAN, and IrDA (both SIR and FIR) are the NDIS media types directly supported in Windows CE. Several IHVs and OEMs implement support for additional media types by writing drivers for those media that emulate Ethernet. Additional media types supported in this fashion include 802.11, HPNA, ArcNet, several types of Wireless media, and Powerline.
Windows CE 3.0 supports an array of new features including intermediate drivers, NDIS WAN, Token Ring, improved adapter binding, a layered miniport driver design, and an enhanced testing application called NDISTest. NDISTest now includes tests for IrDA and Token Ring as well as performance and stress tests.
For miniport drivers, Windows CE is largely source-code compatible with Windows NT; with a few exceptions, Windows CE and Windows NT support identical NDIS APIs. The primary exception is that Windows CE does not provide direct memory access (DMA) support, which is often used to transfer data between networking stacks and user space. Developers can, however, implement DMA mechanisms in their NDIS miniport drivers, although this will be new code that isn't needed on the desktop versions of Windows. Still, NDIS is similar enough from Windows NT to Windows CE that Microsoft recommends you adapt a sample miniport driver or port an existing miniport driver from another operating system, such as Windows NT, rather than write one from scratch.
Sample Code for NDIS Drivers
Windows CE includes the following sample miniport drivers:
- Proxim wireless Ethernet PC Card.
- FastIR, which uses the National Semiconductor PC87338 chipset.
- NE2000-compatible network adapters for PCI, ISA and PCMCIA busses.
- IrSIR infrared serial port intermediate miniport driver.
- Xircom CE 2 Ethernet PC Card.
For tools and information pertaining to developing and debugging products based on Windows CE, see Windows CE Platform Builder, scheduled to ship shortly. This page will be updated when the platform builder ships.
For extensive information on writing device drivers for Windows CE, see the Windows CE Driver Development Kit (DDK) documentation, also included in Windows CE Platform Builder.