module 6: device driver architecture - pudn.comread.pudn.com/downloads83/doc/319149/device...

86
Contents Overview 1 Built-In Vs. Installable Drivers 2 Device Manager 23 Device Manager (continued) 24 ActivateDeviceEx 25 Registry Enumerator 27 Services 32 Bus Drivers 42 DMA 46 Resource Manager 51 Interrupt Model 54 Device Driver Power Management 70 CETK 79 Lab 6: Implementing a Stream Interface Driver 82 Review 83 Module 6: Device Driver Architecture

Upload: others

Post on 30-Mar-2020

19 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Contents

Overview 1

Built-In Vs. Installable Drivers 2

Device Manager 23

Device Manager (continued) 24

ActivateDeviceEx 25

Registry Enumerator 27

Services 32

Bus Drivers 42

DMA 46

Resource Manager 51

Interrupt Model 54

Device Driver Power Management 70

CETK 79

Lab 6: Implementing a Stream Interface Driver 82

Review 83

Module 6: Device Driver Architecture

Page 2: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, places or events is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2001 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, Visual Studio are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries. The names of actual companies and products mentioned herein may be the trademarks of their respective owners.

Page 3: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 1

Overview Built-In Vs. Installable Drivers

Device Manager

ActivateDeviceEx

Registry Enumerator

Services

Bus Drivers

DMA

Resource Manager

Interrupt Model

Device Driver Power Management

CETK

In this module, you will learn about device driver architecture.

After completing this module, you will be able to understand the following:

Explain the differences between built-in and installable drivers. Identify the role of Device Manager in Windows CE .NET. Describe the ActivateDeviceEx function. Describe Registry Enumerator. Describe the Services.exe module. Explain Bus drivers. Identify direct memory access (DMA) in Windows CE .NET. Identify the role of Resource Manager in Windows CE .NET. Describe the interrupt model. Explain power management. Describe CE Test Kit (CETK).

Page 4: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

2 Module 6: Device Driver Architecture

Built-In Vs. Installable Drivers Built-in Drivers

Also referred to as native device driversLoaded in the GWES process space at system bootGenerally for devices that are hardwired or must be loaded at system boot upUses a custom interface

Installable Drivers Also referred to as streams device driversDynamically loaded by the Device Manager either at boot or on insertion notificationExist as standalone DLLsUses the streams interface driver architecture

Hybrid DriversExpose both a custom-purpose interface and a stream interface

Windows CE device drivers are either user mode dynamic-link libraries (DLLs), or are statically linked to the operating system as object files.

Built-In Drivers Built-in drivers are also refereed to as native device drivers.

Some examples of the built-in drivers are the battery driver and the notification LED driver. These drivers are statically linked to GWES, so that means that they do not exist as a standalone DLL. The notification led driver is linked via the nleddrv.lib and the battery driver is linked via the battery.lib.

Other examples of Native Drivers are Display and keyboard. These are stand alone DLLs that are loaded in the GWES process space and have a custom interface exposed to the operating system.

Installable Drivers Installable drivers are also refereed to as streams device drivers.

Installable drivers are user mode DLLs that are loaded dynamically by the Device Manger, device.exe. Installable drivers use the streams interface driver architecture to get commands from the Device Manager and from applications by means of file system calls.

Examples of installable device drivers are:

PCMCIA driver (PCMCIA.DLL) Serial driver (SERIAL.DLL) ATAFLASH driver (ATA.DLL) Ethernet driver (NE2000.DLL, SMSC100FD.DLL)

Page 5: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 3

Hybrid Drivers 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.

Hybrid device drivers are ones that primarily need to expose a custom-purpose interface, but also need to interact with specific parts of the operating system in ways that are only allowed for stream interface drivers. For example, the PC card socket has both native and streams interfaces.

Page 6: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

4 Module 6: Device Driver Architecture

Monolithic Vs. Layered Device Drivers

Hardware

GWES

DDI functions DDI functions

Monolithic Device Driver

Layered Device DriverMDD

PDDDDSI functions

There are two types of sample device drivers provided in Platform Builder:

Layered Device Driver Monolithic Device Driver

Layered Device Driver The Layered device drivers split the code into an upper layer called the Model Device Driver (MDD) and a lower layer called the Platform Dependent Driver (PDD).

The MDD layer contains code that is common to all drivers of a given type. The PDD layer consists of the code that is specific to a given hardware device or platform. The MDD calls specific PDD routines to access the hardware or hardware specific information. When using a layered driver, you can reuse the common MDD code provided by Microsoft, and only write new PDD code that is specific to the your hardware. Or, if porting one of the sample drivers to a new hardware, you only need to port the PDD layer, and the MDD layer can be used directly from the sample driver.

The layered driver style is not required and may not be appropriate for all drivers. In particular, splitting device driver code into two layers imposes additional function call overhead in the operation of device driver. For performance critical situations, a monolithic driver may be more appropriate.

In general, Microsoft provides the MDD for a layered driver. The MDD is common to all platforms and functions, both as source code and as a library. It performs the following tasks:

Links to the PDD layer and defines the DDSI functions it expects to call in that layer

Exposes DDI functions to the operating system Handles complex tasks such as interrupt processing

Page 7: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 5

Each MDD handles a specific set of devices, such as audio hardware or touch screens. The device driver interface (DDI) is a set of functions exposed by an MDD layer or monolithic driver and called by other OS modules. The device driver service provider interface (DDSI) is a set of functions exposed by the PDD layer of a layered device driver and called by the MDD. Classes of related device drivers can share the same DDI.

In general, the MDD requires no changes. If you choose to modify the MDD, be aware that Microsoft does not test, warrant, or support custom MDDs. You are responsible for all further MDD maintenance if Microsoft supplies an updated MDD in order to fix bugs or to support later versions of Windows CE.

Unlike the MDD layer, the PDD layer must be written specifically to your target platform. The PDD generally consists of functions that perform specific discrete tasks. These functions serve to isolate the MDD from the specifics of the hardware. Because the PDD is hardware-dependent, you must create a customized PDD for your platform hardware, or port one of Microsoft's sample PDD layers to your hardware. To assist you, Microsoft provides several sample PDD layers for various built-in devices.

The sample code for the Wavedev drivers is a good example of the layered, streams-interface driver provided by Microsoft. The sample driver is located in %WINCEROOT%\public\common\oak\drivers\WAVEDEV. This code is provided as a convenience and does not imply any restrictions in the way that audio hardware for Windows CE must function. An audio driver could be implemented that is monolithic, is polled, and that drives a peripheral device, if a user wants to do so.

The sample audio driver MDD layer implements a single audio device capable of playing and/or recording pulse code modulation (PCM) waveform audio. The sample supports simultaneous recording and playing but may also be used with audio devices that can perform only one of those functions at a time. The PDD layer is responsible for communicating with the audio hardware to start and stop playing and recording and to initialize and deinitialize the hardware.

Monolithic Device Driver You can forego the MDD and PDD layers by implementing your device driver as a monolithic driver. Source code for a monolithic driver consists of both interrupt service thread code and platform specific code. For example, if performance is a critical factor, a monolithic driver might be a better choice than a layered driver because a monolithic driver avoids the overhead associated with the function calls that take place between the MDD and PDD layers. You might also choose to implement a monolithic driver if the capabilities of the device in question are well matched to the tasks that the functions in the MDD layer perform. In such a case, implementing a monolithic driver might be simpler and more efficient than implementing a layered driver. However, regardless of whether you implement a monolithic driver or a layered driver, you can base your implementation on the source code for any of the sample drivers.

The provided MDD sources are simplistic and should be treated as driver templates. You are encouraged to modify these sources to fit their needs.

Note

Page 8: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

6 Module 6: Device Driver Architecture

Native Device Drivers

Used for built-in devicesCustom interfaces but a standard set of functionality Statically linked to an executable, while other are DLLsSample native device exist for:

Display, Battery, Keyboard, Touch, LED

Some types of devices—such as keyboards and displays—have a custom interface to the operating system. The drivers for these types of devices are called native device drivers because the interfaces they use are specific to Windows CE.

In general, native device drivers are of interest only to original equipment manufacturers (OEMs) who build Windows CE–based platforms. Independent hardware vendors (IHVs) who develop drivers for add-on hardware have no need to design or customize native device drivers. Therefore, the following sections regarding native device drivers are directed primarily to OEMs.

Microsoft defines custom interfaces for each type of native device driver. However, although each type of native device driver has a custom interface, native device drivers present a standard set of functionality for all devices of a particular class. This enables the Windows CE operating system to treat all instances of a particular device class alike, despite any physical differences.

For example, many Windows CE–based platforms use some type of LCD panel as a display. However, there is a wide variety of these panels on the market that have different operating characteristics, such as resolution, bit depth, memory interleaving, and so on. By making all display drivers conform to the same interface, Windows CE can treat all display devices the same, regardless of the physical differences between the devices themselves.

Microsoft Windows CE Platform Builder provides sample native device drivers:

Display Battery Keyboard Touch screen Notification LED

Page 9: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 7

If your target platform contains devices not listed above, you need to create your own native device drivers for the devices. However, if your platform includes devices from the list, consider porting the sample native device drivers to your platform, rather than developing your own native device drivers. By porting the tested device drivers, you can save time and avoid bugs. Even so, it is not mandatory to use the sample code provided with the Platform Builder for the native device driver or for any other driver model. The Platform Builder supplies driver samples solely as a convenience to help you develop your drivers rapidly.

Page 10: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

8 Module 6: Device Driver Architecture

Streams Drivers

What is a Stream Driver?

Common interface and functions to all Streams drivers

Ideal for I/O devices that are a data source or data sink

Interface functions similar to file system APIs—such as ReadFile, IOControl

Streams drivers are used to access, from the application level, the physical peripheral device as if it was a file.

A stream interface driver is any driver that exposes the stream interface functions, regardless of the type of device controlled by the driver.

The stream interface drivers all use the same interface and expose a common set of functions—the stream interface functions. This is in contrast to native device drivers, which each use special, single-purpose interfaces.

The stream interface is appropriate for any I/O device that can be thought of logically as a data source or a data sink. That is, any peripheral that produces or consumes streams of data as its primary function is a good candidate to expose the stream interface. An example is a serial port device. An example of a device that does not produce or consume data in the traditional sense would be a display device, and indeed, the stream interface is not exposed for controlling display hardware

The stream interface functions themselves are designed to closely match the semantics of the normal file system APIs—such as ReadFile, IOControl. As a side effect of this design, devices that are managed by stream interface drivers are exposed to applications through the file system; applications interact with the driver by opening special files in the file system.

This metaphor of treating devices as special files is common to many operating systems, including the desktop versions of Microsoft Windows and most Unix-like operating systems as well. For example, in the desktop versions of Microsoft Windows, printer devices have traditionally been represented by the LPTx: special file names, serial ports by the COMx: special file names, etc.

In very general terms, a Streams interface driver is a mechanism of turning a piece of hardware into a file.

Note

Page 11: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 9

Streams Drivers Architecture

The diagram on the slide illustrates the architecture of stream interface drivers for built-in devices that are loaded by the Device Manager at boot time.

As you can see, applications communicate with the hardware via the stream interface driver and device manager using the file system APIs.

A stream interface driver receives commands from the Device Manager and from applications by means of file system calls. The driver encapsulates all of the information that is necessary to translate those commands into appropriate actions on the devices that it controls.

All stream interface drivers, whether they manage built-in devices or installable devices; or whether they are loaded at boot time or loaded dynamically, have similar interactions with other system components.

Page 12: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

10 Module 6: Device Driver Architecture

Implementing Streams Drivers

How do you implement a Stream Driver?

Select a device file name prefix

Implement the required entry points

Create the *.DEF file

Create the registry values for your driver

To illustrate implementing streams drivers, let us use an example from platform builder. For the following instructions, you will be referring to WINCE400\PLATFORM\SA11X0BD\DRIVERS\PWRBUTTON directory as the example. This driver is a power button driver for the SA110XBD platform. The basic steps on how to create a stream driver:

1. The first step in creating a streams driver is figuring what prefix name to call it. The prefix name refers to the XXX in the entry-point names. For example, if your device file name prefix is PWR, you will implement PWR_Close, PWR_Init, and so on.

2. Next, you will need to implement the required entry points. These entry points are the standard file I/O functions that are used by the kernel. The entry points are summarized below:

Function Description XXX_Close Closes the device context identified by

hOpenContext.

XXX_Deinit Called by the Device Manager to deinitialize a device.

XXX_Init Called by the Device Manager to initialize a device.

XXX_IOControl Sends a command to a device.

XXX_Open Opens a device for reading, writing, or both. An application indirectly invokes this function when it calls CreateFile to open special device file names.

XXX_PowerDown Ends power to the device. It is useful only with devices that can be shut off under software control.

