Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
I det här avsnittet får du lära dig mer om källkoden för en KMDF-baserad USB-klientdrivrutin. Kodexemplen genereras av drivrutinsmallen för USB-användarläge som ingår i Microsoft Visual Studio 2019.
De här avsnitten innehåller information om mallkoden.
Anvisningar om hur du genererar KMDF-mallkoden finns i Skriva din första USB-klientdrivrutin (KMDF).
Drivrutins källkod
Drivrutinsobjektet representerar klientdrivrutinens instans när Windows har läst in drivrutinen i minnet. Den fullständiga källkoden för drivrutinsobjektet finns i Driver.h och Driver.c.
Driver.h
Innan vi går igenom informationen om mallkoden ska vi titta på några deklarationer i huvudfilen (Driver.h) som är relevanta för KMDF-drivrutinsutveckling.
Driver.h, innehåller dessa filer som ingår i Windows Driver Kit (WDK).
#include <ntddk.h>
#include <wdf.h>
#include <usb.h>
#include <usbdlib.h>
#include <wdfusb.h>
#include "device.h"
#include "queue.h"
#include "trace.h"
Ntddk.h- och Wdf.h-huvudfiler ingår alltid för KMDF-drivrutinsutveckling. Rubrikfilen innehåller olika deklarationer och definitioner av metoder och strukturer som du behöver för att kompilera en KMDF-drivrutin.
Usb.h och Usbdlib.h innehåller deklarationer och definitioner av strukturer och rutiner som krävs av en klientdrivrutin för en USB-enhet.
Wdfusb.h innehåller deklarationer och definitioner av strukturer och metoder som krävs för att kommunicera med USB I/O-målobjekten som tillhandahålls av ramverket.
Device.h, Queue.h och Trace.h ingår inte i WDK. Dessa huvudfiler genereras av mallen och beskrivs senare i det här avsnittet.
Nästa block i Driver.h innehåller deklarationer av funktionsrollstyp för rutinen DriverEntry och EvtDriverDeviceAdd - och EvtCleanupCallback-händelseåteranropsrutiner . Alla dessa rutiner implementeras av föraren. Rolltyper hjälper SDV (Static Driver Verifier) att analysera en drivrutins källkod. Mer information om rolltyper finns i Deklarera funktioner med hjälp av funktionsrolltyper för KMDF-drivrutiner.
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD MyUSBDriver_EvtDeviceAdd;
EVT_WDF_OBJECT_CONTEXT_CLEANUP MyUSBDriver_EvtDriverContextCleanup;
Implementeringsfilen, Driver.c, innehåller följande kodblock som använder alloc_text pragma för att ange om driverEntry-funktionen och rutinerna för återanrop av händelser finns i sidbart minne.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDeviceAdd)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDriverContextCleanup)
#endif
Observera att DriverEntry har markerats som INIT, medan rutinerna för återanrop av händelser markeras som PAGE. AVSNITTET INIT anger att den körbara koden för DriverEntry är sidbar och slängs så snart drivrutinen returnerar från sin DriverEntry. Avsnittet PAGE anger att koden inte behöver finnas kvar i fysiskt minne hela tiden. Den kan skrivas till sidfilen när den inte används. För mer information, se Att låsa sidbar kod eller data.
Kort efter att drivrutinen har lästs in allokerar Windows en DRIVER_OBJECT struktur som representerar drivrutinen. Den anropar sedan förarens startpunktsrutin , DriverEntry, och skickar en pekare till strukturen. Eftersom Windows söker efter rutinen efter namn måste varje drivrutin implementera en rutin med namnet DriverEntry. Rutinen utför förarens initieringsuppgifter och anger förarens rutiner för återanrop av händelser till ramverket.
I följande kodexempel visas rutinen DriverEntry som genereras av mallen.
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
WDF_OBJECT_ATTRIBUTES attributes;
//
// Initialize WPP Tracing
//
WPP_INIT_TRACING( DriverObject, RegistryPath );
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
//
// Register a cleanup callback so that we can call WPP_CLEANUP when
// the framework driver object is deleted during driver unload.
//
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = MyUSBDriver_EvtDriverContextCleanup;
WDF_DRIVER_CONFIG_INIT(&config,
MyUSBDriver_EvtDeviceAdd
);
status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
WDF_NO_HANDLE
);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status);
WPP_CLEANUP(DriverObject);
return status;
}
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
return status;
}
DriverEntry-rutinen har två parametrar: en pekare till den DRIVER_OBJECT struktur som allokeras av Windows och en registersökväg för drivrutinen. Parametern RegistryPath representerar den drivrutinsspecifika sökvägen i registret.
I driverEntry-rutinen utför drivrutinen följande uppgifter:
Allokerar globala resurser som krävs under drivrutinens livslängd. I mallkoden allokerar till exempel klientdrivrutinen de resurser som krävs för WPP-programvaruspårning genom att anropa WPP_INIT_TRACING makro.
Registrerar vissa händelseåterkallsrutiner i ramverket.
För att registrera händelseåteranrop anger klientdrivrutinen först pekare till dess implementeringar av EvtDriverXxx-rutinerna i vissa WDF-strukturer. Drivrutinen anropar sedan metoden WdfDriverCreate och tillhandahåller dessa strukturer (beskrivs i nästa steg).
Anropar metoden WdfDriverCreate och hämtar ett handtag till ramverksdrivrutinsobjektet.
När klientdrivrutinen anropar WdfDriverCreate skapar ramverket ett ramverksdrivrutinsobjekt som representerar klientdrivrutinen. När anropet är klart tar klientdrivrutinen emot ett WDFDRIVER-handtag och kan hämta information om drivrutinen, till exempel registersökvägen, versionsinformation och så vidare (se Objektreferens för WDF-drivrutin).
Observera att ramverksdrivrutinsobjektet skiljer sig från Windows-drivrutinsobjektet som beskrivs av DRIVER_OBJECT. När som helst kan klientdrivrutinen få en pekare till Windows DRIVER_OBJECT-strukturen med hjälp av WDFDRIVER-handtaget och anropa metoden WdfGetDriver.
Efter WdfDriverCreate-anropet samarbetar ramverket med klientdrivrutinen för att kommunicera med Windows. Ramverket fungerar som ett abstraktionslager mellan Windows och drivrutinen och hanterar de flesta av de komplicerade drivrutinsaktiviteterna. Klientdrivrutinen registrerar sig med ramverket för händelser som drivrutinen är intresserad av. När vissa händelser inträffar meddelar Windows ramverket. Om drivrutinen har registrerat ett händelseåteranrop för en särskild händelse, meddelar ramverket drivrutinen genom att anropa det registrerade händelseåteranropet. Genom att göra det får föraren möjlighet att hantera händelsen, om det behövs. Om drivrutinen inte registrerade sin händelseåteranrop fortsätter ramverket med sin standardhantering av händelsen.
En av händelseåteranropen som drivrutinen måste registrera är EvtDriverDeviceAdd. Ramverket anropar drivrutinens EvtDriverDeviceAdd-implementering när ramverket är redo att skapa ett enhetsobjekt. I Windows är ett enhetsobjekt en logisk representation av funktionen för den fysiska enhet som klientdrivrutinen läses in för (beskrivs senare i det här avsnittet).
Andra händelseåteranrop som drivrutinen kan registrera är EvtDriverUnload, EvtCleanupCallback och EvtDestroyCallback.
I mallkoden registrerar klientdrivrutinen för två händelser: EvtDriverDeviceAdd och EvtCleanupCallback. Drivrutinen anger en pekare till implementeringen av EvtDriverDeviceAdd i WDF_DRIVER_CONFIG-strukturen och EvtCleanupCallback-händelseåteranropet i WDF_OBJECT_ATTRIBUTES-strukturen .
När Windows är redo att släppa DRIVER_OBJECT struktur och ta bort drivrutinen rapporterar ramverket händelsen till klientdrivrutinen genom att anropa drivrutinens EvtCleanupCallback-implementering . Ramverket anropar återanropet precis innan det tar bort ramverksdrivrutinsobjektet. Klientdrivrutinen kan frigöra alla globala resurser som den allokerade i sin DriverEntry. I mallkoden stoppar till exempel klientdrivrutinen WPP-spårning som aktiverades i DriverEntry.
Följande kodexempel visar klientdrivrutinens EvtCleanupCallback-händelseåteranropsimplementering.
VOID MyUSBDriver_EvtDriverContextCleanup(
_In_ WDFDRIVER Driver
)
{
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE ();
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
//
// Stop WPP Tracing
//
WPP_CLEANUP( WdfDriverWdmGetDriverObject(Driver) );
}
När enheten identifieras av USB-drivrutinsstacken skapar bussdrivrutinen ett fysiskt enhetsobjekt (PDO) för enheten och associerar PDO:n med enhetsnoden. Enhetsnoden är i en stackformation, där PDO:n finns längst ned. Varje stack måste ha en PDO och kan ha filterenhetsobjekt (filter-DOs) och ett funktionsenhetsobjekt (FDO) ovanför den. Mer information finns i Enhetsnoder och Enhetsstackar.
Den här bilden visar enhetsstacken för malldrivrutinen MyUSBDriver_.sys.
Observera enhetsstacken med namnet "Min USB-enhet". USB-drivrutinsstacken skapar PDO:n för enhetsstacken. I exemplet är PDO:en associerad med Usbhub3.sys, som är en av de drivrutiner som ingår i USB-drivrutinsstacken. Som funktionsdrivrutin för enheten måste klientdrivrutinen först skapa FDO:n för enheten och sedan ansluta den till toppen av enhetsstacken.
För en KMDF-baserad klientdrivrutin utför ramverket dessa uppgifter för klientdrivrutinens räkning. För att representera FDO:n för enheten skapar ramverket ett ramverksenhetsobjekt. Klientdrivrutinen kan dock ange vissa initieringsparametrar som ramverket använder för att konfigurera det nya objektet. Den möjligheten ges till klientdrivrutinen när ramverket anropar drivrutinens EvtDriverDeviceAdd-implementering . När objektet har skapats och FDO:n är ansluten till toppen av enhetsstacken tillhandahåller ramverket klientdrivrutinen med en WDFDEVICE-handtag till ramverkets enhetsobjekt. Med hjälp av det här handtaget kan klientdrivrutinen utföra olika enhetsrelaterade åtgärder.
I följande kodexempel visas klientdrivrutinens EvtDriverDeviceAdd-implementering av händelseåteranrop.
NTSTATUS
MyUSBDriver_EvtDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
NTSTATUS status;
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
status = MyUSBDriver_CreateDevice(DeviceInit);
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
return status;
}
Under körningen använder implementeringen av EvtDriverDeviceAddPAGED_CODE makro för att kontrollera att rutinen anropas i en lämplig miljö för växlingsbar kod. Se till att du anropar makrot när du har deklarerat alla dina variabler. Annars misslyckas kompilering eftersom de genererade källfilerna är .c-filer och inte .cpp filer.
Klientdrivrutinens EvtDriverDeviceAdd-implementering anropar hjälpfunktionen MyUSBDriver_CreateDevice för att utföra de uppgifter som krävs.
Följande kodexempel visar hjälpfunktionen MyUSBDriver_CreateDevice. MyUSBDriver_CreateDevice definieras i Device.c.
NTSTATUS
MyUSBDriver_CreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES deviceAttributes;
PDEVICE_CONTEXT deviceContext;
WDFDEVICE device;
NTSTATUS status;
PAGED_CODE();
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware = MyUSBDriver_EvtDevicePrepareHardware;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (NT_SUCCESS(status)) {
//
// Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an
// inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the
// device.h header file. This function will do the type checking and return
// the device context. If you pass a wrong object handle
// it will return NULL and assert if run under framework verifier mode.
//
deviceContext = WdfObjectGet_DEVICE_CONTEXT(device);
deviceContext->PrivateDeviceData = 0;
//
// Create a device interface so that applications can find and talk
// to us.
//
status = WdfDeviceCreateDeviceInterface(
device,
&GUID_DEVINTERFACE_MyUSBDriver_,
NULL // ReferenceString
);
if (NT_SUCCESS(status)) {
//
// Initialize the I/O Package and any Queues
//
status = MyUSBDriver_QueueInitialize(device);
}
}
return status;
}
EvtDriverDeviceAdd har två parametrar: ett handtag till ramverksdrivrutinsobjektet som skapades i föregående anrop till DriverEntry och en pekare till en WDFDEVICE_INIT struktur. Ramverket allokerar den WDFDEVICE_INIT strukturen och skickar den en pekare så att klientdrivrutinen kan fylla i strukturen med initieringsparametrar för det ramverksenhetsobjekt som ska skapas.
I EvtDriverDeviceAdd-implementeringen måste klientdrivrutinen utföra följande uppgifter:
Anropa metoden WdfDeviceCreate för att hämta en WDFDEVICE-referens till det nya enhetsobjektet.
Metoden WdfDeviceCreate gör att ramverket skapar ett ramverksenhetsobjekt för FDO:n och kopplar det överst i enhetsstacken. I WdfDeviceCreate-anropet måste klientdrivrutinen utföra följande uppgifter:
- Ange pekare till de strömåteruppringningsrutiner för Plug and play (PnP) som klientdrivrutinen använder, i den ramverksdefinierade WDFDEVICE_INIT-strukturen. Rutinerna anges först i WDF_PNPPOWER_EVENT_CALLBACKS-strukturen och associeras sedan med WDFDEVICE_INIT genom att anropa metoden WdfDeviceInitSetPnpPowerEventCallbacks .
Windows-komponenter, PnP och energihanterare skickar enhetsrelaterade begäranden till drivrutiner som svar på ändringar i PnP-tillstånd (till exempel startad, stoppad och borttagen) och energisparläge (till exempel arbete eller paus). För KMDF-baserade drivrutiner fångar ramverket upp dessa begäranden. Klientdrivrutinen kan bli informerad om förfrågningar genom att registrera återanropsrutiner, kallade PnP-effekthändelseåteranrop, via ramverket med WdfDeviceCreate-metoden. När Windows-komponenter skickar begäranden hanterar ramverket dem och anropar den motsvarande återkallningsfunktionen för PnP-strömhändelse, om klientdrivrutinen har registrerats.
En av de återanropsrutinerna för PnP-power-händelser som klientdrivrutinen måste implementera är EvtDevicePrepareHardware. Händelseåteranropet anropas när PnP-hanteraren startar enheten. Implementeringen för EvtDevicePrepareHardware beskrivs i följande avsnitt.
- Ange en pekare till drivrutinens enhetskontextstruktur. Pekaren måste anges i den WDF_OBJECT_ATTRIBUTES struktur som initieras genom att anropa WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE makro.
En enhetskontext (kallas ibland enhetstillägg) är en datastruktur (definierad av klientdrivrutinen) för lagring av information om ett specifikt enhetsobjekt. Klientdrivrutinen skickar en pekare till sin enhetskontext till ramverket. Ramverket allokerar ett minnesblock baserat på strukturens storlek och lagrar en pekare till minnesplatsen i ramverkets enhetsobjekt. Klientdrivrutinen kan använda pekaren för att komma åt och lagra information i element i enhetens sammanhang. Mer information om enhetskontexter finns i Kontextutrymme för ramverksobjekt.
När WdfDeviceCreate-anropet har slutförts tar klientdrivrutinen emot ett handtag till det nya ramverksenhetsobjektet, som lagrar en pekare till det minnesblock som allokeras av ramverket för enhetskontexten. Klientdrivrutinen kan nu få en pekare till enhetskontexten genom att anropa makrot WdfObjectGet_DEVICE_CONTEXT .
Registrera en GUID för enhetsgränssnitt till klientens drivrutin genom att anropa metoden WdfDeviceCreateDeviceInterface. Program kan kommunicera med drivrutinen med hjälp av detta GUID. GUID-konstanten deklareras i rubriken public.h.
Konfigurera köer för I/O-överföringar till enheten. Mallkoden definierar MyUSBDriver_QueueInitialize, en hjälprutin för att konfigurera köer, som beskrivs i avsnittet Kökällkod .
Enhetens källkod
Enhetsobjektet representerar instansen av den enhet som klientdrivrutinen läses in i minnet för. Den fullständiga källkoden för enhetsobjektet finns i Device.h och Device.c.
Device.h
Device.h-huvudfilen innehåller public.h, som innehåller vanliga deklarationer som används av alla filer i projektet.
Nästa block i Device.h deklarerar enhetskontexten för klientdrivrutinen.
typedef struct _DEVICE_CONTEXT
{
WDFUSBDEVICE UsbDevice;
ULONG PrivateDeviceData; // just a placeholder
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT)
Den DEVICE_CONTEXT strukturen definieras av klientdrivrutinen och lagrar information om ett ramverksenhetsobjekt. Den deklareras i Device.h och innehåller två medlemmar: ett handtag till ett ramverks USB-målenhetsobjekt (beskrivs senare) och en platshållare. Den här strukturen utökas i senare övningar.
Device.h innehåller även makrot WDF_DECLARE_CONTEXT_TYPE som genererar en infogad funktion WdfObjectGet_DEVICE_CONTEXT. Klientdrivrutinen kan anropa den funktionen för att hämta en pekare till minnesblocket från ramverkets enhetsobjekt.
Följande kodrad deklarerar MyUSBDriver_CreateDevice, en hjälpfunktion som hämtar ett WDFUSBDEVICE-handtag till USB-målenhetsobjektet.
NTSTATUS
MyUSBDriver_CreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
);
USBCreate tar en pekare till en WDFDEVICE_INIT-struktur som parameter. Det här är samma pekare som skickades av ramverket när det anropade klientdrivrutinens EvtDriverDeviceAdd-implementering . I grund och botten utför MyUSBDriver_CreateDevice uppgifterna för EvtDriverDeviceAdd. Källkoden för EvtDriverDeviceAdd-implementeringen beskrivs i föregående avsnitt.
Nästa rad i Device.h deklarerar en deklaration av funktionsrolltyp för evtDevicePrepareHardware-händelseåteranropsrutinen . Händelseåteranropet implementeras av klientdrivrutinen och utför uppgifter som att konfigurera USB-enheten.
EVT_WDF_DEVICE_PREPARE_HARDWARE MyUSBDriver_EvtDevicePrepareHardware;
Device.c
Implementeringsfilen Device.c innehåller följande kodblock som använder alloc_text pragma för att ange att drivrutinsimplementeringen av EvtDevicePrepareHardware finns i sidbart minne.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, MyUSBDriver_CreateDevice)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDevicePrepareHardware)
#endif
I implementeringen för EvtDevicePrepareHardware utför klientdrivrutinen de USB-specifika initieringsuppgifterna. Dessa uppgifter omfattar registrering av klientdrivrutinen, initiering av USB-specifika I/O-målobjekt och val av en USB-konfiguration. I följande tabell visas de specialiserade I/O-målobjekt som tillhandahålls av ramverket. Mer information finns i USB I/O-mål.
| USB I/O-målobjekt (handtag) | Få klarhet genom att ringa... | Beskrivning |
|---|---|---|
| USB-målenhetsobjekt (WDFUSBDEVICE ) | WdfUsbTargetDeviceCreateWithParameters | Representerar en USB-enhet och innehåller metoder för att hämta enhetsbeskrivningen och skicka kontrollbegäranden till enheten. |
| USB-målgränssnittsobjekt (WDFUSBINTERFACE ) | WdfUsbTargetDeviceGetInterface | Representerar ett enskilt gränssnitt och tillhandahåller metoder som en klientdrivrutin kan anropa för att välja en alternativ inställning och hämta information om inställningen. |
| USB målrörsobjekt (WDFUSBPIPE) | WdfUsbInterfaceGetConfiguredPipe | Representerar en enskild kanal för en slutpunkt som har konfigurerats i den aktuella alternativa inställningen för ett gränssnitt. USB-drivrutinsstacken väljer varje gränssnitt i den valda konfigurationen och konfigurerar en kommunikationskanal till varje slutpunkt i gränssnittet. I USB-terminologi kallas kommunikationskanalen för ett rör. |
Det här kodexemplet visar implementeringen för EvtDevicePrepareHardware.
NTSTATUS
MyUSBDriver_EvtDevicePrepareHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourceList,
_In_ WDFCMRESLIST ResourceListTranslated
)
{
NTSTATUS status;
PDEVICE_CONTEXT pDeviceContext;
WDF_USB_DEVICE_CREATE_CONFIG createParams;
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams;
UNREFERENCED_PARAMETER(ResourceList);
UNREFERENCED_PARAMETER(ResourceListTranslated);
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
status = STATUS_SUCCESS;
pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
if (pDeviceContext->UsbDevice == NULL) {
//
// Specifying a client contract version of 602 enables us to query for
// and use the new capabilities of the USB driver stack for Windows 8.
// It also implies that we conform to rules mentioned in the documentation
// documentation for WdfUsbTargetDeviceCreateWithParameters.
//
WDF_USB_DEVICE_CREATE_CONFIG_INIT(&createParams,
USBD_CLIENT_CONTRACT_VERSION_602
);
status = WdfUsbTargetDeviceCreateWithParameters(Device,
&createParams,
WDF_NO_OBJECT_ATTRIBUTES,
&pDeviceContext->UsbDevice
);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"WdfUsbTargetDeviceCreateWithParameters failed 0x%x", status);
return status;
}
//
// Select the first configuration of the device, using the first alternate
// setting of each interface
//
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES(&configParams,
0,
NULL
);
status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->UsbDevice,
WDF_NO_OBJECT_ATTRIBUTES,
&configParams
);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"WdfUsbTargetDeviceSelectConfig failed 0x%x", status);
return status;
}
}
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
return status;
}
Här är en närmare titt på klientdrivrutinens uppgifter som implementeras av mallkoden:
Anger klientdrivrutinens kontraktversion som förberedelse för att registrera sig med den underliggande USB-drivrutinsstacken som laddas av Windows.
Windows kan läsa in USB 3.0- eller USB 2.0-drivrutinsstacken, beroende på vilken värdstyrenhet som USB-enheten är ansluten till. USB 3.0-drivrutinsstacken är ny i Windows 8 och har stöd för flera nya funktioner som definieras av USB 3.0-specifikationen, till exempel streams-funktionen. Den nya drivrutinsstacken implementerar också flera förbättringar, till exempel bättre spårning och bearbetning av USB-begärandeblock (URB), som är tillgängliga via en ny uppsättning URB-rutiner. En klientdrivrutin som tänker använda dessa funktioner eller anropa de nya rutinerna måste ange USBD_CLIENT_CONTRACT_VERSION_602 kontraktsversion. En USBD_CLIENT_CONTRACT_VERSION_602 klientdrivrutin måste följa en viss uppsättning regler. Mer information om dessa regler finns i Metodtips: Använda URL:er.
Om du vill ange kontraktsversionen måste klientdrivrutinen initiera en WDF_USB_DEVICE_CREATE_CONFIG struktur med kontraktsversionen genom att anropa makrot WDF_USB_DEVICE_CREATE_CONFIG_INIT .
Anropar metoden WdfUsbTargetDeviceCreateWithParameters . Metoden kräver ett handtag till det ramverksenhetsobjekt som klientdrivrutinen hämtade tidigare genom att anropa WdfDeviceCreate i drivrutinsimplementeringen av EvtDriverDeviceAdd. Metoden WdfUsbTargetDeviceCreateWithParameters :
- Registrerar klientdrivrutinen med den underliggande USB-drivrutinsstacken.
- Hämtar en WDFUSBDEVICE-referens till USB-målenhetsobjektet som skapas av ramverket. Mallkoden lagrar handtaget till USB-målenhetsobjektet i sin enhetskontext. Genom att använda handtaget kan klientdrivrutinen hämta USB-specifik information om enheten.
Du måste anropa WdfUsbTargetDeviceCreate i stället för WdfUsbTargetDeviceCreateWithParameters om:
Klientdrivrutinen anropar inte den nya uppsättningen URB-rutiner som är tillgängliga medWindows 8-versionen av WDK.
Om klientdrivrutinen anropar WdfUsbTargetDeviceCreateWithParameters förutsätter USB-drivrutinsstacken att alla URB:er allokeras genom att anropa WdfUsbTargetDeviceCreateUrb eller WdfUsbTargetDeviceCreateIsochUrb. URL:er som allokeras med dessa metoder har ogenomskinliga URB-kontextblock som används av USB-drivrutinsstacken för snabbare bearbetning. Om klientdrivrutinen använder en URB som inte allokeras av dessa metoder genererar USB-drivrutinen en felkontroll.
Mer information om URB-allokeringar finns i Allokera och skapa URL:er.
Klientdrivrutinen har inte för avsikt att följa den uppsättning regler som beskrivs i Metodtips: Använda URL:er.
Sådana drivrutiner krävs inte för att ange en klientkontraktsversion och måste därför hoppa över steg 1.
Väljer en USB-konfiguration.
I mallkoden väljer klientdrivrutinen standardkonfigurationen på USB-enheten. Standardkonfigurationen innehåller enhetens konfiguration 0 och alternativ inställning 0 för varje gränssnitt i den konfigurationen.
För att välja standardkonfiguration konfigurerar klientdrivrutinen WDF_USB_DEVICE_SELECT_CONFIG_PARAMS struktur genom att anropa funktionen WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES . Funktionen initierar medlemmen Type till WdfUsbTargetDeviceSelectConfigTypeMultiInterface för att indikera att när flera gränssnitt är tillgängliga, måste en alternativ inställning i varje sådant gränssnitt väljas. Eftersom anropet måste välja standardkonfigurationen anger klientdrivrutinen NULL i parametern SettingPairs och 0 i parametern NumberInterfaces . När det är klart anger MultiInterface.NumberOfConfiguredInterfaces-medlemmen i WDF_USB_DEVICE_SELECT_CONFIG_PARAMS antalet gränssnitt som alternativ inställning 0 har valts för. Andra medlemmar ändras inte.
Not Om klientdrivrutinen vill välja andra inställningar än standardinställningen måste drivrutinen skapa en matris med WDF_USB_INTERFACE_SETTING_PAIR strukturer. Varje element i matrisen anger det enhetsdefinierade gränssnittsnumret och indexet för den alternativa inställning som ska väljas. Den informationen lagras i enhetens konfigurations- och gränssnittsbeskrivningar som kan hämtas genom att anropa metoden WdfUsbTargetDeviceRetrieveConfigDescriptor . Klientdrivrutinen måste sedan anropa WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES och skicka WDF_USB_INTERFACE_SETTING_PAIR-matrisen till ramverket.
Källkod för kö
Ramverksköobjektet representerar I/O-kön för ett specifikt ramverksenhetsobjekt. Den fullständiga källkoden för köobjektet finns i Queue.h och Queue.c.
Queue.h
Deklarerar en händelseåteranropsrutin för händelsen som genereras av ramverkets köobjekt.
Det första blocket i Queue.h deklarerar en kökontext.
typedef struct _QUEUE_CONTEXT {
ULONG PrivateDeviceData; // just a placeholder
} QUEUE_CONTEXT, *PQUEUE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext)
Precis som i en enhetskontext är en kökontext en datastruktur som definieras av klienten för att lagra information om en viss kö.
Nästa kodrad deklarerar MyUSBDriver_QueueInitialize funktion, hjälpfunktionen som skapar och initierar ramverksköobjektet.
NTSTATUS
MyUSBDriver_QueueInitialize(
_In_ WDFDEVICE Device
);
I nästa kodexempel deklareras en deklaration av funktionsrollstyp för evtIoDeviceControl-händelseåteranropsrutinen . Händelseåteranropet implementeras av klientdrivrutinen och anropas när ramverket bearbetar en enhets-I/O-kontrollbegäran.
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MyUSBDriver_EvtIoDeviceControl;
Queue.c
Implementeringsfilen Queue.c innehåller följande kodblock som använder alloc_text pragma för att ange att drivrutinens implementering av MyUSBDriver_QueueInitialize finns i sidbart minne.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, MyUSBDriver_QueueInitialize)
#endif
WDF tillhandahåller ramverksköobjektet för att hantera begärandeflödet till klientdrivrutinen. Ramverket skapar ett ramverksköobjekt när klientdrivrutinen anropar metoden WdfIoQueueCreate . I det anropet kan klientdrivrutinen ange vissa konfigurationsalternativ innan ramverket skapar köer. Dessa alternativ omfattar om kön är energihanterad, tillåter begäranden med noll längd eller är standardkön för drivrutinen. Ett enda ramverksköobjekt kan hantera flera typer av begäranden, till exempel läs-, skriv- och enhets-I/O-kontroll. Klientdrivrutinen kan ange återanrop för händelser för var och en av dessa begäranden.
Klientdrivrutinen måste också ange leveranstypen. Ett köobjekts sändningstyp avgör hur ramverket levererar begäranden till klientdrivrutinen. Leveransmekanismen kan vara sekventiell, parallellt eller av en anpassad mekanism som definieras av klientdrivrutinen. För en sekventiell kö levereras inte en begäran förrän klientdrivrutinen har slutfört den tidigare begäran. I parallellt sändningsläge vidarebefordrar ramverket begäranden så snart de kommer från I/O-chefen. Det innebär att klientdrivrutinen kan ta emot en begäran medan den bearbetar en annan. I den anpassade mekanismen hämtar klienten manuellt nästa begäran från ramverksköobjektet när drivrutinen är redo att bearbeta den.
Vanligtvis måste klientdrivrutinen konfigurera köer i drivrutinens EvtDriverDeviceAdd händelseåteranrop. Mallkoden innehåller hjälprutinen, MyUSBDriver_QueueInitialize, som initierar ramverksköobjektet.
NTSTATUS
MyUSBDriver_QueueInitialize(
_In_ WDFDEVICE Device
)
{
WDFQUEUE queue;
NTSTATUS status;
WDF_IO_QUEUE_CONFIG queueConfig;
PAGED_CODE();
//
// Configure a default queue so that requests that are not
// configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
// other queues get dispatched here.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
&queueConfig,
WdfIoQueueDispatchParallel
);
queueConfig.EvtIoDeviceControl = MyUSBDriver_EvtIoDeviceControl;
status = WdfIoQueueCreate(
Device,
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&queue
);
if( !NT_SUCCESS(status) ) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE, "WdfIoQueueCreate failed %!STATUS!", status);
return status;
}
return status;
}
För att konfigurera köer utför klientdrivrutinen följande uppgifter:
- Anger köens konfigurationsalternativ i en WDF_IO_QUEUE_CONFIG-struktur. Mallkoden använder funktionen WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE för att initiera strukturen. Funktionen anger köobjektet som standardköobjekt, är strömadministrerat och tar emot förfrågningar parallellt.
- Lägger till återanrop för klientdrivrutinens händelser för köens I/O-begäranden. I mallen anger klientdrivrutinen en pekare till sin händelseåteranrop för en enhets-I/O-kontrollbegäran.
- Anropar WdfIoQueueCreate för att hämta en WDFQUEUE-referens till ramverksköobjektet som skapas av ramverket.
Så här fungerar kömekanismen. För att kommunicera med USB-enheten öppnar ett program först ett handtag till enheten genom att anropa SetDixxx-rutinerna och CreateHandle. Genom att använda den här handtaget anropar programmet funktionen DeviceIoControl med en specifik kontrollkod. Beroende på typ av kontrollkod kan programmet ange indata- och utdatabuffertar i det anropet. Samtalet tas så småningom emot av I/O Manager, som sedan skapar en begäran (IRP) och vidarebefordrar den till klientdrivrutinen. Ramverket fångar upp begäran, skapar ett ramverksbegärandeobjekt och lägger till det i ramverksköobjektet. I det här fallet, eftersom klientdrivrutinen registrerade sin händelseåteranrop för enhetens I/O-kontrollbegäran, anropar ramverket återanropet. Dessutom, eftersom köobjektet skapades med flaggan WdfIoQueueDispatchParallel, anropas återanropet så snart begäran läggs till i kön.
VOID
MyUSBDriver_EvtIoDeviceControl(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
TraceEvents(TRACE_LEVEL_INFORMATION,
TRACE_QUEUE,
"!FUNC! Queue 0x%p, Request 0x%p OutputBufferLength %d InputBufferLength %d IoControlCode %d",
Queue, Request, (int) OutputBufferLength, (int) InputBufferLength, IoControlCode);
WdfRequestComplete(Request, STATUS_SUCCESS);
return;
}
När ramverket anropar klientdrivrutinens händelseåteranrop skickar det en referens till ramverksbegärandeobjektet som innehåller begäran (och dess indata- och utdatabuffertar) som skickas av programmet. Dessutom skickar den ett handtag till ramverksköobjektet som innehåller förfrågan. Vid händelseåteranrop bearbetar klientdrivrutinen begäran vid behov. Mallkoden slutför helt enkelt begäran. Klientdrivrutinen kan utföra mer komplicerade uppgifter. Om ett program till exempel begär viss enhetsinformation kan klientdrivrutinen i händelseåteranropet skapa en USB-kontrollbegäran och skicka den till USB-drivrutinsstacken för att hämta den begärda enhetsinformationen. USB-kontrollbegäranden beskrivs i USB-kontrollöverföring.