Art Baker, Jerry Lozano
Gerätetreiber unter Windows 2000, m. CD-ROM
WDM-konform entwickeln, implementieren, debuggen
Art Baker, Jerry Lozano
Gerätetreiber unter Windows 2000, m. CD-ROM
WDM-konform entwickeln, implementieren, debuggen
- Gebundenes Buch
- Merkliste
- Auf die Merkliste
- Bewerten Bewerten
- Teilen
- Produkt teilen
- Produkterinnerung
- Produkterinnerung
Die Programmierung von Gerätetreibern ist die wesentlichste, aber auch schwierigste Aufgabe in der Systemprogrammierung und für Entwickler eine echte Herausforderung. Dieses Buch liefert Ihnen die Grundlagen, die Sie als Windows 2000-Entwickler brauchen: Informationen zur Windows 2000-Architektur und Spezialkenntnisse zur PC-Hardware. Außerdem finden Sie hier genaue Programmieranleitungen und Beispiel-Codes. Besonders wertvoll: die CD-ROM mit Bibliotheken, Beispieltreibern und C++-Klassen.
Die Programmierung von Gerätetreibern ist die wesentlichste, aber auch schwierigste Aufgabe in der Systemprogrammierung und für Entwickler eine echte Herausforderung. Dieses Buch liefert Ihnen die Grundlagen, die Sie als Windows 2000-Entwickler brauchen: Informationen zur Windows 2000-Architektur und Spezialkenntnisse zur PC-Hardware. Außerdem finden Sie hier genaue Programmieranleitungen und Beispiel-Codes. Besonders wertvoll: die CD-ROM mit Bibliotheken, Beispieltreibern und C++-Klassen.
Produktdetails
- Produktdetails
- New Technology
- Verlag: Markt +Technik
- Seitenzahl: 574
- Abmessung: 245mm
- Gewicht: 1070g
- ISBN-13: 9783827260550
- ISBN-10: 3827260558
- Artikelnr.: 25314850
- Herstellerkennzeichnung Die Herstellerinformationen sind derzeit nicht verfügbar.
- New Technology
- Verlag: Markt +Technik
- Seitenzahl: 574
- Abmessung: 245mm
- Gewicht: 1070g
- ISBN-13: 9783827260550
- ISBN-10: 3827260558
- Artikelnr.: 25314850
- Herstellerkennzeichnung Die Herstellerinformationen sind derzeit nicht verfügbar.
Vorwort 21
Einleitung 23
Voraussetzungen für die Lektüre 23
Über den Inhalt 24
Was dieses Buch nicht abdeckt 25
Code auf der Begleit-CD 25
Entstehungsgeschichte dieses Buchs 26
Kapitel 1 Einführung in die Treiber technologie von Windows 2000 27
1.1 Allgemeine Systemarchitektur 28
1.1.1 Entwicklungsziele für Windows 2000 28
1.1.2 Hardware-Privilegstufen in Windows 2000 29
1.1.3 Portabilität 30
1.1.4 Erweiterbarkeit 30
1.1.5 Leistungsfähigkeit 31
1.1.6 Executive-Komponenten 32
1.2 Im Kernel Mode laufende I/O-Komponenten 38
1.2.1 Entwicklungsziele für das I/O-Subsystem 38
1.2.2 Treiberarten von Windows 2000 39
1.3 Spezielle Treiberarchitekturen 42
1.3.1 Videotreiber 43
1.3.2 Druckertreiber 44
1.3.3 Multimediatreiber 46
1.3.4 Netzwerktreiber 47
1.4 Zusammenfassung 48
Kapitel 2 Die Hardwareumgebung 49
2.1 Hardwaregrundlagen 50
2.1.1 Steuer- und Statusregister 50
2.1.2 Registerzugriffe 52
2.1.3 Interrupts 54
2.1.4 Datentransfer 57
2.1.5 DMA-Mechanismen 58
2.1.6 Automatische Erkennung und Konfiguration 59
2.2 Busse und Windows 2000 61
2.2.1 ISA: Industry Standard Architecture 62
2.2.2 EISA: Extended Industry Standard Architecture 64
2.2.3 PCI: Peripheral Component Interconnect 67
2.2.4 USB: Universal Serial Bus 72
2.2.5 IEE 1394: FireWire" 74
2.2.6 PC Card (PCMCIA) 76
2.3 Hinweise für die Arbeit mit Hardware 77
2.3.1 Gründliche Einarbeitung 77
2.3.2 Einsatz von Eigenintelligenz 79
2.3.3 Hardwareprüfungen 79
2.4 Zusammenfassung 79
Kapitel 3 I/O-Verarbeitung im Kernel Mode 81
3.1 Wie kommt es zur Ausführung von Kernel-Mode-Code? 82
3.1.1 Der Trap-/Exception-Kontext 82
3.1.2 Der Interrupt-Kontext 83
3.1.3 Der Kernel-Mode-Thread-Kontext 83
3.2 Interrupt-Prioritäten unter Windows 2000 83
3.2.1 Interrupt Request Level 84
3.2.2 Reihenfolge der Interrupt-Behandlung 85
3.2.3 Software-Interrupts 85
3.3 Zurückgestellte Prozeduraufrufe (DPC) 86
3.3.1 Ablauf eines DPCs 86
3.3.2 Verhalten von DPCs 87
3.4 Zugriff auf Puffer von User-Mode-Code 88
3.4.1 Mechanismen für den Zugriff auf User-Mode-Puffer 89
3.5 Struktur eines Kernel-Mode-Treibers 90
3.5.1 Treiberinitialisierung und Aufräumcode 90
3.5.2 Dispatch-Routinen für die Systemdienste des I/O-Managers 91
3.5.3 Routinen für den Datentransfer 92
3.5.4 Rückrufroutinen als Synchronisationsmechanismus
für den Zugriff auf Ressourcen 93
3.5.5 Andere Treiberroutinen 95
3.6 Lebenszyklus von I/O-Anforderungen 95
3.6.1 Vorbereitung durch den I/O-Manager 96
3.6.2 Vorbereitung durch den Gerätetreiber 96
3.6.3 Start des Gerätes und Interruptbehandlung 97
3.6.4 Nachbereitung durch den Gerätetreiber 98
3.6.5 Nachbereitung durch den I/O-Manager 98
3.7 Zusammenfassung 100
Kapitel 4 Treiber und Kernel-Mode-Objekte 101
4.1 Datenobjekte und Windows 2000 102
4.1.1 Windows 2000 und OOP 102
4.1.2 Objekte in Windows 2000 und in Win32 102
4.2 I/O Request Packets (IRPs) 103
4.2.1 Der Aufbau eines IRPs 104
4.2.2 Manipulation von IRPs 106
4.3 Treiberobjekte 108
4.3.1 Aufbau eines Treiberobjekts 108
4.4 Geräteobjekte und Geräteerweiterungen 109
4.4.1 Aufbau eines Geräteobjektes 110
4.4.2 Manipulation von Geräteobjekten 111
4.4.3 Geräteerweiterung 111
4.5 Controller-Objekte und -Erweiterungen 112
4.5.1 Aufbau eines Controller-Objekts 113
4.5.2 Manipulation von Controller-Objekten 114
4.5.3 Controller-Erweiterungen 114
4.6 Adapter-Objekte 114
4.6.1 Der Aufbau von Adapter-Objekten 115
4.6.2 Manipulation von Adapter-Objekten 116
4.7 Interrupt-Objekte 117
4.7.1 Aufbau eines Interrupt-Objekts 117
4.7.2 Manipulation von Interrupt-Objekten 118
4.8 Zusammenfassung 118
Kapitel 5 Allgemeines zur Treiberentwicklung 119
5.1 Strategien für das Treiberdesign 120
5.1.1 Formale Methoden beim Design einsetzen 120
5.1.2 Inkrementelle Entwicklung 121
5.1.3 Beispieltreiber von Microsoft untersuchen und verwenden 122
5.2 Konventionen und Techniken für die Codeerstellung 122
5.2.1 Benennungskonventionen 123
5.2.2 Header-Dateien 124
5.2.3 Statusrückgabe 125
5.2.4 Der Treibersupport von Windows 2000 126
5.2.5 Initialisierungsroutinen als discardable deklarieren 127
5.2.6 Codeanteile für die Auslagerung freigeben 127
5.3 Speicher anfordern 128
5.3.1 Speicherarten für Treiber 129
5.3.2 Umgang mit dem Kernel-Stack 129
5.3.3 Speicherbelegung auf dem System-Heap 130
5.3.4 Systemseitige Unterstützung für die Unterteilung bereits
angeforderter Speicherblocks 131
5.4 Unicode 135
5.4.1 Datentypen für Unicode-Strings 135
5.4.2 Umgang mit Unicode-Strings 136
5.5 Synchronisation von Interrupts 140
5.5.1 Das Problem 140
5.5.2 Interrupt-Sperre als Mittel zur Synchronisation 141
5.5.3 Regeln für das Sperren von Interrupts 142
5.5.4 Synchronisation über DPCs 142
5.6 Synchronisation mehrerer CPUs 142
5.6.1 Wie funktionieren Spin Locks? 143
5.6.2 Handhabung von Spin Locks 143
5.6.3 Regeln für den Einsatz von Spin Locks 145
5.7 Verkettete Listen 145
5.7.1 Einfach verkettete Listen 145
5.7.2 Doppelt verkettete Listen 146
5.7.3 Datenblock aus Blockliste entfernen 147
5.8 Zusammenfassung 148
Kapitel 6 Initialisierungs- und Aufräumroutinen 149
6.1 Die Routine DriverEntry 150
6.1.1 Ausführungskontext 150
6.1.2 Die Aufgaben der Routine 151
6.1.3 Bekanntgabe von Treiberfunktionen 152
6.1.4 Anlegen von Geräteobjekten 152
6.1.5 Festlegen einer Pufferstrategie 153
6.1.6 Gerätenamen 153
6.2 Praxisbeispiel: Treiber-Initialisierung 155
6.2.1 DriverEntry 155
6.2.2 CreateDevice 156
6.3 Reinitialisierungsroutinen 158
6.3.1 Ausführungskontext 158
6.3.2 Aufgaben 159
6.4 Unload-Routine 159
6.4.1 Ausführungskontext 159
6.4.2 Aufgaben 160
6.5 Praxisbeispiel: Unload 160
6.6 Shutdown-Routine 162
6.6.1 Ausführungskontext 162
6.6.2 Aufgaben 162
6.6.3 Ankündigung 163
6.7 Test des Treibers 163
6.7.1 Prüfprozedur 163
6.7.2 Visual C++ Device Driver AppWizard 164
6.7.3 Windows 2000 DDK 164
6.7.4 Ergebnis der Kompilierung 165
6.7.5 Manuelle Installation von Kernel-Mode-Treibern 165
6.7.6 Laden des Treibers 166
6.7.7 Windows 2000 Computerverwaltung 167
6.8 Das Dienstprogramm WINOBJ 168
6.9 Zusammenfassung 169
Kapitel 7 Dispatch-Routinen 171
7.1 Dispatch-Routinen registrieren 172
7.1.1 Dispatch-Mechanismus für I/O-Anforderungen 172
7.1.2 Unterstützung für einzelnen Funktionscode aktivieren 173
7.1.3 Welche Funktionscodes sollte ein Treiber unterstützen? 174
7.2 Dispatch-Routinen implementieren 175
7.2.1 Ausführungskontext einer Dispatch-Routine 175
7.2.2 Was macht eine Dispatch-Routine? 177
7.2.3 Aktionsradius einer Dispatch-Routine 177
7.3 Schreib- und Leseanforderungen verarbeiten 180
7.3.1 Zugriff auf den User-Mode-Puffer 180
7.4 Codebeispiel: ein Loopback-Gerät 182
7.5 Erweiterung der Dispatch-Schnittstelle 184
7.5.1 Definition privater IOCTL-Codes 184
7.5.2 Möglichkeiten für den IOCTL-Datentransfer 185
7.5.3 Header-Dateien für IOCTL-Code 186
7.5.4 Verarbeitung von IOCTL-Anforderungen 187
7.5.5 Puffer für IOCTL-Anforderungen 189
7.6 Dispatch-Routinen eines Treibers testen 190
7.6.1 Schreiben und Testen eines Treibergerüsts 190
7.6.2 Beispiel für ein Testprogramm 191
7.7 Zusammenfassung 191
Kapitel 8 Interruptgesteuerte I/O 193
8.1 Programmierte I/O: Funktionsprinzip 194
8.1.1 Der Ablauf 194
8.1.2 Synchronisation von Treiberroutinen 195
8.2 Initialisierungs- und Aufräumarbeiten 196
8.2.1 StartIo-Routine 196
8.2.2 DpcForIsr-Routine 197
8.2.3 Verbindung mit einer Interrupt-Quelle 197
8.2.4 Zurücksetzen der Interrupt-Verbindung 199
8.3 StartIo-Routine 200
8.3.1 Ausführungskontext 200
8.3.2 Aufgabenfeld 200
8.4 Interrupt Service Routine (ISR) 201
8.4.1 Ausführungskontext 201
8.5 DpcForIsr-Routinen 202
8.5.1 Ausführungskontext 202
8.5.2 Aufgabenfeld 203
8.5.3 Anhebung der Priorität 203
8.6 Parallele Schnittstellen: Die Hardware 204
8.6.1 Datenübertragung über die parallele Schnittstelle 204
8.6.2 Register 206
8.6.3 Interruptverhalten 206
8.6.4 Ein Loopback-Stecker für die Parallelschnittstelle 207
8.7 Codebeispiel: Loopback-Treiber für die Parallelschnittstelle 207
8.7.1 Funktionsumfang 208
8.7.2 Driver.h 208
8.7.3 Driver.cpp 209
8.7.4 Installation und Test des Beispieltreibers 215
8.7.5 Testmöglichkeiten 217
8.7.6 Allgemeine Prüfverfahren 217
8.8 Zusammenfassung 218
Kapitel 9 I/O-Verarbeitung im Kernel Mode 219
9.1 Geschichtliches zur Plug&Play-Architektur 220
9.1.1 Ziele für Plug&Play 221
9.1.2 Komponenten des Plug&Play-Subsystems 221
9.2 Rolle der Registrierung für althergebrachte Treiber 223
9.3 Geräte mit Plug&Play erkennen 224
9.4 Die Rolle der Treiberschichten beim Plug&Play 225
9.5 Die neue PnP-Dispatch-Routine des WDM 230
9.5.1 IRPs für Plug&Play 232
9.5.2 PnP-Codes für PDOs 234
9.5.3 Weitergabe von PnP-Anforderungen 234
9.5.4 I/O-Komplettierungsroutinen 236
9.5.5 PnP-Anforderungen an einen Bus-FDO-Treiber 239
9.6 Geräte aufzählen 240
9.6.1 Ressourcenbeschreiber für Hardware 240
9.6.2 Umgang des Treibers mit Hardwareressourcen 242
9.7 Geräte und Schnittstellen 243
9.7.1 Definition von Schnittstellen 243
9.7.2 Konstruktion von Schnittstellen 244
9.7.3 Referenzzählung für Schnittstellen 245
9.7.4 Eine Schnittstelle registrieren und verfügbar machen 245
9.8 Codebeispiel: Ein einfacher Plug&Play-Treiber 247
9.9 Zusammenfassung 247
Kapitel 10 Energieverwaltung 249
10.1 Hotplugging 250
10.1.1 Anforderungen an Busse 251
10.1.2 Anforderungen an Geräte 251
10.2 Die OnNow-Initiative 252
10.2.1 Power States 252
10.2.2 Richtlinien 253
10.2.3 Übergangsmatrix 254
10.2.4 Änderungen des Energiestatus 255
10.3 Weckanforderungen 259
10.3.1 Abbruch von Weckanforderungen 261
10.4 Einige weitere Details 261
10.4.1 Leerlaufzeiten 262
10.4.2 Die Benutzeroberfläche für die Energieverwaltung 263
10.5 Zusammenfassung 263
Kapitel 11 Timer 265
11.1 Zeitüberschreitungen von Geräten behandeln 266
11.1.1 Arbeitsweise einer IoTimer-Routine 266
11.1.2 Wie werden Zeitüberschreitungen eines Gerätes bemerkt? 267
11.2 Codebeispiel: Zeitüberschreitung erkennen 268
11.2.1 Geräteerweiterung 268
11.2.2 AddDevice: Timer initialisieren 268
11.2.3 CreateDispatch: Timer starten 269
11.2.4 StartIo und ISR: Zeitlimit setzen 269
11.2.5 IoTimer: Zeitüberschreitung erkennen und ahnden 270
11.3 Zeitüberwachung für Geräte ohne Interrupt 271
11.3.1 Polling von Geräten 271
11.3.2 Arbeitsweise von CustomTimerDpc-Routinen 273
11.3.3 Installieren einer CustomTimerDpc-Routine 274
11.3.4 Zeitpunkt oder -intervall programmieren 276
11.4 Codebeispiel: Ein Treiber mit Timer-Objekt 278
11.4.1 Geräteerweiterung 278
11.4.2 AddDevice 278
11.4.3 TransmitByte 279
11.4.4 PollingTimerDpc 279
11.5 Zusammenfassung 280
Kapitel 12 DMA-Treiber 281
12.1 DMA unter Windows 2000 282
12.1.1 Verkapselung über Adapter-Objekte 282
12.1.2 Das Scatter-/Gather-Problem 283
12.1.3 Memory Descriptor List (MDL) 286
12.1.4 Kohärenz mit dem Cache aufrechterhalten 288
12.1.5 Paketbasierter DMA vs. DMA mit gemeinsamem Puffer 290
12.1.6 Grenzen der DMA-Architektur von Windows 2000 291
12.2 Adapter-Objekte im Einsatz 291
12.2.1 Lokalisieren des Adapter-Objekts 292
12.2.2 Inbesitznahme und Rückgabe eines Adapter-Objekts 294
12.2.3 Programmierung der DMA-Hardware 296
12.2.4 Entleeren des Adapter-Caches 298
12.3 Details zu paketbasierten Slave-DMA-Treibern 299
12.3.1 Funktionsprinzip 299
12.3.2 Splitten von DMA-Transfers 302
12.4 Codebeispiel: Ein paketbasierter Slave-DMA-Treiber 304
12.4.1 Driver.H 304
12.4.2 GetDmaInfo-Routine 305
12.4.3 StartIo-Routine 306
12.4.4 AdapterControl-Routine 307
12.4.5 ISR 308
12.4.6 DpcForIsr-Routine 308
12.5 Paketbasierte Busmaster-DMA-Treiber 310
12.5.1 Einrichten von Busmaster-Hardware 311
12.5.2 Hardware mit Scatter-/Gather-Unterstützung 314
12.5.3 Anlegen von Scatter-/Gather-Listen 315
12.6 Slave-DMA-Treiber mit gemeinsam genutzten Puffern 318
12.6.1 Gemeinsamen DMA-Puffer belegen 318
12.6.2 Auswirkungen auf den Datendurchsatz 319
12.7 Busmaster-DMA-Treiber mit gemeinsamen Puffern 323
12.7.1 Funktionsprinzip 323
12.8 Zusammenfassung 324
Kapitel 13 Konfiguration und Performance-Daten 327
13.1 WMI aus Sicht der Industrie 328
13.2 WMI-Architektur 330
13.3 WMI-Unterstützung in WDM-Treiber einbauen 331
13.3.1 Die Syntax des MOF 332
13.3.2 Beispiel einer MOF-Klassendefinition 335
13.3.3 Kompilieren der MOF-Quelldatei 336
13.3.4 Behandlung von WMI-IRPs 337
13.3.5 Klassen und Instanzen 338
13.3.6 Resümee 347
13.4 Konventionelle Ereignisprotokollierung 347
13.4.1 Funktionsweise der Ereignisprotokollierung 347
13.4.2 Meldungen und Meldungscodes 349
13.4.3 Aufbau einer Definitionsdatei für Meldungen 350
13.4.4 Ein einfaches Beispiel 351
13.4.5 Kompilieren einer Definitionsdatei 353
13.4.6 Ressourcen in das Treiberprojekt einbinden 354
13.4.7 Registrieren eines Treibers als Ereignislieferant 354
13.4.8 Protokolleinträge generieren 354
13.4.9 Ereignispaket zusammenstellen 355
13.4.10 Ereignispaket abschicken 356
13.5 Zusammenfassung 357
Kapitel 14 System-Threads 359
14.1 Kernel-Threads: Definition und Verwendung 360
14.1.1 Wann sollte man Threads einsetzen? 360
14.1.2 Anlegen und Beenden von System-Threads 361
14.1.3 Thread-Prioritäten 363
14.1.4 System-Arbeitsthreads 363
14.2 Thread-Synchronisierung 364
14.2.1 Zeitliche Synchronisierung 364
14.2.2 Allgemeine Synchronisierung 364
14.3 Einsatz von Dispatcher-Objekten 367
14.3.1 Event-Objekte 367
14.3.2 Synchronisation zwischen verschiedenen Treibern 369
14.3.3 Mutex-Objekte 370
14.3.4 Semaphoren 371
14.3.5 Timer-Objekte 373
14.3.6 Thread-Objekte 374
14.3.7 Mutex-Varianten 375
14.3.8 Deadlocks 376
14.4 Codebeispiel: Ein Treiber mit eigenen Threads 378
14.4.1 Funktionsprinzip 378
14.4.2 Geräteerweiterung 379
14.4.3 AddDevice-Routine 380
14.4.4 DispatchReadWrite-Routine 381
14.4.5 Thread.cpp 382
14.4.6 Transfer.cpp 385
14.5 Zusammenfassung 392
Kapitel 15 Zwischentreiber 393
15.1 Zwischentreiber im Überblick 394
15.1.1 Definition: Zwischentreiber 394
15.1.2 Wann verwendet man eine geschichtete Architektur? 395
15.2 Schichttreiber 397
15.2.1 Arbeitsweise geschichteter Treiberdesigns 397
15.2.2 Initialisierungs- und Aufräumcode neuer Treiberschichten 397
15.2.3 Codeauszug: Verbindung mit untergeordnetem Treiber
etablieren 399
15.2.4 Weitere Fragen bei der Initialisierung einer Treiberschicht 401
15.2.5 I/O-Anforderungen in geschichteten Treibern 402
15.2.6 Codeauszug: Aufruf des untergeordneten Treibers 404
15.3 I/O-Komplettierungsroutinen 405
15.3.1 Registrieren einer I/O-Komplettierungsroutine 405
15.3.2 Ausführungskontext 406
15.3.3 Aktionsradius einer I/O-Komplettierungsroutine 407
15.3.4 Codeauszug: I/O-Komplettierungsroutine 409
15.4 Eigene IRPs rekrutieren 410
15.4.1 Der I/O-Stack eines IRPs 411
15.4.2 Welche Größe hat der IRP-Stack 412
15.4.3 IRPs beim I/O-Manager anfordern 413
15.4.4 IRPs von Grund auf selbst generieren 418
15.4.5 Puffer für untergeordnete Treiberschichten bereitstellen 422
15.4.6 Treibereigene IRPs verwalten 423
15.5 Filtertreiber 425
15.5.1 Arbeitsweise 426
15.5.2 Initialisierung und Aufräumcode 427
15.5.3 Transparenz 428
15.6 Codebeispiel: ein Filtertreiber 428
15.6.1 Aufbau der DeviceExtension-Struktur 429
15.6.2 DriverEntry 429
15.6.3 AddDevice 430
15.6.4 GetBufferLimits 431
15.6.5 OverriddenDispatchWrite 432
15.6.6 OverriddenDispatchDeviceIoControl 434
15.6.7 DispatchPassThru 435
15.6.8 Komplettierungsroutinen 436
15.6.9 Installation und Test des Beispieltreibers 439
15.7 Eng gekoppelte Treiber 439
15.7.1 Arbeitsweise 440
15.7.2 Initialisierung und Aufräumcode 441
15.8 Zusammenfassung 442
Kapitel 16 Treiberinstallation 443
16.1 Der Installationsprozess im Überblick 444
16.2 Automatische Installation mit .INF-Dateien 444
16.2.1 Dateistruktur 445
16.2.2 Version-Abschnitt 446
16.2.3 Manufacturers-Abschnitt 447
16.2.4 Modell-Abschnitte 447
16.2.5 DDInstall-Abschnitte 447
16.2.6 SourceDiskNames-Abschnitt 452
16.2.7 SourceDiskFiles-Abschnitt 453
16.2.8 DestinationDirs-Abschnitt 453
16.2.9 DDInstall.Services-Abschnitt 455
16.2.10 ServiceInstall-Abschnitte 456
16.2.11 Eine INF-Beispieldatei 457
16.2.12 Prüf- und Hilfsprogramme für INF-Dateien 458
16.3 Verarbeitung von INF-Dateien 459
16.3.1 Manuelle Installation 459
16.3.2 Automatische Installation 459
16.3.3 Der Hardwareassistent 460
16.3.4 Setup-Klassennamen und Geräte-IDs 461
16.3.5 Erweiterungen des Installationsprozesses 464
16.4 Festlegen der Ladereihenfolge 464
16.4.1 Treiber-Reihenfolge in Geräteobjekt-Stacks 465
16.5 Digitale Signierung von Treibern 466
16.5.1 Wieso Microsoft Treiber verifiziert 466
16.5.2 Digitale Signaturen 467
16.6 Zusammenfassung 467
Kapitel 17 Treiber testen und Fehler suchen 469
17.1 Richtlinien für das Testen von Treibern 470
17.1.1 Allgemeines 470
17.1.2 Hardware Compatibility Tests (HCTs) 473
17.2 Warum Treiber zuweilen versagen 474
17.2.1 Fehlerkategorien 474
17.2.2 Fehlerbilder reproduzieren 478
17.2.3 Strategien für die defensive Programmierung 479
17.2.4 Pflege eines Fehlerprotokolls 479
17.3 Blue Screens lesen 480
17.3.1 Was passiert bei einem Systemzusammenbruch? 480
17.3.2 Informationsgehalt eines Blue Screens 481
17.4 WinDbg im Überblick 483
17.4.1 Der Schlüssel zum Quellcode-Debugging 483
17.4.2 Die wichtigsten Kommandos von WinDbg 484
17.5 Analyse eines Post Mortem Dumps 486
17.5.1 Zielsetzungen einer Analyse 486
17.5.2 Start der Analyse 487
17.5.3 Stackverfolgung 488
17.5.4 Indirekte Ermittlungen 490
17.6 Interaktive Debugger-Sitzungen 493
17.6.1 Starten und Beenden einer interaktiven Debugger-Sitzung 493
17.6.2 Unterbrechungspunkte setzen 495
17.6.3 Unterbrechungspunkte hart kodieren 496
17.6.4 Debug-Meldungen 496
17.7 WinDbg um eigene Befehle erweitern 497
17.7.1 Arbeitsweise der erweiterten Befehle von WinDbg 497
17.7.2 Routinen für die Initialisierung und Versionsprüfung 497
17.7.3 Routinen für die erweiterten Kommandos 499
17.7.4 Schnittstellenroutinen von WinDbg 500
17.7.5 DLL-Erweiterungen erstellen und einsetzen 501
17.8 Codebeispiel: DLL-Erweiterung für WinDbg 501
17.8.1 DBG.CPP 501
17.9 Weitere Debug-Techniken 509
17.9.1 Debug-Zusätze im Treiber-Code belassen 509
17.9.2 Falsche Voraussetzungen erkennen 509
17.9.3 Bugcheck-Rückrufroutinen registrieren 510
17.9.4 Speicherlecks aufdecken 511
17.9.5 Zähler, Ereignisflags und Puffer für die Rückverfolgung 513
17.9.6 Ereignisflags 513
17.9.7 Puffer für die Rückverfolgung 514
17.10 Zusammenfassung 515
Anhang A Debug-Umgebung 517
A.1 Hard- und Softwarevoraussetzungen 518
A.1.1 Der Host 518
A.1.2 Das Zielsystem 519
A.1.3 Verbindung zwischen Host und Zielsystem 519
A.2 Debug-Symboldateien 520
A.2.1 Symboldateien für das Betriebssystem 520
A.2.2 Symboldateien für den Treiber 521
A.3 Post Mortem Dumps auf der Zielmaschine 522
A.3.1 Post Mortem Dump per Tastendruck erzwingen 522
A.4 Aktivieren des Debug-Clients auf der Zielmaschine 523
A.4.1 Anpassen von BOOT.INI 523
Anhang B Bugcheck-Codes 525
Anhang C Der Build-Prozess 543
C.1 Das Hilfsprogramm BUILD 544
C.1.1 Funktionsprinzip 545
C.1.2 Erstellen von Treibern mit BUILD 546
C.1.3 Die Datei SOURCES 547
C.1.4 Protokolldateien von BUILD 549
C.1.5 Rekursive BUILD-Operationen 549
C.2 Treiber mit Visual Studio erstellen 550
C.2.1 DDAppWiz 550
Anhang D Hinweise zur Begleit-CD 553
Anhang E Bibliographie 557
Stichwortverzeichnis 559
Einleitung 23
Voraussetzungen für die Lektüre 23
Über den Inhalt 24
Was dieses Buch nicht abdeckt 25
Code auf der Begleit-CD 25
Entstehungsgeschichte dieses Buchs 26
Kapitel 1 Einführung in die Treiber technologie von Windows 2000 27
1.1 Allgemeine Systemarchitektur 28
1.1.1 Entwicklungsziele für Windows 2000 28
1.1.2 Hardware-Privilegstufen in Windows 2000 29
1.1.3 Portabilität 30
1.1.4 Erweiterbarkeit 30
1.1.5 Leistungsfähigkeit 31
1.1.6 Executive-Komponenten 32
1.2 Im Kernel Mode laufende I/O-Komponenten 38
1.2.1 Entwicklungsziele für das I/O-Subsystem 38
1.2.2 Treiberarten von Windows 2000 39
1.3 Spezielle Treiberarchitekturen 42
1.3.1 Videotreiber 43
1.3.2 Druckertreiber 44
1.3.3 Multimediatreiber 46
1.3.4 Netzwerktreiber 47
1.4 Zusammenfassung 48
Kapitel 2 Die Hardwareumgebung 49
2.1 Hardwaregrundlagen 50
2.1.1 Steuer- und Statusregister 50
2.1.2 Registerzugriffe 52
2.1.3 Interrupts 54
2.1.4 Datentransfer 57
2.1.5 DMA-Mechanismen 58
2.1.6 Automatische Erkennung und Konfiguration 59
2.2 Busse und Windows 2000 61
2.2.1 ISA: Industry Standard Architecture 62
2.2.2 EISA: Extended Industry Standard Architecture 64
2.2.3 PCI: Peripheral Component Interconnect 67
2.2.4 USB: Universal Serial Bus 72
2.2.5 IEE 1394: FireWire" 74
2.2.6 PC Card (PCMCIA) 76
2.3 Hinweise für die Arbeit mit Hardware 77
2.3.1 Gründliche Einarbeitung 77
2.3.2 Einsatz von Eigenintelligenz 79
2.3.3 Hardwareprüfungen 79
2.4 Zusammenfassung 79
Kapitel 3 I/O-Verarbeitung im Kernel Mode 81
3.1 Wie kommt es zur Ausführung von Kernel-Mode-Code? 82
3.1.1 Der Trap-/Exception-Kontext 82
3.1.2 Der Interrupt-Kontext 83
3.1.3 Der Kernel-Mode-Thread-Kontext 83
3.2 Interrupt-Prioritäten unter Windows 2000 83
3.2.1 Interrupt Request Level 84
3.2.2 Reihenfolge der Interrupt-Behandlung 85
3.2.3 Software-Interrupts 85
3.3 Zurückgestellte Prozeduraufrufe (DPC) 86
3.3.1 Ablauf eines DPCs 86
3.3.2 Verhalten von DPCs 87
3.4 Zugriff auf Puffer von User-Mode-Code 88
3.4.1 Mechanismen für den Zugriff auf User-Mode-Puffer 89
3.5 Struktur eines Kernel-Mode-Treibers 90
3.5.1 Treiberinitialisierung und Aufräumcode 90
3.5.2 Dispatch-Routinen für die Systemdienste des I/O-Managers 91
3.5.3 Routinen für den Datentransfer 92
3.5.4 Rückrufroutinen als Synchronisationsmechanismus
für den Zugriff auf Ressourcen 93
3.5.5 Andere Treiberroutinen 95
3.6 Lebenszyklus von I/O-Anforderungen 95
3.6.1 Vorbereitung durch den I/O-Manager 96
3.6.2 Vorbereitung durch den Gerätetreiber 96
3.6.3 Start des Gerätes und Interruptbehandlung 97
3.6.4 Nachbereitung durch den Gerätetreiber 98
3.6.5 Nachbereitung durch den I/O-Manager 98
3.7 Zusammenfassung 100
Kapitel 4 Treiber und Kernel-Mode-Objekte 101
4.1 Datenobjekte und Windows 2000 102
4.1.1 Windows 2000 und OOP 102
4.1.2 Objekte in Windows 2000 und in Win32 102
4.2 I/O Request Packets (IRPs) 103
4.2.1 Der Aufbau eines IRPs 104
4.2.2 Manipulation von IRPs 106
4.3 Treiberobjekte 108
4.3.1 Aufbau eines Treiberobjekts 108
4.4 Geräteobjekte und Geräteerweiterungen 109
4.4.1 Aufbau eines Geräteobjektes 110
4.4.2 Manipulation von Geräteobjekten 111
4.4.3 Geräteerweiterung 111
4.5 Controller-Objekte und -Erweiterungen 112
4.5.1 Aufbau eines Controller-Objekts 113
4.5.2 Manipulation von Controller-Objekten 114
4.5.3 Controller-Erweiterungen 114
4.6 Adapter-Objekte 114
4.6.1 Der Aufbau von Adapter-Objekten 115
4.6.2 Manipulation von Adapter-Objekten 116
4.7 Interrupt-Objekte 117
4.7.1 Aufbau eines Interrupt-Objekts 117
4.7.2 Manipulation von Interrupt-Objekten 118
4.8 Zusammenfassung 118
Kapitel 5 Allgemeines zur Treiberentwicklung 119
5.1 Strategien für das Treiberdesign 120
5.1.1 Formale Methoden beim Design einsetzen 120
5.1.2 Inkrementelle Entwicklung 121
5.1.3 Beispieltreiber von Microsoft untersuchen und verwenden 122
5.2 Konventionen und Techniken für die Codeerstellung 122
5.2.1 Benennungskonventionen 123
5.2.2 Header-Dateien 124
5.2.3 Statusrückgabe 125
5.2.4 Der Treibersupport von Windows 2000 126
5.2.5 Initialisierungsroutinen als discardable deklarieren 127
5.2.6 Codeanteile für die Auslagerung freigeben 127
5.3 Speicher anfordern 128
5.3.1 Speicherarten für Treiber 129
5.3.2 Umgang mit dem Kernel-Stack 129
5.3.3 Speicherbelegung auf dem System-Heap 130
5.3.4 Systemseitige Unterstützung für die Unterteilung bereits
angeforderter Speicherblocks 131
5.4 Unicode 135
5.4.1 Datentypen für Unicode-Strings 135
5.4.2 Umgang mit Unicode-Strings 136
5.5 Synchronisation von Interrupts 140
5.5.1 Das Problem 140
5.5.2 Interrupt-Sperre als Mittel zur Synchronisation 141
5.5.3 Regeln für das Sperren von Interrupts 142
5.5.4 Synchronisation über DPCs 142
5.6 Synchronisation mehrerer CPUs 142
5.6.1 Wie funktionieren Spin Locks? 143
5.6.2 Handhabung von Spin Locks 143
5.6.3 Regeln für den Einsatz von Spin Locks 145
5.7 Verkettete Listen 145
5.7.1 Einfach verkettete Listen 145
5.7.2 Doppelt verkettete Listen 146
5.7.3 Datenblock aus Blockliste entfernen 147
5.8 Zusammenfassung 148
Kapitel 6 Initialisierungs- und Aufräumroutinen 149
6.1 Die Routine DriverEntry 150
6.1.1 Ausführungskontext 150
6.1.2 Die Aufgaben der Routine 151
6.1.3 Bekanntgabe von Treiberfunktionen 152
6.1.4 Anlegen von Geräteobjekten 152
6.1.5 Festlegen einer Pufferstrategie 153
6.1.6 Gerätenamen 153
6.2 Praxisbeispiel: Treiber-Initialisierung 155
6.2.1 DriverEntry 155
6.2.2 CreateDevice 156
6.3 Reinitialisierungsroutinen 158
6.3.1 Ausführungskontext 158
6.3.2 Aufgaben 159
6.4 Unload-Routine 159
6.4.1 Ausführungskontext 159
6.4.2 Aufgaben 160
6.5 Praxisbeispiel: Unload 160
6.6 Shutdown-Routine 162
6.6.1 Ausführungskontext 162
6.6.2 Aufgaben 162
6.6.3 Ankündigung 163
6.7 Test des Treibers 163
6.7.1 Prüfprozedur 163
6.7.2 Visual C++ Device Driver AppWizard 164
6.7.3 Windows 2000 DDK 164
6.7.4 Ergebnis der Kompilierung 165
6.7.5 Manuelle Installation von Kernel-Mode-Treibern 165
6.7.6 Laden des Treibers 166
6.7.7 Windows 2000 Computerverwaltung 167
6.8 Das Dienstprogramm WINOBJ 168
6.9 Zusammenfassung 169
Kapitel 7 Dispatch-Routinen 171
7.1 Dispatch-Routinen registrieren 172
7.1.1 Dispatch-Mechanismus für I/O-Anforderungen 172
7.1.2 Unterstützung für einzelnen Funktionscode aktivieren 173
7.1.3 Welche Funktionscodes sollte ein Treiber unterstützen? 174
7.2 Dispatch-Routinen implementieren 175
7.2.1 Ausführungskontext einer Dispatch-Routine 175
7.2.2 Was macht eine Dispatch-Routine? 177
7.2.3 Aktionsradius einer Dispatch-Routine 177
7.3 Schreib- und Leseanforderungen verarbeiten 180
7.3.1 Zugriff auf den User-Mode-Puffer 180
7.4 Codebeispiel: ein Loopback-Gerät 182
7.5 Erweiterung der Dispatch-Schnittstelle 184
7.5.1 Definition privater IOCTL-Codes 184
7.5.2 Möglichkeiten für den IOCTL-Datentransfer 185
7.5.3 Header-Dateien für IOCTL-Code 186
7.5.4 Verarbeitung von IOCTL-Anforderungen 187
7.5.5 Puffer für IOCTL-Anforderungen 189
7.6 Dispatch-Routinen eines Treibers testen 190
7.6.1 Schreiben und Testen eines Treibergerüsts 190
7.6.2 Beispiel für ein Testprogramm 191
7.7 Zusammenfassung 191
Kapitel 8 Interruptgesteuerte I/O 193
8.1 Programmierte I/O: Funktionsprinzip 194
8.1.1 Der Ablauf 194
8.1.2 Synchronisation von Treiberroutinen 195
8.2 Initialisierungs- und Aufräumarbeiten 196
8.2.1 StartIo-Routine 196
8.2.2 DpcForIsr-Routine 197
8.2.3 Verbindung mit einer Interrupt-Quelle 197
8.2.4 Zurücksetzen der Interrupt-Verbindung 199
8.3 StartIo-Routine 200
8.3.1 Ausführungskontext 200
8.3.2 Aufgabenfeld 200
8.4 Interrupt Service Routine (ISR) 201
8.4.1 Ausführungskontext 201
8.5 DpcForIsr-Routinen 202
8.5.1 Ausführungskontext 202
8.5.2 Aufgabenfeld 203
8.5.3 Anhebung der Priorität 203
8.6 Parallele Schnittstellen: Die Hardware 204
8.6.1 Datenübertragung über die parallele Schnittstelle 204
8.6.2 Register 206
8.6.3 Interruptverhalten 206
8.6.4 Ein Loopback-Stecker für die Parallelschnittstelle 207
8.7 Codebeispiel: Loopback-Treiber für die Parallelschnittstelle 207
8.7.1 Funktionsumfang 208
8.7.2 Driver.h 208
8.7.3 Driver.cpp 209
8.7.4 Installation und Test des Beispieltreibers 215
8.7.5 Testmöglichkeiten 217
8.7.6 Allgemeine Prüfverfahren 217
8.8 Zusammenfassung 218
Kapitel 9 I/O-Verarbeitung im Kernel Mode 219
9.1 Geschichtliches zur Plug&Play-Architektur 220
9.1.1 Ziele für Plug&Play 221
9.1.2 Komponenten des Plug&Play-Subsystems 221
9.2 Rolle der Registrierung für althergebrachte Treiber 223
9.3 Geräte mit Plug&Play erkennen 224
9.4 Die Rolle der Treiberschichten beim Plug&Play 225
9.5 Die neue PnP-Dispatch-Routine des WDM 230
9.5.1 IRPs für Plug&Play 232
9.5.2 PnP-Codes für PDOs 234
9.5.3 Weitergabe von PnP-Anforderungen 234
9.5.4 I/O-Komplettierungsroutinen 236
9.5.5 PnP-Anforderungen an einen Bus-FDO-Treiber 239
9.6 Geräte aufzählen 240
9.6.1 Ressourcenbeschreiber für Hardware 240
9.6.2 Umgang des Treibers mit Hardwareressourcen 242
9.7 Geräte und Schnittstellen 243
9.7.1 Definition von Schnittstellen 243
9.7.2 Konstruktion von Schnittstellen 244
9.7.3 Referenzzählung für Schnittstellen 245
9.7.4 Eine Schnittstelle registrieren und verfügbar machen 245
9.8 Codebeispiel: Ein einfacher Plug&Play-Treiber 247
9.9 Zusammenfassung 247
Kapitel 10 Energieverwaltung 249
10.1 Hotplugging 250
10.1.1 Anforderungen an Busse 251
10.1.2 Anforderungen an Geräte 251
10.2 Die OnNow-Initiative 252
10.2.1 Power States 252
10.2.2 Richtlinien 253
10.2.3 Übergangsmatrix 254
10.2.4 Änderungen des Energiestatus 255
10.3 Weckanforderungen 259
10.3.1 Abbruch von Weckanforderungen 261
10.4 Einige weitere Details 261
10.4.1 Leerlaufzeiten 262
10.4.2 Die Benutzeroberfläche für die Energieverwaltung 263
10.5 Zusammenfassung 263
Kapitel 11 Timer 265
11.1 Zeitüberschreitungen von Geräten behandeln 266
11.1.1 Arbeitsweise einer IoTimer-Routine 266
11.1.2 Wie werden Zeitüberschreitungen eines Gerätes bemerkt? 267
11.2 Codebeispiel: Zeitüberschreitung erkennen 268
11.2.1 Geräteerweiterung 268
11.2.2 AddDevice: Timer initialisieren 268
11.2.3 CreateDispatch: Timer starten 269
11.2.4 StartIo und ISR: Zeitlimit setzen 269
11.2.5 IoTimer: Zeitüberschreitung erkennen und ahnden 270
11.3 Zeitüberwachung für Geräte ohne Interrupt 271
11.3.1 Polling von Geräten 271
11.3.2 Arbeitsweise von CustomTimerDpc-Routinen 273
11.3.3 Installieren einer CustomTimerDpc-Routine 274
11.3.4 Zeitpunkt oder -intervall programmieren 276
11.4 Codebeispiel: Ein Treiber mit Timer-Objekt 278
11.4.1 Geräteerweiterung 278
11.4.2 AddDevice 278
11.4.3 TransmitByte 279
11.4.4 PollingTimerDpc 279
11.5 Zusammenfassung 280
Kapitel 12 DMA-Treiber 281
12.1 DMA unter Windows 2000 282
12.1.1 Verkapselung über Adapter-Objekte 282
12.1.2 Das Scatter-/Gather-Problem 283
12.1.3 Memory Descriptor List (MDL) 286
12.1.4 Kohärenz mit dem Cache aufrechterhalten 288
12.1.5 Paketbasierter DMA vs. DMA mit gemeinsamem Puffer 290
12.1.6 Grenzen der DMA-Architektur von Windows 2000 291
12.2 Adapter-Objekte im Einsatz 291
12.2.1 Lokalisieren des Adapter-Objekts 292
12.2.2 Inbesitznahme und Rückgabe eines Adapter-Objekts 294
12.2.3 Programmierung der DMA-Hardware 296
12.2.4 Entleeren des Adapter-Caches 298
12.3 Details zu paketbasierten Slave-DMA-Treibern 299
12.3.1 Funktionsprinzip 299
12.3.2 Splitten von DMA-Transfers 302
12.4 Codebeispiel: Ein paketbasierter Slave-DMA-Treiber 304
12.4.1 Driver.H 304
12.4.2 GetDmaInfo-Routine 305
12.4.3 StartIo-Routine 306
12.4.4 AdapterControl-Routine 307
12.4.5 ISR 308
12.4.6 DpcForIsr-Routine 308
12.5 Paketbasierte Busmaster-DMA-Treiber 310
12.5.1 Einrichten von Busmaster-Hardware 311
12.5.2 Hardware mit Scatter-/Gather-Unterstützung 314
12.5.3 Anlegen von Scatter-/Gather-Listen 315
12.6 Slave-DMA-Treiber mit gemeinsam genutzten Puffern 318
12.6.1 Gemeinsamen DMA-Puffer belegen 318
12.6.2 Auswirkungen auf den Datendurchsatz 319
12.7 Busmaster-DMA-Treiber mit gemeinsamen Puffern 323
12.7.1 Funktionsprinzip 323
12.8 Zusammenfassung 324
Kapitel 13 Konfiguration und Performance-Daten 327
13.1 WMI aus Sicht der Industrie 328
13.2 WMI-Architektur 330
13.3 WMI-Unterstützung in WDM-Treiber einbauen 331
13.3.1 Die Syntax des MOF 332
13.3.2 Beispiel einer MOF-Klassendefinition 335
13.3.3 Kompilieren der MOF-Quelldatei 336
13.3.4 Behandlung von WMI-IRPs 337
13.3.5 Klassen und Instanzen 338
13.3.6 Resümee 347
13.4 Konventionelle Ereignisprotokollierung 347
13.4.1 Funktionsweise der Ereignisprotokollierung 347
13.4.2 Meldungen und Meldungscodes 349
13.4.3 Aufbau einer Definitionsdatei für Meldungen 350
13.4.4 Ein einfaches Beispiel 351
13.4.5 Kompilieren einer Definitionsdatei 353
13.4.6 Ressourcen in das Treiberprojekt einbinden 354
13.4.7 Registrieren eines Treibers als Ereignislieferant 354
13.4.8 Protokolleinträge generieren 354
13.4.9 Ereignispaket zusammenstellen 355
13.4.10 Ereignispaket abschicken 356
13.5 Zusammenfassung 357
Kapitel 14 System-Threads 359
14.1 Kernel-Threads: Definition und Verwendung 360
14.1.1 Wann sollte man Threads einsetzen? 360
14.1.2 Anlegen und Beenden von System-Threads 361
14.1.3 Thread-Prioritäten 363
14.1.4 System-Arbeitsthreads 363
14.2 Thread-Synchronisierung 364
14.2.1 Zeitliche Synchronisierung 364
14.2.2 Allgemeine Synchronisierung 364
14.3 Einsatz von Dispatcher-Objekten 367
14.3.1 Event-Objekte 367
14.3.2 Synchronisation zwischen verschiedenen Treibern 369
14.3.3 Mutex-Objekte 370
14.3.4 Semaphoren 371
14.3.5 Timer-Objekte 373
14.3.6 Thread-Objekte 374
14.3.7 Mutex-Varianten 375
14.3.8 Deadlocks 376
14.4 Codebeispiel: Ein Treiber mit eigenen Threads 378
14.4.1 Funktionsprinzip 378
14.4.2 Geräteerweiterung 379
14.4.3 AddDevice-Routine 380
14.4.4 DispatchReadWrite-Routine 381
14.4.5 Thread.cpp 382
14.4.6 Transfer.cpp 385
14.5 Zusammenfassung 392
Kapitel 15 Zwischentreiber 393
15.1 Zwischentreiber im Überblick 394
15.1.1 Definition: Zwischentreiber 394
15.1.2 Wann verwendet man eine geschichtete Architektur? 395
15.2 Schichttreiber 397
15.2.1 Arbeitsweise geschichteter Treiberdesigns 397
15.2.2 Initialisierungs- und Aufräumcode neuer Treiberschichten 397
15.2.3 Codeauszug: Verbindung mit untergeordnetem Treiber
etablieren 399
15.2.4 Weitere Fragen bei der Initialisierung einer Treiberschicht 401
15.2.5 I/O-Anforderungen in geschichteten Treibern 402
15.2.6 Codeauszug: Aufruf des untergeordneten Treibers 404
15.3 I/O-Komplettierungsroutinen 405
15.3.1 Registrieren einer I/O-Komplettierungsroutine 405
15.3.2 Ausführungskontext 406
15.3.3 Aktionsradius einer I/O-Komplettierungsroutine 407
15.3.4 Codeauszug: I/O-Komplettierungsroutine 409
15.4 Eigene IRPs rekrutieren 410
15.4.1 Der I/O-Stack eines IRPs 411
15.4.2 Welche Größe hat der IRP-Stack 412
15.4.3 IRPs beim I/O-Manager anfordern 413
15.4.4 IRPs von Grund auf selbst generieren 418
15.4.5 Puffer für untergeordnete Treiberschichten bereitstellen 422
15.4.6 Treibereigene IRPs verwalten 423
15.5 Filtertreiber 425
15.5.1 Arbeitsweise 426
15.5.2 Initialisierung und Aufräumcode 427
15.5.3 Transparenz 428
15.6 Codebeispiel: ein Filtertreiber 428
15.6.1 Aufbau der DeviceExtension-Struktur 429
15.6.2 DriverEntry 429
15.6.3 AddDevice 430
15.6.4 GetBufferLimits 431
15.6.5 OverriddenDispatchWrite 432
15.6.6 OverriddenDispatchDeviceIoControl 434
15.6.7 DispatchPassThru 435
15.6.8 Komplettierungsroutinen 436
15.6.9 Installation und Test des Beispieltreibers 439
15.7 Eng gekoppelte Treiber 439
15.7.1 Arbeitsweise 440
15.7.2 Initialisierung und Aufräumcode 441
15.8 Zusammenfassung 442
Kapitel 16 Treiberinstallation 443
16.1 Der Installationsprozess im Überblick 444
16.2 Automatische Installation mit .INF-Dateien 444
16.2.1 Dateistruktur 445
16.2.2 Version-Abschnitt 446
16.2.3 Manufacturers-Abschnitt 447
16.2.4 Modell-Abschnitte 447
16.2.5 DDInstall-Abschnitte 447
16.2.6 SourceDiskNames-Abschnitt 452
16.2.7 SourceDiskFiles-Abschnitt 453
16.2.8 DestinationDirs-Abschnitt 453
16.2.9 DDInstall.Services-Abschnitt 455
16.2.10 ServiceInstall-Abschnitte 456
16.2.11 Eine INF-Beispieldatei 457
16.2.12 Prüf- und Hilfsprogramme für INF-Dateien 458
16.3 Verarbeitung von INF-Dateien 459
16.3.1 Manuelle Installation 459
16.3.2 Automatische Installation 459
16.3.3 Der Hardwareassistent 460
16.3.4 Setup-Klassennamen und Geräte-IDs 461
16.3.5 Erweiterungen des Installationsprozesses 464
16.4 Festlegen der Ladereihenfolge 464
16.4.1 Treiber-Reihenfolge in Geräteobjekt-Stacks 465
16.5 Digitale Signierung von Treibern 466
16.5.1 Wieso Microsoft Treiber verifiziert 466
16.5.2 Digitale Signaturen 467
16.6 Zusammenfassung 467
Kapitel 17 Treiber testen und Fehler suchen 469
17.1 Richtlinien für das Testen von Treibern 470
17.1.1 Allgemeines 470
17.1.2 Hardware Compatibility Tests (HCTs) 473
17.2 Warum Treiber zuweilen versagen 474
17.2.1 Fehlerkategorien 474
17.2.2 Fehlerbilder reproduzieren 478
17.2.3 Strategien für die defensive Programmierung 479
17.2.4 Pflege eines Fehlerprotokolls 479
17.3 Blue Screens lesen 480
17.3.1 Was passiert bei einem Systemzusammenbruch? 480
17.3.2 Informationsgehalt eines Blue Screens 481
17.4 WinDbg im Überblick 483
17.4.1 Der Schlüssel zum Quellcode-Debugging 483
17.4.2 Die wichtigsten Kommandos von WinDbg 484
17.5 Analyse eines Post Mortem Dumps 486
17.5.1 Zielsetzungen einer Analyse 486
17.5.2 Start der Analyse 487
17.5.3 Stackverfolgung 488
17.5.4 Indirekte Ermittlungen 490
17.6 Interaktive Debugger-Sitzungen 493
17.6.1 Starten und Beenden einer interaktiven Debugger-Sitzung 493
17.6.2 Unterbrechungspunkte setzen 495
17.6.3 Unterbrechungspunkte hart kodieren 496
17.6.4 Debug-Meldungen 496
17.7 WinDbg um eigene Befehle erweitern 497
17.7.1 Arbeitsweise der erweiterten Befehle von WinDbg 497
17.7.2 Routinen für die Initialisierung und Versionsprüfung 497
17.7.3 Routinen für die erweiterten Kommandos 499
17.7.4 Schnittstellenroutinen von WinDbg 500
17.7.5 DLL-Erweiterungen erstellen und einsetzen 501
17.8 Codebeispiel: DLL-Erweiterung für WinDbg 501
17.8.1 DBG.CPP 501
17.9 Weitere Debug-Techniken 509
17.9.1 Debug-Zusätze im Treiber-Code belassen 509
17.9.2 Falsche Voraussetzungen erkennen 509
17.9.3 Bugcheck-Rückrufroutinen registrieren 510
17.9.4 Speicherlecks aufdecken 511
17.9.5 Zähler, Ereignisflags und Puffer für die Rückverfolgung 513
17.9.6 Ereignisflags 513
17.9.7 Puffer für die Rückverfolgung 514
17.10 Zusammenfassung 515
Anhang A Debug-Umgebung 517
A.1 Hard- und Softwarevoraussetzungen 518
A.1.1 Der Host 518
A.1.2 Das Zielsystem 519
A.1.3 Verbindung zwischen Host und Zielsystem 519
A.2 Debug-Symboldateien 520
A.2.1 Symboldateien für das Betriebssystem 520
A.2.2 Symboldateien für den Treiber 521
A.3 Post Mortem Dumps auf der Zielmaschine 522
A.3.1 Post Mortem Dump per Tastendruck erzwingen 522
A.4 Aktivieren des Debug-Clients auf der Zielmaschine 523
A.4.1 Anpassen von BOOT.INI 523
Anhang B Bugcheck-Codes 525
Anhang C Der Build-Prozess 543
C.1 Das Hilfsprogramm BUILD 544
C.1.1 Funktionsprinzip 545
C.1.2 Erstellen von Treibern mit BUILD 546
C.1.3 Die Datei SOURCES 547
C.1.4 Protokolldateien von BUILD 549
C.1.5 Rekursive BUILD-Operationen 549
C.2 Treiber mit Visual Studio erstellen 550
C.2.1 DDAppWiz 550
Anhang D Hinweise zur Begleit-CD 553
Anhang E Bibliographie 557
Stichwortverzeichnis 559
Vorwort 21
Einleitung 23
Voraussetzungen für die Lektüre 23
Über den Inhalt 24
Was dieses Buch nicht abdeckt 25
Code auf der Begleit-CD 25
Entstehungsgeschichte dieses Buchs 26
Kapitel 1 Einführung in die Treiber technologie von Windows 2000 27
1.1 Allgemeine Systemarchitektur 28
1.1.1 Entwicklungsziele für Windows 2000 28
1.1.2 Hardware-Privilegstufen in Windows 2000 29
1.1.3 Portabilität 30
1.1.4 Erweiterbarkeit 30
1.1.5 Leistungsfähigkeit 31
1.1.6 Executive-Komponenten 32
1.2 Im Kernel Mode laufende I/O-Komponenten 38
1.2.1 Entwicklungsziele für das I/O-Subsystem 38
1.2.2 Treiberarten von Windows 2000 39
1.3 Spezielle Treiberarchitekturen 42
1.3.1 Videotreiber 43
1.3.2 Druckertreiber 44
1.3.3 Multimediatreiber 46
1.3.4 Netzwerktreiber 47
1.4 Zusammenfassung 48
Kapitel 2 Die Hardwareumgebung 49
2.1 Hardwaregrundlagen 50
2.1.1 Steuer- und Statusregister 50
2.1.2 Registerzugriffe 52
2.1.3 Interrupts 54
2.1.4 Datentransfer 57
2.1.5 DMA-Mechanismen 58
2.1.6 Automatische Erkennung und Konfiguration 59
2.2 Busse und Windows 2000 61
2.2.1 ISA: Industry Standard Architecture 62
2.2.2 EISA: Extended Industry Standard Architecture 64
2.2.3 PCI: Peripheral Component Interconnect 67
2.2.4 USB: Universal Serial Bus 72
2.2.5 IEE 1394: FireWire" 74
2.2.6 PC Card (PCMCIA) 76
2.3 Hinweise für die Arbeit mit Hardware 77
2.3.1 Gründliche Einarbeitung 77
2.3.2 Einsatz von Eigenintelligenz 79
2.3.3 Hardwareprüfungen 79
2.4 Zusammenfassung 79
Kapitel 3 I/O-Verarbeitung im Kernel Mode 81
3.1 Wie kommt es zur Ausführung von Kernel-Mode-Code? 82
3.1.1 Der Trap-/Exception-Kontext 82
3.1.2 Der Interrupt-Kontext 83
3.1.3 Der Kernel-Mode-Thread-Kontext 83
3.2 Interrupt-Prioritäten unter Windows 2000 83
3.2.1 Interrupt Request Level 84
3.2.2 Reihenfolge der Interrupt-Behandlung 85
3.2.3 Software-Interrupts 85
3.3 Zurückgestellte Prozeduraufrufe (DPC) 86
3.3.1 Ablauf eines DPCs 86
3.3.2 Verhalten von DPCs 87
3.4 Zugriff auf Puffer von User-Mode-Code 88
3.4.1 Mechanismen für den Zugriff auf User-Mode-Puffer 89
3.5 Struktur eines Kernel-Mode-Treibers 90
3.5.1 Treiberinitialisierung und Aufräumcode 90
3.5.2 Dispatch-Routinen für die Systemdienste des I/O-Managers 91
3.5.3 Routinen für den Datentransfer 92
3.5.4 Rückrufroutinen als Synchronisationsmechanismus
für den Zugriff auf Ressourcen 93
3.5.5 Andere Treiberroutinen 95
3.6 Lebenszyklus von I/O-Anforderungen 95
3.6.1 Vorbereitung durch den I/O-Manager 96
3.6.2 Vorbereitung durch den Gerätetreiber 96
3.6.3 Start des Gerätes und Interruptbehandlung 97
3.6.4 Nachbereitung durch den Gerätetreiber 98
3.6.5 Nachbereitung durch den I/O-Manager 98
3.7 Zusammenfassung 100
Kapitel 4 Treiber und Kernel-Mode-Objekte 101
4.1 Datenobjekte und Windows 2000 102
4.1.1 Windows 2000 und OOP 102
4.1.2 Objekte in Windows 2000 und in Win32 102
4.2 I/O Request Packets (IRPs) 103
4.2.1 Der Aufbau eines IRPs 104
4.2.2 Manipulation von IRPs 106
4.3 Treiberobjekte 108
4.3.1 Aufbau eines Treiberobjekts 108
4.4 Geräteobjekte und Geräteerweiterungen 109
4.4.1 Aufbau eines Geräteobjektes 110
4.4.2 Manipulation von Geräteobjekten 111
4.4.3 Geräteerweiterung 111
4.5 Controller-Objekte und -Erweiterungen 112
4.5.1 Aufbau eines Controller-Objekts 113
4.5.2 Manipulation von Controller-Objekten 114
4.5.3 Controller-Erweiterungen 114
4.6 Adapter-Objekte 114
4.6.1 Der Aufbau von Adapter-Objekten 115
4.6.2 Manipulation von Adapter-Objekten 116
4.7 Interrupt-Objekte 117
4.7.1 Aufbau eines Interrupt-Objekts 117
4.7.2 Manipulation von Interrupt-Objekten 118
4.8 Zusammenfassung 118
Kapitel 5 Allgemeines zur Treiberentwicklung 119
5.1 Strategien für das Treiberdesign 120
5.1.1 Formale Methoden beim Design einsetzen 120
5.1.2 Inkrementelle Entwicklung 121
5.1.3 Beispieltreiber von Microsoft untersuchen und verwenden 122
5.2 Konventionen und Techniken für die Codeerstellung 122
5.2.1 Benennungskonventionen 123
5.2.2 Header-Dateien 124
5.2.3 Statusrückgabe 125
5.2.4 Der Treibersupport von Windows 2000 126
5.2.5 Initialisierungsroutinen als discardable deklarieren 127
5.2.6 Codeanteile für die Auslagerung freigeben 127
5.3 Speicher anfordern 128
5.3.1 Speicherarten für Treiber 129
5.3.2 Umgang mit dem Kernel-Stack 129
5.3.3 Speicherbelegung auf dem System-Heap 130
5.3.4 Systemseitige Unterstützung für die Unterteilung bereits
angeforderter Speicherblocks 131
5.4 Unicode 135
5.4.1 Datentypen für Unicode-Strings 135
5.4.2 Umgang mit Unicode-Strings 136
5.5 Synchronisation von Interrupts 140
5.5.1 Das Problem 140
5.5.2 Interrupt-Sperre als Mittel zur Synchronisation 141
5.5.3 Regeln für das Sperren von Interrupts 142
5.5.4 Synchronisation über DPCs 142
5.6 Synchronisation mehrerer CPUs 142
5.6.1 Wie funktionieren Spin Locks? 143
5.6.2 Handhabung von Spin Locks 143
5.6.3 Regeln für den Einsatz von Spin Locks 145
5.7 Verkettete Listen 145
5.7.1 Einfach verkettete Listen 145
5.7.2 Doppelt verkettete Listen 146
5.7.3 Datenblock aus Blockliste entfernen 147
5.8 Zusammenfassung 148
Kapitel 6 Initialisierungs- und Aufräumroutinen 149
6.1 Die Routine DriverEntry 150
6.1.1 Ausführungskontext 150
6.1.2 Die Aufgaben der Routine 151
6.1.3 Bekanntgabe von Treiberfunktionen 152
6.1.4 Anlegen von Geräteobjekten 152
6.1.5 Festlegen einer Pufferstrategie 153
6.1.6 Gerätenamen 153
6.2 Praxisbeispiel: Treiber-Initialisierung 155
6.2.1 DriverEntry 155
6.2.2 CreateDevice 156
6.3 Reinitialisierungsroutinen 158
6.3.1 Ausführungskontext 158
6.3.2 Aufgaben 159
6.4 Unload-Routine 159
6.4.1 Ausführungskontext 159
6.4.2 Aufgaben 160
6.5 Praxisbeispiel: Unload 160
6.6 Shutdown-Routine 162
6.6.1 Ausführungskontext 162
6.6.2 Aufgaben 162
6.6.3 Ankündigung 163
6.7 Test des Treibers 163
6.7.1 Prüfprozedur 163
6.7.2 Visual C++ Device Driver AppWizard 164
6.7.3 Windows 2000 DDK 164
6.7.4 Ergebnis der Kompilierung 165
6.7.5 Manuelle Installation von Kernel-Mode-Treibern 165
6.7.6 Laden des Treibers 166
6.7.7 Windows 2000 Computerverwaltung 167
6.8 Das Dienstprogramm WINOBJ 168
6.9 Zusammenfassung 169
Kapitel 7 Dispatch-Routinen 171
7.1 Dispatch-Routinen registrieren 172
7.1.1 Dispatch-Mechanismus für I/O-Anforderungen 172
7.1.2 Unterstützung für einzelnen Funktionscode aktivieren 173
7.1.3 Welche Funktionscodes sollte ein Treiber unterstützen? 174
7.2 Dispatch-Routinen implementieren 175
7.2.1 Ausführungskontext einer Dispatch-Routine 175
7.2.2 Was macht eine Dispatch-Routine? 177
7.2.3 Aktionsradius einer Dispatch-Routine 177
7.3 Schreib- und Leseanforderungen verarbeiten 180
7.3.1 Zugriff auf den User-Mode-Puffer 180
7.4 Codebeispiel: ein Loopback-Gerät 182
7.5 Erweiterung der Dispatch-Schnittstelle 184
7.5.1 Definition privater IOCTL-Codes 184
7.5.2 Möglichkeiten für den IOCTL-Datentransfer 185
7.5.3 Header-Dateien für IOCTL-Code 186
7.5.4 Verarbeitung von IOCTL-Anforderungen 187
7.5.5 Puffer für IOCTL-Anforderungen 189
7.6 Dispatch-Routinen eines Treibers testen 190
7.6.1 Schreiben und Testen eines Treibergerüsts 190
7.6.2 Beispiel für ein Testprogramm 191
7.7 Zusammenfassung 191
Kapitel 8 Interruptgesteuerte I/O 193
8.1 Programmierte I/O: Funktionsprinzip 194
8.1.1 Der Ablauf 194
8.1.2 Synchronisation von Treiberroutinen 195
8.2 Initialisierungs- und Aufräumarbeiten 196
8.2.1 StartIo-Routine 196
8.2.2 DpcForIsr-Routine 197
8.2.3 Verbindung mit einer Interrupt-Quelle 197
8.2.4 Zurücksetzen der Interrupt-Verbindung 199
8.3 StartIo-Routine 200
8.3.1 Ausführungskontext 200
8.3.2 Aufgabenfeld 200
8.4 Interrupt Service Routine (ISR) 201
8.4.1 Ausführungskontext 201
8.5 DpcForIsr-Routinen 202
8.5.1 Ausführungskontext 202
8.5.2 Aufgabenfeld 203
8.5.3 Anhebung der Priorität 203
8.6 Parallele Schnittstellen: Die Hardware 204
8.6.1 Datenübertragung über die parallele Schnittstelle 204
8.6.2 Register 206
8.6.3 Interruptverhalten 206
8.6.4 Ein Loopback-Stecker für die Parallelschnittstelle 207
8.7 Codebeispiel: Loopback-Treiber für die Parallelschnittstelle 207
8.7.1 Funktionsumfang 208
8.7.2 Driver.h 208
8.7.3 Driver.cpp 209
8.7.4 Installation und Test des Beispieltreibers 215
8.7.5 Testmöglichkeiten 217
8.7.6 Allgemeine Prüfverfahren 217
8.8 Zusammenfassung 218
Kapitel 9 I/O-Verarbeitung im Kernel Mode 219
9.1 Geschichtliches zur Plug&Play-Architektur 220
9.1.1 Ziele für Plug&Play 221
9.1.2 Komponenten des Plug&Play-Subsystems 221
9.2 Rolle der Registrierung für althergebrachte Treiber 223
9.3 Geräte mit Plug&Play erkennen 224
9.4 Die Rolle der Treiberschichten beim Plug&Play 225
9.5 Die neue PnP-Dispatch-Routine des WDM 230
9.5.1 IRPs für Plug&Play 232
9.5.2 PnP-Codes für PDOs 234
9.5.3 Weitergabe von PnP-Anforderungen 234
9.5.4 I/O-Komplettierungsroutinen 236
9.5.5 PnP-Anforderungen an einen Bus-FDO-Treiber 239
9.6 Geräte aufzählen 240
9.6.1 Ressourcenbeschreiber für Hardware 240
9.6.2 Umgang des Treibers mit Hardwareressourcen 242
9.7 Geräte und Schnittstellen 243
9.7.1 Definition von Schnittstellen 243
9.7.2 Konstruktion von Schnittstellen 244
9.7.3 Referenzzählung für Schnittstellen 245
9.7.4 Eine Schnittstelle registrieren und verfügbar machen 245
9.8 Codebeispiel: Ein einfacher Plug&Play-Treiber 247
9.9 Zusammenfassung 247
Kapitel 10 Energieverwaltung 249
10.1 Hotplugging 250
10.1.1 Anforderungen an Busse 251
10.1.2 Anforderungen an Geräte 251
10.2 Die OnNow-Initiative 252
10.2.1 Power States 252
10.2.2 Richtlinien 253
10.2.3 Übergangsmatrix 254
10.2.4 Änderungen des Energiestatus 255
10.3 Weckanforderungen 259
10.3.1 Abbruch von Weckanforderungen 261
10.4 Einige weitere Details 261
10.4.1 Leerlaufzeiten 262
10.4.2 Die Benutzeroberfläche für die Energieverwaltung 263
10.5 Zusammenfassung 263
Kapitel 11 Timer 265
11.1 Zeitüberschreitungen von Geräten behandeln 266
11.1.1 Arbeitsweise einer IoTimer-Routine 266
11.1.2 Wie werden Zeitüberschreitungen eines Gerätes bemerkt? 267
11.2 Codebeispiel: Zeitüberschreitung erkennen 268
11.2.1 Geräteerweiterung 268
11.2.2 AddDevice: Timer initialisieren 268
11.2.3 CreateDispatch: Timer starten 269
11.2.4 StartIo und ISR: Zeitlimit setzen 269
11.2.5 IoTimer: Zeitüberschreitung erkennen und ahnden 270
11.3 Zeitüberwachung für Geräte ohne Interrupt 271
11.3.1 Polling von Geräten 271
11.3.2 Arbeitsweise von CustomTimerDpc-Routinen 273
11.3.3 Installieren einer CustomTimerDpc-Routine 274
11.3.4 Zeitpunkt oder -intervall programmieren 276
11.4 Codebeispiel: Ein Treiber mit Timer-Objekt 278
11.4.1 Geräteerweiterung 278
11.4.2 AddDevice 278
11.4.3 TransmitByte 279
11.4.4 PollingTimerDpc 279
11.5 Zusammenfassung 280
Kapitel 12 DMA-Treiber 281
12.1 DMA unter Windows 2000 282
12.1.1 Verkapselung über Adapter-Objekte 282
12.1.2 Das Scatter-/Gather-Problem 283
12.1.3 Memory Descriptor List (MDL) 286
12.1.4 Kohärenz mit dem Cache aufrechterhalten 288
12.1.5 Paketbasierter DMA vs. DMA mit gemeinsamem Puffer 290
12.1.6 Grenzen der DMA-Architektur von Windows 2000 291
12.2 Adapter-Objekte im Einsatz 291
12.2.1 Lokalisieren des Adapter-Objekts 292
12.2.2 Inbesitznahme und Rückgabe eines Adapter-Objekts 294
12.2.3 Programmierung der DMA-Hardware 296
12.2.4 Entleeren des Adapter-Caches 298
12.3 Details zu paketbasierten Slave-DMA-Treibern 299
12.3.1 Funktionsprinzip 299
12.3.2 Splitten von DMA-Transfers 302
12.4 Codebeispiel: Ein paketbasierter Slave-DMA-Treiber 304
12.4.1 Driver.H 304
12.4.2 GetDmaInfo-Routine 305
12.4.3 StartIo-Routine 306
12.4.4 AdapterControl-Routine 307
12.4.5 ISR 308
12.4.6 DpcForIsr-Routine 308
12.5 Paketbasierte Busmaster-DMA-Treiber 310
12.5.1 Einrichten von Busmaster-Hardware 311
12.5.2 Hardware mit Scatter-/Gather-Unterstützung 314
12.5.3 Anlegen von Scatter-/Gather-Listen 315
12.6 Slave-DMA-Treiber mit gemeinsam genutzten Puffern 318
12.6.1 Gemeinsamen DMA-Puffer belegen 318
12.6.2 Auswirkungen auf den Datendurchsatz 319
12.7 Busmaster-DMA-Treiber mit gemeinsamen Puffern 323
12.7.1 Funktionsprinzip 323
12.8 Zusammenfassung 324
Kapitel 13 Konfiguration und Performance-Daten 327
13.1 WMI aus Sicht der Industrie 328
13.2 WMI-Architektur 330
13.3 WMI-Unterstützung in WDM-Treiber einbauen 331
13.3.1 Die Syntax des MOF 332
13.3.2 Beispiel einer MOF-Klassendefinition 335
13.3.3 Kompilieren der MOF-Quelldatei 336
13.3.4 Behandlung von WMI-IRPs 337
13.3.5 Klassen und Instanzen 338
13.3.6 Resümee 347
13.4 Konventionelle Ereignisprotokollierung 347
13.4.1 Funktionsweise der Ereignisprotokollierung 347
13.4.2 Meldungen und Meldungscodes 349
13.4.3 Aufbau einer Definitionsdatei für Meldungen 350
13.4.4 Ein einfaches Beispiel 351
13.4.5 Kompilieren einer Definitionsdatei 353
13.4.6 Ressourcen in das Treiberprojekt einbinden 354
13.4.7 Registrieren eines Treibers als Ereignislieferant 354
13.4.8 Protokolleinträge generieren 354
13.4.9 Ereignispaket zusammenstellen 355
13.4.10 Ereignispaket abschicken 356
13.5 Zusammenfassung 357
Kapitel 14 System-Threads 359
14.1 Kernel-Threads: Definition und Verwendung 360
14.1.1 Wann sollte man Threads einsetzen? 360
14.1.2 Anlegen und Beenden von System-Threads 361
14.1.3 Thread-Prioritäten 363
14.1.4 System-Arbeitsthreads 363
14.2 Thread-Synchronisierung 364
14.2.1 Zeitliche Synchronisierung 364
14.2.2 Allgemeine Synchronisierung 364
14.3 Einsatz von Dispatcher-Objekten 367
14.3.1 Event-Objekte 367
14.3.2 Synchronisation zwischen verschiedenen Treibern 369
14.3.3 Mutex-Objekte 370
14.3.4 Semaphoren 371
14.3.5 Timer-Objekte 373
14.3.6 Thread-Objekte 374
14.3.7 Mutex-Varianten 375
14.3.8 Deadlocks 376
14.4 Codebeispiel: Ein Treiber mit eigenen Threads 378
14.4.1 Funktionsprinzip 378
14.4.2 Geräteerweiterung 379
14.4.3 AddDevice-Routine 380
14.4.4 DispatchReadWrite-Routine 381
14.4.5 Thread.cpp 382
14.4.6 Transfer.cpp 385
14.5 Zusammenfassung 392
Kapitel 15 Zwischentreiber 393
15.1 Zwischentreiber im Überblick 394
15.1.1 Definition: Zwischentreiber 394
15.1.2 Wann verwendet man eine geschichtete Architektur? 395
15.2 Schichttreiber 397
15.2.1 Arbeitsweise geschichteter Treiberdesigns 397
15.2.2 Initialisierungs- und Aufräumcode neuer Treiberschichten 397
15.2.3 Codeauszug: Verbindung mit untergeordnetem Treiber
etablieren 399
15.2.4 Weitere Fragen bei der Initialisierung einer Treiberschicht 401
15.2.5 I/O-Anforderungen in geschichteten Treibern 402
15.2.6 Codeauszug: Aufruf des untergeordneten Treibers 404
15.3 I/O-Komplettierungsroutinen 405
15.3.1 Registrieren einer I/O-Komplettierungsroutine 405
15.3.2 Ausführungskontext 406
15.3.3 Aktionsradius einer I/O-Komplettierungsroutine 407
15.3.4 Codeauszug: I/O-Komplettierungsroutine 409
15.4 Eigene IRPs rekrutieren 410
15.4.1 Der I/O-Stack eines IRPs 411
15.4.2 Welche Größe hat der IRP-Stack 412
15.4.3 IRPs beim I/O-Manager anfordern 413
15.4.4 IRPs von Grund auf selbst generieren 418
15.4.5 Puffer für untergeordnete Treiberschichten bereitstellen 422
15.4.6 Treibereigene IRPs verwalten 423
15.5 Filtertreiber 425
15.5.1 Arbeitsweise 426
15.5.2 Initialisierung und Aufräumcode 427
15.5.3 Transparenz 428
15.6 Codebeispiel: ein Filtertreiber 428
15.6.1 Aufbau der DeviceExtension-Struktur 429
15.6.2 DriverEntry 429
15.6.3 AddDevice 430
15.6.4 GetBufferLimits 431
15.6.5 OverriddenDispatchWrite 432
15.6.6 OverriddenDispatchDeviceIoControl 434
15.6.7 DispatchPassThru 435
15.6.8 Komplettierungsroutinen 436
15.6.9 Installation und Test des Beispieltreibers 439
15.7 Eng gekoppelte Treiber 439
15.7.1 Arbeitsweise 440
15.7.2 Initialisierung und Aufräumcode 441
15.8 Zusammenfassung 442
Kapitel 16 Treiberinstallation 443
16.1 Der Installationsprozess im Überblick 444
16.2 Automatische Installation mit .INF-Dateien 444
16.2.1 Dateistruktur 445
16.2.2 Version-Abschnitt 446
16.2.3 Manufacturers-Abschnitt 447
16.2.4 Modell-Abschnitte 447
16.2.5 DDInstall-Abschnitte 447
16.2.6 SourceDiskNames-Abschnitt 452
16.2.7 SourceDiskFiles-Abschnitt 453
16.2.8 DestinationDirs-Abschnitt 453
16.2.9 DDInstall.Services-Abschnitt 455
16.2.10 ServiceInstall-Abschnitte 456
16.2.11 Eine INF-Beispieldatei 457
16.2.12 Prüf- und Hilfsprogramme für INF-Dateien 458
16.3 Verarbeitung von INF-Dateien 459
16.3.1 Manuelle Installation 459
16.3.2 Automatische Installation 459
16.3.3 Der Hardwareassistent 460
16.3.4 Setup-Klassennamen und Geräte-IDs 461
16.3.5 Erweiterungen des Installationsprozesses 464
16.4 Festlegen der Ladereihenfolge 464
16.4.1 Treiber-Reihenfolge in Geräteobjekt-Stacks 465
16.5 Digitale Signierung von Treibern 466
16.5.1 Wieso Microsoft Treiber verifiziert 466
16.5.2 Digitale Signaturen 467
16.6 Zusammenfassung 467
Kapitel 17 Treiber testen und Fehler suchen 469
17.1 Richtlinien für das Testen von Treibern 470
17.1.1 Allgemeines 470
17.1.2 Hardware Compatibility Tests (HCTs) 473
17.2 Warum Treiber zuweilen versagen 474
17.2.1 Fehlerkategorien 474
17.2.2 Fehlerbilder reproduzieren 478
17.2.3 Strategien für die defensive Programmierung 479
17.2.4 Pflege eines Fehlerprotokolls 479
17.3 Blue Screens lesen 480
17.3.1 Was passiert bei einem Systemzusammenbruch? 480
17.3.2 Informationsgehalt eines Blue Screens 481
17.4 WinDbg im Überblick 483
17.4.1 Der Schlüssel zum Quellcode-Debugging 483
17.4.2 Die wichtigsten Kommandos von WinDbg 484
17.5 Analyse eines Post Mortem Dumps 486
17.5.1 Zielsetzungen einer Analyse 486
17.5.2 Start der Analyse 487
17.5.3 Stackverfolgung 488
17.5.4 Indirekte Ermittlungen 490
17.6 Interaktive Debugger-Sitzungen 493
17.6.1 Starten und Beenden einer interaktiven Debugger-Sitzung 493
17.6.2 Unterbrechungspunkte setzen 495
17.6.3 Unterbrechungspunkte hart kodieren 496
17.6.4 Debug-Meldungen 496
17.7 WinDbg um eigene Befehle erweitern 497
17.7.1 Arbeitsweise der erweiterten Befehle von WinDbg 497
17.7.2 Routinen für die Initialisierung und Versionsprüfung 497
17.7.3 Routinen für die erweiterten Kommandos 499
17.7.4 Schnittstellenroutinen von WinDbg 500
17.7.5 DLL-Erweiterungen erstellen und einsetzen 501
17.8 Codebeispiel: DLL-Erweiterung für WinDbg 501
17.8.1 DBG.CPP 501
17.9 Weitere Debug-Techniken 509
17.9.1 Debug-Zusätze im Treiber-Code belassen 509
17.9.2 Falsche Voraussetzungen erkennen 509
17.9.3 Bugcheck-Rückrufroutinen registrieren 510
17.9.4 Speicherlecks aufdecken 511
17.9.5 Zähler, Ereignisflags und Puffer für die Rückverfolgung 513
17.9.6 Ereignisflags 513
17.9.7 Puffer für die Rückverfolgung 514
17.10 Zusammenfassung 515
Anhang A Debug-Umgebung 517
A.1 Hard- und Softwarevoraussetzungen 518
A.1.1 Der Host 518
A.1.2 Das Zielsystem 519
A.1.3 Verbindung zwischen Host und Zielsystem 519
A.2 Debug-Symboldateien 520
A.2.1 Symboldateien für das Betriebssystem 520
A.2.2 Symboldateien für den Treiber 521
A.3 Post Mortem Dumps auf der Zielmaschine 522
A.3.1 Post Mortem Dump per Tastendruck erzwingen 522
A.4 Aktivieren des Debug-Clients auf der Zielmaschine 523
A.4.1 Anpassen von BOOT.INI 523
Anhang B Bugcheck-Codes 525
Anhang C Der Build-Prozess 543
C.1 Das Hilfsprogramm BUILD 544
C.1.1 Funktionsprinzip 545
C.1.2 Erstellen von Treibern mit BUILD 546
C.1.3 Die Datei SOURCES 547
C.1.4 Protokolldateien von BUILD 549
C.1.5 Rekursive BUILD-Operationen 549
C.2 Treiber mit Visual Studio erstellen 550
C.2.1 DDAppWiz 550
Anhang D Hinweise zur Begleit-CD 553
Anhang E Bibliographie 557
Stichwortverzeichnis 559
Einleitung 23
Voraussetzungen für die Lektüre 23
Über den Inhalt 24
Was dieses Buch nicht abdeckt 25
Code auf der Begleit-CD 25
Entstehungsgeschichte dieses Buchs 26
Kapitel 1 Einführung in die Treiber technologie von Windows 2000 27
1.1 Allgemeine Systemarchitektur 28
1.1.1 Entwicklungsziele für Windows 2000 28
1.1.2 Hardware-Privilegstufen in Windows 2000 29
1.1.3 Portabilität 30
1.1.4 Erweiterbarkeit 30
1.1.5 Leistungsfähigkeit 31
1.1.6 Executive-Komponenten 32
1.2 Im Kernel Mode laufende I/O-Komponenten 38
1.2.1 Entwicklungsziele für das I/O-Subsystem 38
1.2.2 Treiberarten von Windows 2000 39
1.3 Spezielle Treiberarchitekturen 42
1.3.1 Videotreiber 43
1.3.2 Druckertreiber 44
1.3.3 Multimediatreiber 46
1.3.4 Netzwerktreiber 47
1.4 Zusammenfassung 48
Kapitel 2 Die Hardwareumgebung 49
2.1 Hardwaregrundlagen 50
2.1.1 Steuer- und Statusregister 50
2.1.2 Registerzugriffe 52
2.1.3 Interrupts 54
2.1.4 Datentransfer 57
2.1.5 DMA-Mechanismen 58
2.1.6 Automatische Erkennung und Konfiguration 59
2.2 Busse und Windows 2000 61
2.2.1 ISA: Industry Standard Architecture 62
2.2.2 EISA: Extended Industry Standard Architecture 64
2.2.3 PCI: Peripheral Component Interconnect 67
2.2.4 USB: Universal Serial Bus 72
2.2.5 IEE 1394: FireWire" 74
2.2.6 PC Card (PCMCIA) 76
2.3 Hinweise für die Arbeit mit Hardware 77
2.3.1 Gründliche Einarbeitung 77
2.3.2 Einsatz von Eigenintelligenz 79
2.3.3 Hardwareprüfungen 79
2.4 Zusammenfassung 79
Kapitel 3 I/O-Verarbeitung im Kernel Mode 81
3.1 Wie kommt es zur Ausführung von Kernel-Mode-Code? 82
3.1.1 Der Trap-/Exception-Kontext 82
3.1.2 Der Interrupt-Kontext 83
3.1.3 Der Kernel-Mode-Thread-Kontext 83
3.2 Interrupt-Prioritäten unter Windows 2000 83
3.2.1 Interrupt Request Level 84
3.2.2 Reihenfolge der Interrupt-Behandlung 85
3.2.3 Software-Interrupts 85
3.3 Zurückgestellte Prozeduraufrufe (DPC) 86
3.3.1 Ablauf eines DPCs 86
3.3.2 Verhalten von DPCs 87
3.4 Zugriff auf Puffer von User-Mode-Code 88
3.4.1 Mechanismen für den Zugriff auf User-Mode-Puffer 89
3.5 Struktur eines Kernel-Mode-Treibers 90
3.5.1 Treiberinitialisierung und Aufräumcode 90
3.5.2 Dispatch-Routinen für die Systemdienste des I/O-Managers 91
3.5.3 Routinen für den Datentransfer 92
3.5.4 Rückrufroutinen als Synchronisationsmechanismus
für den Zugriff auf Ressourcen 93
3.5.5 Andere Treiberroutinen 95
3.6 Lebenszyklus von I/O-Anforderungen 95
3.6.1 Vorbereitung durch den I/O-Manager 96
3.6.2 Vorbereitung durch den Gerätetreiber 96
3.6.3 Start des Gerätes und Interruptbehandlung 97
3.6.4 Nachbereitung durch den Gerätetreiber 98
3.6.5 Nachbereitung durch den I/O-Manager 98
3.7 Zusammenfassung 100
Kapitel 4 Treiber und Kernel-Mode-Objekte 101
4.1 Datenobjekte und Windows 2000 102
4.1.1 Windows 2000 und OOP 102
4.1.2 Objekte in Windows 2000 und in Win32 102
4.2 I/O Request Packets (IRPs) 103
4.2.1 Der Aufbau eines IRPs 104
4.2.2 Manipulation von IRPs 106
4.3 Treiberobjekte 108
4.3.1 Aufbau eines Treiberobjekts 108
4.4 Geräteobjekte und Geräteerweiterungen 109
4.4.1 Aufbau eines Geräteobjektes 110
4.4.2 Manipulation von Geräteobjekten 111
4.4.3 Geräteerweiterung 111
4.5 Controller-Objekte und -Erweiterungen 112
4.5.1 Aufbau eines Controller-Objekts 113
4.5.2 Manipulation von Controller-Objekten 114
4.5.3 Controller-Erweiterungen 114
4.6 Adapter-Objekte 114
4.6.1 Der Aufbau von Adapter-Objekten 115
4.6.2 Manipulation von Adapter-Objekten 116
4.7 Interrupt-Objekte 117
4.7.1 Aufbau eines Interrupt-Objekts 117
4.7.2 Manipulation von Interrupt-Objekten 118
4.8 Zusammenfassung 118
Kapitel 5 Allgemeines zur Treiberentwicklung 119
5.1 Strategien für das Treiberdesign 120
5.1.1 Formale Methoden beim Design einsetzen 120
5.1.2 Inkrementelle Entwicklung 121
5.1.3 Beispieltreiber von Microsoft untersuchen und verwenden 122
5.2 Konventionen und Techniken für die Codeerstellung 122
5.2.1 Benennungskonventionen 123
5.2.2 Header-Dateien 124
5.2.3 Statusrückgabe 125
5.2.4 Der Treibersupport von Windows 2000 126
5.2.5 Initialisierungsroutinen als discardable deklarieren 127
5.2.6 Codeanteile für die Auslagerung freigeben 127
5.3 Speicher anfordern 128
5.3.1 Speicherarten für Treiber 129
5.3.2 Umgang mit dem Kernel-Stack 129
5.3.3 Speicherbelegung auf dem System-Heap 130
5.3.4 Systemseitige Unterstützung für die Unterteilung bereits
angeforderter Speicherblocks 131
5.4 Unicode 135
5.4.1 Datentypen für Unicode-Strings 135
5.4.2 Umgang mit Unicode-Strings 136
5.5 Synchronisation von Interrupts 140
5.5.1 Das Problem 140
5.5.2 Interrupt-Sperre als Mittel zur Synchronisation 141
5.5.3 Regeln für das Sperren von Interrupts 142
5.5.4 Synchronisation über DPCs 142
5.6 Synchronisation mehrerer CPUs 142
5.6.1 Wie funktionieren Spin Locks? 143
5.6.2 Handhabung von Spin Locks 143
5.6.3 Regeln für den Einsatz von Spin Locks 145
5.7 Verkettete Listen 145
5.7.1 Einfach verkettete Listen 145
5.7.2 Doppelt verkettete Listen 146
5.7.3 Datenblock aus Blockliste entfernen 147
5.8 Zusammenfassung 148
Kapitel 6 Initialisierungs- und Aufräumroutinen 149
6.1 Die Routine DriverEntry 150
6.1.1 Ausführungskontext 150
6.1.2 Die Aufgaben der Routine 151
6.1.3 Bekanntgabe von Treiberfunktionen 152
6.1.4 Anlegen von Geräteobjekten 152
6.1.5 Festlegen einer Pufferstrategie 153
6.1.6 Gerätenamen 153
6.2 Praxisbeispiel: Treiber-Initialisierung 155
6.2.1 DriverEntry 155
6.2.2 CreateDevice 156
6.3 Reinitialisierungsroutinen 158
6.3.1 Ausführungskontext 158
6.3.2 Aufgaben 159
6.4 Unload-Routine 159
6.4.1 Ausführungskontext 159
6.4.2 Aufgaben 160
6.5 Praxisbeispiel: Unload 160
6.6 Shutdown-Routine 162
6.6.1 Ausführungskontext 162
6.6.2 Aufgaben 162
6.6.3 Ankündigung 163
6.7 Test des Treibers 163
6.7.1 Prüfprozedur 163
6.7.2 Visual C++ Device Driver AppWizard 164
6.7.3 Windows 2000 DDK 164
6.7.4 Ergebnis der Kompilierung 165
6.7.5 Manuelle Installation von Kernel-Mode-Treibern 165
6.7.6 Laden des Treibers 166
6.7.7 Windows 2000 Computerverwaltung 167
6.8 Das Dienstprogramm WINOBJ 168
6.9 Zusammenfassung 169
Kapitel 7 Dispatch-Routinen 171
7.1 Dispatch-Routinen registrieren 172
7.1.1 Dispatch-Mechanismus für I/O-Anforderungen 172
7.1.2 Unterstützung für einzelnen Funktionscode aktivieren 173
7.1.3 Welche Funktionscodes sollte ein Treiber unterstützen? 174
7.2 Dispatch-Routinen implementieren 175
7.2.1 Ausführungskontext einer Dispatch-Routine 175
7.2.2 Was macht eine Dispatch-Routine? 177
7.2.3 Aktionsradius einer Dispatch-Routine 177
7.3 Schreib- und Leseanforderungen verarbeiten 180
7.3.1 Zugriff auf den User-Mode-Puffer 180
7.4 Codebeispiel: ein Loopback-Gerät 182
7.5 Erweiterung der Dispatch-Schnittstelle 184
7.5.1 Definition privater IOCTL-Codes 184
7.5.2 Möglichkeiten für den IOCTL-Datentransfer 185
7.5.3 Header-Dateien für IOCTL-Code 186
7.5.4 Verarbeitung von IOCTL-Anforderungen 187
7.5.5 Puffer für IOCTL-Anforderungen 189
7.6 Dispatch-Routinen eines Treibers testen 190
7.6.1 Schreiben und Testen eines Treibergerüsts 190
7.6.2 Beispiel für ein Testprogramm 191
7.7 Zusammenfassung 191
Kapitel 8 Interruptgesteuerte I/O 193
8.1 Programmierte I/O: Funktionsprinzip 194
8.1.1 Der Ablauf 194
8.1.2 Synchronisation von Treiberroutinen 195
8.2 Initialisierungs- und Aufräumarbeiten 196
8.2.1 StartIo-Routine 196
8.2.2 DpcForIsr-Routine 197
8.2.3 Verbindung mit einer Interrupt-Quelle 197
8.2.4 Zurücksetzen der Interrupt-Verbindung 199
8.3 StartIo-Routine 200
8.3.1 Ausführungskontext 200
8.3.2 Aufgabenfeld 200
8.4 Interrupt Service Routine (ISR) 201
8.4.1 Ausführungskontext 201
8.5 DpcForIsr-Routinen 202
8.5.1 Ausführungskontext 202
8.5.2 Aufgabenfeld 203
8.5.3 Anhebung der Priorität 203
8.6 Parallele Schnittstellen: Die Hardware 204
8.6.1 Datenübertragung über die parallele Schnittstelle 204
8.6.2 Register 206
8.6.3 Interruptverhalten 206
8.6.4 Ein Loopback-Stecker für die Parallelschnittstelle 207
8.7 Codebeispiel: Loopback-Treiber für die Parallelschnittstelle 207
8.7.1 Funktionsumfang 208
8.7.2 Driver.h 208
8.7.3 Driver.cpp 209
8.7.4 Installation und Test des Beispieltreibers 215
8.7.5 Testmöglichkeiten 217
8.7.6 Allgemeine Prüfverfahren 217
8.8 Zusammenfassung 218
Kapitel 9 I/O-Verarbeitung im Kernel Mode 219
9.1 Geschichtliches zur Plug&Play-Architektur 220
9.1.1 Ziele für Plug&Play 221
9.1.2 Komponenten des Plug&Play-Subsystems 221
9.2 Rolle der Registrierung für althergebrachte Treiber 223
9.3 Geräte mit Plug&Play erkennen 224
9.4 Die Rolle der Treiberschichten beim Plug&Play 225
9.5 Die neue PnP-Dispatch-Routine des WDM 230
9.5.1 IRPs für Plug&Play 232
9.5.2 PnP-Codes für PDOs 234
9.5.3 Weitergabe von PnP-Anforderungen 234
9.5.4 I/O-Komplettierungsroutinen 236
9.5.5 PnP-Anforderungen an einen Bus-FDO-Treiber 239
9.6 Geräte aufzählen 240
9.6.1 Ressourcenbeschreiber für Hardware 240
9.6.2 Umgang des Treibers mit Hardwareressourcen 242
9.7 Geräte und Schnittstellen 243
9.7.1 Definition von Schnittstellen 243
9.7.2 Konstruktion von Schnittstellen 244
9.7.3 Referenzzählung für Schnittstellen 245
9.7.4 Eine Schnittstelle registrieren und verfügbar machen 245
9.8 Codebeispiel: Ein einfacher Plug&Play-Treiber 247
9.9 Zusammenfassung 247
Kapitel 10 Energieverwaltung 249
10.1 Hotplugging 250
10.1.1 Anforderungen an Busse 251
10.1.2 Anforderungen an Geräte 251
10.2 Die OnNow-Initiative 252
10.2.1 Power States 252
10.2.2 Richtlinien 253
10.2.3 Übergangsmatrix 254
10.2.4 Änderungen des Energiestatus 255
10.3 Weckanforderungen 259
10.3.1 Abbruch von Weckanforderungen 261
10.4 Einige weitere Details 261
10.4.1 Leerlaufzeiten 262
10.4.2 Die Benutzeroberfläche für die Energieverwaltung 263
10.5 Zusammenfassung 263
Kapitel 11 Timer 265
11.1 Zeitüberschreitungen von Geräten behandeln 266
11.1.1 Arbeitsweise einer IoTimer-Routine 266
11.1.2 Wie werden Zeitüberschreitungen eines Gerätes bemerkt? 267
11.2 Codebeispiel: Zeitüberschreitung erkennen 268
11.2.1 Geräteerweiterung 268
11.2.2 AddDevice: Timer initialisieren 268
11.2.3 CreateDispatch: Timer starten 269
11.2.4 StartIo und ISR: Zeitlimit setzen 269
11.2.5 IoTimer: Zeitüberschreitung erkennen und ahnden 270
11.3 Zeitüberwachung für Geräte ohne Interrupt 271
11.3.1 Polling von Geräten 271
11.3.2 Arbeitsweise von CustomTimerDpc-Routinen 273
11.3.3 Installieren einer CustomTimerDpc-Routine 274
11.3.4 Zeitpunkt oder -intervall programmieren 276
11.4 Codebeispiel: Ein Treiber mit Timer-Objekt 278
11.4.1 Geräteerweiterung 278
11.4.2 AddDevice 278
11.4.3 TransmitByte 279
11.4.4 PollingTimerDpc 279
11.5 Zusammenfassung 280
Kapitel 12 DMA-Treiber 281
12.1 DMA unter Windows 2000 282
12.1.1 Verkapselung über Adapter-Objekte 282
12.1.2 Das Scatter-/Gather-Problem 283
12.1.3 Memory Descriptor List (MDL) 286
12.1.4 Kohärenz mit dem Cache aufrechterhalten 288
12.1.5 Paketbasierter DMA vs. DMA mit gemeinsamem Puffer 290
12.1.6 Grenzen der DMA-Architektur von Windows 2000 291
12.2 Adapter-Objekte im Einsatz 291
12.2.1 Lokalisieren des Adapter-Objekts 292
12.2.2 Inbesitznahme und Rückgabe eines Adapter-Objekts 294
12.2.3 Programmierung der DMA-Hardware 296
12.2.4 Entleeren des Adapter-Caches 298
12.3 Details zu paketbasierten Slave-DMA-Treibern 299
12.3.1 Funktionsprinzip 299
12.3.2 Splitten von DMA-Transfers 302
12.4 Codebeispiel: Ein paketbasierter Slave-DMA-Treiber 304
12.4.1 Driver.H 304
12.4.2 GetDmaInfo-Routine 305
12.4.3 StartIo-Routine 306
12.4.4 AdapterControl-Routine 307
12.4.5 ISR 308
12.4.6 DpcForIsr-Routine 308
12.5 Paketbasierte Busmaster-DMA-Treiber 310
12.5.1 Einrichten von Busmaster-Hardware 311
12.5.2 Hardware mit Scatter-/Gather-Unterstützung 314
12.5.3 Anlegen von Scatter-/Gather-Listen 315
12.6 Slave-DMA-Treiber mit gemeinsam genutzten Puffern 318
12.6.1 Gemeinsamen DMA-Puffer belegen 318
12.6.2 Auswirkungen auf den Datendurchsatz 319
12.7 Busmaster-DMA-Treiber mit gemeinsamen Puffern 323
12.7.1 Funktionsprinzip 323
12.8 Zusammenfassung 324
Kapitel 13 Konfiguration und Performance-Daten 327
13.1 WMI aus Sicht der Industrie 328
13.2 WMI-Architektur 330
13.3 WMI-Unterstützung in WDM-Treiber einbauen 331
13.3.1 Die Syntax des MOF 332
13.3.2 Beispiel einer MOF-Klassendefinition 335
13.3.3 Kompilieren der MOF-Quelldatei 336
13.3.4 Behandlung von WMI-IRPs 337
13.3.5 Klassen und Instanzen 338
13.3.6 Resümee 347
13.4 Konventionelle Ereignisprotokollierung 347
13.4.1 Funktionsweise der Ereignisprotokollierung 347
13.4.2 Meldungen und Meldungscodes 349
13.4.3 Aufbau einer Definitionsdatei für Meldungen 350
13.4.4 Ein einfaches Beispiel 351
13.4.5 Kompilieren einer Definitionsdatei 353
13.4.6 Ressourcen in das Treiberprojekt einbinden 354
13.4.7 Registrieren eines Treibers als Ereignislieferant 354
13.4.8 Protokolleinträge generieren 354
13.4.9 Ereignispaket zusammenstellen 355
13.4.10 Ereignispaket abschicken 356
13.5 Zusammenfassung 357
Kapitel 14 System-Threads 359
14.1 Kernel-Threads: Definition und Verwendung 360
14.1.1 Wann sollte man Threads einsetzen? 360
14.1.2 Anlegen und Beenden von System-Threads 361
14.1.3 Thread-Prioritäten 363
14.1.4 System-Arbeitsthreads 363
14.2 Thread-Synchronisierung 364
14.2.1 Zeitliche Synchronisierung 364
14.2.2 Allgemeine Synchronisierung 364
14.3 Einsatz von Dispatcher-Objekten 367
14.3.1 Event-Objekte 367
14.3.2 Synchronisation zwischen verschiedenen Treibern 369
14.3.3 Mutex-Objekte 370
14.3.4 Semaphoren 371
14.3.5 Timer-Objekte 373
14.3.6 Thread-Objekte 374
14.3.7 Mutex-Varianten 375
14.3.8 Deadlocks 376
14.4 Codebeispiel: Ein Treiber mit eigenen Threads 378
14.4.1 Funktionsprinzip 378
14.4.2 Geräteerweiterung 379
14.4.3 AddDevice-Routine 380
14.4.4 DispatchReadWrite-Routine 381
14.4.5 Thread.cpp 382
14.4.6 Transfer.cpp 385
14.5 Zusammenfassung 392
Kapitel 15 Zwischentreiber 393
15.1 Zwischentreiber im Überblick 394
15.1.1 Definition: Zwischentreiber 394
15.1.2 Wann verwendet man eine geschichtete Architektur? 395
15.2 Schichttreiber 397
15.2.1 Arbeitsweise geschichteter Treiberdesigns 397
15.2.2 Initialisierungs- und Aufräumcode neuer Treiberschichten 397
15.2.3 Codeauszug: Verbindung mit untergeordnetem Treiber
etablieren 399
15.2.4 Weitere Fragen bei der Initialisierung einer Treiberschicht 401
15.2.5 I/O-Anforderungen in geschichteten Treibern 402
15.2.6 Codeauszug: Aufruf des untergeordneten Treibers 404
15.3 I/O-Komplettierungsroutinen 405
15.3.1 Registrieren einer I/O-Komplettierungsroutine 405
15.3.2 Ausführungskontext 406
15.3.3 Aktionsradius einer I/O-Komplettierungsroutine 407
15.3.4 Codeauszug: I/O-Komplettierungsroutine 409
15.4 Eigene IRPs rekrutieren 410
15.4.1 Der I/O-Stack eines IRPs 411
15.4.2 Welche Größe hat der IRP-Stack 412
15.4.3 IRPs beim I/O-Manager anfordern 413
15.4.4 IRPs von Grund auf selbst generieren 418
15.4.5 Puffer für untergeordnete Treiberschichten bereitstellen 422
15.4.6 Treibereigene IRPs verwalten 423
15.5 Filtertreiber 425
15.5.1 Arbeitsweise 426
15.5.2 Initialisierung und Aufräumcode 427
15.5.3 Transparenz 428
15.6 Codebeispiel: ein Filtertreiber 428
15.6.1 Aufbau der DeviceExtension-Struktur 429
15.6.2 DriverEntry 429
15.6.3 AddDevice 430
15.6.4 GetBufferLimits 431
15.6.5 OverriddenDispatchWrite 432
15.6.6 OverriddenDispatchDeviceIoControl 434
15.6.7 DispatchPassThru 435
15.6.8 Komplettierungsroutinen 436
15.6.9 Installation und Test des Beispieltreibers 439
15.7 Eng gekoppelte Treiber 439
15.7.1 Arbeitsweise 440
15.7.2 Initialisierung und Aufräumcode 441
15.8 Zusammenfassung 442
Kapitel 16 Treiberinstallation 443
16.1 Der Installationsprozess im Überblick 444
16.2 Automatische Installation mit .INF-Dateien 444
16.2.1 Dateistruktur 445
16.2.2 Version-Abschnitt 446
16.2.3 Manufacturers-Abschnitt 447
16.2.4 Modell-Abschnitte 447
16.2.5 DDInstall-Abschnitte 447
16.2.6 SourceDiskNames-Abschnitt 452
16.2.7 SourceDiskFiles-Abschnitt 453
16.2.8 DestinationDirs-Abschnitt 453
16.2.9 DDInstall.Services-Abschnitt 455
16.2.10 ServiceInstall-Abschnitte 456
16.2.11 Eine INF-Beispieldatei 457
16.2.12 Prüf- und Hilfsprogramme für INF-Dateien 458
16.3 Verarbeitung von INF-Dateien 459
16.3.1 Manuelle Installation 459
16.3.2 Automatische Installation 459
16.3.3 Der Hardwareassistent 460
16.3.4 Setup-Klassennamen und Geräte-IDs 461
16.3.5 Erweiterungen des Installationsprozesses 464
16.4 Festlegen der Ladereihenfolge 464
16.4.1 Treiber-Reihenfolge in Geräteobjekt-Stacks 465
16.5 Digitale Signierung von Treibern 466
16.5.1 Wieso Microsoft Treiber verifiziert 466
16.5.2 Digitale Signaturen 467
16.6 Zusammenfassung 467
Kapitel 17 Treiber testen und Fehler suchen 469
17.1 Richtlinien für das Testen von Treibern 470
17.1.1 Allgemeines 470
17.1.2 Hardware Compatibility Tests (HCTs) 473
17.2 Warum Treiber zuweilen versagen 474
17.2.1 Fehlerkategorien 474
17.2.2 Fehlerbilder reproduzieren 478
17.2.3 Strategien für die defensive Programmierung 479
17.2.4 Pflege eines Fehlerprotokolls 479
17.3 Blue Screens lesen 480
17.3.1 Was passiert bei einem Systemzusammenbruch? 480
17.3.2 Informationsgehalt eines Blue Screens 481
17.4 WinDbg im Überblick 483
17.4.1 Der Schlüssel zum Quellcode-Debugging 483
17.4.2 Die wichtigsten Kommandos von WinDbg 484
17.5 Analyse eines Post Mortem Dumps 486
17.5.1 Zielsetzungen einer Analyse 486
17.5.2 Start der Analyse 487
17.5.3 Stackverfolgung 488
17.5.4 Indirekte Ermittlungen 490
17.6 Interaktive Debugger-Sitzungen 493
17.6.1 Starten und Beenden einer interaktiven Debugger-Sitzung 493
17.6.2 Unterbrechungspunkte setzen 495
17.6.3 Unterbrechungspunkte hart kodieren 496
17.6.4 Debug-Meldungen 496
17.7 WinDbg um eigene Befehle erweitern 497
17.7.1 Arbeitsweise der erweiterten Befehle von WinDbg 497
17.7.2 Routinen für die Initialisierung und Versionsprüfung 497
17.7.3 Routinen für die erweiterten Kommandos 499
17.7.4 Schnittstellenroutinen von WinDbg 500
17.7.5 DLL-Erweiterungen erstellen und einsetzen 501
17.8 Codebeispiel: DLL-Erweiterung für WinDbg 501
17.8.1 DBG.CPP 501
17.9 Weitere Debug-Techniken 509
17.9.1 Debug-Zusätze im Treiber-Code belassen 509
17.9.2 Falsche Voraussetzungen erkennen 509
17.9.3 Bugcheck-Rückrufroutinen registrieren 510
17.9.4 Speicherlecks aufdecken 511
17.9.5 Zähler, Ereignisflags und Puffer für die Rückverfolgung 513
17.9.6 Ereignisflags 513
17.9.7 Puffer für die Rückverfolgung 514
17.10 Zusammenfassung 515
Anhang A Debug-Umgebung 517
A.1 Hard- und Softwarevoraussetzungen 518
A.1.1 Der Host 518
A.1.2 Das Zielsystem 519
A.1.3 Verbindung zwischen Host und Zielsystem 519
A.2 Debug-Symboldateien 520
A.2.1 Symboldateien für das Betriebssystem 520
A.2.2 Symboldateien für den Treiber 521
A.3 Post Mortem Dumps auf der Zielmaschine 522
A.3.1 Post Mortem Dump per Tastendruck erzwingen 522
A.4 Aktivieren des Debug-Clients auf der Zielmaschine 523
A.4.1 Anpassen von BOOT.INI 523
Anhang B Bugcheck-Codes 525
Anhang C Der Build-Prozess 543
C.1 Das Hilfsprogramm BUILD 544
C.1.1 Funktionsprinzip 545
C.1.2 Erstellen von Treibern mit BUILD 546
C.1.3 Die Datei SOURCES 547
C.1.4 Protokolldateien von BUILD 549
C.1.5 Rekursive BUILD-Operationen 549
C.2 Treiber mit Visual Studio erstellen 550
C.2.1 DDAppWiz 550
Anhang D Hinweise zur Begleit-CD 553
Anhang E Bibliographie 557
Stichwortverzeichnis 559