XXX_PowerUp Restores power to a device.

Page 13: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 11

XXX_Read Reads data from the device identified by the open context.

XXX_Seek Moves the data pointer in the device.

XXX_Write Writes data to the device.

In WINCE400\PLATFORM\SA11X0BD\DRIVERS\PWRBUTTON\pwrbuttonpdd.c, you can see actual code examples of the above entry points.

3. Next, you will need to create the *.DEF file. For our example, the *.DEF file is named PWRBUTTON.DEF. This is the definition file that is passed to the linker. This file contains the exported function names.

You need to name the *.DLL and the *.DEF the same names.

You should use a C declspec here as well.

Here is an example copy of the PWRBUTTON.DEF file: ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; /* Copyright � 1999 Intel Corp. */ LIBRARY PWRBUTTON EXPORTS PWR_Init PWR_Deinit PWR_Open PWR_Close PWR_Read PWR_Write PWR_Seek PWR_IOControl PWR_PowerDown PWR_PowerUp PWR_PowerHandler PWR_DllEntry

4. Next, you will need to create the registry entries for this driver so that the driver can be loaded by Device.exe. Here is an example of the registry needed for the power button driver. [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PWRBUTTON] "Prefix"="PWR" "Dll"="PwrButton.Dll" "Order"=dword:2 "Ioctl"=dword:4 In addition, you can store any driver specific information in the registry that can be used later by the driver. The Dll entry is what device.exe looks for when loading the driver. Note the Prefix setting. In this case, the prefix is PWR. This PWR corresponds to the same 3 letter in each entry point. Also, the registry key named IOCTL is check by device.exe and if present

Note

Note

Page 14: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

12 Module 6: Device Driver Architecture

device.exe calls PWR_IOControl with the dwCode parameter set. That value in this case is 4. The ORDER is the load order of this driver.

Page 15: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 13

Streams Entry Points: Open and Close

XXX_Open-Opens a device for reading and/or writing. -An application indirectly invokes this function when it calls CreateFile to open special device file names.-When this function is called, your device should allocate the resources that it needs for each open context and prepare for operation

XXX_Close-In response to CloseHandle, the operating system invokes this function.

Here are details on each required stream driver entry point function.

XXX_Open DWORD XXX_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode );

Parameters hDeviceContext

Handle to the device context. The XXX_Init function creates and returns this handle.

AccessCode Specifies the requested access code of the device. The access is a combination of read and write.

ShareMode Specifies the requested file share mode of the PC Card device. The share mode is a combination of file read and write sharing.

This function opens a device for reading and/or writing. An application indirectly invokes this function when it calls CreateFile to open special device file names.

When this function is called, your device should allocate the resources that it needs for each open context and prepare for operation. This might involve preparing the device for reading or writing and initializing data structures it uses for operation.

A device context is a graphics device interface (GDI) structure containing information that governs the display of text and graphics on a particular output device. A device context stores, retrieves, and modifies the attributes of graphic objects and specifies graphic modes. The graphic objects stored in a device context include a pen for line drawing, a brush for painting

Syntax

Note

Page 16: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

14 Module 6: Device Driver Architecture

and filling, a font for text output, a bitmap for copying or scrolling, a palette for defining the available colors, and a region for clipping.

XXX_Close BOOL XXX_Close( DWORD hOpenContext );

Parameters hOpenContext

Handle returned by the XXX_Open function, used to identify the open context of the device.

An application calls CloseHandle to stop using a stream interface driver. The hFile parameter specifies the handle associated with the device context. In response to CloseHandle, the operating system invokes this function.

The file handle specified for hOpenContext is no longer valid after this function returns; if an application tries to perform stream I/O operations on that handle after calling CloseHandle, those operations fail.

Syntax

Page 17: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 15

Streams Entry Points: Init and Deinit

XXX_Init

Called when Device Manager loads the driver

Initializes resources that are to be used

Memory mapping

XXX_Deinit

Called when Device Manager unloads the driver

Frees allocated resources, stops the IST

Here are details on each required stream driver entry point function.

XXX_Init DWORD XXX_Init( DWORD dwContext );

Parameters dwContext

Specifies a pointer to a string containing the registry path to the active key for the stream interface driver

The Device Manager calls this function as a result of a call to the ActivateDeviceEx function. When the user starts using a device, such as when a PC Card is inserted, the Device Manager calls this function to initialize the device. This function is not called by applications. Stream interface drivers that support multiple instances of the same type of special device file name, such as sample serial port driver of Microsoft, which supports multiple COMx: device filenames—should expect their XXX_Init function to be called once for each instance. For such drivers, this function should return separate handles each time it is called. Stream interface drivers that only support a single special device file name then this function can return any non-zero value

After this function returns, the Device Manager checks the registry for a key named Ioctl for the driver, and if such a key is present, the Device Manager calls XXX_IOControl, passing the value specified by Ioctl as the dwCode parameter. Your driver can use this function to finish initializing itself after it has been installed.

XXX_Deinit BOOL XXX_Deinit( DWORD hDeviceContext );

Parameters hDeviceContext

Syntax

Syntax

Page 18: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

16 Module 6: Device Driver Architecture

Handle to the device context. The XXX_Init function creates and returns this identifier.

The Device Manager calls the XXX_Deinit function of a driver as a result of a call to the DeactivateDevice function. Your stream interface driver should free any resources it has allocated and terminate.

Page 19: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 17

Streams Entry Points: Read, Write and Seek

XXX_Read

Invoked when application calls ReadFile function

XXX_Write

Invoked when application calls WriteFile function

XXX_Seek

Allows moving the current I/O pointer

Here are details on each required stream driver entry point function.

XXX_Read DWORD XXX_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count );

Parameters hOpenContext

Handle to the open context of the device. The XXX_Open function creates and returns this identifier.

pBuffer Pointer to the buffer that stores the data read from the device. This buffer should be at least Count bytes long.

Count Specifies the number of bytes to read from the device into pBuffer

This function reads data from the device identified by the open context.

An application calls the ReadFile function to read from the device. The operating system, in turn, invokes this function. The hFile parameter is a handle to your device. The pData parameter points to the buffer that contains the data read from the device. The Size parameter indicates the number of bytes that the application wants to read from the device. The pSizeRead parameter is a pointer to a value where this function can store the number of bytes actually read from the device. The value returned from this function is equal to the value contained in pSizeRead, unless this function returns –1 for an error. If this function returns an error, pSizeRead contains 0.

XXX_Write

Syntax

Syntax

Page 20: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

18 Module 6: Device Driver Architecture

DWORD XXX_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count );

Parameters hOpenContext

Handle to the open context of the device. The call to the XXX_Open function returns this identifier.

pBuffer Pointer to the buffer that contains the data to write

Count Specifies the number of bytes to write from the pSourceBytes buffer into the device

This function writes data to the device.

An application uses the WriteFile function to write to the device. The operating system, in turn, invokes this function.

XXX_Seek DWORD XXX_Seek( DWORD hOpenContext, long Amount, WORD Type );

Parameters hOpenContext

Handle to the open context of the device. The XXX_Open function creates and returns this identifier.

Amount Specifies the number of bytes to move the data pointer in the device. A positive value moves the data pointer toward the end of the file, and a negative value moves it toward the beginning.

Type Specifies the starting point for the data pointer:

An application calls the SetFilePointer function to move the data pointer in the device. The operating system, in turn, invokes this function. If your device is capable of being opened multiple times, this function modifies only the data pointer for the instance specified by hOpenContext.

Syntax

Page 21: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 19

Streams Entry Points: IOControl

XXX_IOControl

Allows performing custom operations that do not necessarily apply to files

I/O control code identifies the operation

I/O control code is device-specific

Streams drivers have an IOControl function to handle miscellaneous requests:

XXX_IOControl BOOL XXX_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut );

Parameters hOpenContext

Handle to the open context of the device. The XXX_Open function creates and returns this identifier.

dwCode Specifies a value indicating the I/O control operation to perform. These codes are device-specific and are usually exposed to programmers by means of a header file.

pBufIn Pointer to the buffer containing data to be transferred to the device

dwLenIn Specifies the number of bytes of data in the buffer specified for pBufIn

pBufOut Pointer to the buffer used to transfer the output data from the device

dwLenOut Specifies the maximum number of bytes in the buffer specified by pBufOut

pdwActualOut Pointer to the DWORD buffer that this function uses to return the actual number of bytes received from the device

Syntax

Page 22: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

20 Module 6: Device Driver Architecture

This function sends a command to a device.

An application uses the DeviceIOControl function to specify an operation to be performed. The operating system, in turn, invokes this XXX_IOControl. The dwCode parameter contains the input or output operation to be performed; these codes are usually specific to each device driver and are exposed to application programmers by means of a header file that the device driver programmer makes available.

If the HKEY_LOCAL_MACHINE\Drivers\BuiltIn\YourDevice\Ioctl registry key is defined for your device driver, the Device Manager will invoke your device driver's XXX_IOControl function when it loads and initializes your device driver. It uses the Ioctl registry value for the dwCode parameter and NULL for the pBufIn and pBufOut parameters. Your device might use this option to load other modules that require the installation of the basic device driver, or as a trigger to perform any other actions that are not suited to being part of your device driver's XXX_Init function. The actual sequence of calls made to a device driver in this scenario is XXX_Init, XXX_Open, XXX_IOControl, and XXX_Close. The XXX_Open call is necessary for the Device Manager to get a valid context handle, and XXX_Close is necessary to leave the device driver available to serve applications.

Page 23: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 21

Streams Entry Points: PowerUp and PowerDown

XXX_PowerDown

Restores power to a device

XXX_PowerUp

Suspends power to the device

Useful only with devices that can be shut off under software control

Here are details on each required entry point function:

XXX_PowerDown void XXX_PowerDown( DWORD hDeviceContext );

Parameters hDeviceContext

Handle to the device context. The call to the XXX_Init function returns this identifier.

This function suspends power to the device. It is useful only with devices that can be shut off under software control. Such devices are typically, but not exclusively, PC Card devices.

This function is required by the stream driver interface and is called in the power path of the kernel. IOCTL_POWER_XXX is separate and is not called in the power path of the kernel.

The operating system invokes this function to suspend power to the device. The operating system might call this function when it is about to enter the power-save mode. This function should not call any functions that might cause it to block, and it should return as quickly as possible. One strategy for returning quickly is to have this function set a global variable to indicate that a power loss occurred and to defer any complicated processing until later, when the driver's XXX_PowerUp function is called.

XXX_PowerUp void XXX_PowerUp( DWORD hDeviceContext );

Parameters hDeviceContext

Syntax

Syntax

Page 24: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

22 Module 6: Device Driver Architecture

Handle to the device context. The call to the XXX_Init function returns this identifier.

This function restores power to a device.

This function is required by the stream driver interface and is called in the power path of the kernel. IOCTL_POWER_XXX is separate and is not called in the power path of the kernel.

The operating system invokes this function to restore power to a device. The operating system might call this function as it is leaving its power-save mode. This function should not call any functions that may cause it to block, and it should return as quickly as possible. This function should set a global variable to indicate that power was restored and perform any necessary processing later.

Page 25: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 23

Device Manager

Device Manager implemented as Device.exe

User-level process that runs continously

Not part of the Kernel but launch via Kernel

HKEY_LOCAL_MACHINE\Init] "Launch20"=“Device.exe"

Separate application that interacts with the kernel, the registry and stream interface driver DLLs’

Provides ActivateDeviceEx and DeactivateDeviceEx APIs’

The Device Manager is the user-level process that is launched by the kernel early in the boot process. It is implemented as Device.exe.

Device Manager performs the following tasks:

Detects that a user has attached a peripheral to the Windows CE–based platform, and attempts to load a device driver for the peripheral.

For example, when a user inserts a PC Card, the Device Manager attempts to locate and load a device driver for that PC Card.

Registers special file names with the kernel that map the stream I/O functions that are used by applications to the implementations of those functions within a stream interface driver.

Finds the appropriate device driver for a peripheral by obtaining a Plug and Play identifier from the peripheral device or by invoking a detection routine to find a driver that can handle the device.

Loads and tracks drivers by reading and writing registry values. Unloads drivers when their devices are no longer needed. For example, the

Device Manager unloads a PC Card device driver when a user removes the card.

Tracks the following boot phases.

Page 26: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

24 Module 6: Device Driver Architecture

Device Manager (continued)

Device Driver Loading Process

DEVICE.EXE(I/O Resource Manager)Kernel

REGENUM.DLL

PCIBUS.DLL

loads

loads

loads

REGENUM.DLL(for ISA busses)

Registry enumerator is re-entrant

The device driver loading process involves:

At OEMInit time in the OAL, debugging services such as Ethernet debugging, serial debugging, or both, need to be started.

