Driver Design Guide

A good device driver needs to be well written. Remember that it is a trusted part of the operating system. The PC will not get the protection that a normal Win32 application has. If anything goes wrong, it is likely that the system will crash, or worse — destroy valuable data before crashing.

For important projects, my ultimate design philosophy is to design code three times. At the outset, it is definitely worth while knowing where I am going and what features I am going to implement. I then prefer an incremental approach, in which each individual section of code is designed as I go along. However, as with many programs, it often only when I have finished a first-cut prototype of the whole driver that I really understand what is going on.

Doing some real hardware interactions might invalidate some of my initial design decisions. Similarly, I might decide to expose an IOCTL interface instead of reads and writes. In the end, it might be best to scrap some sections of code and start again[4].

Documentation

It is particularly important that you document your driver well and provide ample comments in your code. Device drivers are complicated enough at the best of times. Having to look at someone else's code without any explanation of what is going on can be difficult.

Your hardware and firmware engineering colleagues should have provided you with a suitable interface specification for the device you have to control. You will bring these requirements together with the specification of any bus or class drivers you intend to use. At the end of the day you will have specified your driver's lower edge — the hardware and software interactions that you use to get your job done.

Similarly, you should define your upper edge. This is the interface that your driver presents to user mode applications or higher-level drivers. You need to define what devices you make available and which Win32 functions can be used to access the devices. Specify the parameters in detail and the contents of all buffers. Give your Win32 colleagues a suitable header file.

Good Design

Whatever your approach, a good design needs to be configurable, portable, preemptive, interruptible, and multiprocessor-safe.

A driver should be as configurable as possible — at least as configurable as the hardware allows. A device on a configurable bus ought to have a Plug and Play driver. Use the registry and the control panel to control how a driver runs.

Making a driver portable between platforms is usually fairly straightforward. Drivers should be easily recompiled to support non-x86 processors. WDM drivers need to run on W98 and W2000. Make sure that you only use routines that are available on both platforms. Write the driver in C or C++ and avoid using assembly language. Use the appropriate kernel routines to access hardware. Beware of data types that have different sizes on different processors. Other processors may have different virtual memory page sizes.

When your driver is running at a low interrupt level (see the following text), the operating system may perform a context switch at any time. Remember that higher priority interrupts can occur at any time, so your driver must bear this in mind. Even your ISR can be interrupted.

In an NT/W2000 multiprocessor system, your driver could be called simultaneously on different processors, so the code needs to be reentrant. As NT/W2000 supports symmetric multiprocessing, an interrupt may be handled on any processor. Even on a single processor system, your code needs to be reentrant. Your code could block while processing one IRP, or a context switch could occur. This could allow another IRP to start processing before the first IRP has completed.

Various techniques and routines are used to handle interrupts and multiprocessor systems. For example, critical sections ensure that a device operation is not interrupted. Spin lock tokens can guard access to work queues. Various dispatcher objects (events, Mutexes, semaphores, timers, and threads) can be used to signal conditions and synchronize with the other parts of your driver.

The kernel also provides mechanisms to arbitrate access to shared hardware devices such as the system DMA controllers. In NT7W2000, if your own hardware has a controller that is shared between devices, use the Controller object to arbitrate access, rather than rolling your own.

Look to see if the kernel already provides you with a feature (e.g., string manipulation facilities and management of a pool of memory blocks). These facilities are discussed as appropriate throughout the book.

A good driver checks each and every return value from any routines that it uses (e.g., in the kernel or other drivers). If an error occurs, make sure you rollback your transaction correctly before returning the error.

The layered device driver design

By now you will, of course, be writing Y2K-compliant code. However, this problem may not occur all that often in drivers. If appropriate, your driver should include accessibility options for people who have difficulty performing certain tasks. Bear in mind that many of your users may not use English as their first language, so internationalize as necessary. For example, provide different language event messages. Note that the strings that drivers must use are in Unicode.

Finally, it is best if you follow the driver routine naming conventions described later. Document and test your work as you go along. At the end, go through all the comments and make sure they are up to date.

Kernel Calls

Device drivers cannot access any standard C, C++, or Win32 functions. You cannot use the C++ new and delete operators. Instead, you must make use of a large number of kernel routines. The main categories of kernel routines are shown in Table 3.1.

The book describes each kernel routine when it is first used. However, I decided not to provide a full list of each function's parameters because it would take up too much room. You may find it useful to have the Driver Development Kit (DDK) documentation to hand. However, I hope that you can follow the thread of the argument without having to sit beside your computer.

If you are using one of the proprietary toolkits listed in Chapter 1, these will provide a set of classes that make drivers easier to write. This includes routines that make it easier to call the kernel. For example, drivers must use Unicode strings when talking to the rest of the kernel. The kernel routines that manipulate Unicode strings are fairly laborious to use. Most proprietary toolkits provide a class wrapper for Unicode strings that makes them easier to use.

There are various Win32 function calls that are used only in connection with device drivers. In particular, the SetupDi… routines are used to find devices that implement a specified device interface.

Table 3.1 Kernel Mode Routines available to drivers

Ex… Executive Support
Hal… Hardware Abstraction Layer (NT/W2000 only)
Io… I/O Manager (including Plug and Play routines)
Ke… Kernel
Ks… Kernel stream IRP management routines
Mm… Memory Manager
Ob… Object Manager
Po… Power Management
Ps… Process Structure
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату