| Acknowledgments | xv |
| Introduction | xvii |
| 1 Beginning a Driver Project | 1 |
| A Brief History of Device Drivers | 1 |
| An Overview of the Operating Systems | 5 |
| Windows XP Overview | 5 |
| Windows 98/Windows Me Overview | 8 |
| What Kind of Driver Do I Need? | 10 |
| WDM Drivers | 12 |
| Other Types of Drivers | 15 |
| Management Overview and Checklist | 16 |
| 2 Basic Structure of a WDM Driver | 21 |
| How Drivers Work | 21 |
| How Applications Work | 22 |
| Device Drivers | 24 |
| How the System Finds and Loads Drivers | 27 |
| Device and Driver Layering | 28 |
| Plug and Play Devices | 31 |
| Legacy Devices | 35 |
| Recursive Enumeration | 38 |
| Order of Driver Loading | 38 |
| IRP Routing | 41 |
| The Two Basic Data Structures | 47 |
| Driver Objects | 47 |
| Device Objects | 51 |
| The DriverEntry Routine | 54 |
| Overview of DriverEntry | 56 |
| DriverUnload | 59 |
| The AddDevice Routine | 60 |
| Creating a Device Object | 61 |
| Naming Devices | 63 |
| Other Global Device Initialization | 80 |
| Putting the Pieces Together | 86 |
| Windows 98/Me Compatibility Notes | 87 |
| Differences in DriverEntry Call | 87 |
| DriverUnload | 87 |
| The \GLOBAL?? Directory | 87 |
| Unimplemented Device Types | 87 |
| 3 Basic Programming Techniques | 89 |
| The Kernel-Mode Programming Environment | 89 |
| Using Standard Run-Time Library Functions | 91 |
| A Caution About Side Effects | 92 |
| Error Handling | 92 |
| Status Codes | 93 |
| Structured Exception Handling | 95 |
| Bug Checks | 106 |
| Memory Management | 107 |
| User-Mode and Kernel-Mode Address Spaces | 108 |
| Heap Allocator | 117 |
| Linked Lists | 124 |
| Lookaside Lists | 130 |
| String Handling | 134 |
| Miscellaneous Programming Techniques | 138 |
| Accessing the Registry | 138 |
| Accessing Files | 147 |
| Floating-Point Calculations | 151 |
| Making Debugging Easier | 152 |
| Windows 98/Me Compatibility Notes | 157 |
| File I/O | 157 |
| Floating Point | 158 |
| 4 Synchronization | 161 |
| An Archetypal Synchronization Problem | 162 |
| Interrupt Request Level | 164 |
| IRQL in Operation | 167 |
| IRQL Compared with Thread Priorities | 168 |
| IRQL and Paging | 168 |
| Implicitly Controlling IRQL | 169 |
| Explicitly Controlling IRQL | 171 |
| Spin Locks | 172 |
| Some Facts About Spin Locks | 173 |
| Working with Spin Locks | 174 |
| Queued Spin Locks | 176 |
| Kernel Dispatcher Objects | 177 |
| How and When You Can Block | 178 |
| Waiting on a Single Dispatcher Object | 180 |
| Waiting on Multiple Dispatcher Objects | 183 |
| Kernel Events | 184 |
| Kernel Semaphores | 188 |
| Kernel Mutexes | 190 |
| Kernel Timers | 191 |
| Using Threads for Synchronization | 197 |
| Thread Alerts and APCs | 198 |
| Other Kernel-Mode Synchronization Primitives | 201 |
| Fast Mutex Objects | 201 |
| Interlocked Arithmetic | 206 |
| Interlocked List Access | 210 |
| Windows 98/Me Compatibility Notes | 214 |
| 5 The I/O Request Packet | 215 |
| Data Structures | 215 |
| Structure of an IRP | 215 |
| The I/O Stack | 219 |
| The "Standard Model" for IRP Processing | 222 |
| Creating an IRP | 223 |
| Forwarding to a Dispatch Routine | 226 |
| Duties of a Dispatch Routine | 232 |
| The StartIo Routine | 239 |
| The Interrupt Service Routine | 241 |
| Deferred Procedure Call Routine | 241 |
| Completion Routines | 242 |
| Queuing I/O Requests | 256 |
| Using the DEVQUEUE Object | 260 |
| Using Cancel-Safe Queues | 263 |
| Cancelling I/O Requests | 269 |
| If It Weren't for Multitasking. | 270 |
| Synchronizing Cancellation | 270 |
| Some Details of IRP Cancellation | 272 |
| How the DEVQUEUE Handles Cancellation | 273 |
| Cancelling IRPs You Create or Handle | 280 |
| Handling IRP_MJ_CLEANUP | 289 |
| Cleanup with a DEVQUEUE | 291 |
| Cleanup with a Cancel-Safe Queue | 293 |
| SummaryEight IRP-Handling Scenarios | 293 |
| Scenario 1Pass Down with Completion Routine | 294 |
| Scenario 2Pass Down Without Completion Routine | 295 |
| Scenario 3Complete in the Dispatch Routine | 296 |
| Scenario 4Queue for Later Processing | 297 |
| Scenario 5Your Own Asynchronous IRP | 299 |
| Scenario 6Your Own Synchronous IRP | 301 |
| Scenario 7Synchronous Pass Down | 303 |
| Scenario 8Asynchronous IRP Handled Synchronously | 304 |
| 6 Plug and Play for Function Drivers | 307 |
| IRP_MJ_PNP Dispatch Function | 310 |
| Starting and Stopping Your Device | 312 |
| IRP_MN_START_DEVICE | 313 |
| IRP_MN_STOP_DEVICE | 316 |
| IRP_MN_REMOVE_DEVICE | 318 |
| IRP_MN_SURPRISE_REMOVAL | 319 |
| Managing PnP State Transitions | 320 |
| Starting the Device | 323 |
| Is It OK to Stop the Device? | 323 |
| While the Device Is Stopped | 325 |
| Is It OK to Remove the Device? | 326 |
| Synchronizing Removal | 328 |
| Why Do I Need This @#$! Remove Lock, Anyway? | 332 |
| How the DEVQUEUE Works with PnP | 338 |
| Other Configuration Functionality | 342 |
| Filtering Resource Requirements | 342 |
| Device Usage Notifications | 344 |
| PnP Notifications | 347 |
| Windows 98/Me Compatibility Notes | 360 |
| Surprise Removal | 360 |
| PnP Notifications | 361 |
| The Remove Lock | 361 |
| 7 Reading and Writing Data | 363 |
| Configuring Your Device | 363 |
| Addressing a Data Buffer | 367 |
| Specifying a Buffering Method | 368 |
| Ports and Registers | 373 |
| Port Resources | 375 |
| Memory Resources | 377 |
| Servicing an Interrupt | 379 |
| Configuring an Interrupt | 379 |
| Handling Interrupts | 381 |
| Deferred Procedure Calls | 385 |
| A Simple Interrupt-Driven Device | 389 |
| Direct Memory Access | 397 |
| Transfer Strategies | 399 |
| Performing DMA Transfers | 401 |
| Using a Common Buffer | 418 |
| A Simple Bus-Master Device | 421 |
| Windows 98/Me Compatibility Notes | 424 |
| 8 Power Management | 425 |
| The WDM Power Model | 426 |
| The Roles of WDM Drivers | 426 |
| Device Power and System Power States | 427 |
| Power State Transitions | 428 |
| Handling IRP_MJ_POWER Requests | 429 |
| Managing Power Transitions | 433 |
| Required Infrastructure | 436 |
| Initial Triage | 437 |
| System Power IRPs That Increase Power | 438 |
| System Power IRPs That Decrease Power | 446 |
| Device Power IRPs | 449 |
| Additional Power-Management Details | 460 |
| Flags to Set in AddDevice | 460 |
| Device Wake-Up Features | 461 |
| Powering Off When Idle | 469 |
| Using Sequence Numbers to Optimize State Changes | 474 |
| Windows 98/Me Compatibility Notes | 475 |
| The Importance of DO_POWER_PAGABLE | 475 |
| Completing Power IRPs | 475 |
| Requesting Device Power IRPs | 476 |
| PoCallDriver | 476 |
| Other Differences | 476 |
| 9 I/O Control Operations | 479 |
| The DeviceIoControl API | 480 |
| Synchronous and Asynchronous Calls to DeviceIoControl | 481 |
| Defining I/O Control Codes | 483 |
| Handling IRP_MJ_DEVICE_CONTROL | 485 |
| METHOD_BUFFERED | 488 |
| The DIRECT Buffering Methods | 489 |
| METHOD_NEITHER | 491 |
| Designing a Safe and Secure IOCTL Interface | 492 |
| Internal I/O Control Operations | 494 |
| Notifying Applications of Interesting Events | 497 |
| Using a Shared Event for Notification | 499 |
| Using a Pending IOCTL for Notification | 500 |
| Windows 98/Me Compatibility Notes | 506 |
| 10 Windows Management Instrumentation | 507 |
| WMI Concepts | 508 |
| A Sample Schema | 510 |
| Mapping WMI Classes to C Structures | 511 |
| WDM Drivers and WMI | 512 |
| Delegating IRPs to WMILIB | 514 |
| Advanced Features | 523 |
| Windows 98/Me Compatibility Notes | 534 |
| 11 Controller and Multifunction Devices | 535 |
| Overall Architecture | 536 |
| Child Device Objects | 536 |
| Handling PnP Requests | 539 |
| Telling the PnP Manager About Our Children | 541 |
| PDO Handling of PnP Requests | 542 |
| Handling Device Removal | 546 |
| Handling IRP_MN_QUERY_ID | 547 |
| Handling IRP_MN_QUERY_DEVICE_RELATIONS | 548 |
| Handling IRP_MN_QUERY_INTERFACE | 549 |
| Handling Power Requests | 554 |
| Handling Child Device Resources | 558 |
| 12 The Universal Serial Bus | 559 |
| Programming Architecture | 561 |
| Device Hierarchy | 561 |
| What's in a Device? | 563 |
| Information Flow | 566 |
| Descriptors | 579 |
| Working with the Bus Driver | 589 |
| Initiating Requests | 590 |
| Configuration | 593 |
| Managing Bulk Transfer Pipes | 604 |
| Managing Interrupt Pipes | 613 |
| Control Requests | 614 |
| Managing Isochronous Pipes | 617 |
| Idle Power Management for USB Devices | `635 |
| 13 Human Interface Devices | 639 |
| Drivers for HID Devices | 640 |
| Reports and Report Descriptors | 640 |
| Sample Keyboard Descriptor | 641 |
| HIDFAKE Descriptor | 644 |
| HIDCLASS Minidrivers | 646 |
| DriverEntry | 646 |
| Driver Callback Routines | 647 |
| Internal IOCTL Interface | 655 |
| Windows 98/Me Compatibility Notes | 670 |
| Handling IRP_MN_QUERY_ID | 670 |
| Joysticks | 671 |
| 14 Specialized Topics | 673 |
| Logging Errors | 673 |
| Creating an Error Log Packet | 675 |
| Creating a Message File | 678 |
| System Threads | 682 |
| Creating and Terminating System Threads | 683 |
| Using a System Thread for Device Polling | 686 |
| Work Items | 689 |
| Watchdog Timers | 691 |
| Windows 98/Me Compatibility Notes | 695 |
| Error Logging | 695 |
| Waiting for System Threads to Finish | 695 |
| Work Items | 696 |
| 15 Distributing Device Drivers | 697 |
| The Role of the Registry | 698 |
| The Hardware (Instance) Key | 699 |
| The Class Key | 701 |
| The Driver Key | 703 |
| The Service (Software) Key | 703 |
| Accessing the Registry from a Program | 704 |
| Device Object Properties | 708 |
| The INF File | 709 |
| Install Sections | 713 |
| Populating the Registry | 718 |
| Security Settings | 723 |
| Strings and Localization | 725 |
| Device Identifiers | 726 |
| Driver Ranking | 732 |
| Tools for INF Files | 734 |
| Defining a Device Class | 737 |
| A Property Page Provider | 738 |
| Customizing Setup | 743 |
| Installers and Co-installers | 744 |
| Preinstalling Driver Files | 752 |
| Value-Added Software | 753 |
| Installing a Driver Programmatically | 754 |
| The RunOnce Key | 754 |
| Launching an Application | 755 |
| The Windows Hardware Quality Lab | 756 |
| Running the Hardware Compatibility Tests | 757 |
| Submitting a Driver Package | 763 |
| Windows 98/Me Compatibility Notes | 769 |
| Property Page Providers | 769 |
| Installers and Co-installers | 770 |
| Preinstalled Driver Packages | 770 |
| Digital Signatures | 770 |
| Installing Drivers Programmatically | 770 |
| CONFIGMG API | 770 |
| INF Incompatibilities | 771 |
| Registry Usage | 771 |
| Getting Device Properties | 772 |
| 16 Filter Drivers | 773 |
| The Role of a Filter Driver | 773 |
| Upper Filter Drivers | 773 |
| Lower Filter Drivers | 778 |
| Mechanics of a Filter Driver | 779 |
| The DriverEntry Routine | 779 |
| The AddDevice Routine | 780 |
| The DispatchAny Function | 783 |
| The DispatchPower Routine | 784 |
| The DispatchPnp Routine | 784 |
| Installing a Filter Driver | 787 |
| Installing a Class Filter | 787 |
| Installing a Device Filter with the Function Driver | 790 |
| Installing a Device Filter for an Existing Device | 790 |
| Case Studies | 791 |
| Lower Filter for Traffic Monitoring | 791 |
| Named Filters | 792 |
| Bus Filters | 795 |
| Keyboard and Mouse Filters | 796 |
| Filtering Other HID Devices | 799 |
| Windows 98/Me Compatibility Notes | 800 |
| WDM Filters for VxD Drivers | 800 |
| INF Shortcut | 800 |
| Class Filter Drivers | 800 |
| A Coping with Cross-Platform Incompatibilities | 801 |
| Determining the Operating System Version | 801 |
| Run-Time Dynamic Linking | 802 |
| Checking Platform Compatibility | 803 |
| Defining Win98/Me Stubs for Kernel-Mode Routines | 805 |
| Version Compatibility | 806 |
| Stub Functions | 807 |
| Using WDMSTUB | 809 |
| Interaction Between WDMSTUB and WDMCHECK | 810 |
| Special Licensing Note | 810 |
| B Using WDMWIZ.AWX | 811 |
| Basic Driver Information | 811 |
| DeviceIoControl Codes | 814 |
| I/O Resources | 815 |
| USB Endpoints | 815 |
| WMI Support | 817 |
| Parameters for the INF File | 818 |
| Now What? | 819 |
| INDEX | 821 |