The OAL may need to configure and enumerate a bus, such as the PCI bus, to the extent needed in order to operate a device used for debugging services

The OAL is responsible for putting this resource information in a registry key and making it accessible to the driver

Then, Device.exe is loaded and started. It loads the Resource Manager to read a list of available resources from the registry.

Device.exe loads the registry enumerator from HKEY_LOCAL_MACHINE\Drivers\RootKey. The registry enumerator starts the process of scanning the registry for more buses and devices to be loaded for sub-keys of the root key.

The Registry Enumerator is used in place of an ISA bus driver. The OEM or user may use an ISA PnP bus driver is desired. All ISA resources must be allocated through the registry. RegEnum.dll can be used again to enumerate the registry for ISA devices.

One of the Drivers subkeys may be PCI. Normally, the DLL listed under the PCI key is the PCI bus driver (PCIBUS.DLL), which is loaded by the registry enumerator. Registry entries for the PCI bus driver include resources available to the PCI bus. The HKEY_LOCAL_MACHINE\Drivers\RootKey\Order value is set so that the PCI bus driver is usually loaded last. This is so all of the fixed resources are allocated before the flexible resources of the PCI devices are configured. Driver registry entries are listed under the PCI key, which represent devices that reside on the PCI bus.

Page 27: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 25

ActivateDeviceEx

What is ActivateDeviceEx?

A function used by Device.exe to load a device driver

A function used by the Registry Enumerator on each subkey it finds (to load driver)

ActivateDeviceEx uses the Dll, Prefix, Index and Flagsfields of registry.

Use ActivateDeviceEx to load drivers. You can useActivateDevice, but it simply calls ActivateDeviceEx.

The ActivateDeviceEx function is used to load a device driver. It reads the registry key specified in its lpszDevKey parameter (which is the first parameter) to get the DLL name, device prefix, index, and other values.

This function replaces ActivateDevice and RegisterDevice.

For example here is a sample registry entry:

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PM] "Prefix"="PWR" "Dll"="pm.dll" "Order"=dword:0 "Ioctl"=dword:4 "Index"=dword:0 "IClass"=multi_sz:"{A32942B7-920C-486b-B0E6-92A702A99B35}"

The function in the case call would be ActivateDeviceEx(“\\HKEY_LOCAL_MACHINE\\Drivers\\BuiltIn\\PM”, …) The function would then read the registry and get the DLL name, pwr.dll, the device prefix PWR, the index 0, and other values.

The Index value indicates port numbering with stream interface drivers, such as COM0: or COM1:. If there is no Index value, one will be generated.

Next, it adds the device to the active device list, and stores the relevant values there. If no device index was specified in the key named in lpszDevKey, then it assigns a free index. Then, it loads the device driver DLL in the process space of the Device Manager.

Here is the detailed definition of this function:

Note

Note

Page 28: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

26 Module 6: Device Driver Architecture

HANDLE ActivateDeviceEx( LPCWSTR lpszDevKey, LPCVOID lpRegEnts, DWORD cRegEnts, LPVOID lpvParam );

Parameters lpszDevKey

A pointer to a string containing the registry path of the device driver's key. This key contains the driver's DLL name, prefix, and other data.

lpRegEnts Points to an array of REGINI structures, each of which defines a registry value to be added to the device's instance key before its driver is loaded. This value is usually set to NULL.

cRegEnts A count of the number of REGINI structures to which lpRegEnts points. This affects a generalization of ActivateDevice.

lpvParam An opaque pointer to a driver-specific data structure. It can be used to pass parameters to the loaded driver without having to write them through the registry. lpvParam will be passed to the XXX_Init function as the second parameter.

Page 29: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 27

Registry Enumerator

What is a Registry Enumerator?

Loaded by Device Manager (Device.exe)

Finds new devices by reading registry entries

Re-entrant

Implemented as REGENUM.DLL

Code located at WINCE400\public\common\oak\DRIVERS\REGENUM

The Registry enumerator is part of the new device driver loading model.

It is loaded early on by device.exe during the driver loading process. It finds new devices by reading entries from the registry. It is re-entrant, can be called again while a previous invocation is still active,

and supports hierarchical usage so it can load itself over a different registry key. When it gets unloaded, it unloads anything it directly loaded.

It is implemented as REGENUM.DLL.

The code is located at WINCE400\public\common\oak\DRIVERS\REGENUM

Page 30: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

28 Module 6: Device Driver Architecture

Registry Enumerator (continued)

How does the Registry Enumerator work?

Device.exe loads Registry Enumerator checking HKLM\Drivers\RootKey

Init function is called with the HKLM\Drivers\RootKeykey

Registry Enumerator examines key below HKLM\Drivers\RootKey based on “Order” value

Registry Enumerator traverses subkeys of HKLM\Drivers\RootKey and initializes a driver for each entry.

The Registry Enumerator works in the following manner:

First in the registry you will notice the following settings. This settings overrides the default RootKey value. The RootKey value is usually defaulted to Drivers. In this case, the value is defaulted to Drivers\Builtin.

[HKEY_LOCAL_MACHINE\Drivers] "RootKey"="Drivers\\BuiltIn" [HKEY_LOCAL_MACHINE\Drivers\BuiltIn] "Dll"="RegEnum.dll" 1. The Device Manager loads the Registry Enumerator by checking

HKLM\Drivers\RootKey, where RootKey is set to “Drivers\Builtin” by default in the registry. It examines the Dll value for RegEnum.dll and invokes it. This is the root enumerator DLL.

2. The Registry Enumerator’s entry point, Init, not the stream interface function, is called with the HKLM\Drivers\Builtin key.

3. The Registry Enumerator examines the first level of keys just below the key passed to it (HKLM\Drivers\Builtin) according to Order.

4. It traverses the subkeys of HKEY_LOCAL_MACHINE\Drivers\BuiltIn (or where ever the HKLM\Drivers\RootKey value points) one by one, and initializes a driver for each entry.

5. It loads the DLL indicated by the DLL value, and then creates a subkey for the driver under HKLM\Drivers\Active. Then it calls the Init function or the driver's entry point and passes in a string containing that subkey.

During registry enumeration, the subkeys are defined as given in the following table:

Page 31: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 29

Subkey Type Description Order DWORD Smallest value gets loaded first.

Dll SZCHAR Which DLL to load for registry enumerator to load via ActiveDevice. Only the Dll key is required. If there is no Order value, the driver will get loaded after drivers with defined Orders.

Index DWORD Identifier when multiple instances are present, between 0 and 9, inclusively. The Index value indicates port numbering with stream interface drivers, such as COM0: or COM1:. If there is no Index value, one will be generated.

Flags DWORD Modifies the default behavior of the enumerator. The Flags value is a set of bits to modify the behavior of ActivateDevice; this value is optional and is set to zero if there is no value present. Bits 3 through 23 are reserved and must be set to zero. The driver being loaded may use bits 24 through 31 of the Flags value

Flags are defined as described in the following table.

Flag Value Description DEVFLAGS_NONE 0x00000000 No flags defined.

DEVFLAGS_UNLOAD 0x00000001 Unload driver after call to entry point returns, XXX_Init, or Init.

DEVFLAGS_LOADLIBRARY 0x00000002 Use LoadLibrary instead of LoadDriver.

DEVFLAGS_NOLOAD 0x00000004 Do not load DLL.

Page 32: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

30 Module 6: Device Driver Architecture

Registry Enumerator (continued)

Registry Enumerator Example (Simplified)

[HKLM\Drivers]"RootKey"="Drivers""Dll"="RegEnum.dll“

[HKLM\Drivers\Debug]"Dll"="RegEnum.dll""Order"=dword:0"Flags"=dword:1

[HKLM\Drivers\Debug\EDBG]"Flags"=dword:4

[HKLM\Drivers\Virtual]"Dll"="RegEnum.dll""Order"=dword:1"Flags"=dword:1

[HKLM\Drivers]"RootKey"="Drivers""Dll"="RegEnum.dll“

[HKLM\Drivers\Debug]"Dll"="RegEnum.dll""Order"=dword:0"Flags"=dword:1

[HKLM\Drivers\Debug\EDBG]"Flags"=dword:4

[HKLM\Drivers\Virtual]"Dll"="RegEnum.dll""Order"=dword:1"Flags"=dword:1

[HKLM\Drivers\Virtual\NDIS]"Dll"="NDIS.dll""Order"=dword:1"Prefix"="NDS“

[HKLM\Drivers\PCI]"Dll"="PCIbus.dll""Order"=dword:4"Flags"=dword:1

[HKLM\Drivers\Virtual\NDIS]"Dll"="NDIS.dll""Order"=dword:1"Prefix"="NDS“

[HKLM\Drivers\PCI]"Dll"="PCIbus.dll""Order"=dword:4"Flags"=dword:1

Here is a very simple example to illustrate how the registry enumerator works:

1. When Device.exe is loaded, it examines the HKEY_LOCAL_MACHINE\Drivers key for the RootKey value. It then examines the key pointed to by RootKey, in this case Drivers, for a DLL to load. In this example, it is the Registry Enumerator (RegEnum.dll). The Registry Enumerator’s entry point, Init, not the stream interface function, is called with the HKEY_LOCAL_MACHINE\Drivers key. The Registry Enumerator examines the first level of keys just below the key passed to it according to Order. In this example, the Drivers\Debug key has Order of 0, so it is examined first.

2. Next, the DLL loaded for Drivers\Debug is again the Registry Enumerator; it is re-entrant. The Flags value is set to 1, meaning that RegEnum.dll is unloaded, or its reference count is decremented. Its reference count is decremented in this example. Init is called with the Drivers\Debug key. It examines the immediate subkeys for ordering information. In this example, Drivers\Debug\EDBG has the lowest and only Order key. The Flags value for Drivers\Debug\EDBG is 0x0004, which indicates that the DLL is not loaded because the Ethernet debugging adapter driver has already been loaded by the OAL.

3. The next key under the Drivers key is Drivers\Virtual with Order equal to 1. The Registry Enumerator is activated through a call to its Init function with the parameter Drivers\Virtual. Init examines the immediate subkeys for ordering information. In this example, Drivers\Virtual\NDIS has the lowest and only Order key. The Dll value is examined and NDIS.dll is loaded. The entry point, NDS_Init, is called with driver’s Active key due the properties of the stream interface, which contains a Key value equal to Drivers\Virtual\NDIS. There are no more keys under Drivers\Virtual, so the Registry Enumerator's Init function returns.

Page 33: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 31

4. The last key examined is Drivers\PCI. In this example, the Drivers\PCI key has Order of 4. The DLL is PCIbus.dll, a PCI bus driver and enumerator. This driver is explained more in detail later. This driver is then loaded. This driver takes care of configuring and enumerating the PCI bus, allocating resources to the devices it finds on the bus, and populating the registry with device instances.

Page 34: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

32 Module 6: Device Driver Architecture

Services

Purpose of a Service

Services.exe Vs. Device.exe

Activating / Controlling a Service

Registering a Service Programmatically

Stopping a Running Service

Services.exe at System Startup

Services API’s

In this section, you will learn about the service module. You will learn the differences between services.exe and device.exe. You will learn how to activate/control a service. You will also learn how to register a service programmatically. Then, you will learn how to stop a running service. You will learn about services.exe at system startup and services APIs’.

Page 35: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 33

Purpose of a Service

Supplements existing device.exeHosts services that do not require direct access to the systemIsolates those services from the system servicesEnhances device stability in a service failure and decreases the likelihood of a system crashProvides a super service

Services.exe is a process that supplements the existing Device.exe process in loading system services. It expands the Device.exe functionality by hosting services that do not require direct access to the system and isolating those services from the system services. In the event of a service failure, separating the services protects a system service from failing as well. This functionality allows for enhanced device stability in a service failure and decreases the likelihood of a crash of the system.

A super service is an enhanced standard service designed to allow all ports to be monitored by Services.exe itself. The super service is notified when it is required to handle any network traffic. Use of a super server can be advantageous, because the accept threads for all services being handled by a super service total only 1. Without a super service, each service must create its own thread, resulting in additional system overhead.

Services.exe is launched via the registry.

[HKEY_LOCAL_MACHINE\init] "Launch60"="services.exe" "Depend60"=hex:14,00

Note

Page 36: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

34 Module 6: Device Driver Architecture

Service.exe Vs. Device.exe

Device.exe loads device drivers that manage devices

Sevice.exe loads device drivers that manages software services

Services.exe is like Device.exe that hosts multiple services.

To use both Device.exe and Services.exe, 2 of the 32 available Windows CE process slots will be used

Services.exe and Device.exe are designed to complement each other. Device.exe is meant to load device drivers for controlling the hardware and services.exe is meant to load services other than those that control the hardware. Services.exe is like device.exe that hosts multiple services. Services.exe is designed to isolate non-system services from device drivers running in device.exe.

