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 2009Inhaltsverzeichnis
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
ii1. 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
1aber 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.:
21.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
3bidirektional, 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
62. 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
7Kernel-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.
8Der 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
9als 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.
102.5. Treiberübersicht
Abb. 2.3.:
112.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)
153. 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
17A. Quelltexte
18c:\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 thec:\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_DRIVERSie können auch lesen