Entwicklung des USB-Rotary-Sensor-Device und zugehörigem WDF-Treiber - Januar 2009
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Entwicklung des USB-Rotary-Sensor-Device und zugehörigem WDF-Treiber von Matthias Rick und Sven Schraps 22. Januar 2009
Inhaltsverzeichnis 1. Die Hardware 1 1.1. Der Melexis MLX90316 Drehwinkelgeber . . . . . . . . . . . . . . . . . . . 1 1.2. Entwicklung des Boards . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2.1. Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2.2. Die Leiterplatte als Eagle-Entwurf . . . . . . . . . . . . . . . . . . 2 1.2.3. Die fertige Leiterplatte mit benötigter Bestückung . . . . . . . . . 3 1.3. Über die Firmware im Mikrocontroller AT90USB1287 . . . . . . . . . . . 3 1.3.1. Programmierung des USB-Controllers . . . . . . . . . . . . . . . . 3 1.3.1.1. Die Endpoints im Controller . . . . . . . . . . . . . . . . 4 1.3.2. Die Deskriptoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2. Die Treiberanbindung 7 2.1. WDF - Die Windows Driver Foundation . . . . . . . . . . . . . . . . . . . 7 2.2. Der WDF-Driver-Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.1. Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.2. KMDF - Kernel Mode Driver Framework . . . . . . . . . . . . . . 8 2.2.3. UMDF - User Mode Driver Framework . . . . . . . . . . . . . . . 8 2.3. Die Entwicklungsumgebung . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.4. HID - Die “Human Interface Device”-Class . . . . . . . . . . . . . . . . . 10 2.5. Treiberübersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.6. Der USBRotary-Treiber . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.6.1. Schnellübersicht über Quelltextdatein und deren Funktionen . . . 12 2.6.1.1. Quelltextdatein . . . . . . . . . . . . . . . . . . . . . . . 12 2.6.1.2. Funktionenbeschreibung für drv.cpp . . . . . . . . . . . . 12 2.6.1.3. Funktionenbeschreibung für hid.cpp . . . . . . . . . . . . 14 2.6.1.4. Die Headerfiles . . . . . . . . . . . . . . . . . . . . . . . . 14 3. Quellen und Hilfsmittel 16 3.1. Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2. Internetseiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.3. Werkzeuge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.4. sonstige Hilfsmittel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 A. Quelltexte 18 ii
1. Die Hardware 1.1. Der Melexis MLX90316 Drehwinkelgeber Basis des USB-Rotary-Sensor-Device ist der Drehwinkelsensor Melexis MLX90316. Dieser befindet sich auf einem Evaluation Board und die aktuellen Drehwinkel können via SPI (Serial Peripheral Interface) von einem Mikrocontroller abgerufen werden. Der MLX90316 verwendet die Triaxis Hall Technologie, welche auf IMCs (Integrated Magnetic Concentrators) basiert. Dank dieser Technologie wird die Entwicklung kleiner, kostengünstiger und dabei hochgenauer kontaktloser Drehwinkelgeber ermöglicht. Abb. 1.1.: Die Ermittlung der Lage des Magnetfeldes geschieht über 4 Hall-Sonden im inneren des Chips. Die Drehwinkelwerte werden dann mit einer Auflösung von 14 Bit über die SPI-Schnittstelle weitergegeben. Es sei darüber hinaus erwähnt, dass der der MLX90316 eine sehr umfangreiche Parame- terzahl besitzt, die alle mit einem geeigneten Programmiergerät angepasst werden können. Dieses stand uns zu diesem Projekt nicht zur Verfügung, aber die Standardparameter sind für unsere Anwendung ausreichend. 1.2. Entwicklung des Boards 1.2.1. Komponenten Um den Drehwinkelgeber ansprechen zu können, benötigen wir einen Controller, der sowohl ein SPI-Interface hat, als auch einen integrierten USB-Controller, der unsere Daten dann an den PC weiterleitet. Die Wahl fiel auf den Mikrocontroller Atmel AT90USB1287. Für diesen Prozessor wurde dann noch eine Leiterplatte mit EAGLE entworfen, die 1
aber nicht ausschließlich für dieses Projekt gedacht ist. Deswegen sind zusätzlich zu den benötigten Komponenten auch noch andere vorgesehen. Wichtig für das USB- Rotary-Device-Projekt sind die USB-Buchse zur Kommunikation mit dem PC, die JTAG- Schnittstelle zum Flashen des Controllers, die SPI-Verbindung zum Drehwinkelgeber, Buttons zur Eingabe und ein Display zur Anzeige von Debug-Ausgaben. 1.2.2. Die Leiterplatte als Eagle-Entwurf Abb. 1.2.: 2
1.2.3. Die fertige Leiterplatte mit benötigter Bestückung Abb. 1.3.: 1.3. Über die Firmware im Mikrocontroller AT90USB1287 1.3.1. Programmierung des USB-Controllers Der Mikrocontroller verfügt über einen integrierten USB-Controller. Dieser unterstützt sowohl Low Speed, als auch Full Speed USB. Wir haben uns für Full Speed USB entschieden und haben somit eine Übertragungsrate von 12Mbit/s zwischen PC und Mikrocontroller. Die Kommunikation zwischen USB-Host-Controller im PC und dem USB-Controller im Device findet über Pipes im Host und zugehörige Endpoints im Device statt. Jeder Endpoint bzw. Pipe kann einen unterschiedlichen Übertragungsmodus haben. Jeder Endpoint gehört zu einem Inferface und jedes Interface gehört zu einer Configuration. Es kann sowohl mehrere Interfaces innerhalb einer Configuration, als auch an sich mehrere Configurations geben. An Übertragungsmodi gibt es prinzipiell 3 Verschiedene: Interrupt Transfer, Bulk Transfer, Isochronous Transfer. Wir haben den Interrupt Transfer gewählt, da unser Gerät nur dann Daten senden soll, wenn eine Änderung der Werte stattfindet. All diese Informationen über die Konfiguration werden beim Einstecken des Gerätes über Deskriptoren an den PC gesand. Dies geht als Control Transfer über den Endpoint 0, welcher für die Konfiguration des Gerätes zuständig ist. Er ist als einziger Endpoint 3
bidirektional, wodurch er sowohl Daten empfangen (in der Regel Befehlswörter), als auch Daten senden kann (Deskriptoren). Hier ein Überblick über die Struktur der Deskriptoren: Abb. 1.4.: 1.3.1.1. Die Endpoints im Controller Das USB-Rotary-Sensor-Device benötigt 2 Endpoints. Enpoint 0 zur Übertragung der Deskriptoren und einen In-Endpoint. Die Richtung wird immer vom Host aus betrachtet. Das bedeutet, dass der In-Endpoint Daten vom Mikrocontroller an den Rechner sendet. Zusätzlich haben wir zum Testen auch noch einen Out-Endpoint in Betrieb genommen. Dadurch ergibt sich folgende Konfiguration: • Endpoint 0: – Type: Default Control Enpoint – Mode: Control Transfer • Endpoint 1: – IN Endpoint – Mode: Interrupt Transfer • Endpoint 2: – OUT Endpoint – Mode: Interrupt Transfer 1.3.2. Die Deskriptoren Wie bereits bei 1.3.1 erwähnt spielen die Deskriptoren eine entscheidende Rolle bei der Initialisierung des Gerätes. Dadurch ist dann im PC-System der PnP(Plug and Play)- Manager von Windows in der Lage das Gerät zu identifizieren und den dazugehörigen Treiber einzubinden, so fern dieser installiert ist. 4
• Device-Deskriptor bLength 0x12 bDescriptorType 0x01 bcdUSB 0x0110 bDeviceClass 0x00 bDeviceSubClass 0x00 bDeviceProtocol 0x00 bMaxPacketSize 0x40 idVendor 0x03EB idProduct 0x2020 bcdDevice 0x0100 iManufacturer 0x00 iProduct 0x00 iSerialNumber 0x00 bNumConfigurations 0x01 • Configuration Deskriptor bLength 0x09 bDescriptorType 0x02 wTotalLength 0x0020 bNumInterfaces 0x00 bConfigurationValue 0x01 iConfiguration 0x01 bmAttributes 0x00 bMaxPower 0x80 • Interface Deskriptor bLength 0x09 bDescriptorType 0x04 bInterfaceNumber 0x00 bAlternateSetting 0x00 bNumEndpoints 0x02 bInterfaceClass 0x00 bInterfaceSubClass 0x00 bInterfaceProtocol 0x01 iInterface 0x00 • Endpoint-1 Deskriptor (IN) bLength 0x07 bDescriptorType 0x05 bEndpointAddress 0x81 bmAttributes 0x03 wMaxPacketSize 0x4000 bInterval 0x00 5
• Endpoint-2 Deskriptor (OUT) bLength 0x07 bDescriptorType 0x05 bEndpointAddress 0x02 bmAttributes 0x00 wMaxPacketSize 0x4000 bInterval 0x00 6
2. Die Treiberanbindung 2.1. WDF - Die Windows Driver Foundation Mit dem Windows Driver Model (WDM) schuf Microsoft vor mehr als zehn Jahren eine gemeinsame Treiberplattform für die damals aktuellen Systeme Windows 98 und später Windows 2000. Hiermit war es erstmals möglich, sowohl für die DOS- als auch für die NT-basierten Microsoft-Betriebssysteme ein einheitliches Treiberkonzept für Hardware- Einheiten umzusetzen. Das WDM ermöglicht eine Vereinheitlichung der Treiber für damals noch neue Technologien wie USB und Firewire (IEEE 1394), denn es müssen nicht mehr zwei unterschiedliche Treiberarchitekturen (VxDs, NT Driver Model) berücksichtigt werden, was das Erstellen von Treibern deutlich verbessert und erleichtert. Als wesentliche Kritikpunkte gelten bei WDM-Programmierern, dass der zu erstellende Code für das, was er eigentlich bewirken soll, zu komplex und zu umfangreich erscheint. Insbesondere die Implementierung von Plug&Play- sowie von Stromspar-Mechanismen gilt als ein verhältnismäßig schwieriges und code-aufwendiges Unterfangen, zumal es in den letzten Jahren auf diesem Gebiet mit ACPI einiges an Erweiterungen gegeben hat, die das WDM in seiner ursprünglichen Auslegung noch nicht berücksichtigen konnte. An diesen Punkten setzt die Windows Driver Foundation an, die zum einen leichter zu entwicklende Treiber verspricht und auf der anderen Seite mehr Unterstützung für neuere Devices. WDF bietet ein Grundgerüst an, welches sich beliebig erweitern lässt. Der Basis-Treiber als solches beherrscht bereits Dinge wie Plug and Play und Power Management. Man muss sich nicht zusätzlich um die Zustandsverwaltung kümmern - kann dies aber dennoch durch eingreifen in das Standardverhalten tun. Zudem werden Synchronisationsprobleme durch geeignete Queue-Konzepte weitrechend vermieden, so dass es hier weniger Schwierigkeiten gibt, als dies noch bei WDM der Fall war. Die Windows Driver Foundation bietet Unterstützung für Kernel-Mode-Treiber mit dem sogenannten Kernel Mode Driver Framework (KMDF) und User-Mode-Treiber im User Mode Driver Framework (UMDF) an. WDF stellt zu dem ein neues Konzept in der Treiberentwicklung für Windows vor - Objektorientierung. KMDF Treiber werden in aller Regel in C geschrieben, bieten somit also Akzeptanz für alle bisherigen WDM Entwickler an. UMDF Treiber sind in C++ geschrieben, und bieten somit die volle Kraft der Objektorientierung und COM an. Gerätetreiber können als Kernel-Mode-Treiber mit KMDF für alle Windows Versio- nen ab Windows 2000 geschrieben werden, wobei hier einige betriebssystemspezifische Restriktionen und eventuell nicht vorhandene Features zu beachten sind. Mit UMDF geschriebenen Treiber können ab Windows XP eingesetzt werden. WDF bietet zudem Tools an, mit den man Treiber verifizieren, testen und debuggen kann, diese Tools sind unter anderem Static Driver Verifier (SDV), PREFast for Drivers (PFD) sowie der DDK 7
Kernel-Debugger. Windows Driver Foundation mit ihren 2 Frameworks KMDF und UMDF ist also ein sehr mächtiges Werkzeug um Treiber effizient unter Windows zu entwickeln. 2.2. Der WDF-Driver-Stack 2.2.1. Übersicht Abb. 2.1.: 2.2.2. KMDF - Kernel Mode Driver Framework Eine Applikation sendet I/O Anfragen an die Win32 API, diese werden von der API an die I/O Routinen des Windows Kernels weitergeleitet. Der I/O Manager des Windows Kernels erstellt aus dieser Anfrage ein I/O request packet (IRP) und leitet dies zu den Zieltreiber, durch Aufrufen des dafür vorgesehenen Entry Points auf, weiter. KMDF verarbeitet diesen Request und kreiert ein WDF request Objekt und ruft, falls notwendig, weitere Callback-Routinen auf und leitet diesen WDF request an darunterliegende Treiber weiter. 2.2.3. UMDF - User Mode Driver Framework Eine Applikation sendet I/O Anfragen an die Win32 API, diese werden von der API an die I/O Routinen des Windows Kernels weitergeleitet. Der I/O Manager des Windows Kernels erstellt aus dieser Anfrage ein I/O request packet (IRP) und leitet dies zu den Zieltreiber durch Aufrufen des dafür vorgesehenen Entry Points auf, weiter. Ist der aufzurufende Treiber ein User-Mode Treiber, kann dieser nicht direkt, wie bei KMDF aufgerufen werden, da dieser sich überhalb des Kernel Modes befindet und Kernel Mode nicht auf User Mode zurückgreifen kann. Der I/O Manager sendet die IRPs an eine im Kernel Mode befindliche Komponente namens Reflector. Der Reflector ist ein Kernel-Mode WDM Filter Treiber, der den User-Mode Treiber im Kernel-Mode "repräsentiert". Dieser leitet die IRPs weiter zu den User-Mode Driver Host Process. Der Reflector dient als Schnittstelle zwischen User-Mode und Kernel-Mode. 8
Der Driver Host Process inkludiert für den User-Mode Treiber COM-Komponenten, welche die Hardware aus dem User-Mode her steuern. Zudem wird durch den Driver Host Process der eigentliche UMDF-Treiber einbezogen. Dieser repräsentiert das User-Mode Device Driver Interface (DDI). UMDF ist eine dynamic-link library (DLL) von "COM- style" Objekten welche das Management der I/O, Plug and Play, Powermanagerment, sowie Flusssteuerung übernehmen. Das Run-Time Environment sendet I/O Anfragen, lädt den Treiber, erstellt und vernichtet den User-Mode Device Stack, verwaltet die Threads und leitet die Nachrichten vom Reflector zum Driver Manager. 2.3. Die Entwicklungsumgebung Da wir uns beim Schreiben eines KMDF-Treibers im Kern des Betriebssystems befinden, können kleine Änderungen schnell dazu führen, dass das gesamte Betriebssystem nicht mehr funktioniert. Deshalb haben wir unseren Treiber auf einer Virtuellen Maschine entwickelt, bei der wir im Notfall problemlos zu einem noch funktionierenden Zustand zurückkehren können. Des weiteren benötigt man zum Debuggen eigentlich einen zweiten Rechner, der beispielsweise über eine Serielle Schnittstelle mit dem PC, auf dem entwickelt wird, verbunden ist. Dies entfällt in unserem Fall, da wir eine serielle Schnittstelle über eine Named Pipe emulieren. Abb. 2.2.: Zunächst haben wir auf der Virtuellen Maschine Windows XP SP2 installiert. Dies eignet sich als Entwicklungssystem, da man auch nicht zertifizierte Treiber installieren kann. Dies ist bei 64-Bit-Windows-Systemen leider nicht mehr möglich. Des weiteren benötigt man das WinDDK, das sowohl Compiler, als auch SampleTreiber zur Verfügung stellt. Eine graphische Entwicklungsumgebung sucht man da jedoch vergebens. Man kann aber mit ein paar Einstellungen und unter Zuhilfenahme zweier Konfigurations- Dateien auch das Microsoft Visual Studio zum entwicklen von Treibern verwenden. Die Vorteile dabei sind zum einen eine wesentlich komfortablere Umgebung als bei einem reinen Texteditor und man braucht nicht jedes mal die Compile-Befehle in der Dos-Shell einzutippen. Ein sehr hilfreiches Tool um das Entwicklungssystem während der Laufzeit Debuggen zu können ist WinDbg. Dies läuft auf dem Host-PC und liefert zum einen die Ausgaben, die wir über KdPrint() ausgeben und zum anderen kann man damit Bugchecks (oft auch 9
als Blue Screens bezeichnet) analysieren. 2.4. HID - Die “Human Interface Device”-Class Die “Human Interface Device”-Class ist eine Geräteklasse des USB-Standrads für Com- puter, welche Geräte beschreibt, mit denen Benutzer direkt interagieren. Meist wird HID bei Geräten wie Tastatur, Maus, Joystick und Grafiktabletts verwendet. Allerdings kann HID auch für weitere Zwecke, wie z. B. Relaiskarten, Steuerungen (Knöpfe und Schalter), Thermometer, Multimeter, Telefonie und viele weitere Einsatzzwecke verwendet werden. Beschränkt wird die Nutzbarkeit unter anderem durch eine geringere Anzahl an Endpunkten und geringe Datenübertragungsrate. Ein HID-Gerätetreiber (hid.dll) ist bei Windows schon vorinstalliert. Wird ein HID- Gerät (während des Betriebs) angeschlossen, wird es meist direkt als Gerätetyp Eingabe- geräte (Human Interface Devices) erkannt und wird dann im Gerätemanager angezeigt. Für die Devicespezifischen Daten werden dann zu den Device-, Configuration-, Interface- und Endpointdeskriptoren auch noch ein HID-Deskriptor und ein HID-Report-Deskriptor übertragen. Somit kann man es sich eigentlich sparen einen Treiber für das Gerät zu schreiben. Wir haben uns dennoch für eine andere Methode entschieden. Wir erkennen das Gerät zunächst als normales USB-Gerät und reichen dann dieses mit den zusätzlichen Informationen (HID-Deskriptor und HID-Report-Deskriptor) an den HID-Gerätetreiber weiter. 10
2.5. Treiberübersicht Abb. 2.3.: 11
2.6. Der USBRotary-Treiber 2.6.1. Schnellübersicht über Quelltextdatein und deren Funktionen 2.6.1.1. Quelltextdatein • drv.cpp – initialisiert den Treiber und das damit verbundene Device – initialisiert Kontexte, Handler, Pipes, Queues usw. – stellt I/O Operationen zur Verfügung, damit das Device über seperate Appli- kation mittels read und write Befehle ansprechbar ist – führt D0 Entry/ D0 Exit Funktionen aus – bietet einen Continuous Reader an, der ständig neue Daten vom Device holt – stellt zudem eine Timerfunktion bereit, die nach bestimmter Zeit HID Input Reports versendet • hid.cpp – leitet HID-spezifische Informationen des Devices an übergelegene HID-Treiber weiter – KDMF kann normal kein HID, daher wird ein HID-Mapper benötigt, der sich als HIDMinitreiber registriert und Vermittler zwischen hid.dll und den KMDF spielt 2.6.1.2. Funktionenbeschreibung für drv.cpp • NTSTATUS DriverEntry( ... ) – Einstiegspunkt für den Treiber – erzeugt ein Device mit zugehörigem Device Context – verknüpft Event-Hander EvtDeviceAdd mit dem Device (für Konfiguration) • NTSTATUS EvtDeviceAdd( ... ) – konfiguriert das Device – verknüpft das Device mit entsprechenden Event-Handlern (EvtDeviceD0Entry, EvtIoWrite, EvtIoDeviceControl,...) – erstellt die benötigten Pipes und konfiguriert diese 12
• NTSTATUS EvtDevicePrepareHardware( ... ) – initalisiert die USB-Endpoints – holt sich Informationen über Device per DeviceDescriptor – ruft die Initialisierung für den Continuous Reader auf (ConfigContReaderFo- rInterruptEndPoint) • NTSTATUS ConfigContReaderForInterruptEndPoint( ... ) – initialisiert den Continuous Reader für die InterruptInPipe – leitet die Verarbeitung aus der InterruptInPipe an ensprechende Funktion (EvtUsbInterruptPipeReadComplete) weiter • VOID EvtUsbInterruptPipeReadComplete( ... ) – zieht Degree- und ButtonState-Information aus dem Buffer und setzt dieses in die Context-Struktur – startet den Timer zur weiteren Verarbeitung der Inputinformationen • VOID CompleteReadReport( ... ) – holt sich über das Device die Context-Struktur – allokiert Speicher für den "inputReport" – inputReport übernimmt die Werte von Degree und ButtonState – Request mit inputReport wird weiterversendet • NTSTATUS EvtDeviceD0Entry( ... ) – startet den Continuous Reader auf der InterruptInPipe • NTSTATUS EvtDeviceD0Exit( ... ) – stoppt den Continuous Reader auf der InterruptInPipe und cancelt I/O- Sendeanfragen • VOID EvtIoDeviceControl( ... ) – übernimmt die Abarbeitung von Kontrollinformationen • VOID EvtIoWrite( ... ) – holt sich den Context aus der Queue – allokiert Speicher – bereitet die im Context enthaltene Pipe (InterruptOutPipe) für das versenden des Requests vor – meldet EvtRequestReadCompletionRoutine an 13
– sendet den Request • VOID EvtRequestReadCompletionRoutine( ... ) – holt sich Statusinformationen über die Versendung und wertet diese aus – sendet den Request an upper-layer Treiber weiter • VOID EvtIoRead( ... ) – holt sich den Context aus der Queue – bereitet die im Context enthaltene Pipe (InterruptInPipe) für das versenden des Requests vor – meldet EvtRequestReadCompletionRoutine an – sendet den Request • VOID EvtTimerFunction( ... ) – holt sich den Context aus dem Timer – sendet Context an die CompleteReadReport-Funktion 2.6.1.3. Funktionenbeschreibung für hid.cpp • VOID EvtInternalDeviceControl( ... ) – ruft die entsprechenden HID-Funktionen laut Control-Codes auf • GetHidDescriptor( ... ) – sendet den im Headerfile beschriebenen HID-Deskriptor an entsprechende HID-Treiber • GetReportDescriptor( ... ) – sendet den im Headerfile beschriebenen HID-Report-Deskriptor an entspre- chende HID-Treiber • NTSTATUS GetDeviceAttributes( ... ) – sendet HID-Geräte-Attribute an oberhalb gelegene HID-Treiber 2.6.1.4. Die Headerfiles • drv.h – eigene definierte Funktionsrümpfe – Strukturen von: Treiber-Kontext, HID-Deskritor, HID-Report-Deskriptor, Input-Report – einige Konstanten 14
• ownhidport.h – leicht modifizierte hidport.h aus DDK • prototypes.h – einige Funktionsrumpf-Prototypen (für Standardtreiber) 15
3. Quellen und Hilfsmittel 3.1. Literatur • Developing Drivers with the Windows Driver Foundation: Penny Orwick, Guy Smith; Microsoft Press, 2007 • Programming the Windows Driver Model, Walter Oney, Microsoft Press, 2002 • Atmel AT90USB - Datasheet • “USB 2.0” - Spezifikation • “USB - HID Usage Tables”- Spezifikation • “Device Class Definition for Human Interface Devices” - Spezifikation • Skript der Lehrveranstaltung Windows-Treiber-Programmierung der Fh Lausitz 3.2. Internetseiten • http://www.beyondlogic.org/usbnutshell/ • http://www.usb.org/ • http://michael-gerber.net/ • http://www.ssalewski.de/ • http://de.wikipedia.org/wiki/Universal_Serial_Bus • http://de.wikipedia.org/wiki/Human_Interface_Device 3.3. Werkzeuge • VMWare 6.0.1 • Microsoft Visual Studio 2008 • WinDDK 6001.18001 • WinDbg 6.10 • Sysnucleus USB Trace 16
• USBlyzer • HID-Descriptor-Tool • CodeVision AVR-Compiler • E-LAB AVRco 3.4. sonstige Hilfsmittel • Sample-Treiber des WinDDK 6001.18001 17
A. Quelltexte 18
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 1 #include "ntddk.h" #include "wdf.h" #include "prototypes.h" #include "hidport.h" #include "drv.h" #include "usbdi.h" #include "wdfusb.h" #include "initguid.h" NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { WDF_DRIVER_CONFIG config; NTSTATUS status; KdPrint((DRIVERNAME "DriverEntry\n")); WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd); status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE ); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfDriverCreate failed 0x%x\n", status)); } return status; } NTSTATUS EvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ) { WDF_OBJECT_ATTRIBUTES attributes; NTSTATUS status; WDFDEVICE device; PDEVICE_CONTEXT pDevContext; WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; WDF_TIMER_CONFIG timerConfig; WDFTIMER timerHandle; WDFQUEUE queue; WDF_IO_QUEUE_CONFIG queueConfig; UNREFERENCED_PARAMETER(Driver); PAGED_CODE(); KdPrint((DRIVERNAME "EvtDeviceAdd\n")); WdfFdoInitSetFilter(DeviceInit); WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); pnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware; pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry; pnpPowerCallbacks.EvtDeviceD0Exit = EvtDeviceD0Exit; WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT); status = WdfDeviceCreate(&DeviceInit, &attributes, &device); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfDeviceCreate failed status: 0x%x\n", status)); return status; }
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 2 pDevContext = GetDeviceContext(device); status = WdfDeviceCreateDeviceInterface(device, (LPGUID) &GUID_DEVINTERFACE_USBROTARY, NULL);// Reference String if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfDeviceCreateDeviceInterface failed status: 0x%x\n", status)); return status; } WDF_IO_QUEUE_CONFIG ioQueueConfig; WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchParallel); ioQueueConfig.EvtIoInternalDeviceControl = EvtInternalDeviceControl; ioQueueConfig.EvtIoDeviceControl = EvtIoDeviceControl; ioQueueConfig.EvtIoWrite = EvtIoWrite; ioQueueConfig.EvtIoRead = EvtIoRead; status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue); if (!NT_SUCCESS(status)) { KdPrint(("WdfIoQueueCreate failed status: 0x%x\n", status)); return status; } WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual); queueConfig.PowerManaged = WdfFalse; status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pDevContext->InterruptMsgQueue ); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfIoQueueCreate failed status: 0x%x\n", status)); return status; } WDF_TIMER_CONFIG_INIT( &timerConfig, EvtTimerFunction ); timerConfig.AutomaticSerialization = FALSE; WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = device; status = WdfTimerCreate( &timerConfig, &attributes, &timerHandle ); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfTimerCreate failed status:0x%x\n", status)); return status; } pDevContext->DebounceTimer = timerHandle; return status; } NTSTATUS EvtDevicePrepareHardware( IN WDFDEVICE Device, IN WDFCMRESLIST ResourceList, IN WDFCMRESLIST ResourceListTranslated )
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 3 { NTSTATUS status; PDEVICE_CONTEXT pDeviceContext; WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams; UCHAR numberConfiguredPipes; WDF_OBJECT_ATTRIBUTES attributes; PVOID usbDeviceDescriptor = NULL; UNREFERENCED_PARAMETER(ResourceList); UNREFERENCED_PARAMETER(ResourceListTranslated); KdPrint((DRIVERNAME "EvtDevicePrepareHardware\n")); pDeviceContext = GetDeviceContext(Device); if (pDeviceContext->UsbDevice == NULL) { status = WdfUsbTargetDeviceCreate(Device, WDF_NO_OBJECT_ATTRIBUTES, &pDeviceContext->UsbDevice); if (!NT_SUCCESS(status)) { KdPrint(("WdfUsbTargetDeviceCreate failed 0x%x\n", status)); return status; } } WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE(&configParams); status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->UsbDevice, WDF_NO_OBJECT_ATTRIBUTES, &configParams); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfUsbTargetDeviceSelectConfig failed status: 0x%x\n", status)); return status; } pDeviceContext->UsbInterface = configParams.Types.SingleInterface.ConfiguredUsbInterface; numberConfiguredPipes = configParams.Types.SingleInterface.NumberConfiguredPipes; KdPrint((DRIVERNAME "Number of Configured Pipes: %d\n", numberConfiguredPipes)); KdPrint((DRIVERNAME "WdfMemoryCreate\n")); WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = Device; status = WdfMemoryCreate( &attributes, NonPagedPool, 0, sizeof(USB_DEVICE_DESCRIPTOR), &pDeviceContext->DeviceDescriptor, &usbDeviceDescriptor ); if(!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfMemoryCreate for Device Descriptor failed status: 0x%x\n", status)); return status; } WdfUsbTargetDeviceGetDeviceDescriptor( pDeviceContext->UsbDevice, (PUSB_DEVICE_DESCRIPTOR)usbDeviceDescriptor ); /*KdPrint((DRIVERNAME "UsbTargetDeviceGetInterface\n")); WdfUsbTargetDeviceGetInterface(pDeviceContext->UsbDevice, 0); */ KdPrint((DRIVERNAME "Enumerating Pipes for the USB Interface\n")); WDFUSBPIPE pipe; WDF_USB_PIPE_INFORMATION pipeInfo; UCHAR index; for(index=0; index < numberConfiguredPipes; index++) {
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 4 WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); pipe = WdfUsbInterfaceGetConfiguredPipe( pDeviceContext->UsbInterface, index, //PipeIndex, &pipeInfo ); WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(pipe); if(WdfUsbPipeTypeInterrupt == pipeInfo.PipeType && WdfUsbTargetPipeIsInEndpoint(pipe)) { KdPrint((DRIVERNAME "Interrupt In Pipe\n")); pDeviceContext->InterruptInPipe = pipe; } if(WdfUsbPipeTypeInterrupt == pipeInfo.PipeType && WdfUsbTargetPipeIsOutEndpoint(pipe)) { KdPrint((DRIVERNAME "Interrupt Out Pipe\n")); pDeviceContext->InterruptOutPipe = pipe; } } // Alle Pipes da? if(!(pDeviceContext->InterruptInPipe && pDeviceContext->InterruptOutPipe)) { status = STATUS_INVALID_DEVICE_STATE; KdPrint((DRIVERNAME "Error: Not all Pipes detected\n")); } // Continuous Reader status = ConfigContReaderForInterruptEndPoint(pDeviceContext); return status; } NTSTATUS ConfigContReaderForInterruptEndPoint( PDEVICE_CONTEXT DeviceContext ) { WDF_USB_CONTINUOUS_READER_CONFIG contReaderConfig; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE (); KdPrint((DRIVERNAME "ConfigContReaderForInterruptEndPoint Enter\n")); WDF_USB_CONTINUOUS_READER_CONFIG_INIT(&contReaderConfig, EvtUsbInterruptPipeReadComplete, DeviceContext, // Context 2); // TransferLength status = WdfUsbTargetPipeConfigContinuousReader(DeviceContext->InterruptInPipe, &contReaderConfig); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "ConfigContReaderForInterruptEndPoint failed %x\n", status)); return status; } KdPrint((DRIVERNAME "ConfigContReaderForInterruptEndPoint Exit, status:0x%x\n", status)); return status; } VOID EvtUsbInterruptPipeReadComplete( WDFUSBPIPE Pipe, WDFMEMORY Buffer, size_t NumBytesTransferred, WDFCONTEXT Context ) { WDFDEVICE device; PDEVICE_CONTEXT devContext = (PDEVICE_CONTEXT)Context; PUCHAR Degree = NULL; UCHAR CurrentDegree; UCHAR CurrentButtonState;
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 5 PWORD BufferContent=NULL; BOOLEAN status; WORD Content; UNREFERENCED_PARAMETER(NumBytesTransferred); UNREFERENCED_PARAMETER(Pipe); // KdPrint((DRIVERNAME "EvtUsbInterruptPipeReadComplete Enter\n")); BufferContent = (PWORD) WdfMemoryGetBuffer(Buffer, NULL); Content = *BufferContent; if(Content!=0x0000){ //KdPrint((DRIVERNAME "Degree: %X - ButtonState: %X\n", Content,); devContext->CurrentDegree = (UCHAR)Content; devContext->CurrentButtonState = (UCHAR) (Content >> 8); //KdPrint((DRIVERNAME "Degree: %X\n", devContext->CurrentDegree)); WdfTimerStart( devContext->DebounceTimer, WDF_REL_TIMEOUT_IN_MS(1) //1ms-Timer ); } } VOID CompleteReadReport( WDFDEVICE Device ) { NTSTATUS status = STATUS_SUCCESS; WDFREQUEST request; PDEVICE_CONTEXT pDevContext = NULL; size_t bytesReturned = 0; UCHAR Degree = 0; UCHAR ButtonState = 0; ULONG bytesToCopy = 0; PINPUT_REPORT inputReport = NULL; pDevContext = GetDeviceContext(Device); //KdPrint((DRIVERNAME "CompleteReadReport\n")); status = WdfIoQueueRetrieveNextRequest(pDevContext->InterruptMsgQueue, &request); if (NT_SUCCESS(status)) { bytesToCopy = sizeof(INPUT_REPORT); status = WdfRequestRetrieveOutputBuffer(request, bytesToCopy, (PVOID*)&inputReport, &bytesReturned);// BufferLength if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfRequestRetrieveOutputBuffer failed with status: 0x%x\n ", status)); } else { Degree = pDevContext->CurrentDegree; ButtonState = pDevContext->CurrentButtonState; if (Degree!=0x00) { KdPrint(("Degree: 0x%X\n", Degree)); inputReport->Degree=Degree; inputReport->Button=ButtonState; } } bytesReturned = bytesToCopy; KdPrint((DRIVERNAME "bytesReturned: %d\n", bytesReturned)); WdfRequestCompleteWithInformation(request, status, bytesReturned); } else if (status != STATUS_NO_MORE_ENTRIES) { KdPrint((DRIVERNAME "WdfIoQueueRetrieveNextRequest status %08x\n", status)); }
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 6 return; } NTSTATUS EvtDeviceD0Entry( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState ) { PDEVICE_CONTEXT devContext = NULL; NTSTATUS status = STATUS_SUCCESS; UCHAR commandData = 0; devContext = GetDeviceContext(Device); // WdfIoTargetStart startet den Contineous Reader status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(devContext->InterruptInPipe)); KdPrint((DRIVERNAME "EvtDeviceD0Entry Exit, status: 0x%x\n", status)); return status; } NTSTATUS EvtDeviceD0Exit( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE TargetState ) { PDEVICE_CONTEXT devContext; PAGED_CODE(); devContext = GetDeviceContext(Device); WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget( devContext->InterruptInPipe), WdfIoTargetCancelSentIo); KdPrint((DRIVERNAME "EvtDeviceD0Exit Exit\n")); return STATUS_SUCCESS; } VOID EvtIoDeviceControl( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode ) { KdPrint((DRIVERNAME "EvtIoDeviceControl\n")); WDFDEVICE device; PDEVICE_CONTEXT pDevContext; size_t bytesTransferred = 0; NTSTATUS status; WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; WDF_MEMORY_DESCRIPTOR memDesc; WDFMEMORY memory; UNREFERENCED_PARAMETER(InputBufferLength); UNREFERENCED_PARAMETER(OutputBufferLength); device = WdfIoQueueGetDevice(Queue); pDevContext = GetDeviceContext(device); switch(IoControlCode) { /* case IOCTL_SCANTEST: KdPrint((DRIVERNAME "IOCTLSCANTEST\n")); if(InputBufferLength < sizeof(UCHAR)) { status = STATUS_BUFFER_OVERFLOW;
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 7 bytesTransferred = sizeof(UCHAR); break; } status = WdfRequestRetrieveInputMemory(Request, &memory); if (!NT_SUCCESS(status)) { KdPrint(("WdfRequestRetrieveMemory failed 0x%x", status)); break; } WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, BmRequestHostToDevice, BmRequestToDevice, USBFX2LK_SET_BARGRAPH_DISPLAY, // Request 0, // Value 0); // Index WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&memDesc, memory, NULL); status = WdfUsbTargetDeviceSendControlTransferSynchronously( pDevContext->UsbDevice, NULL, // Optional WDFREQUEST NULL, // PWDF_REQUEST_SEND_OPTIONS &controlSetupPacket, &memDesc, (PULONG)&bytesTransferred); if (!NT_SUCCESS(status)) { KdPrint(("SendControlTransfer failed 0x%x", status)); break; } break; */ default: status = STATUS_INVALID_DEVICE_REQUEST; break; } WdfRequestCompleteWithInformation(Request, status, bytesTransferred); return; } VOID EvtIoWrite( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ) { KdPrint((DRIVERNAME "EvtIoWrite\n")); WDFUSBPIPE pipe; NTSTATUS status; WDFMEMORY reqMemory; PDEVICE_CONTEXT pDeviceContext; UNREFERENCED_PARAMETER(Queue); pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); pipe = pDeviceContext->InterruptOutPipe; KdPrint((DRIVERNAME "WdfRequestRetrieveInputMemory\n")); status = WdfRequestRetrieveInputMemory(Request, &reqMemory); if (!NT_SUCCESS(status)){ goto Exit; } KdPrint((DRIVERNAME "WdfUsbTargetPipeFormatRequestForWrite\n")); status = WdfUsbTargetPipeFormatRequestForWrite( pipe, Request, reqMemory, NULL // Offsets ); if (!NT_SUCCESS(status)) {
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 8 goto Exit; } KdPrint((DRIVERNAME "WdfRequestSetCompletionRoutine\n")); WdfRequestSetCompletionRoutine( Request, EvtRequestReadCompletionRoutine, pipe ); KdPrint((DRIVERNAME "WdfRequestSend\n")); if (WdfRequestSend( Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS ) == FALSE) { KdPrint((DRIVERNAME "EvtIoWrite\n")); status = WdfRequestGetStatus(Request); goto Exit; } KdPrint((DRIVERNAME "WdfRequestSend ausgeführt\n")); Exit: if (!NT_SUCCESS(status)) { WdfRequestCompleteWithInformation( Request, status, 0 ); } return; } VOID EvtRequestReadCompletionRoutine( IN WDFREQUEST Request, IN WDFIOTARGET Target, PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, IN WDFCONTEXT Context ) { NTSTATUS status; size_t bytesRead = 0; PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams; UNREFERENCED_PARAMETER(Target); UNREFERENCED_PARAMETER(Context); status = CompletionParams->IoStatus.Status; usbCompletionParams = CompletionParams->Parameters.Usb.Completion; bytesRead = usbCompletionParams->Parameters.PipeRead.Length; if (NT_SUCCESS(status)){ KdPrint(("Number of bytes read: %I64d\n", (INT64)bytesRead)); } else { KdPrint(("Read failed - request status 0x%x UsbdStatus 0x%x\n", status, usbCompletionParams->UsbdStatus)); } WdfRequestCompleteWithInformation(Request, status, bytesRead); return; } VOID EvtIoRead( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ) {
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.cpp 9 WDFUSBPIPE pipe; NTSTATUS status; WDFMEMORY reqMemory; PDEVICE_CONTEXT pDeviceContext; UNREFERENCED_PARAMETER(Queue); KdPrint((DRIVERNAME "EvtIoRead\n")); pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); pipe = pDeviceContext->InterruptInPipe; KdPrint((DRIVERNAME "WdfRequestRetrieveOutputMemory\n")); status = WdfRequestRetrieveOutputMemory(Request, &reqMemory); if(!NT_SUCCESS(status)){ goto Exit; } KdPrint((DRIVERNAME "WdfUsbTargetPipeFormatRequestForRead\n")); status = WdfUsbTargetPipeFormatRequestForRead(pipe, Request, reqMemory, NULL // Offsets ); if (!NT_SUCCESS(status)) { goto Exit; } KdPrint((DRIVERNAME "WdfRequestSetCompletionRoutine\n")); WdfRequestSetCompletionRoutine( Request, EvtRequestReadCompletionRoutine, pipe ); KdPrint((DRIVERNAME "WdfRequestSend\n")); if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) { KdPrint((DRIVERNAME "WdfRequestGetStatus\n")); status = WdfRequestGetStatus(Request); goto Exit; } KdPrint((DRIVERNAME "WdfRequestSend ausgeführt\n")); Exit: if (!NT_SUCCESS(status)) { WdfRequestCompleteWithInformation(Request, status, 0); } return; } VOID EvtTimerFunction( IN WDFTIMER Timer ) { PDEVICE_CONTEXT devContext = GetDeviceContext(WdfTimerGetParentObject(Timer)); if (devContext->CurrentDegree != 0x00) { CompleteReadReport((WDFDEVICE)WdfTimerGetParentObject(Timer)); } }
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\hid.cpp 1 #include VOID EvtInternalDeviceControl( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode ) { NTSTATUS status = STATUS_SUCCESS; WDFDEVICE device; PDEVICE_CONTEXT devContext = NULL; UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); device = WdfIoQueueGetDevice(Queue); devContext = GetDeviceContext(device); switch(IoControlCode) { case IOCTL_HID_GET_DEVICE_DESCRIPTOR: KdPrint(("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n")); // // Retrieves the device's HID descriptor. // status = GetHidDescriptor(device, Request); break; case IOCTL_HID_GET_DEVICE_ATTRIBUTES: KdPrint(("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n")); // //Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure. // status = GetDeviceAttributes(Request); break; case IOCTL_HID_GET_REPORT_DESCRIPTOR: KdPrint(("IOCTL_HID_GET_REPORT_DESCRIPTOR\n")); // //Obtains the report descriptor for the HID device. // status = GetReportDescriptor(device, Request); break; case IOCTL_HID_READ_REPORT: //KdPrint(("IOCTL_HID_READ_REPORT\n")); // // Returns a report from the device into a class driver-supplied buffer. // For now queue the request to the manual queue. The request will // be retrived and completd when continuous reader reads new data // from the device. // status = WdfRequestForwardToIoQueue(Request, devContext->InterruptMsgQueue); if(!NT_SUCCESS(status)){ KdPrint((DRIVERNAME "WdfRequestForwardToIoQueue failed with status: 0x%x\n", status)); WdfRequestComplete(Request, status); } return; break; // // This feature is only supported on WinXp and later. Compiling in W2K // build environment will fail without this conditional preprocessor statement. // #if (OSVER(NTDDI_VERSION) > NTDDI_WIN2K)
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\hid.cpp 2 case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: KdPrint(("IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST\n")); // // Hidclass sends this IOCTL for devices that have opted-in for Selective // Suspend feature. This feature is enabled by adding a registry value // "SelectiveSuspendEnabled" = 1 in the hardware key through inf file // (see hidusbfx2.inf). Since hidclass is the power policy owner for // this stack, it controls when to send idle notification and when to // cancel it. This IOCTL is passed to USB stack. USB stack pends it. // USB stack completes the request when it determines that the device is // idle. Hidclass's idle notification callback get called that requests a // wait-wake Irp and subsequently powers down the device. // The device is powered-up either when a handle is opened for the PDOs // exposed by hidclass, or when usb stack completes wait // wake request. In the first case, hidclass cancels the notification // request (pended with usb stack), cancels wait-wake Irp and powers up // the device. In the second case, an external wake event triggers completion // of wait-wake irp and powering up of device. // return; #endif // (OSVER(NTDDI_VERSION) > NTDDI_WIN2K) case IOCTL_HID_SET_FEATURE: KdPrint(("IOCTL_HID_SET_FEATURE\n")); // // This sends a HID class feature report to a top-level collection of // a HID class device. // WdfRequestComplete(Request, status); return; case IOCTL_HID_GET_FEATURE: KdPrint(("IOCTL_HID_GET_FEATURE\n")); // // returns a feature report associated with a top-level collection // case IOCTL_HID_WRITE_REPORT: KdPrint(("IOCTL_HID_WRITE_REPORT\n")); // //Transmits a class driver-supplied report to the device. // #if 0 // // Following two ioctls are not defined in the Win2K HIDCLASS.H headerfile // case IOCTL_HID_GET_INPUT_REPORT: KdPrint(("IOCTL_HID_GET_INPUT_REPORT\n")); // // returns a HID class input report associated with a top-level // collection of a HID class device. // KdPrint((DRIVERNAME "IOCTL_HID_GET_INPUT_REPORT\n")); status = STATUS_NOT_SUPPORTED; break; case IOCTL_HID_SET_OUTPUT_REPORT: KdPrint(("IOCTL_HID_SET_OUTPUT_REPORT\n")); // // sends a HID class output report to a top-level collection of a HID // class device. // KdPrint((DRIVERNAME "IOCTL_HID_SET_OUTPUT_REPORT\n")); status = STATUS_NOT_SUPPORTED; break; #endif case IOCTL_HID_GET_STRING: KdPrint(("IOCTL_HID_GET_STRING\n")); // // Requests that the HID minidriver retrieve a human-readable string // for either the manufacturer ID, the product ID, or the serial number // from the string descriptor of the device. The minidriver must send // a Get String Descriptor request to the device, in order to retrieve // the string descriptor, then it must extract the string at the
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\hid.cpp 3 // appropriate index from the string descriptor and return it in the // output buffer indicated by the IRP. Before sending the Get String // Descriptor request, the minidriver must retrieve the appropriate // index for the manufacturer ID, the product ID or the serial number // from the device extension of a top level collection associated with // the device. // case IOCTL_HID_ACTIVATE_DEVICE: KdPrint(("IOCTL_HID_ACTIVATE_DEVICE\n")); // // Makes the device ready for I/O operations. // case IOCTL_HID_DEACTIVATE_DEVICE: KdPrint(("IOCTL_HID_DEACTIVATE_DEVICE\n")); // // Causes the device to cease operations and terminate all outstanding // I/O requests. // default: KdPrint(("Default\n")); status = STATUS_NOT_SUPPORTED; break; } WdfRequestComplete(Request, status); return; } NTSTATUS GetHidDescriptor( IN WDFDEVICE Device, IN WDFREQUEST Request ) { NTSTATUS status = STATUS_SUCCESS; size_t bytesToCopy = 0; WDFMEMORY memory; UNREFERENCED_PARAMETER(Device); KdPrint((DRIVERNAME "GetHidDescriptor Entry\n")); status = WdfRequestRetrieveOutputMemory(Request, &memory); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfRequestRetrieveOutputMemory failed 0x%x\n", status)); return status; } bytesToCopy = G_DefaultHidDescriptor.bLength; status = WdfMemoryCopyFromBuffer(memory, 0, // Offset (PVOID) &G_DefaultHidDescriptor, bytesToCopy); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfMemoryCopyFromBuffer failed 0x%x\n", status)); return status; } WdfRequestSetInformation(Request, bytesToCopy); KdPrint((DRIVERNAME "GetHidDescriptor Exit = 0x%x\n", status)); return status; } NTSTATUS GetReportDescriptor( IN WDFDEVICE Device, IN WDFREQUEST Request ) { NTSTATUS status = STATUS_SUCCESS; ULONG_PTR bytesToCopy;
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\hid.cpp 4 WDFMEMORY memory; UNREFERENCED_PARAMETER(Device); KdPrint((DRIVERNAME "GetReportDescriptor Entry\n")); status = WdfRequestRetrieveOutputMemory(Request, &memory); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfRequestRetrieveOutputMemory failed 0x%x\n", status)); return status; } bytesToCopy = G_DefaultHidDescriptor.DescriptorList[0].wReportLength; status = WdfMemoryCopyFromBuffer(memory, 0, (PVOID) G_DefaultReportDescriptor, bytesToCopy); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfMemoryCopyFromBuffer failed 0x%x\n", status)); return status; } WdfRequestSetInformation(Request, bytesToCopy); KdPrint((DRIVERNAME "GetReportDescriptor Exit = 0x%x\n", status)); return status; } NTSTATUS GetDeviceAttributes( IN WDFREQUEST Request ) { NTSTATUS status = STATUS_SUCCESS; PHID_DEVICE_ATTRIBUTES deviceAttributes = NULL; PUSB_DEVICE_DESCRIPTOR usbDeviceDescriptor = NULL; PDEVICE_CONTEXT deviceInfo = NULL; KdPrint((DRIVERNAME "GetDeviceAttributes Entry\n")); deviceInfo = GetDeviceContext(WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request))); status = WdfRequestRetrieveOutputBuffer(Request, sizeof (HID_DEVICE_ATTRIBUTES), (PVOID *)&deviceAttributes, //(PVOID *) hinzugeüfut NULL); if (!NT_SUCCESS(status)) { KdPrint((DRIVERNAME "WdfRequestRetrieveOutputBuffer failed 0x%x\n", status)); return status; } usbDeviceDescriptor = (PUSB_DEVICE_DESCRIPTOR) WdfMemoryGetBuffer(deviceInfo-> DeviceDescriptor, NULL); //(PUSB_DEVICE_DESCRIPTPOR) deviceAttributes->Size = sizeof (HID_DEVICE_ATTRIBUTES); deviceAttributes->VendorID = usbDeviceDescriptor->idVendor; deviceAttributes->ProductID = usbDeviceDescriptor->idProduct;; deviceAttributes->VersionNumber = usbDeviceDescriptor->bcdDevice; WdfRequestSetInformation(Request, sizeof (HID_DEVICE_ATTRIBUTES)); KdPrint((DRIVERNAME "GetDeviceAttributes Exit = 0x%x\n", status)); return status; }
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.h 1 #define DRIVERNAME "USB-HID-Rotary-Treiber - " #include #include #include "usbdi.h" #include "usbdlib.h" #include #include "wdfusb.h" #include "ownhidport.h" #include typedef UCHAR HID_REPORT_DESCRIPTOR, *PHID_REPORT_DESCRIPTOR; typedef unsigned short WORD, *PWORD, *LPWORD; DEFINE_GUID(GUID_DEVINTERFACE_USBROTARY, 0xaae8042f, 0x233d, 0x4236, 0x89, 0xc, 0xe, 0xc5, 0x9e, 0xdb, 0x37, 0x38); /*---------------------------------------------------------------------------------------- ---- KONTEXT ----------------------------------------------------------------------------------------*/ typedef struct _DEVICE_CONTEXT { WDFUSBDEVICE UsbDevice; WDFUSBINTERFACE UsbInterface; WDFUSBPIPE InterruptInPipe; WDFUSBPIPE InterruptOutPipe; WDFMEMORY DeviceDescriptor; UCHAR CurrentDegree; UCHAR CurrentButtonState; WDFQUEUE InterruptMsgQueue; WDFTIMER DebounceTimer; } DEVICE_CONTEXT, *PDEVICE_CONTEXT; WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext) CONST HID_REPORT_DESCRIPTOR G_DefaultReportDescriptor[] = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x04, // USAGE (Joystick) 0xa1, 0x01, // COLLECTION (Application) 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x38, // USAGE (Wheel) 0x15, 0x01, // LOGICAL_MINIMUM (1) 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (255) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x05, 0x09, // USAGE_PAGE (Button) 0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x29, 0x08, // USAGE_MAXIMUM (Button 8) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x08, // REPORT_COUNT (8) 0x55, 0x00, // UNIT_EXPONENT (0) 0x65, 0x00, // UNIT (None) 0x81, 0x02, // INPUT (Data,Var,Abs) 0xc0 // END_COLLECTION }; CONST HID_DESCRIPTOR G_DefaultHidDescriptor = { 0x09, // length of HID descriptor 0x21, // descriptor type == HID 0x21 0x0100, // hid spec release 0x00, // country code == Not Specified 0x01, // number of HID class descriptors { 0x22, // descriptor type sizeof(G_DefaultReportDescriptor) } // total length of report descriptor };
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.h 2 // LED-Konstanten #define LED1 0x01 #define LED2 0x02 #define LED3 0x04 #define LED4 0x08 #define LED5 0x10 typedef struct _INPUT_REPORT { BYTE Degree : 8; BYTE Button : 8; }INPUT_REPORT, *PINPUT_REPORT; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTSTATUS EvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ); VOID EvtInternalDeviceControl( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode ); NTSTATUS GetHidDescriptor( IN WDFDEVICE Device, IN WDFREQUEST Request ); NTSTATUS GetReportDescriptor( IN WDFDEVICE Device, IN WDFREQUEST Request ); NTSTATUS GetDeviceAttributes( IN WDFREQUEST Request ); NTSTATUS EvtDevicePrepareHardware( IN WDFDEVICE Device, IN WDFCMRESLIST ResourceList, IN WDFCMRESLIST ResourceListTranslated ); NTSTATUS EvtDeviceD0Entry( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState ); NTSTATUS EvtDeviceD0Exit( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE TargetState );
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.h 3 NTSTATUS ConfigContReaderForInterruptEndPoint( PDEVICE_CONTEXT DeviceContext ); VOID EvtUsbInterruptPipeReadComplete( WDFUSBPIPE Pipe, WDFMEMORY Buffer, size_t NumBytesTransferred, WDFCONTEXT Context ); #if (OSVER(NTDDI_VERSION) > NTDDI_WIN2K) NTSTATUS SendIdleNotification( IN WDFREQUEST Request ); #endif //(OSVER(NTDDI_VERSION) > NTDDI_WIN2K) VOID EvtTimerFunction( IN WDFTIMER Timer ); NTSTATUS ConfigContReaderForInterruptEndPoint( PDEVICE_CONTEXT DeviceContext ); NTSTATUS EvtDevicePrepareHardware( IN WDFDEVICE Device, IN WDFCMRESLIST ResourceList, IN WDFCMRESLIST ResourceListTranslated ); VOID EvtUsbInterruptPipeReadComplete( WDFUSBPIPE Pipe, WDFMEMORY Buffer, size_t NumBytesTransferred, WDFCONTEXT Context ); VOID EvtIoRead( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ); VOID EvtRequestReadCompletionRoutine( IN WDFREQUEST Request, IN WDFIOTARGET Target, PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, IN WDFCONTEXT Context ); VOID EvtIoWrite( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ); VOID EvtIoDeviceControl( IN WDFQUEUE Queue, IN WDFREQUEST Request,
c:\Dokumente und Einstellungen\matthias\Desktop\Atmel USB Treiber\WdfDriver\drv.h 4 IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode ); VOID CompleteReadReport( WDFDEVICE Device );
C:\Dokumente und Einstellungen\matthias\...WdfDriver\objchk_wxp_x86\i386\usbrotary.inf 1 [Version] Signature="$CHICAGO$" Class=HIDClass ClassGuid={745A17A0-74D3-11D0-B6FE-00A0C90F57DA} Provider=%VENDOR% LayoutFile=layout.inf DriverVer=01/21/2009,6.0.6001.18000 CatalogFile=kmdfsamples.cat [SourceDisksFiles] usbrotary.sys = 99 hidkmdf.sys = 99 [SourceDisksNames] 99 = %DISK_NAME%,,,"" [DestinationDirs] CopyFilesSYS = 12 CopyFilesDLL = 11 [Manufacturer] %VENDOR%=Vendor, NTx86 ; For Win2K [Vendor] %USBRotary% = usbrotary.Inst, USB\Vid_03EB&Pid_2020&Rev_0100 %customCollection% = customCollection.Inst, HID_DEVICE_UP:FF00_U:0001 ; For XP and later [Vendor.NTx86] %USBRotary% = usbrotary.Inst, USB\Vid_03EB&Pid_2020&Rev_0100 %customCollection% = customCollection.Inst, HID_DEVICE_UP:FF00_U:0001 ;=============================================================== ; HID USB Rotary Device ;=============================================================== [usbrotary.Inst.NT] CopyFiles = CopyFilesSYS, CopyFilesDLL [usbrotary.Inst.NT.HW] AddReg = usbrotary_Parameters.AddReg ; ; hidkmdf is the function driver and usbrotary is the lower filter ; [usbrotary.Inst.NT.Services] AddService = hidkmdf,0x00000002,hidkmdf_Service_Inst, AddService = usbrotary,, usbrotary_Service_Inst [CopyFilesSYS] usbrotary.sys HidClass.sys,,,0x00000010 ;COPYFLG_NO_OVERWRITE (for win2k) HidParse.sys,,,0x00000010 ;COPYFLG_NO_OVERWRITE (for win2k) hidkmdf.sys [CopyFilesDLL] Hid.dll,,,0x00000010 ;COPYFLG_NO_OVERWRITE (for win2k) [usbrotary_Parameters.AddReg] HKR,,"LowerFilters",0x00010000,"usbrotary" [usbrotary_Service_Inst] DisplayName = %USBRotary% ServiceType = %SERVICE_KERNEL_DRIVER% StartType = %SERVICE_DEMAND_START% ErrorControl = %SERVICE_ERROR_IGNORE% ServiceBinary = %12%\usbrotary.sys LoadOrderGroup = PNP Filter [hidkmdf_Service_Inst] DisplayName = %hidkmdf.SVCDESC% ServiceType = 1 ; SERVICE_KERNEL_DRIVER
Sie können auch lesen