It is possible to run Device.exe without the enhanced Services.exe support in order to conserve a process slot; however, this is done at the expense of the increased stability that Services.exe offers.

An example of a service is Httpd or telnetd.

Note

Page 37: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 35

Activating / Controlling a Service

Activating a service:

Use built-in registry key

Use ActivateService function

Controlling a running service:Open a handle using CreateFileSend an I/O control or ReadFile, WriteFile and SetFilePointer functions

-- OR Use GetServiceHandle function

Activating a service can be done be using the registry. This is where Service.exe loads a service based on the value located in the registry under HKLM\Services. Also, activating a service can also be done programmatically using the ActivateService function.

ActivateService(L"TELNETD", 0); A running service can be controlled in much the same way as a device driver can be controlled using Device.exe. Steps to accomplish this are as follows:

1. Use CreateFile function with the appropriate prefix and index values for a previously registered service to open a handle to the service

2. Send an I/O control to the service, Note: DeviceIoControl then calls the XXX_IOControl function related to that service. Here is an example of how to obtain the current state of the telnet server. HANDLE hService = CreateFile(L"TEL0:",0,0,NULL,OPEN_EXISTING,0,NULL); if(hService != INVALID_HANDLE_VALUE) {

DWORD dwState; //state values are defined in service.h DeviceIoControl(hService, IOCTL_SERVICE_STATUS, NULL, 0, &dwState, sizeof(DWORD), NULL, NULL); CloseHandle(hService);

} 3. Additional operations are permitted if the service supports a stream-based

interface. The additional operations consist of the ReadFile, WriteFile, and SetFilePointer functions.

Another way to control a service is to use the GetServiceHandle function.

It is important to note that the ReadFile, WriteFile and SetFilePointer

Example

Note

Page 38: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

36 Module 6: Device Driver Architecture

functions will not work with a handle returned by GetServiceHandle.

Here is an example of the appropriate use of the GetServiceHandle function.

HANDLE hService = GetServiceHandle("TEL0:", NULL, NULL); if(hService != INVALID_HANDLE_VALUE) { DWORD dwState; ServiceIoControl(hService, IOCTL_SERVICE_STATUS, NULL, 0, &dwState, sizeof(DWORD), NULL, NULL); }

Example

Page 39: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 37

Registering a Service Programmatically

Use RegisterService functionRegisterService is analogous to the RegisterDevicefunction used to start device drivers running under Device.exe

HANDLE hService = RegisterService("TEL",0,“telnetd.dll",0);

HANDLE hService = RegisterService("TEL",0,“telnetd.dll",0);

It is possible for Services.exe to be started programmatically. This is accomplished through a call to the RegisterService function. RegisterService is analogous to the RegisterDevice function, which is used to start device drivers running under Device.exe.

In the example above, RegisterService to start a telnet server is used. If the telnet server is currently running on TEL0, then Services.exe will return FALSE and set the last error to ERROR_DEVICE_IN_USE.

The response of a service to xxx_Init is dependent on the service, although there are flags defined in the Services.h file for the dwContext value. These flags are as follows.

#define SERVICE_INIT_STARTED 0x00000000 #define SERVICE_INIT_STOPPED 0x00000001 If the SERVICE_INIT_STOPPED flag is not set in dwContext, then the service is responsible for creating a thread on startup in order for it to call the accept function. If the SERVICE_INIT_STOPPED flag has been set in dwContext, then the service will not be required to create a thread, as the service will be using super services to handle the accept thread.

Page 40: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

38 Module 6: Device Driver Architecture

Stopping a Running Service

Use DeregisterService function

DeregisterService identifies and labels the service as invalid

DeregisterService disallows any call attempts to CreateFile on a given service handle

HANDLE hService = GetServiceHandle( L"TEL0:", NULL,NULL);

if (0 != hService) DeregisterService(hService);

HANDLE hService = GetServiceHandle( L"TEL0:", NULL,NULL);

if (0 != hService) DeregisterService(hService);

A service instance may be stopped by a call to the DeregisterService function. DeregisterService identifies and labels the service as invalid, disallowing any call attempts to CreateFile on a given service handle.

It is important to note that DerigisterService takes the same handle value that was returned to the call to RegisterService and not a file handle created using CreateFile. It is possible that an application may not have this handle; for instance, if the particular service was started upon system initialization. In this case, to receive this value, a call to GetServiceHandle must be made.

The example illustrated on the slide shows how to use GetServiceHandle to receive the necessary information.

Once DeregisterService has been called for the last opened RegisterService for a particular DLL, the DLL will be unloaded from memory. It is extremely important that no other threads are created by the DLL that is currently running, as unloading the DLL unloads any code pages causing all threads running in its context to fail

Page 41: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 39

Service.exe at System Startup

Enumerates through registry subkeys of HKLM\Services

[HKLM\Services\TELNETD]"Dll"="TELNETD.DLL" "Order"=dword:8 "Keep"=dword:1 "Prefix"="TEL" "Index"=dword:0 "Context"=dword:1 "DisplayName"="Telnet Server" "Description"="Services incoming telnet requests"

[HKLM\Services\TELNETD]"Dll"="TELNETD.DLL" "Order"=dword:8 "Keep"=dword:1 "Prefix"="TEL" "Index"=dword:0 "Context"=dword:1 "DisplayName"="Telnet Server" "Description"="Services incoming telnet requests"

Upon startup, Services.exe enumerates through all registry subkeys located under the HKLM\Services registry key. Each subkey represents a service. Services.exe then uses the information located in the subkeys to initialize each service, one at a time and in the order specified in the registry.

The following subkey values are supported for Services.exe:

Registry Description Order(DWORD) Each service must have a value set for Order. Services.exe

will load a service based upon its order value, with the service having the lowest order being loaded first.

Dll(SzChar) Dynamic- link library (DLL) file to be loaded.

Keep(DWORD) If keep = 0, the DLL will be unloaded immediately after initialization.

Prefix(SzChar) Prefix of DLL.

Index(DWORD) Device index.

DisplayName(SzChar) Display service name.

Description(SzChar) Description of display service.

Context(DWORD) Initial value passed into the initialization routine.

The Services.exe super server will then call xxx_Init exported from the super service's .dll file, where xxx is the prefix specified in the registry entry. Upon completion of this task, the super server then passes the DWORD value located in the Context registry entry as the only argument to xxx_Init. The service initialization will be complete if xxx_Init returns a non-zero value. If xxx_Init returns a value of zero then the service will automatically be unloaded. After each service has been loaded, Services.exe checks to see if any sockets should be bound to the super services accept thread.

Page 42: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

40 Module 6: Device Driver Architecture

Services APIs’

Services.exe implements the following functions

XXX_Close

XXX_Deinit

XXX_Init

XXX_IOControl

XXX_Read

XXX_Seek

XXX_Write

Services.exe implements the following functions:

XXX_Close This function is to be implemented by a service and will be called by Services.exe. It is called when a device instance is closing due to a call to either CloseHandle or CloseService.

XXX_Deinit This function is to be implemented by a service and will be called by Services.exe. It is called during DeregisterService.

XXX_Init This function is to be implemented by a service and will be called by Services.exe.

XXX_IOControl This function is used to send a control code to a service. The control code specifies the action that the driver is to perform. For example, a control code can ask a service to return information or direct the service to carry out an action on the device. A number of standard control codes are defined in the SDK header files. In addition, a service can define its own device-specific control code.

XXX_Read This function is to be implemented by a service and will be called by Services.exe. This function need only be implemented by a streaming service.

XXX_Seek This function is to be implemented by a service and will be called by Services.exe. This function need only be implemented by a streaming service.

XXX_Write

Page 43: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 41

This function is to be implemented by a service and will be called by Services.exe. This function need only be implemented by a streaming service.

Page 44: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

42 Module 6: Device Driver Architecture

Bus Drivers

What is a Bus Driver?

Load drivers for the devices onto their respective buses

Examples are:

PCI (PCIBus.dll)

PCMCIA (PCMCIA.DLL)

IEEE 1394

USB

Bus drivers are responsible for determining what drivers to load and in what order for devices on their bus.

Some examples of bus drivers are:

PCI bus driver PCMCIA bus driver 1394 bus driver

Page 45: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 43

PCI Bus Drivers

PCI Bus Driver enumerates the PCI bus and loads device drivers for any of the devices it finds

PCI Bus Driver implemented as PCIBus.dll

Sources available at \WINCE400\public\common\oak\DRIVERS\PCIBUS

PCIBus.dll is loaded by the registry enumerator

PCIBus.dll is usually loaded last. So that all of the fixed resources are allocated before the flexible resources of the PCI devices are configured

The PCI bus driver is responsible for enumerating the PCI bus and then loading device drivers for any of the devices it finds.

Enumerating means figuring out what resources are required for each device and assigning it to that device. That is for each PCI device, you access its config space and program it. Such resources include memory base addresses, I/O base addresses and IRQ numbers.

PCI bus driver is implemented as PCIBus.dll. PCIBus.dll is loaded by the registry enumerator.

PCIBus.dll is usually loaded last. So that all of the fixed resources are allocated before the flexible resources of the PCI devices are configured.

Sources available at \WINCE400\public\common\oak\DRIVERS\PCIBUS.

Note

Page 46: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

44 Module 6: Device Driver Architecture

PCI Bus: Enumerate and Load Device Drivers

How does PCI Bus enumerate and load device drivers?CPU

PCI Device PCI Device

PCI-PCI Bridge

PCI Device

PCI Bus 0

PCI Bus 1

Registry

PCIBus.dllScans and enumerates

Compares

Loads driver for PCI device

The example illustrated on the slide is a typical layout of PCI devices.It contains 3 PCI devices. Two on PCI bus 0 and one on PCI Bus 1. The PCI-PCI bridge connects both PCI bus 0 and PCI bus 1. The PCIBus.dll also configures this bridge too.

PCI configuration is optional if the registry value NoConfig is set equal to dword:1 under the PCI registry key. This is useful for the CEPC since the BIOS already configures the PCI bus.

The general flow of how the PCI enumerates and loads device drivers is as follows:

1. The PCIBus.dll first configures the PCI bus. It scans the PCI bus gathering resource information from each device including PCI-PCI bridges.

2. For each device found, it searches the registry under PCI\Template key or PCI\Instance for any matches.

3. When a match is found, the registry key is examined for the ConfigEntry value. If it exists, the driver DLL is loaded. That DLL helps assist in allocating and placing resources. All resources requested from PCI bus driver is from the Resource Manager.

4. Then the PCI bus driver enumerates the PCI bus by rescanning it. When a device is found, it checks the registry again for Template and Instance key.

5. If a match is found with an Instance key, that means device is already configured and placed.

6. If a match is found with a Template key, the information of the template is copied to an Instance for the driver.

7. After PCI bus is configured and enumerated, PCI bus driver walks the registry of the created instances and loads each driver.

Note

Page 47: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 45

8. Scans all PCI devices. It enumerates the PCI bus by scanning for devices and enumerating the resources required by the devices. These resources include memory base addresses, I/O base addresses and IRQ numbers.

9. The PCI bus driver then assigns resources to each of the devices. 10. Then a second enumeration pass is run to match the devices with registry

entries found under the PCI\Template key. 11. The PCI bus driver then looks for matches under the PCI\Instance key to

look for exact matches 12. The device is matched to a registry entry by applying a best-fit algorithm to

the registry entries.

Page 48: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

46 Module 6: Device Driver Architecture

DMA

DMA stands for Direct Memory Access

It is a method of moving data from a device to memory, or vice versa, without the help of the microprocessor

Two ways of performing DMA

Using CEDDK functions

Using Kernel functions

Direct memory access (DMA) is a method of moving data from a device to memory, or vice versa, without the help of the microprocessor.

There are two ways of performing DMA:

CEDDK functions Kernel functions

Using the CEDDK functions for DMA is recommended. CEDDK exposes functions typically used by devices drivers for handling bus address translations, allocating and mapping device memory, setting up DMA buffers, and so on. Ceddk.dll is the Microsoft® Windows® CE Driver Development Kit (DDK) dynamic-link library (DLL) file.

CEDDK Functions Uses the HalAllocateCommonBuffer, HalFreeCommonBuffer functions Implementation calls the kernel functions. Handles bus and platform specific address translations. Useful for common buffer DMA. Uses default memory alignment of 64 K.

Kernel Functions Uses the AllocPhysMem, FreePhysMem functions. CEDDK implementation calls these functions. Must perform your own platform specific address translations, you can call

HalTranslateSystemAddress to translate the address. May be useful for scatter/gather DMA. Can change the default memory alignment.

Page 49: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 47

The Ceddk functions handle address translations between the system and the PCI bus or ISA bus for the DMA controller. You can support other bus types. They translate a physical RAM address into the corresponding bus-relative physical address for the DMA controller. To set up a common buffer for busmaster DMA using the Ceddk functions, a busmaster DMA device driver can call HalAllocateCommonBuffer with the DMA_ADAPTER_OBJECT structure.

You can also use the AllocPhysMem and FreePhysMem kernel functions for common buffer DMA transfers. If you use the kernel functions, call HalTranslateSystemAddress to translate the address you are passing to the DMA controller to avoid a memory violation. These functions take an alignment parameter, the Ceddk functions use default alignment of 64 KB. For a common buffer DMA example with the kernel functions, see %_WINCEROOT%\Public\Common\OAK\Drivers\USB\HCD.

Page 50: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

48 Module 6: Device Driver Architecture

Using CEDDK Functions for DMA

// Allocate an adapter object on the stack DMA_ADAPTER_OBJECT AdapterObject; AdapterObject.ObjectSize = sizeof (AdapterObject); AdapterObject.InterfaceType = PCIBus; AdapterObject.BusNumber = 0; // Allocate a single paged 4k output buffer dma_out_page[0] = (PUCHAR) HalAllocateCommonBuffer(

&AdapterObject, dma_buffer_size * AUDIO_DMA_BUFFERS,&dma_out_logical_address, FALSE);

if (!dma_out_page[0]) { ERRMSG("DMA Buffer Page Allocation Failed"); return FALSE;

}

// Allocate an adapter object on the stack DMA_ADAPTER_OBJECT AdapterObject; AdapterObject.ObjectSize = sizeof (AdapterObject); AdapterObject.InterfaceType = PCIBus; AdapterObject.BusNumber = 0; // Allocate a single paged 4k output buffer dma_out_page[0] = (PUCHAR) HalAllocateCommonBuffer(

&AdapterObject, dma_buffer_size * AUDIO_DMA_BUFFERS,&dma_out_logical_address, FALSE);

