When porting a layered driver to new hardware, you generally do not need to modify the code in the MDD layer. It is also less complicated to duplicate an existing layered driver and add or remove functionality than to create a new driver from scratch. Many of the drivers included in Windows Embedded CE take advantage of the layered driver architecture.
The MDD/PDD architecture can help driver developers save time during the development of driver updates, such as providing quick fix engineering (QFE) fixes to customers. Restricting modifications to the PDD layer increases development efficiencies.
Lesson Summary
Windows Embedded CE supports native and stream drivers. Native drivers are the best choice for any devices that are not stream devices. For example, a display device driver must be able to process data in arbitrary patterns and is therefore a good candidate for a native driver. Other devices, such as storage hardware and serial ports, are good candidates for stream drivers because these devices handle data primarily in the form of ordered streams of bytes as if they were files. Both native and stream drivers can use either the monolithic or layered driver design. In general, it is advantageous to use the layered architecture based on MDD and PDD because it facilitates code reuse and the development of driver updates. Monolithic drivers might be the right choice if you want to avoid the additional function calls between MDD and PDD for performance reasons.
Lesson 2: Implementing a Stream Interface Driver
On Windows Embedded CE, a stream driver is a device driver that implements the stream interface API. Regardless of hardware specifics, all CE stream drivers expose the stream interface functions to the operating system so the Device Manager of Windows Embedded CE can load and manage these drivers. As the name implies, stream drivers are suitable for I/O devices that act as sources or sinks of streams of data, such as integrated hardware components and peripheral devices. It is also possible for a stream driver to access other drivers to provide applications with more convenient access to the underlying hardware. In any case, you need to know the stream interface functions and how to implement them if you want to develop a fully functional and reliable stream driver.
After this lesson, you will be able to:
■ Understand the purpose of Device Manager.
■ Identify stream driver requirements.
■ Implement and use a stream driver.
Estimated lesson time: 40 minutes.
Device Manager
The Windows Embedded CE Device Manager is the OS component that manages the stream device drivers on the system. The OAL (Oal.exe) loads the kernel (Kernel.dll), and the kernel loads Device Manager during the boot process. Specifically, the kernel loads the Device Manager shell (Device.dll), which in turn loads the actual core Device Manager code (Devmgr.dll), which again is in charge of loading, unloading, and interfacing with stream drivers, as illustrated in Figure 6-2.
Figure 6-2 The Device Manager in Windows Embedded CE 6.0
Stream drivers can be loaded as part of the operating system at boot time or on demand when the corresponding hardware is connected in a Plug and Play fashion. User applications can then use the stream drivers through file system APIs, such as ReadFile and WriteFile, or by means of DeviceIoControl calls. Stream drivers that Device Manager exposes through the file system appear to applications as regular file resources with special file names. The DeviceIoControl function, on the other hand, enables applications to perform direct input and output operations. However, applications interact with the stream drivers indirectly through Device Manager in both cases.
Driver Naming Conventions
For an application to use a stream driver through the file system, the stream driver must be presented as a file resource so that the application can specify the device file in a CreateFile call to get a handle to the device. Having obtained the handle, the application can then use ReadFile or WriteFile to perform I/O operations, which Device Manager translates into corresponding calls to stream interface functions to perform the desired read and write actions. For Windows Embedded CE 6.0 to recognize stream device resources and redirect file I/O operations to the appropriate stream drive, stream drivers must follow a special naming convention that distinguishes these resources from ordinary files.
Windows Embedded CE 6.0 supports the following naming conventions for stream drivers:
■ Legacy names The classical naming convention for stream drivers consists of three upper case letters, a digit, and a colon. The format is XXX[0-9]:, where XXX stands for the three-letter driver name and [0-9] is the index of the driver as specified in the driver's registry settings (see Lesson 3, "Configuring and Loading a Driver"). Because the driver index has only one digit, legacy names only support up to ten instances of a stream driver. The first instance corresponds to index 1, the ninth instance uses index 9, and the tenth instance refers to index 0. For example, CreateFile(L"COM1:"...) accesses the stream driver for the first serial port by using the legacy name COM1:.
The legacy naming convention does not support more than ten instances per stream driver.
■ Device names To access a stream driver with an index of ten or higher, you can use the device name instead of the legacy name. The device name conforms to the format \$device\XXX[index], where \$device\ is a namespace that indicates that this is a device name, XXX stands for the three-letter driver name and [index] is the index of the driver. The index can have multiple digits. For example, CreateFile(L"\$device\COM11"...) would access the stream driver for the eleventh serial port. Stream drivers with a legacy name can also be accessed, such as CreateFile(L"\$device\COM1"...).
Although legacy names and device names differ in format and supported range of driver instances, CreateFile returns the same handle with access to the same stream driver in both cases.
■ Bus name Stream drivers for devices on a bus, such as Personal Computer Memory Card International Association (PCMCIA) or Universal Serial Bus (USB), correspond to bus names that the relevant bus driver passes to Device Manager when enumerating the drivers available on the bus. Bus names relate to the underlying bus structure. The general format is \$bus\BUSNAME_[bus number]_[device number]_[function number], where \$bus\ is a namespace that indicates that this is a bus name, BUSNAME refers to the name or type of the bus, and [bus number], [device number], and [function number] are bus-specific identifiers. For example, CreateFile(L"\$ bus\PCMCIA_0_0_0"…) accesses device 0, function 0, on PCMCIA bus 0, which might correspond to a serial port.
Bus names are primarily used to obtain handles for unloading and reloading bus drivers and for power management, but not for data read and write operations.