Exam objectives in this chapter:
■ Loading and using device drivers on Windows Embedded CE
■ Managing interrupts on the system
■ Understanding memory access and memory handling
■ Enhancing driver portability and system integration
■ To complete the lessons in this chapter, you must have the following:
■ At least some basic knowledge about Windows Embedded CE software development, including fundamental concepts related to driver development, such as I/O control (IOCTL) and Direct Memory Access (DMA).
■ An understanding of interrupt handling and how to respond to interrupts in a device driver.
■ Familiarity with memory management in C and C++, as well as and knowledge of how to avoid memory leaks.
■ A development computer with Microsoft Visual Studio® 2005 Service Pack 1 and Platform Builder for Windows Embedded CE 6.0 R2 installed.
Lesson 1: Understanding Device Driver Basics
On Windows Embedded CE, a device driver is a dynamic-link library (DLL) that provides a layer of abstraction between the underlying hardware, OS, and applications running on the target device. The driver exposes a set of known functions and provides the logic to initialize and communicate with the hardware. Software developers can then call the driver's functions in their applications to interact with the hardware. If a device driver adheres to a well-known application programming interface (API) such as Device Driver Interface (DDI), you can load the driver as part of the operating system, such as a display driver or a driver for a storage device. Without having to know details about the physical hardware, applications can then call standard Windows API functions, such as ReadFile or WriteFile, to use the peripheral device. You can support different types of peripherals by adding different drivers to the OS design without having to reprogram your applications.
After this lesson, you will be able to:
■ Differentiate between native and stream drivers.
■ Describe the advantages and disadvantages of monolithic and layered driver architectures.
Estimated lesson time: 15 minutes.
Native and Stream Drivers
A Windows Embedded CE device driver is a DLL that exposes the standard DllMain function as the entry point, so that a parent process can load the driver by calling LoadLibrary or LoadDriver. Drivers loaded by means of LoadLibrary can be paged out, but the operating system does not page out drivers loaded through LoadDriver.
While all drivers expose the DllMain entry point, Windows Embedded CE supports two different types of drivers: native drivers and stream drivers. Native CE drivers typically support input and output peripherals, such as display drivers, keyboard drivers, and touchscreen drivers. The Graphics, Windowing, and Events Subsystem (GWES) loads and manages these drivers directly. Native drivers implement specific functions according to their purpose, which GWES can determine by calling the GetProcAddress API. GetProcAddress returns a pointer to the desired function or NULL if the driver does not support the function.
Stream drivers, on the other hand, expose a well-known set of functions that enable Device Manager to load and manage these drivers. For Device Manager to interact with a stream driver, the driver must implement the Init, Deinit, Open, Close, Read, Write, Seek, and IOControl functions. In many stream drivers, the Read, Write, and Seek functions provide access to the stream content, yet not all peripherals are stream devices. If the device has special requirements beyond Read, Write, and Seek, you can use the IOControl function to implement the required functionality. The IOControl function is a universal function that can accommodate any special needs of a stream device driver. For example, you can extend a driver's functionality by passing a custom IOCTL command code and input and output buffers.
Native drivers must implement different types of interfaces depending on the nature of the driver. For complete information about the supported driver types, see the section "Windows Embedded CE Drivers" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN® Web site at http://msdn2.microsoft.com/en-us/library/aa930800.aspx.
Monolithic vs. Layered Driver Architecture
Native and stream drivers only differ in terms of the APIs they expose. You can load both types of drivers during system startup or on demand, and both types can use a monolithic or layered design, as illustrated in Figure 6-1.
Figure 6-1 Monolithic and layered driver architectures
Monolithic Drivers
A monolithic driver relies on a single DLL to implement both the interface to the operating system and applications, and the logic to the hardware. The development costs for monolithic drivers are generally higher than for layered drivers, yet despite this disadvantage, monolithic drivers also have advantages. The primary advantage is a performance gain by avoiding additional function calls between separate layers in the driver architecture. Memory requirements are also slightly lower in comparison to layered drivers. A monolithic driver might also be the right choice for uncommon, custom hardware. If no layered driver code exists that you could reuse, and if this is a unique driver project, you might find it advantageous to implement a driver in a monolithic architecture. This is especially true if reusable monolithic source code is available.
Layered Drivers
In order to facilitate code reuse and lower development overhead and costs, Windows Embedded CE supports a layered driver architecture based on model device driver (MDD) and platform device driver (PDD). MDD and PDD provide an additional abstraction layer for driver updates and to accelerate the development of device drivers for new hardware. The MDD layer contains the interface to the operating system and the applications. On the hardware side, MDD interfaces with the PDD layer. The PDD layer implements the actual functions to communicate with the hardware.