if (!dma_out_page[0]) { ERRMSG("DMA Buffer Page Allocation Failed"); return FALSE;

}

Refer to the example displayed on the slide of using the CEDDK functions, HalAllocateCommonBuffer and HalFreeCommonBuffer to implement DMA.

The example displayed on the slide is taken from WINCE400\PUBLIC\COMMON\OAK\DRIVERS\WAVEDEV\UNIFIED\ENSONIQ\Es1371.cpp

Looking at the code example above, you will need to declare the variable AdpaterObect of type DMA_ADAPTER_OBJECT. The DMA_ADAPTER_OBJECT is DMA adapter descriptor structure.

Next, fill that variable structure with the size of the object, the interface type and the bus number. The HalAllocateCommonBuffer function needs this structure filled out in order to properly allocate memory and to map it so that it can be simultaneously accessible from both the processor and a device for DMA operations.

The HalAllocateCommonBuffer function also needs the correct size of buffer to allocate which in this example is dma_buffer_size * AUDIO_DMA_BUFFERS, and a pointer to the bus-relative, logical address buffer which is dma_out_logical_address, and finally a flag to choose cached or uncached buffer allocation which in this case is FALSE meaning uncached bufffer location.

If the call to HalAllocateCommonBuffer succeeds, it returns the allocated buffer, or NULL if the call fails. The driver can use the allocated common buffer as a storage area for DMA transfers. The function also returns the physical bus-relative address of the buffer, which can be provided to the DMA controller

To free this buffer created above, use the following HalFreeCommonBuffer function where the pointer to the virtual address buffer dma_out_logical_address is passed in.

Note

Page 51: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 49

HalFreeCommonBuffer(NULL, 0, dma_out_logical_address, dma_out_page[0], FALSE);

Page 52: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

50 Module 6: Device Driver Architecture

Using Kernel Functions for DMA

// Allocate an adapter object on the stack CPhysMem::CPhysMem( DWORD cbSize, . . .) {. . .m_pPhysicalBufferAddr = (PUCHAR)AllocPhysMem(cbSize,

PAGE_READWRITE|PAGE_NOCACHE,0, // Default alignment0, // Reserved&m_dwNormalPA);

m_dwNormalVA = (DWORD) m_pPhysicalBufferAddr;}

CPhysMem::~CPhysMem() { FreePhysMem(m_pPhysicalBufferAddr);

// Allocate an adapter object on the stack CPhysMem::CPhysMem( DWORD cbSize, . . .) {. . .m_pPhysicalBufferAddr = (PUCHAR)AllocPhysMem(cbSize,

PAGE_READWRITE|PAGE_NOCACHE,0, // Default alignment0, // Reserved&m_dwNormalPA);

m_dwNormalVA = (DWORD) m_pPhysicalBufferAddr;}

