Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit onderwerp vindt u informatie over de broncode voor een USB-clientstuurprogramma op basis van KMDF. De codevoorbeelden worden gegenereerd door de stuurprogrammasjabloon voor de USB-gebruikersmodus die is opgenomen in Microsoft Visual Studio 2019.
Deze secties bevatten informatie over de sjablooncode.
Zie Hoe u uw eerste USB-clientstuurprogramma (KMDF) schrijft voor instructies voor het genereren van de KMDF-sjablooncode.
Broncode van stuurprogramma
Het stuurprogrammaobject vertegenwoordigt het exemplaar van het clientstuurprogramma nadat Windows het stuurprogramma in het geheugen heeft geladen. De volledige broncode voor het stuurprogrammaobject bevindt zich in Driver.h en Driver.c.
Driver.h
Voordat u de details van de sjablooncode bespreekt, bekijken we enkele declaraties in het headerbestand (Driver.h) die relevant zijn voor de ontwikkeling van KMDF-stuurprogramma's.
Driver.h, bevat deze bestanden, opgenomen in de 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- en Wdf.h-headerbestanden zijn altijd opgenomen voor het ontwikkelen van KMDF-stuurprogramma's. Het headerbestand bevat verschillende declaraties en definities van methoden en structuren die u nodig hebt om een KMDF-stuurprogramma te compileren.
Usb.h en Usbdlib.h bevatten declaraties en definities van structuren en routines die vereist zijn voor een clientstuurprogramma voor een USB-apparaat.
Wdfusb.h bevat declaraties en definities van structuren en methoden die nodig zijn om te communiceren met de USB I/O-doelobjecten die door het framework worden geleverd.
Device.h, Queue.h en Trace.h zijn niet opgenomen in de WDK. Deze headerbestanden worden gegenereerd door de sjabloon en worden verderop in dit onderwerp besproken.
Het volgende blok in Driver.h biedt declaraties voor functieroltypen voor de DriverEntry-routine en EvtDriverDeviceAdd en EvtCleanupCallback-gebeurtenis callback-routines. Al deze routines worden door de bestuurder geïmplementeerd. Roltypen helpen Static Driver Verifier (SDV) de broncode van een stuurprogramma te analyseren. Zie Functies declareren met functieroltypen voor KMDF-stuurprogramma's voor meer informatie over roltypen.
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD MyUSBDriver_EvtDeviceAdd;
EVT_WDF_OBJECT_CONTEXT_CLEANUP MyUSBDriver_EvtDriverContextCleanup;
Het implementatiebestand Driver.c bevat het volgende codeblok dat gebruikmaakt alloc_text van pragma om op te geven of de driverEntry-functie en callback-routines voor gebeurtenissen zich in wisselbaar geheugen bevinden.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDeviceAdd)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDriverContextCleanup)
#endif
U ziet dat DriverEntry is gemarkeerd als INIT, terwijl de callback-routines van de gebeurtenis zijn gemarkeerd als PAGE. In de INIT-sectie wordt aangegeven dat de executabele code voor DriverEntry paginabaar is en wordt verwijderd zodra de driver terugkeert van zijn DriverEntry. De sectie PAGE geeft aan dat de code niet de hele tijd in het fysieke geheugen hoeft te blijven; het kan naar het paginabestand worden geschreven wanneer het niet in gebruik is. Zie Paginabare code of gegevens vergrendelen voor meer informatie.
Kort nadat uw stuurprogramma is geladen, wijst Windows een DRIVER_OBJECT structuur toe die uw stuurprogramma vertegenwoordigt. Vervolgens wordt de routine voor het toegangspunt van uw stuurprogramma, DriverEntry, aangeroepen en een pointer naar de structuur doorgegeven. Omdat Windows zoekt naar de routine op naam, moet elk stuurprogramma een routine met de naam DriverEntry implementeren. De routine voert de initialisatietaken van het stuurprogramma uit en geeft de callbackroutines voor de gebeurtenis van het stuurprogramma op aan het framework.
In het volgende codevoorbeeld ziet u de DriverEntry-routine die door de sjabloon is gegenereerd.
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;
}
De Routine DriverEntry heeft twee parameters: een aanwijzer naar de DRIVER_OBJECT structuur die door Windows wordt toegewezen en een registerpad voor het stuurprogramma. De parameter RegistryPath vertegenwoordigt het stuurprogrammaspecifieke pad in het register.
In de Routine DriverEntry voert het stuurprogramma deze taken uit:
Wijst globale bronnen toe die vereist zijn tijdens de levensduur van het stuurprogramma. In de sjablooncode wijst het clientstuurprogramma bijvoorbeeld resources toe die vereist zijn voor WPP-softwaretracering door de WPP_INIT_TRACING macro aan te roepen.
Registreert bepaalde callbackroutines voor gebeurtenissen met het framework.
Om de callbacks voor gebeurtenissen te registreren, geeft het clientstuurprogramma eerst aanwijzers op voor de implementaties van de EvtDriverXxx-routines in bepaalde WDF-structuren. Het stuurprogramma roept vervolgens de WdfDriverCreate-methode aan en levert deze structuren (besproken in de volgende stap).
Roept de WdfDriverCreate-methode aan en haalt een ingang op naar het frameworkstuurprogrammaobject.
Nadat het clientstuurprogramma WdfDriverCreate aanroept, maakt het framework een frameworkstuurprogrammaobject dat het clientstuurprogramma vertegenwoordigt. Wanneer de aanroep is voltooid, ontvangt het clientstuurprogramma een WDFDRIVER-ingang en kan het informatie over het stuurprogramma ophalen, zoals het registerpad, de versiegegevens, enzovoort (zie WDF Driver Object Reference).
Houd er rekening mee dat het frameworkstuurprogrammaobject verschilt van het Windows-stuurprogrammaobject dat wordt beschreven door DRIVER_OBJECT. Op elk gewenst moment kan het clientstuurprogramma een aanwijzer naar de Structuur van WindowsDRIVER_OBJECT krijgen met behulp van de WDFDRIVER-ingang en het aanroepen van de WdfGetDriver-methode .
Na de aanroep WdfDriverCreate werkt het framework samen met het clientstuurprogramma om te communiceren met Windows. Het framework fungeert als een abstractielaag tussen Windows en het stuurprogramma en verwerkt de meeste ingewikkelde stuurprogrammataken. Het clientstuurprogramma registreert zich bij het framework voor gebeurtenissen waarin het stuurprogramma geïnteresseerd is. Wanneer bepaalde gebeurtenissen optreden, wordt het framework door Windows op de hoogte gebracht. Als de driver een gebeurtenis-callback voor een bepaalde gebeurtenis heeft geregistreerd, meldt het framework de driver door de geregistreerde gebeurtenis-callback aan te roepen. Door dit te doen, krijgt de bestuurder de mogelijkheid om de gebeurtenis indien nodig af te handelen. Als het stuurprogramma de callback van de gebeurtenis niet heeft geregistreerd, gaat het framework verder met de standaardafhandeling van de gebeurtenis.
Een van de callbacks van gebeurtenissen die het stuurprogramma moet registreren, is EvtDriverDeviceAdd. Het framework roept de EvtDriverDeviceAdd-implementatie van het stuurprogramma aan wanneer het framework gereed is om een apparaatobject te maken. In Windows is een apparaatobject een logische weergave van de functie van het fysieke apparaat waarvoor het clientstuurprogramma wordt geladen (verderop in dit onderwerp besproken).
Andere callbacks voor gebeurtenissen die het stuurprogramma kan registreren zijn EvtDriverUnload, EvtCleanupCallback en EvtDestroyCallback.
In de sjablooncode registreert het clientstuurprogramma zich voor twee gebeurtenissen: EvtDriverDeviceAdd en EvtCleanupCallback. Het stuurprogramma specificeert een aanwijzer op naar zijn implementatie van EvtDriverDeviceAdd in de WDF_DRIVER_CONFIG structuur en de EvtCleanupCallback event callback in de WDF_OBJECT_ATTRIBUTES structuur.
Wanneer Windows klaar is om de DRIVER_OBJECT structuur vrij te geven en het stuurprogramma te verwijderen, rapporteert het framework die gebeurtenis aan het clientstuurprogramma door de EvtCleanupCallback-implementatie van het stuurprogramma aan te roepen. Het framework roept die callback aan net voordat het frameworkstuurprogrammaobject wordt verwijderd. Het clientstuurprogramma kan alle globale resources die het heeft toegewezen in zijn DriverEntry vrijgeven. In de sjablooncode stopt het clientstuurprogramma bijvoorbeeld WPP-tracering die geactiveerd is in DriverEntry.
In het volgende codevoorbeeld ziet u de EvtCleanupCallback-gebeurtenis callback-implementatie van het clientstuurprogramma.
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) );
}
Nadat het apparaat is herkend door de USB-stuurprogrammastack, maakt het busstuurprogramma een fysiek apparaatobject (PDO) voor het apparaat en koppelt het de PDO aan het apparaatknooppunt. Het apparaatknooppunt bevindt zich in een stapelvorming, waarbij de PDO zich onderaan bevindt. Elke stack moet één PDO hebben en kan filterapparaatobjecten (filter-DO's) en een functieapparaatobject (FDO) erboven hebben. Zie Apparaatknooppunten en apparaatstacks voor meer informatie.
In deze illustratie ziet u de apparaatsstack voor het sjabloonstuurprogramma, MyUSBDriver_.sys.
Let op de apparaatstack met de naam 'Mijn USB-apparaat'. De USB-stuurprogrammastack maakt de PDO voor de apparaatstapel. In het voorbeeld is de PDO gekoppeld aan Usbhub3.sys, een van de stuurprogramma's die zijn opgenomen in de USB-stuurprogrammastack. Als functiestuurprogramma voor het apparaat moet het clientstuurprogramma eerst de FDO voor het apparaat maken en deze vervolgens aan de bovenkant van de apparaatstack koppelen.
Voor een clientstuurprogramma op basis van KMDF voert het framework deze taken uit namens het clientstuurprogramma. Om de FDO voor het apparaat weer te geven, maakt het framework een frameworkapparaatobject. Het clientstuurprogramma kan echter bepaalde initialisatieparameters opgeven die door het framework worden gebruikt om het nieuwe object te configureren. Deze mogelijkheid wordt gegeven aan het clientstuurprogramma wanneer het framework de EvtDriverDeviceAdd-implementatie van het stuurprogramma aanroept. Nadat het object is gemaakt en de FDO is gekoppeld aan de bovenkant van de apparaatstack, biedt het framework het clientstuurprogramma een WDFDEVICE-ingang aan het frameworkapparaatobject. Door deze ingang te gebruiken, kan het clientstuurprogramma verschillende apparaatgerelateerde bewerkingen uitvoeren.
In het volgende codevoorbeeld ziet u de evtDriverDeviceAdd-gebeurtenis callback-implementatie van het clientstuurprogramma.
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;
}
Tijdens de uitvoering gebruikt de implementatie van EvtDriverDeviceAdd de PAGED_CODE macro om te controleren of de routine wordt aangeroepen in een geschikte omgeving voor wisselbare code. Zorg ervoor dat u de macro aanroept na het declareren van al uw variabelen; anders mislukt de compilatie omdat de gegenereerde bronbestanden .c-bestanden zijn en niet .cpp bestanden.
De evtDriverDeviceAdd-implementatie van het clientstuurprogramma roept de MyUSBDriver_CreateDevice helperfunctie aan om de vereiste taken uit te voeren.
In het volgende codevoorbeeld ziet u de helperfunctie MyUSBDriver_CreateDevice. MyUSBDriver_CreateDevice is gedefinieerd in 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 heeft twee parameters: een ingang voor het frameworkstuurprogrammaobject dat in de vorige aanroep naar DriverEntry is gemaakt en een aanwijzer naar een WDFDEVICE_INIT structuur. Het framework wijst de WDFDEVICE_INIT structuur toe en geeft deze een aanwijzer door, zodat het clientstuurprogramma de structuur kan vullen met initialisatieparameters voor het frameworkapparaatobject dat moet worden gemaakt.
In de implementatie EvtDriverDeviceAdd moet het clientstuurprogramma deze taken uitvoeren:
Roep de methode WdfDeviceCreate aan om een WDFDEVICE-ingang op te halen naar het nieuwe apparaatobject.
De methode WdfDeviceCreate zorgt ervoor dat het framework een frameworkapparaatobject voor de FDO maakt en deze aan de bovenkant van de apparaatstack koppelt. In de aanroep WdfDeviceCreate moet het clientstuurprogramma de volgende taken uitvoeren:
- Geef aanwijzers op naar de Plug and play-routines voor power callback (PnP) van het clientstuurprogramma in de door het framework opgegeven WDFDEVICE_INIT structuur. De routines worden eerst ingesteld in de WDF_PNPPOWER_EVENT_CALLBACKS structuur en vervolgens gekoppeld aan WDFDEVICE_INIT door de methode WdfDeviceInitSetPnpPowerEventCallbacks aan te roepen.
Windows-onderdelen, PnP- en powermanagers, verzenden apparaatgerelateerde aanvragen naar stuurprogramma's als reactie op wijzigingen in de PnP-status (zoals gestart, gestopt en verwijderd) en energiestatus (zoals werken of onderbreken). Voor KMDF-stuurprogramma's onderschept het framework deze aanvragen. Het clientstuurprogramma kan op de hoogte worden gesteld van de aanvragen door callback-routines met de naam PnP power event callbacks te registreren met het framework, met behulp van de WdfDeviceCreate-aanroep . Wanneer Windows-onderdelen aanvragen verzenden, handelt het framework deze af en roept het de bijbehorende callback voor PnP-energiegebeurtenissen aan, als het clientstuurprogramma staat geregistreerd.
Een van de callbackroutines voor PnP-stroomgebeurtenissen die het clientstuurprogramma moet implementeren, is EvtDevicePrepareHardware. Deze eventcallback wordt aangeroepen wanneer de PnP-manager het apparaat opstart. De implementatie voor EvtDevicePrepareHardware wordt besproken in de volgende sectie.
- Geef een aanwijzer op naar de contextstructuur van het stuurprogramma. De aanwijzer moet worden ingesteld in de WDF_OBJECT_ATTRIBUTES structuur die wordt geïnitialiseerd door de WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE macro aan te roepen.
Een apparaatcontext (ook wel apparaatextensie genoemd) is een gegevensstructuur (gedefinieerd door het clientstuurprogramma) voor het opslaan van informatie over een specifiek apparaatobject. Het clientstuurprogramma geeft een aanwijzer van zijn apparaatcontext door aan het framework. Het framework wijst een blok geheugen toe op basis van de grootte van de structuur en slaat een aanwijzer op die geheugenlocatie op in het frameworkapparaatobject. Het clientstuurprogramma kan de aanwijzer gebruiken om informatie te openen en op te slaan in leden van de apparaatcontext. Zie Contextruimte voor frameworkobjecten voor meer informatie over apparaatcontext.
Nadat de WdfDeviceCreate-aanroep is voltooid, ontvangt het clientstuurprogramma een ingang voor het nieuwe frameworkapparaatobject, waarin een aanwijzer wordt opgeslagen in het blok geheugen dat door het framework voor de apparaatcontext is toegewezen. Het clientstuurprogramma kan nu een aanwijzer naar de apparaatcontext krijgen door de WdfObjectGet_DEVICE_CONTEXT macro aan te roepen.
Registreer een apparaatinterface-GUID voor het clientstuurprogramma door de methode WdfDeviceCreateDeviceInterface aan te roepen. Toepassingen kunnen communiceren met het stuurprogramma met behulp van deze GUID. De GUID-constante wordt gedeclareerd in de header public.h.
Stel wachtrijen in voor I/O-overdrachten naar het apparaat. De sjablooncode definieert MyUSBDriver_QueueInitialize, een helperroutine voor het instellen van wachtrijen, die wordt besproken in de sectie Wachtrijbroncode .
Broncode van apparaat
Het apparaatobject vertegenwoordigt het exemplaar van het apparaat waarvoor het clientstuurprogramma in het geheugen wordt geladen. De volledige broncode voor het apparaatobject bevindt zich in Device.h en Device.c.
Device.h
Het headerbestand Device.h bevat public.h, dat algemene declaraties bevat die door alle bestanden in het project worden gebruikt.
Het volgende blok in Device.h declareert de apparaatcontext voor het clientstuurprogramma.
typedef struct _DEVICE_CONTEXT
{
WDFUSBDEVICE UsbDevice;
ULONG PrivateDeviceData; // just a placeholder
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT)
De DEVICE_CONTEXT structuur wordt gedefinieerd door het clientstuurprogramma en slaat informatie op over een frameworkapparaatobject. Deze wordt gedeclareerd in Device.h en bevat twee leden: een ingang voor het USB-doelapparaatobject van een framework (later besproken) en een tijdelijke aanduiding. Deze structuur wordt in latere oefeningen uitgebreid.
Device.h bevat ook de WDF_DECLARE_CONTEXT_TYPE macro, die een inlinefunctie genereert , WdfObjectGet_DEVICE_CONTEXT. Het clientstuurprogramma kan die functie aanroepen om een aanwijzer op te halen naar het geheugenblok van het frameworkapparaatobject.
De volgende coderegel declareert MyUSBDriver_CreateDevice, een helperfunctie waarmee een WDFUSBDEVICE-handle wordt verkregen voor het USB-doelobject apparaat.
NTSTATUS
MyUSBDriver_CreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
);
USBCreate verwijst naar een WDFDEVICE_INIT structuur als parameter. Dit is dezelfde aanwijzer die is doorgegeven door het framework toen de evtDriverDeviceAdd-implementatie van het clientstuurprogramma werd aangeroepen. In principe voert MyUSBDriver_CreateDevice de taken van EvtDriverDeviceAdd uit. De broncode voor evtDriverDeviceAdd-implementatie wordt in de vorige sectie besproken.
De volgende regel in Device.h declareert een declaratie van het functieroltype voor de callbackroutine EvtDevicePrepareHardware-gebeurtenis . De gebeurtenis-callback wordt geïmplementeerd door het clientstuurprogramma en voert taken uit zoals het configureren van het USB-apparaat.
EVT_WDF_DEVICE_PREPARE_HARDWARE MyUSBDriver_EvtDevicePrepareHardware;
Device.c
Het device.c-implementatiebestand bevat het volgende codeblok dat gebruikmaakt alloc_text van pragma om op te geven dat de implementatie van EvtDevicePrepareHardware van het stuurprogramma zich in wisselbaar geheugen bevindt.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, MyUSBDriver_CreateDevice)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDevicePrepareHardware)
#endif
In de implementatie voor EvtDevicePrepareHardware voert het clientstuurprogramma de USB-specifieke initialisatietaken uit. Deze taken omvatten het registreren van het clientstuurprogramma, het initialiseren van USB-specifieke I/O-doelobjecten en het selecteren van een USB-configuratie. In de volgende tabel ziet u de gespecialiseerde I/O-doelobjecten die door het framework worden geleverd. Zie USB I/O-doelen voor meer informatie.
| USB I/O-doelobject (ingang) | Verkrijg een handle door het aanroepen van... | Beschrijving |
|---|---|---|
| USB-doelapparaatobject (WDFUSBDEVICE) | WdfUsbTargetDeviceCreateWithParameters | Vertegenwoordigt een USB-apparaat en biedt methoden voor het ophalen van de apparaatdescriptor en het verzenden van besturingsaanvragen naar het apparaat. |
| USB-doelinterfaceobject (WDFUSBINTERFACE) | WdfUsbTargetDeviceGetInterface | Vertegenwoordigt een afzonderlijke interface en biedt methoden die een clientstuurprogramma kan aanroepen om een alternatieve instelling te selecteren en informatie over de instelling op te halen. |
| USB-doelkanaalobject (WDFUSBPIPE) | WdfUsbInterfaceGetConfiguredPipe | Vertegenwoordigt een afzonderlijke pijp voor een eindpunt dat is geconfigureerd in de huidige alternatieve instelling voor een interface. De USB-stuurprogrammastack selecteert elke interface in de geselecteerde configuratie en stelt een communicatiekanaal in voor elk eindpunt binnen de interface. In USB-terminologie wordt dat communicatiekanaal een pijp genoemd. |
In dit codevoorbeeld ziet u de implementatie voor 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;
}
Hier volgt een beter overzicht van de taken van het clientstuurprogramma, zoals geïmplementeerd door de sjablooncode:
Geeft de contractversie van het clientstuurprogramma op ter voorbereiding van registratie bij de onderliggende USB-stuurprogrammastack, die door Windows is geladen.
Windows kan de USB 3.0- of USB 2.0-stuurprogrammastack laden, afhankelijk van de hostcontroller waaraan het USB-apparaat is gekoppeld. De USB 3.0-stuurprogrammastack is nieuw in Windows 8 en ondersteunt verschillende nieuwe functies die zijn gedefinieerd door de USB 3.0-specificatie, zoals de mogelijkheid voor streams. De nieuwe stuurprogrammastack implementeert ook verschillende verbeteringen, zoals betere tracering en verwerking van USB-aanvraagblokken (URL's), die beschikbaar zijn via een nieuwe set URB-routines. Een clientstuurprogramma dat deze functies wil gebruiken of de nieuwe routines wil aanroepen, moet de USBD_CLIENT_CONTRACT_VERSION_602 contractversie opgeven. Een USBD_CLIENT_CONTRACT_VERSION_602 clientstuurprogramma moet voldoen aan een bepaalde set regels. Zie Best Practices: URL's gebruiken voor meer informatie over deze regels.
Als u de contractversie wilt opgeven, moet het clientstuurprogramma een WDF_USB_DEVICE_CREATE_CONFIG structuur initialiseren met de contractversie door de WDF_USB_DEVICE_CREATE_CONFIG_INIT macro aan te roepen.
Roept de WdfUsbTargetDeviceCreateWithParameters-methode aan. Voor de methode is een ingang vereist voor het frameworkapparaatobject dat het clientstuurprogramma eerder heeft verkregen door WdfDeviceCreate aan te roepen in de implementatie van EvtDriverDeviceAdd. De methode WdfUsbTargetDeviceCreateWithParameters :
- Registreert het clientstuurprogramma met de onderliggende USB-stuurprogrammastack.
- Haalt een WDFUSBDEVICE-verwijzing op voor het USB-doelapparaatobject dat door het framework is gecreëerd. De sjablooncode slaat de handle op in het USB-doelapparaat-object in zijn apparaatcontext. Door deze ingang te gebruiken, kan het clientstuurprogramma USB-specifieke informatie over het apparaat verkrijgen.
U moet WdfUsbTargetDeviceCreate aanroepen in plaats van WdfUsbTargetDeviceCreateWithParameters als:
Uw clientstuurprogramma roept de nieuwe set URB-routines die beschikbaar zijn met de Windows 8-versie van de WDK niet aan.
Als uw clientstuurprogramma WdfUsbTargetDeviceCreateWithParameters aanroept, gaat de USB-stuurprogrammastack ervan uit dat alle URB's worden toegewezen door WdfUsbTargetDeviceCreateUrb of WdfUsbTargetDeviceCreateIsochUrb aan te roepen. URB's die door deze methoden worden toegewezen, hebben ondoorzichtige URB-contextblokken die door de USB-driverstack worden gebruikt voor snellere verwerking. Als het clientstuurprogramma een URB gebruikt die niet door deze methoden wordt toegewezen, genereert het USB-stuurprogramma een bugcheck.
Zie Het toewijzen en bouwen van URB's voor meer informatie over URB-toewijzingen.
Uw clientstuurprogramma is niet van plan om te voldoen aan de set regels die worden beschreven in Aanbevolen procedures: URBs gebruiken.
Dergelijke stuurprogramma's hoeven geen clientcontractversie op te geven en moeten daarom stap 1 overslaan.
Selecteert een USB-configuratie.
In de sjablooncode selecteert het clientstuurprogramma de standaardconfiguratie op het USB-apparaat. De standaardconfiguratie bevat configuratie 0 van het apparaat en de alternatieve instelling 0 van elke interface binnen die configuratie.
Als u de standaardconfiguratie wilt selecteren, configureert het clientstuurprogramma de WDF_USB_DEVICE_SELECT_CONFIG_PARAMS structuur door de WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES-functie aan te roepen. De functie initialiseert het lid Type in WdfUsbTargetDeviceSelectConfigTypeMultiInterface om aan te geven dat als er meerdere interfaces beschikbaar zijn, een alternatieve instelling in elk van deze interfaces moet worden geselecteerd. Omdat de aanroep de standaardconfiguratie moet selecteren, geeft het clientstuurprogramma NULL op in de parameter SettingPairs en 0 in de parameter NumberInterfaces . Na voltooiing geeft het lid MultiInterface.NumberOfConfiguredInterfaces van WDF_USB_DEVICE_SELECT_CONFIG_PARAMS het aantal interfaces aan waarvoor alternatieve instelling 0 is geselecteerd. Andere leden worden niet gewijzigd.
Notitie Als het clientstuurprogramma andere instellingen dan de standaardinstelling wil selecteren, moet het stuurprogramma een matrix van WDF_USB_INTERFACE_SETTING_PAIR structuren maken. Elk element in de matrix specificeert het door het apparaat gedefinieerde interfacenummer en de index van de alternatieve instelling die moet worden geselecteerd. Deze informatie wordt opgeslagen in de configuratie- en interfacedescriptors van het apparaat die kunnen worden verkregen door de methode WdfUsbTargetDeviceRetrieveConfigDescriptor aan te roepen. Het clientstuurprogramma moet vervolgens WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES aanroepen en de WDF_USB_INTERFACE_SETTING_PAIR array doorgeven aan het framework.
Broncode van wachtrij
Het frameworkwachtrijobject vertegenwoordigt de I/O-wachtrij voor een specifiek frameworkapparaatobject. De volledige broncode voor het wachtrijobject bevindt zich in Queue.h en Queue.c.
Queue.h
Declareert een terugbelroutine voor gebeurtenissen voor de gebeurtenis die wordt gegenereerd door het wachtrijobject van het framework.
Het eerste blok in Queue.h declareert een wachtrijcontext.
typedef struct _QUEUE_CONTEXT {
ULONG PrivateDeviceData; // just a placeholder
} QUEUE_CONTEXT, *PQUEUE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext)
Net als bij een apparaatcontext is een wachtrijcontext een gegevensstructuur die door de client is gedefinieerd om informatie over een bepaalde wachtrij op te slaan.
De volgende regel code verklaart de functie "MyUSBDriver_QueueInitialize", de helperfunctie waarmee het frameworkwachtrijobject wordt gemaakt en geïnitialiseerd.
NTSTATUS
MyUSBDriver_QueueInitialize(
_In_ WDFDEVICE Device
);
In het volgende codevoorbeeld wordt een declaratie van het functieroltype gedeclareert voor de callbackroutine evtIoDeviceControl . De gebeurtenis-callback wordt geïmplementeerd door het clientstuurprogramma en wordt aangeroepen wanneer het framework een I/O-beheeraanvraag van een apparaat verwerkt.
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MyUSBDriver_EvtIoDeviceControl;
Queue.c
Het implementatiebestand Queue.c bevat het volgende codeblok dat gebruikmaakt van alloc_text pragma om op te geven dat de implementatie van het stuurprogramma van MyUSBDriver_QueueInitialize zich in pageerbaar geheugen bevindt.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, MyUSBDriver_QueueInitialize)
#endif
WDF biedt het frameworkwachtrijobject voor het verwerken van de aanvraagstroom naar het clientstuurprogramma. Het framework maakt een frameworkwachtrijobject wanneer het clientstuurprogramma de methode WdfIoQueueCreate aanroept. In die aanroep kan het clientstuurprogramma bepaalde configuratieopties opgeven voordat het framework wachtrijen maakt. Deze opties omvatten onder andere of de wachtrij wordt beheerd door stroombeheer, of het aanvragen van lengte nul toestaat, of dat het de standaardwachtrij voor het stuurprogramma is. Eén frameworkwachtrijobject kan verschillende typen aanvragen verwerken, zoals lezen, schrijven en apparaat I/O-besturing. Het clientstuurprogramma kan callbacks voor gebeurtenissen opgeven voor elk van deze aanvragen.
Het clientstuurprogramma moet ook het verzendtype opgeven. Het verzendtype van een wachtrijobject bepaalt hoe het framework aanvragen levert aan het clientstuurprogramma. Het leveringsmechanisme kan opeenvolgend, parallel of door een aangepast mechanisme worden gedefinieerd door het clientstuurprogramma. Voor een sequentiële wachtrij wordt een verzoek pas bezorgd als de clientdriver het vorige verzoek heeft voltooid. In de parallelle verzendmodus stuurt het framework de aanvragen door zodra ze binnenkomen bij I/O-manager. Dit betekent dat het clientstuurprogramma één aanvraag kan ontvangen tijdens het verwerken van een andere aanvraag. In het aangepaste mechanisme haalt de client handmatig de volgende aanvraag uit het framework queue-object wanneer het stuurprogramma klaar is om deze te verwerken.
Normaal gesproken moet de client driver wachtrijen instellen in de driver's EvtDriverDeviceAdd event callback. De sjablooncode biedt de helperroutine, MyUSBDriver_QueueInitialize, waarmee het frameworkwachtrijobject wordt geïnitialiseerd.
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;
}
Als u wachtrijen wilt instellen, voert het clientstuurprogramma deze taken uit:
- Hiermee geeft u de configuratieopties van de wachtrij in een WDF_IO_QUEUE_CONFIG structuur. De sjablooncode maakt gebruik van de WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE-functie om de structuur te initialiseren. De functie specificeert het wachtrijobject als het standaardwachtrijobject, wordt beheerd voor energiebeheer en ontvangt aanvragen parallel.
- Het voegt de evenement-callbacks van het clientstuurprogramma toe voor I/O-aanvragen voor de wachtrij. In de sjabloon specificeert het clientstuurprogramma een pointer naar zijn event callback voor een I/O-aanvraag voor apparaatbeheer.
- Roept WdfIoQueueCreate aan om een WDFQUEUE-ingang op te halen naar het frameworkwachtrijobject dat door het framework is gemaakt.
Hier ziet u hoe het wachtrijmechanisme werkt. Als u wilt communiceren met het USB-apparaat, opent een toepassing eerst een ingang naar het apparaat door de SetDixxx-routines en CreateHandle aan te roepen. Met deze ingang roept de toepassing de functie DeviceIoControl aan met een specifieke besturingscode. Afhankelijk van het type besturingscode kan de toepassing invoer- en uitvoerbuffers in die aanroep opgeven. De oproep wordt ten slotte ontvangen door de I/O Manager, die vervolgens een aanvraag (IRP) maakt en deze naar het clientstuurprogramma doorstuurt. Het framework onderschept de aanvraag, maakt een frameworkaanvraagobject en voegt dit toe aan het frameworkwachtrijobject. In dit geval activeert het framework de callback, omdat het cliëntstuurprogramma zijn gebeurtenisaanroepback voor de I/O-beheeraanvraag van het apparaat heeft geregistreerd. Omdat het wachtrijobject is gemaakt met de vlag WdfIoQueueDispatchParallel, wordt de callback aangeroepen zodra de aanvraag aan de wachtrij is toegevoegd.
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;
}
Wanneer het framework de gebeurtenisaanroep van het clientstuurprogramma aanroept, wordt er een ingang doorgegeven aan het frameworkaanvraagobject dat de aanvraag bevat (en de invoer- en uitvoerbuffers) die door de toepassing worden verzonden. Daarnaast wordt een referentie naar het framework-wachtrijobject verzonden dat de aanvraag bevat. In de gebeurtenis callback verwerkt het clientstuurprogramma de aanvraag indien nodig. De sjablooncode voltooit de aanvraag. Het clientstuurprogramma kan meer betrokken taken uitvoeren. Als een toepassing bijvoorbeeld bepaalde apparaatgegevens aanvraagt, kan het clientstuurprogramma in het geval van callback een USB-besturingsaanvraag maken en deze verzenden naar de STACK van het USB-stuurprogramma om de gevraagde apparaatgegevens op te halen. Aanvragen voor USB-besturingselementen worden besproken in USB-besturingsoverdracht.