Entwicklung des USB-Rotary-Sensor-Device und zugehörigem WDF-Treiber - Januar 2009

 
WEITER LESEN
Entwicklung des USB-Rotary-Sensor-Device und zugehörigem WDF-Treiber - Januar 2009
Entwicklung des USB-Rotary-Sensor-Device
      und zugehörigem WDF-Treiber
          von Matthias Rick und Sven Schraps

                  22. Januar 2009
Entwicklung des USB-Rotary-Sensor-Device und zugehörigem WDF-Treiber - 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
Entwicklung des USB-Rotary-Sensor-Device und zugehörigem WDF-Treiber - Januar 2009
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
Entwicklung des USB-Rotary-Sensor-Device und zugehörigem WDF-Treiber - Januar 2009
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