CPhysMem::~CPhysMem() { FreePhysMem(m_pPhysicalBufferAddr);

Refer to the example displayed on the slide of using the kernel functions, AllocPhysMem and FreePhysMem to implement DMA.

The above example came from WINCE400\PUBLIC\COMMON\OAK\DRIVERS\USB\HCD\UHCD\cphysmem.cpp.

The AllocPhysMem function allocates physically contiguously memory. The size of the allocation is determined via the first argument. In this example, its cbSize. This is the number of bytes to allocated. The 2nd argument in this function determines the type of access. In this case, PAGE_READWRITE|PAGE_NOCACHE which enables both read and write access to the committed region of pages and no caching of the committed regions of pages. The 3rd argument is the alignment mask. It is set to 0 meaning the default system alignment is used. The 4th argument is reserved and must to be 0. The last argument is a pointer to a DWORD that stores the physical address of the memory allocation.

The FreePhysMem function releases physical memory back to the system. In this example, the m_pPhysicalBufferAddr is the virtual address returned from the AllocPhysMem function.

Note

Page 53: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 51

Resource Manager

What is Resource Manager?

Manages all I/O resources by telling whether resource is available to device driver

Uses registry setup to pre-allocate resources

Used by bus drivers to request IRQ and I/O space resources when assigning resources to device driver

Initial state of Resource Manager is defined in registry

Define your own resources using ResourceCreateList, ResourceRelease and ResourceRequest APIs.

The Resource Manager is an intrinsic part of the Device Manager. The Resource Manager tracks the available system resources that are initialized from the registry before any devices are loaded.

Every platform has unique sets IRQs and I/O space available. All of these resources are initially available.

IRQ and I/O space resources are almost always pre-allocated by the OAL and the registry or requested by bus drivers such as the PCI bus driver and the PCMCIA bus driver.

Built-in devices, such as ISA devices, are known by the system. Thus any resources needed by these devices are excluded from the available resources provided to the I/O Resource Manager at boot time.

Bus drivers, such as the PCI bus driver, request IRQ and I/O space resources from the I/O resource manager as it loads device drivers for the devices it finds.

The same is true for the PCMCIA driver and I/O resources required by PC Card client drivers. The PCMCIA driver will release resources as PC Cards are removed from the system.

IRQs for built-in and fixed devices should be mapped to SYSINTRs in the OAL as well as be excluded from the available resources. Some IRQs are shared, typically those used with the PCI bus.

You can define your own resources using the following APIs: ResourceCreateList, ResourceRelease and ResourceRequest.

The ResourceCreateList function creates a valid set of numbers for use by drivers. The ResourceRelease function allows drivers to use resources created by ResourceCreateList. The ResourceRequest function allows drivers to use resources made available by ResourceRelease.

Here is an example to illustrate the use of these functions. The example came from \WINCE400\public\common\oak\DRIVERS\PCMCIA\I82365\INIT.C.

Page 54: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

52 Module 6: Device Driver Architecture

// // 0x3E0 is the standard Intel compatible socket controller I/O port. // The Vadem VG-469 defaults to 0x3E2 !!! // for (tmp=0; 1; tmp+=2) { if (tmp > 2) { ResourceRelease(RESMGR_IRQ, g_Irq, 1); return CERR_BAD_ADAPTER; } if (ResourceRequest(RESMGR_IOSPACE, IoBase+tmp, IoLen)) { if (GetIOPorts(IoBase+tmp, IoLen) && IsValidPCICSig()) { // Got the correct resources and mapped them! IoBase += tmp; // for future reference break; } ResourceRelease(RESMGR_IOSPACE, IoBase+tmp, IoLen); } } The example above calls ResourceRelease function if there are more than 2 sockets. The driver assumes pcmcia controller only contains two sockets. The function uses RESMGR_IRQ as the resource identifier which is for the IRQ resources. It releases g_Irg which is the pcmcia driver global interrupt request number. The last argument is 1 which implies releasing one resource.

If there are a valid number of sockets, the ResourceRequest function is called to allocate IO space resource. It uses RESMGR_IOSPACE as the resource identifier and allocates IO base starting from IoBase + tmp and a length of IoLen.

If ResourceRequest function fails then ResourceRelease is called to release the IO space resources. It release the IO base starting at IoBase + tmp and of length of IoLen.

Page 55: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 53

Resource Manager (continued)

Initial State of Resource Manager

[HKEY_LOCAL_MACHINE\Drivers\Resources\IRQ]"Identifier"=dword:1"Minimum"=dword:1"Space"=dword:F"Ranges"="1,3-7,9-0xF""Shared"="1,3-7,9-0xF"

[HKEY_LOCAL_MACHINE\Drivers\Resources\IO]"Identifier"=dword:2"Minimum"=dword:0"Space"=dword:10000"Ranges"="0-0xFFFF"

[HKEY_LOCAL_MACHINE\Drivers\Resources\IRQ]"Identifier"=dword:1"Minimum"=dword:1"Space"=dword:F"Ranges"="1,3-7,9-0xF""Shared"="1,3-7,9-0xF"

[HKEY_LOCAL_MACHINE\Drivers\Resources\IO]"Identifier"=dword:2"Minimum"=dword:0"Space"=dword:10000"Ranges"="0-0xFFFF"

The initial state of the Resource manager is defined in the registry. The above example is the initial state of the Resource Manager for the CEPC platform. The registry entries are for IRQ and IO.

Refer to the example on the slide:

The registry entry [HKEY_LOCAL_MACHINE\Drivers\Resources\IRQ], this defines the resources for the interrupts. The Identifier key defines the resource identifier which is a label for a set of numbers. The resource ids are defined in WINCE400\public\common\DDK\INC\RESMGR.H. The following is copy of those values:

#define RESMGR_IRQ 0x00001 The Minimum key defines the smallest number IRQ resource of this type. In our example, that value is 1. Next the Space key defines the number of IRQ resources in the resource space which in our case is 15. The Ranges key defines the list of initially available resources ranges of IRQ. The Shared key lists the available IRQ resources that are sharable. In the above example, it is 1, 3 through 7 and 9 through 15.

Looking at the next registry entry [HKEY_LOCAL_MACHINE\Drivers\Resources\IO], this defines the resources for the IO. The Identifier key defines the resource identifier which is a label for a set of numbers. The resource ids are defined in WINCE400\public\common\DDK\INC\RESMGR.H. The following is a copy of those values:

#define RESMGR_IOSPACE 0x00002 The Minimum key defines the smallest number IO resource of this type. In the example displayed on the slide, that value is 0. Next, the Space key defines the number of IO resources in the resource space which in our case is 0x10000. The Ranges key defines the list of initially available resources ranges of IO. In the above example, that range is from 0x0 to 0xFFFF.

Page 56: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

54 Module 6: Device Driver Architecture

Interrupt Model

I/O Routines

OEM Hardware

ISR OAL routinesOAL

ExceptionHandler

Interrupt Support Handler

Kernel

IST

Driver

1

3 4 9

5

7

6

28

There are two components in the interrupt processing for Windows CE .NET. These two components are Interrupt Service Routine (ISR) and the Interrupt Service Thread (IST).

ISRs are usually small pieces of code that return a logical interrupt ID to the kernel. The kernel then invokes the proper IST to perform any possibly lengthy operations that must occur to fully process the interrupt. Device drivers then register their ISR with the kernel, so that the kernel calls the ISR when the appropriate physical interrupt occurs. Device drivers then spawn an IST, which then registers to receive interrupt notifications.

ISTs can use the InterruptInitialize function to register themselves, and use the WaitForSingleObject function to wait for interrupt events. Stream interface drivers generally do these things when their XXX_Init function is called.

Native device drivers that do not use the stream interface must find a convenient time during initialization to register their ISR and spawn an IST.

Page 57: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 55

Interrupt Service Thread Priorities

Nested InterruptsAre supported in Windows CE 3.0 and later versions in conjunction with the Real-Time Priority System Interrupts of a higher priority may preempt ISRs of a lower priority Kernel saves and restores the ISR’s state when high priority interrupt occurs and completes respectivelyLevel of interrupt nesting is limited solely by what the Windows CE-based platform's hardware can support

Interrupt LatenciesHave no upper limitMostly the latency for servicing interrupts in Windows CE is less than the Windows-based desktop platforms

The registry contains default priority level for Window CE device drivers. You can change these values either through the source code or directly in the registry.

Nested Interrupts Windows CE 3.0 and later allow nested interrupts, in conjunction with the Real-Time Priority System. In Windows CE 3.0 and later, interrupts of a higher priority may preempt ISRs of a lower priority; ISRs now only mask interrupts of equal or lower priority than themselves. The kernel handles the details of saving an ISR's state when a higher priority interrupt occurs and restoring it after the high priority ISR has completed. In most cases, a preempted ISR does not detect that it has been preempted. The level of interrupt nesting is limited solely by what the Windows CE-based platform's hardware can support.

Interrupt Latencies Because the ISTs of device drivers can be preempted by high-priority threads, there is no absolute upper limit. In general, however, the latency for servicing interrupts in Windows CE is less than the latency for Windows-based desktop platforms; device drivers are unlikely to lose data unless they are starved for processor time by other high-priority threads running on the operating system. Device drivers for polled devices may be able to achieve higher sampling rates, because polled devices do not generate interrupts and are, therefore, not subject to the same latencies.

Page 58: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

56 Module 6: Device Driver Architecture

Implementing Interrupts: Simple Example

First step of implementing interrupts is to find out how the interrupt is physically connected to device

Example

Platform GPIOPin 0 Power button

Power button is connected to general purpose I/O pin 0

Some interrupts are level triggered and some are edge triggered

The first step in implementing interrupts is to figure out how the interrupt is physically connected to your device. The illustration on the slide shows an example where the power button is connected to general purpose I/O pin 0. Do not forget to program the general purpose I/O controller to make that I/O pin an input pin and also set the triggering type. Some interrupts are level triggered and some are edge triggered. Normally the connection of the incoming IRQ to the software ISR is done early in the OEMInit function.

Page 59: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 57

Single IRQ Model: OEMInterruptHandler

OEMInterruptHandler(unsigned int ra) {. . .} else if(POWER_OFF_INT_STAT) {

// mask & clear the interuptPOWER_OFF_INT_MASK (0);POWER_OFF_INT_CLR (1);v_pDrvGlob->misc.offButton = 1;iSysIntr= SYSINTR_POWER;

. . .return iSysIntr;

}

OEMInterruptHandler(unsigned int ra) {. . .} else if(POWER_OFF_INT_STAT) {

// mask & clear the interuptPOWER_OFF_INT_MASK (0);POWER_OFF_INT_CLR (1);v_pDrvGlob->misc.offButton = 1;iSysIntr= SYSINTR_POWER;

. . .return iSysIntr;

}

Implement the interrupt service routineExample

GPIO pin 0

Return sysintr

Hardware interrupt happens

OEMInterruptHandler gets called

If interrupt is via GPIO pin 0, then mask the interrupt and return SYSINTR_POWER

Next, step in implementing interrupts is to setup the OEM adaptation layer (OAL) to handle this interrupt. You need to implement the interrupt service routine. The code sample shown in the slide illustrates an example where OEMInterruptHandler is the ISR. OEMInterrupt is responsible for handling all the interrupts that are likely to take place. The procedure followed in handling the interrupts is as follows:

1. Hardware interrupt happens. 2. OEMInterruptHandler gets called. The first thing the ISR checks is which

interrupt occurred by reading the interrupt pending register. 3. If interrupt is via GPIO pin 0, then mask the interrupt and return

SYSINTR_POWER.

The example shown in the slide is taken from \WINCE400\PLATFORM\SA11X0BD\KERNEL\HAL\ARM\INT11x0.C

OEMInterruptHandler() is called by the Kernel when any interrupt occurs.

On a CPU type which has multiple IRQ lines, you implement a table of HookInterrupt calls in the OEMInit function. Since the kernel knows which interrupt line was pulsed, it can then jump straight to the ISR because of this table of HookInterrupt links.

Note

Page 60: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

58 Module 6: Device Driver Architecture

OEMInterruptEnable

BOOL OEMInterruptEnable ( DWORD idInt, .. .) {. . . BOOL bRet = TRUE;switch (idInt) {case SYSINTR_POWER:

POWER_OFF_INT_CLR (1); POWER_OFF_INT_MASK (1); break;

. . .return bRet;

}

BOOL OEMInterruptEnable ( DWORD idInt, .. .) {. . . BOOL bRet = TRUE;switch (idInt) {case SYSINTR_POWER:

POWER_OFF_INT_CLR (1); POWER_OFF_INT_MASK (1); break;

. . .return bRet;

}

Performs any hardware operations necessary to allow a device to generate the specified interrupt including

Setting a hardware priority for the device, setting a hardware interrupt enable port, and clearing any pending interrupt conditions from the device

The code example in the slide shows OEMInterruptEnable. This function is called by the kernel to setup the interrupt mapping between an ISR and the driver's IST. The OEMInterruptEnable function performs any hardware operations necessary to allow a device to generate the specified interrupt. This can include setting a hardware priority for the device, setting a hardware interrupt enable port, and clearing any pending interrupt conditions from the device.

When a device driver calls the InterruptInitialize kernel routine, the kernel in turn calls OEMInterruptEnable. The system cannot be preempted when this function is called.

This example shown in the slide is taken from \WINCE400\PLATFORM\SA11X0BD\KERNEL\HAL\ARM\CFWSARM.C

Note

Page 61: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 59

OEMInterruptDisable

BOOL OEMInterruptDisable ( DWORD idInt ) {. . . switch (idInt) {case SYSINTR_POWER:

POWER_OFF_INT_MASK (0); break;

. . .return bRet;

}

BOOL OEMInterruptDisable ( DWORD idInt ) {. . . switch (idInt) {case SYSINTR_POWER:

POWER_OFF_INT_MASK (0); break;

. . .return bRet;

}

When a device driver is being unloaded and calls the InterruptDisable kernel routine, the kernel in turn calls OEMInterruptDisableSystem cannot be preempted when this function is called OEMInterruptDisable function disables the specified hardware interrupt identified in idIntExample

Inside your OAL, you must also implement a function to handle an installable driver. This driver need to disconnect from any interrupts it connected to with InterruptInitialize. The code sample in the slide shows how the system disconnects from the system's power off hardware line.

When a device driver is being unloaded and calls the InterruptDisable kernel routine, the kernel in turn calls OEMInterruptDisable. The system cannot be preempted when this function is called.

The OEMInterruptDisable function basically disables the specified hardware interrupt identified in idInt.

The example shown in the slide is taken from \WINCE400\PLATFORM\SA11X0BD\KERNEL\HAL\ARM\CFWSARM.C.

Note

Page 62: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

60 Module 6: Device Driver Architecture

OEMInterruptDone

BOOL OEMInterruptDone ( DWORD idInt ) {. . . switch (idInt) {case SYSINTR_POWER:// the power button is a toggle so

let both rising...POWER_OFF_RISING_EDGE; POWER_OFF_INT_MASK (1); break;

. . .}

BOOL OEMInterruptDone ( DWORD idInt ) {. . . switch (idInt) {case SYSINTR_POWER:// the power button is a toggle so

let both rising...POWER_OFF_RISING_EDGE; POWER_OFF_INT_MASK (1); break;

. . .}

Kernel calls the OEMInterrupt function when a device driver calls InterruptDoneSystem cannot be preempted when this function is called OEMInterruptDone should re-enable the interrupt if the interrupt was previously masked Example

During the processing of any given interrupt, the hardware IRQ has been masked. You must implement the OEMInterruptDone function to service the kernel call to unmask this interrupt, as illustrated by the code example in the slide.

The kernel calls the OEMInterrupt function when a device driver calls InterruptDone. The system cannot be preempted when this function is called.

OEMInterruptDone should re-enable the interrupt if the interrupt was previously masked.

The example shown in the slide is taken from \WINCE400\PLATFORM\SA11X0BD\KERNEL\HAL\ARM\CFWSARM.C.

Note

Page 63: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 61

Interrupt Service Thread

Is user-mode thread of device drivers for built-in devices

Does the actual processing of the interrupt

Creates an event object associated with the logical interrupt by calling CreateEvent function

IST remains idle most of the time, awakened when the kernel signals the event object

IST usually runs at above-normal priority, boost priority with CeSetThreadPriority function

The interrupt service thread is a user-mode thread that does most of the interrupt processing. The IST is idle most of the time and relies on the operating system to wake it when there is processing to do. This is achieved by associating an event object with an interrupt identifier.

The first operation an IST performs is to create an event object and associate it with the logical interrupt. The CreateEvent function is used to create an event object. As soon as possible, the IST should be waiting for this event to be signaled. When the hardware interrupt occurs, the kernel signals the event on behalf of the ISR; then the IST performs necessary I/O operations in the device to collect the data and process it. When the interrupt processing is done, the IST should inform the kernel so that the hardware interrupt is enabled again.

Usually, IST threads run at above-normal priority. They can boost themselves at a higher priority before registering their event with the kernel. An IST can change its thread priority by using the CeSetThreadPriority function.

Page 64: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

62 Module 6: Device Driver Architecture

Interrupt Service Thread (continued)

InterruptInitialize

Call InterruptInitialize to link the Event with the Interrupt ID of the ISR

WaitForSingleObject

Can be used to wait for an event to be signaled

This call is usually inside a loop so that when interrupt is processed, the IST gets back to this call waiting for the next interrupt to be handled

InterruptDone

After the interrupt data is processed, the IST must call the InterruptDone function to instruct the kernel to enable the hardware interrupt related to this thread

The following functions are used by IST:

InterruptInitialize Call InterruptInitialize to link the Event with the Interrupt ID of the ISR.

WaitForSingleObject The WaitForSingleObject function can be used to wait for an event to be signaled. This call is usually inside a loop so that as soon as an interrupt is processed, the IST gets back to this call waiting for the next interrupt to be handled.

InterruptDone As soon as the interrupt data is processed, the IST must call the InterruptDone function to instruct the kernel to enable the hardware interrupt related to this thread.

Page 65: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 63

Typical IST Start

struct ISTData // Declare the Strucure to pass to the IST{

HANDLE hThread; // IST HandleDWORD sysIntr; // Logical IDHANDLE hEvent; // handle to the event to wait for interruptvolatile BOOL abort; // flag to test to exit the IST

};

ISTData g_KeypadISTData;// Create event to link to IST g_KeypadISTData.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);// Translate IRQ to an logical ID (x86 CEPC)g_KeypadISTData.sysIntr =Mapirq2Sysintr(5);// start the threadg_KeypadISTData.hThread = CreateThread(NULL,0,&KeypadIST,

&g_KeypadISTData, 0, NULL);

structstruct ISTDataISTData // Declare the // Declare the StrucureStrucure to pass to the ISTto pass to the IST{{

HANDLE HANDLE hThreadhThread; // IST Handle; // IST HandleDWORD DWORD sysIntrsysIntr; // Logical ID; // Logical IDHANDLE HANDLE hEventhEvent; // handle to the event to wait for interrupt; // handle to the event to wait for interruptvolatile BOOL abort; // flag to test to exit the ISTvolatile BOOL abort; // flag to test to exit the IST

};};

ISTDataISTData g_KeypadISTDatag_KeypadISTData;;// Create event to link to IST // Create event to link to IST g_KeypadISTData.hEventg_KeypadISTData.hEvent = = CreateEvent(NULLCreateEvent(NULL, FALSE, FALSE, NULL);, FALSE, FALSE, NULL);// Translate IRQ to an logical ID (x86 CEPC)// Translate IRQ to an logical ID (x86 CEPC)g_KeypadISTData.sysIntrg_KeypadISTData.sysIntr =Mapirq2Sysintr(5);=Mapirq2Sysintr(5);// start the thread// start the threadg_KeypadISTData.hThreadg_KeypadISTData.hThread = CreateThread(NULL,0,&KeypadIST,= CreateThread(NULL,0,&KeypadIST,

&&g_KeypadISTDatag_KeypadISTData, 0, NULL);, 0, NULL);

The code sample on the slide illustrates an example of some of the things an IST might do at startup, which are:

Create a structure and store interrupt values in it. Use CreateEvent as the IST trigger. Find the Interrupt ID from the ISR routine (for x86). Store the handle for the thread you are creating.

Page 66: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

64 Module 6: Device Driver Architecture

Typical IST Start (continued) yp ( )

//Change the threads priorityCeSetThreadPriority(g_KeypadISTData.hThread,0);

//disconnect any previous event from logical IDInterruptDisable(g_KeypadISTData.sysIntr);

// Connect Logical ID with EventInterruptInitialize(g_KeypadISTData.sysIntr, g_KeypadISTData.hEvent,NULL,0);

//Change the threads priority//Change the threads priorityCeSetThreadPriority(g_KeypadISTData.hThread,0);CeSetThreadPriority(g_KeypadISTData.hThread,0);

//disconnect any previous event from logical ID//disconnect any previous event from logical IDInterruptDisable(g_KeypadISTData.sysIntrInterruptDisable(g_KeypadISTData.sysIntr););

// Connect Logical ID with Event// Connect Logical ID with EventInterruptInitialize(g_KeypadISTData.sysIntrInterruptInitialize(g_KeypadISTData.sysIntr, , g_KeypadISTDatag_KeypadISTData..hEvent,NULL,0);hEvent,NULL,0);

Set the IST Thread Priority

Disconnect any previous events from the associated ISR

Connect to the associated ISR

The code sample given in the slide performs the following:

Set the IST Thread Priority. Disconnect any previous events from the associated ISR. Connect to the associated ISR.

Page 67: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 65

Typical IST Start (continued) DWORD KeypadIST(void *dat){

ISTData* pData= (ISTData*)dat;// loop until told to stopWhile(!pData->abort){ // wait for the interrupt event...

WaitForSingleObject(pData->hEvent, INFINITE)if(pData->abort)break;// Handle the interrupt...

// Let OS know the interrupt processing is doneInterruptDone(pData->sysIntr);

}Return 0;

}

DWORD DWORD KeypadIST(voidKeypadIST(void **datdat)){ {

ISTDataISTData* * pDatapData= (= (ISTDataISTData*)*)datdat;;// loop until told to stop// loop until told to stopWhile(!pDataWhile(!pData-->abort)>abort){ // wait for the interrupt event... { // wait for the interrupt event...

WaitForSingleObject(pDataWaitForSingleObject(pData-->>hEventhEvent, INFINITE), INFINITE)if(pDataif(pData-->abort)>abort)break;break;// Handle the interrupt...// Handle the interrupt...

// Let OS know the interrupt processing is done// Let OS know the interrupt processing is doneInterruptDone(pDataInterruptDone(pData-->>sysIntrsysIntr); );

}}Return 0;Return 0;

}}

Code a loop that runs until manually abortedOnce in the loop, immediately block until the triggering event is returned from the kernelDo the actual processing of the interrupt.Signal InterruptDone in the Kernel with the Interrupt ID

In the main function of the IST the triggering event and data processing takes place:

Code a loop that runs until manually aborted. Once in the loop, immediately block until the triggering event is returned

from the kernel. This signifies that the IRQ has pulsed. Do the actual processing of the interrupt. Signal InterruptDone in the Kernel with the Interrupt ID. This will call

down to OEMInterrruptDone in your OAL to unmask the IRQ.

Page 68: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

66 Module 6: Device Driver Architecture

Typical IST Stop // set abort flag to true to let thread know// that it should exitg_KeypadISTData.abort =TRUE;

//disconnect event from logical ID//this internally sets g_KeypadISTData.sysIntr which in turn//sets g_KeypadISTData.hEvent through the kernelInterruptDisable(g_KeypadISTData.sysIntr);

//wait for thread to exitWaitForSingleObject(g_KeypadISTData.hEvent,INFINITE);

CloseHandle(g_KeypadISTData.hEvent);CloseHandle(g_KeypadISTData.hThread);

// set abort flag to true to let thread know// set abort flag to true to let thread know// that it should exit// that it should exitg_KeypadISTData.abortg_KeypadISTData.abort =TRUE;=TRUE;

//disconnect event from logical ID//disconnect event from logical ID//this internally sets //this internally sets g_KeypadISTData.sysIntrg_KeypadISTData.sysIntr which in turnwhich in turn//sets //sets g_KeypadISTData.hEventg_KeypadISTData.hEvent through the kernelthrough the kernelInterruptDisable(g_KeypadISTData.sysIntrInterruptDisable(g_KeypadISTData.sysIntr););

//wait for thread to exit//wait for thread to exitWaitForSingleObject(g_KeypadISTData.hEvent,INFINITEWaitForSingleObject(g_KeypadISTData.hEvent,INFINITE););

CloseHandle(g_KeypadISTData.hEventCloseHandle(g_KeypadISTData.hEvent););CloseHandle(g_KeypadISTData.hThreadCloseHandle(g_KeypadISTData.hThread););

Set a flag that will cancel the IST loopCall InterruptDisable to disconnect the triggering event from the logical IDClose the Thread Add Reference for the code

The code sample on the slide illustrates a possible way of disconnecting an interrupt. If this is an installable type device, you may want to disconnect it and turn off the IST permanently. To do this:

Set a flag that will cancel the IST loop. Call InterruptDisable to disconnect the triggering event from the logical

ID. Close the Thread.

Page 69: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 67

Installable ISRs

Installed by driver

Handles interrupts for that device

Need if interrupts are shared

Installed ISR can be generic routine to check if device is the one requesting service

Giisr.dll is the generic installable ISR

Installable ISRs is a way that allows interrupts to be shared by multiple devices. It also extends the ability to process the device in the ISR in addition to the IST. Now an ISR can be programmed in a high-level language; not restricted to assembly language only.

Installable ISRs provide the ability to share a single IRQ on a platform for use with multiple PCI devices. A sample installable ISR is included.

An installable interrupt service routine (ISR) is an ISR that can be installed and allowed to hook an interrupt after the kernel has been built. Traditionally, if an interrupt had to be hooked to service some event, the code had to be built into the kernel when it was originally built. If a new device was inserted into a PCI bus for example, the code to handle the interrupt request (IRQ) would need to already be in the kernel, otherwise the IRQ could not be hooked. OEMs could have planned for some expansion and built an OAL that accommodated various devices in a flexible way. However, if an unknown device was inserted into the board support package (BSP), there would be a high likelihood that you would not be able to access it.

A driver may choose to install an interrupt service routine (ISR) to handle interrupts for the device. This is required if the interrupt is shared between two or more devices, which is a common occurrence with PCI devices. The installed ISR can be a generic routine that simply checks to see if the device is the one requesting service, or it can be custom-designed by the driver writer. Giisr.dll is the generic installable ISR.

Page 70: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

68 Module 6: Device Driver Architecture

Implementing Installable ISRs

Set up the registry for your installable ISRRequired registry settings are IsrDll and IsrHandler

IsrDll is the interrupt service routine DLL nameIsrHandler is the ISR function name

[HKLM\Drivers\BuiltIn\PCI\Template\WaveDev]"Prefix"="WAV""Dll"="es1371.dll""Order"=dword:0"Class"=dword:04"SubClass"=dword:01"ProgIF"=dword:00"VendorID"=multi_sz:"1274","1274""DeviceID"=multi_sz:"1371","5880""IsrDll"="giisr.dll""IsrHandler"="ISRHandler"

[HKLM\Drivers\BuiltIn\PCI\Template\WaveDev]"Prefix"="WAV""Dll"="es1371.dll""Order"=dword:0"Class"=dword:04"SubClass"=dword:01"ProgIF"=dword:00"VendorID"=multi_sz:"1274","1274""DeviceID"=multi_sz:"1371","5880""IsrDll"="giisr.dll""IsrHandler"="ISRHandler"

First you need to set up the registry for your installable ISR. The required registry settings are IsrDll and IsrHandler. IsrDll is the interrupt service routine DLL name and IsrHandler is the ISR function name.

In the code sample illustrated in the slide, the IsrDll is giisr.dll.

The example shown in the slide is taken from \WINCE400\PLATFORM\CEPC\FILES\PLATFORM.REG.

Note

Page 71: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 69

Implementing Installable ISRs (continued)

GIISR_INFO Info;m_hIsrHandler = LoadIntChainHandler(isri.szIsrDll,

isri.szIsrHandler, (BYTE) isri.dwIrq);TransBusAddrToStatic(PCIBus, 0, PortAddress,

m_dwPciLength, &inIoSpace, &dwPhysAddr);

Info.SysIntr = m_IntrAudio;Info.CheckPort = TRUE;. . . Info.PortAddr = (DWORD)dwPhysAddr + ES1371_dSTATUS_OFF;Info.Mask = ES1371_INTSTAT_PENDING;

if (!KernelLibIoControl(m_hIsrHandler, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL))

return FALSE;

GIISR_INFO Info;m_hIsrHandler = LoadIntChainHandler(isri.szIsrDll,

isri.szIsrHandler, (BYTE) isri.dwIrq);TransBusAddrToStatic(PCIBus, 0, PortAddress,

m_dwPciLength, &inIoSpace, &dwPhysAddr);

Info.SysIntr = m_IntrAudio;Info.CheckPort = TRUE;. . . Info.PortAddr = (DWORD)dwPhysAddr + ES1371_dSTATUS_OFF;Info.Mask = ES1371_INTSTAT_PENDING;

if (!KernelLibIoControl(m_hIsrHandler, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL))

return FALSE;

After setting up the registry for your installable ISR, the next step is to modify your driver. The code sample on the slide illustrates an example of a PCI sound driver. The isri.szIsrDll contains the name of the installable ISR. This is obtained from the registry. A call to LoadIntChainHandler is needed to install an ISR to handle a particular interrupt. The ISR handler is in isri.szIsrHandler. This is also obtained from the registry. In this instance, it is ISRHandler.

Then TransBusAddrToStatic is called to translate a PCI bus-relative address to a system or physical address, then it creates a static, process independent, virtual address mapping for that location. In doing this, the registers on the card are visible to the generic installable ISR driver (Giisr.dll), which checks the cards registers to know if it was the interrupting source when there is the potential of multiple interrupting sources.

Next the Info structure is filled with all the relevant information and a call to KernelLibIoControl is made. Calling KernelLibIoControl sets up the communication with an interrupt handler.

The example shown in the slide is taken from \WINCE400\public\common\oak\DRIVERS\WAVEDEV\UNIFIED\ENSONIQ\ES1371.CPP.

Note

Page 72: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

70 Module 6: Device Driver Architecture

Device Driver Power Management

Power Management implemented via power manager (Pm.dll)Loaded by device manager

Built into operating system image by default, can be removed by setting the BSP_NOPM environment variableDefine your own System Power States (for example)

RunAC, RunDC, Suspend etc.Device Power States (statically predefined)

Full on (D0), Low on (D1), Standby (D2), Sleep (D3), Off (D4)

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PM]"Prefix"="PWR""Dll"="pm.dll""Order"=dword:0

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PM]"Prefix"="PWR""Dll"="pm.dll""Order"=dword:0

Why use power management? To reduce the power consumption of a Windows CE-based target device and to maintain and preserve the file system in RAM during the Reset, On, Idle, and Suspend power states.

Microsoft® Windows® CE .NET provides power management through the Power Manager. The Power Manager improves overall system power efficiency, provides power management for each device, and coexists with applications and drivers that do not support the Power Manager.

The Power Manager is built into the operating system image by default, but can be removed by setting the BSP_NOPM environment variable. This removes Pm.dll from the operating system image.

Device Power State You define your own system power states. For example, RunAC, RunDC, Suspend, and so on. These are not predefined, and are not necessarily linearly ordered. You define state names as registry keys in the system configuration. There is no limit on how many system power states you can define.

You are also responsible for creating explicit mappings between the predefined Device Power States and the System Power States.

Device Power state definitions are statically predefined. The Power Manager passes a device state to a driver and the driver is responsible for mapping the state to its device capabilities and then performing the applicable state transition on its physical device.

A physical device does not have to support all of the device power states. The only device power state that all devices must support is the full on state, D0. A driver that is issued a request to enter a power state not supported by its device enters the next available power state supported. If a device cannot wake up the system then it should power off rather than staying in standby.

Page 73: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 71

When a device driver is loaded it should put the device into full on, D0. Before a driver is unloaded, if possible, it should put the device into off, D4.

Device Power States are defined as follows.

Device Power State Registry Key Description Full on D0 State in which the device is on and

running. It is receiving full power from the system and is delivering full functionality to the user.

Low on D1 State in which the device is fully functional at a lower power and/or performance state than D0. D1 is applicable when the device is being used, but where peak performance is unnecessary and power is at a premium.

Standby D2 State in which the device is partially powered with automatic wakeup on request. A device in state D2 is effectively standing by.

Sleep D3 State in which the device is partially powered with device-initiated wakeup if available. A device in state D3 is sleeping but capable of raising the System Power State on its own. It consumes only enough power to be able to do so; which must be less than or equal to the amount of power used in state D2.

Off D4 State in which the device is unpowered. A device in state D4 should not be consuming any significant power. Some peripheral busses require static terminations that intrinsically use non-zero power when a device is physically connected to the bus; a device on such a bus can still support D4.

Page 74: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

72 Module 6: Device Driver Architecture

Power Management: Driver Calls

Power-manageable stream devices notify the Power Manager when they are loaded by ActivateDeviceExPower Manager determines the device's capabilities using an IOCTL_POWER_CAPABILITIES device IOCTL DevicePowerNotify

Enables power event notification for applications and drivers Devices are not required to implement all five device power states Power Manager may request that the device go into any device power stateIf a device is requested to go into a power state it does not support, it is expected to go into the next higher-functioning state that it does support Power Manager may issue an IOCTL_POWER_SET that essentially puts the device into its already-current state Power Manager is not required to set the device's power state to its default value as specified in a system power state

The process from a driver saves state information in RAM, set flag that power is turning off and exit

Power-manageable stream devices can automatically notify the Power Manager of their presence when they are loaded by ActivateDeviceEx. If the GUID {A32942B7-920C-486b-B0E6-92A702A99B35} is part of their IClass value, which is a REG_MULTI_SZ, the Power Manager receives a device notification when the device is loaded.

Once the Power Manager receives the device notification, it determines the device's capabilities using an IOCTL_POWER_CAPABILITIES device IOCTL. Then it starts managing the device's power according to the current system power state and any requirements for the device imposed by applications.

If an OEM chooses to implement a device API outside of the stream interface, they need to customize the Power Manager to be notified of the device's existence.

DevicePowerNotify DevicePowerNotify enables power event notification for applications and drivers. Device drivers call this to request that the Power Manager update their device power state. DevicePowerNotify uses new CE .NET IOCTLs like IOCTL_POWER_SET.

The Power Manager adjusts a device's power state using the IOCTL_POWER_SET device IOCTL. Device driver developers should be aware of the following when implementing support for this IOCTL:

Devices are not required to implement all five device power states. The only required power state is D0.

The Power Manager may request that the device go into any device power state, not just the ones the device claims to support in its IOCTL_POWER_CAPABILITIES handler.

If a device is requested to go into a power state it does not support, it is expected to go into the next higher-functioning state that it does support. For

Page 75: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 73

example, if a device does not support D2 (stand by) it should go into D1 (low on). The D3 (sleep state requires some special handling.

The Power Manager may issue an IOCTL_POWER_SET that essentially puts the device into its already-current state. In this case, the device driver can simply return success and do nothing.

The Power Manager is not required to set the device's power state to its default value as specified in a system power state. For example, if a device's power state falls into the range bounded by the system power state and application requirements the Power Manager may choose not to alter the device's power state.

Like all device drivers, block device drivers must limit themselves to minimal, very fast processing of the POWER_DOWN message. To accomplish this, they should save any volatile state information in RAM, set a flag to indicate that power is about to be turned off, and exit.

Page 76: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

74 Module 6: Device Driver Architecture

Device Notification Mechanism for Application

Alerts applications, services, and device drivers to the appearance and disappearance of device interfacesIs the Windows CE equivalent to Plug and Play eventingon Windows NT Device drivers cause notifications by AdvertiseInterfacefunctionTo register and deregister for device interface notifications use:

RequestDeviceNotifications functionStopDeviceNotifications function

Device interface notification is the mechanism used to alert applications, services, and device drivers to the appearance and disappearance of device interfaces. It is the Windows CE equivalent to Plug and Play eventing on Windows NT.

AdvertiseInterface informs the device manager to notify all interested parties of the availability or the removal of a device interface. It does not load or unload any drivers. Drivers that use AdvertiseInterface should also advertise when the interface goes away.

This call is not available to applications. It can only be invoked from within the device manager's process context (i.e. by device drivers).

A driver in Device.exe that exposes an interface publicizes this by setting the IClass value set appropriately in the registry key passed into ActivateDeviceEx with AdvertiseInterface. A device driver announces what device interfaces it exposes in the registry. This announcement is available to applications and drivers that are interested by using RequestDeviceNotifications. Stop device notifications by calling StopDeviceNotifications. These functions allow you to access the device interface's GUID and name. The GUID describes the device interface and the name clarifies to the system between multiple instances of that device interface. For example, there is a GUID for the generic stream interface, and the names COM1: and DSK1: refer to two distinct instances of the generic stream interface.

Note

Page 77: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 75

Advertising a Device Interface to Applications

Different ways to advertise a device interface to interested applications or drivers are:

The interface can be defined in the IClass value of the registry key used to activate the device

The IClass value can be defined in the Active key by a device driver's Init function The IClass value can be defined in the REGINI parameter to ActivateDeviceEx

Device manager APIs send notifications for all of the driver's defined interfaces after

The driver is loaded and initializedAgain after the driver is unloaded

There are different ways that a device interface can be advertised to interested applications or drivers. These ways can be:

The interface can be defined in the IClass value of the registry key used to activate the device.

The IClass value can be defined in the Active key by a device driver's Init function.

The IClass value can be defined in the REGINI parameter to ActivateDeviceEx.

All of the above variants cause the device manager APIs to send notifications for all of the driver's defined interfaces after the driver is loaded and initialized, and then again after the driver is unloaded.

Page 78: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

76 Module 6: Device Driver Architecture

RequestDeviceNotifications

PVOID PWR_Init( PVOID Context ) {PPM pPm = NULL;GUID guid =DEVCLASS_POWER_MANAGER_GUID;. . .pPm->hDeviceNotify = RequestDeviceNotifications(&guid,

pPm->hDeviceMsgQ, TRUE);if ( !VALID_FILE_HANDLE(pPm->hDeviceNotify)) {

CloseMsgQueue(pPm->hDeviceMsgQ);dwErr = GetLastError();break;

}. . .

}

PVOID PWR_Init( PVOID Context ) {PPM pPm = NULL;GUID guid =DEVCLASS_POWER_MANAGER_GUID;. . .pPm->hDeviceNotify = RequestDeviceNotifications(&guid,

pPm->hDeviceMsgQ, TRUE);if ( !VALID_FILE_HANDLE(pPm->hDeviceNotify)) {

CloseMsgQueue(pPm->hDeviceMsgQ);dwErr = GetLastError();break;

}. . .

}

Device interfaces are designated by GUIDs

Register Device Notification

Device interfaces refer to the methods available to applications for accessing the features exposed by a device driver. A device driver can have multiple interfaces at the same time, or it can have no interface.

Device interfaces are designated by GUIDs. The device interface GUID is referred to as IClass in the registry. The GUID's interface is defined in the header file that declares the interface.

The following code sample shows how device interface GUIDs are typically defined.

#define DEVCLASS_IFCNAME_STRING TEXT("{12345678-1234-1234-1122334455667788}") #define DEVCLASS_IFCNAME_GUID { 0x12345678, 0x1234, 0x1234, { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 } }

For the preceding cod sample:

#define DEVCLASS_POWER_MANAGER_STRING TEXT("{A32942B7-920C-486b-B0E6-92A702A99B35}") #define DEVCLASS_POWER_MANAGER_GUID { 0xA32942B7, 0x920C, 0x486b, { 0xB0, 0xE6, 0x92, 0xA7, 0x02, 0xA9, 0x9B, 0x35 } }

For details on device notification, see WINCE400\public\common\oak\INC\pmp.h

A device driver announces what interfaces it exposes in the registry. A driver can have multiple interfaces, or zero interfaces. This announcement is available to applications and drivers that are interested by using RequestDeviceNotifications.

Each interface is assigned a GUID. Applications that are interested in the interface gets the interface's GUID and the interface's name. The GUID

Note

Page 79: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 77

describes the interface and the name clarifies to the system between multiple instances of that device interface. Hence there is a GUID for the generic stream interface and the names COM1: and DSK1: refer to two distinct instances of the generic stream interface.

It is possible for two different interface classes (i.e., GUIDs) to have instances with the same name. The name has meaning only in the context of the access methods proscribed by the interface named by the GUID. Both the serial and the block device interfaces use the generic stream interface to access the device and so they share a namespace but there may be other unrelated namespaces (none such happen to exist at this writing but they certainly will in the future).

A device driver should only claim to export an interface if it implements that interface entirely. Any interaction with a driver expects full support of the interface it advertises.

Page 80: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

78 Module 6: Device Driver Architecture

StopDeviceNotification

Deregister Device Notification

BOOL PWR_Deinit( PPM pPm ) {. . .

// stop PnP notifictions//if (pPm->hDeviceNotify) {

StopDeviceNotifications(pPm->hDeviceNotify);}. . .

}

BOOL PWR_Deinit( PPM pPm ) {. . .

// stop PnP notifictions//if (pPm->hDeviceNotify) {

StopDeviceNotifications(pPm->hDeviceNotify);}. . .

}

Stops notifications of the appearance and disappearance of device interfaces Example

When a streams interface driver de-initializes itself, it must also close it's interface to the device notification system.

The StopDeviceNotification function stops notifications of the appearance and disappearance of device interfaces.

The code shown in the slide is taken from \WINCE400\public\common\oak\DRIVERS\PM\DRV.

Note

Page 81: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 79

CETK

Stands for the Windows CE Test KitA test tool that replaces the tool formerly known as the Driver Development Test Kit (DDTK)Incorporates the tests into an easy-to-use user interface (UI)Used for testing your driversProvided test for each class of devicesTo start CETK:

On the Start menu, point to Programs, point to Microsoft Windows CE .NET, and then click Window CE .NET Test Kit

The Integrated Development Environment (IDE) also uses a separate test tool called the Windows CE Test Kit (CETK). This test tool replaces the tool formerly known as the Driver Development Test Kit (DDTK) and improves on the DDTK functionality by incorporating the tests into an easy-to-use user interface. It is provided to assist OEMs in testing the port of their drivers to the operating system. This tool is not included in the IDE and must be accessed outside of Platform Builder.

The CETK is located on the Windows Start menu under Programs, in the same location as Platform Builder.

Documentation for this tool is available within the CETK tool itself.

To start CETK:

• On the Start menu, point to Programs, point to Microsoft Windows CE .NET, and then click Window CE .NET Test Kit.

Page 82: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

80 Module 6: Device Driver Architecture

Harness and Logging Engine Tux test harness

Is a 32-bit, client/server test harness that executes test code modules stored in dynamic-link library (.dll) files Focuses on grouping tests, and maintaining both statistics as well as the user interface Allows testers to quickly write clean, short, and platform-independent test cases

Kato engine testIs 32-bit, object-oriented, client/server logging engineAllows applications to log to a single interface and have their output routed to multiple user-defined output devices Is thread-safe and process-safe Provides an ANSI and UNICODE interface for Microsoft® Windows® 95, Windows 98, and Windows NT

CETK uses the Tux Test harness and the Kato Logging Engine to test images.

Tux Test Harness The Tux Test Harness (Tux) is a 32-bit, client/server test harness that executes test code modules stored in dynamic-link library (.dll) files. Tux focuses on grouping tests, and maintaining both statistics as well as the user interface. This allows testers to quickly write clean, short, and platform-independent test cases without the overhead required to write a complete application. As a client/server application, tests can be administered and executed on both local and remote development workstations. The client also has the ability to run in stand-alone mode (no server required), which provides a command-line method of quickly executing test cases and test scripts. It is also possible for multiple clients to be controlled simultaneously by a single Tux server.

Kato Logging Engine The Kato Logging Engine (Kato) is 32-bit, object-oriented, client/server logging engine that allows applications to log to a single interface and have their output routed to multiple user-defined output devices. Kato is completely thread-safe and process-safe. It provides an ANSI and UNICODE interface for Microsoft® Windows® 95, Windows 98, and Windows NT®. Applications that link with the Kato client DLL have the capability to create any number of Kato logging objects. These objects transmit all log and data information to a Kato server running either locally or remotely.

Test Kit Tests There are many tests included in the CETK.

Page 83: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 81

Demonstration: Run the CETK

In this demonstration, you will learn to use CETK to run a test.

Page 84: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

82 Module 6: Device Driver Architecture

Lab 6: Implementing a Stream Interface Driver

After completing this lab, you will be able to:

Develop a stream interface device driver.

Page 85: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

Module 6: Device Driver Architecture 83

Review Built-In Vs. InstallableDevice ManagerActivate DeviceRegistry EnumeratorServicesBus DriversIO Resource ManagerInterruptsDevice Driver Power ManagementCETK

1. Which streams interface function is implemented to respond to an application call to ReadFile?

2. Device loads the Registry Enumerator. What does the Registry Enumerator load?

3. Which function, exposed by Device manager, is used to load a driver?

4. What key does the Registry Enumerator begin looking under?

5. How do you load a Service?

6. Name four examples of bus drivers

7. When implementing DMA using the CEDDK function, name the functions used?

8. What key does the IO Resource Manager use to find its default resources?

Page 86: Module 6: Device Driver Architecture - pudn.comread.pudn.com/downloads83/doc/319149/Device Driver...Module 6: Device Driver Architecture 1 Overview Built-In Vs.Installable Drivers

84 Module 6: Device Driver Architecture

9. Name the call that links an IRQ to an ISR and then the call that links an ISR

to an IST?

10. Which device driver call, new to Windows CE .NET, is made to report power states and which parameters are passed?

11. Which program is used to generate digital signatures?

12. What are the names of the test harness and logging engine in the CETK?