Informazioni sulla struttura del codice del driver client USB (KMDF)
In questo argomento verranno fornite informazioni sul codice sorgente per un driver client USB basato su KMDF. Gli esempi di codice vengono generati dal modello di driver in modalità utente USB incluso in Microsoft Visual Studio 2019.
Queste sezioni forniscono informazioni sul codice del modello.
- Codice sorgente del driver
- Codice sorgente del dispositivo
- Codice sorgente della coda
- Argomenti correlati
Per istruzioni sulla generazione del codice del modello KMDF, vedere Come scrivere il primo driver client USB (KMDF).
Codice sorgente del driver
L'oggetto driver rappresenta l'istanza del driver client dopo che Windows carica il driver in memoria. Il codice sorgente completo per l'oggetto driver si trova in Driver.h e Driver.c.
Driver.h
Prima di discutere i dettagli del codice del modello, esaminiamo alcune dichiarazioni nel file di intestazione (Driver.h) rilevanti per lo sviluppo di driver KMDF.
Driver.h, contiene questi file, inclusi in 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"
I file di intestazione Ntddk.h e Wdf.h sono sempre inclusi per lo sviluppo di driver KMDF. Il file di intestazione include varie dichiarazioni e definizioni di metodi e strutture che è necessario compilare un driver KMDF.
Usb.h e Usbdlib.h includono dichiarazioni e definizioni di strutture e routine richieste da un driver client per un dispositivo USB.
Wdfusb.h include dichiarazioni e definizioni di strutture e metodi necessari per comunicare con gli oggetti di destinazione I/O USB forniti dal framework.
Device.h, Queue.h e Trace.h non sono inclusi in WDK. Tali file di intestazione vengono generati dal modello e vengono descritti più avanti in questo argomento.
Il blocco successivo in Driver.h fornisce dichiarazioni di tipo di ruolo della funzione per la routine DriverEntry e EvtDriverDeviceAdd e EvtCleanupCallback routine di callback degli eventi. Tutte queste routine vengono implementate dal driver. I tipi di ruolo consentono a Static Driver Verifier (SDV) di analizzare il codice sorgente di un driver. Per altre informazioni sui tipi di ruolo, vedere Dichiarazione di funzioni tramite tipi di ruolo di funzione per i driver KMDF.
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD MyUSBDriver_EvtDeviceAdd;
EVT_WDF_OBJECT_CONTEXT_CLEANUP MyUSBDriver_EvtDriverContextCleanup;
Il file di implementazione Driver.c contiene il blocco di codice seguente che usa alloc_text
pragma per specificare se le routine di callback driverEntry e di evento sono in memoria impaginabile.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDeviceAdd)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDriverContextCleanup)
#endif
Si noti che DriverEntry è contrassegnato come INIT, mentre le routine di callback degli eventi sono contrassegnate come PAGE. La sezione INIT indica che il codice eseguibile per DriverEntry è pageable e scartato non appena il driver torna dal driverEntry. La sezione PAGE indica che il codice non deve rimanere sempre nella memoria fisica; può essere scritto nel file di pagina quando non è in uso. Per altre informazioni, vedere Blocco di codice o dati pageable.
Poco dopo il caricamento del driver, Windows alloca una struttura DRIVER_OBJECT che rappresenta il driver. Chiama quindi la routine del punto di ingresso del driver, DriverEntry e passa un puntatore alla struttura. Poiché Windows cerca la routine in base al nome, ogni driver deve implementare una routine denominata DriverEntry. La routine esegue le attività di inizializzazione del driver e specifica le routine di callback degli eventi del driver nel framework.
Nell'esempio di codice seguente viene illustrata la routine DriverEntry generata dal modello.
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;
}
La routine DriverEntry ha due parametri: un puntatore alla struttura DRIVER_OBJECT allocata da Windows e un percorso del Registro di sistema per il driver. Il parametro RegistryPath rappresenta il percorso specifico del driver nel Registro di sistema.
Nella routine DriverEntry il driver esegue queste attività:
Alloca le risorse globali necessarie durante la durata del driver. Ad esempio, nel codice modello, il driver client alloca le risorse necessarie per la traccia software WPP chiamando la macro WPP_INIT_TRACING.
Registra alcune routine di callback di eventi con il framework.
Per registrare i callback degli eventi, il driver client specifica innanzitutto i puntatori alle relative implementazioni delle routine EvtDriverXxx in determinate strutture WDF. Il driver chiama quindi il metodo WdfDriverCreate e fornisce tali strutture (descritte nel passaggio successivo).
Chiama il metodo WdfDriverCreate e recupera un handle per l'oggetto driver del framework.
Dopo che il driver client chiama WdfDriverCreate, il framework crea un oggetto driver framework per rappresentare il driver client. Al termine della chiamata, il driver client riceve un handle WDFDRIVER e può recuperare informazioni sul driver, ad esempio il percorso del Registro di sistema, le informazioni sulla versione e così via (vedere Guida di riferimento agli oggetti del driver WDF).
Si noti che l'oggetto driver framework è diverso dall'oggetto driver di Windows descritto da DRIVER_OBJECT. In qualsiasi momento, il driver client può ottenere un puntatore alla struttura di windowsDRIVER_OBJECT usando l'handle WDFDRIVER e chiamando il metodo WdfGetDriver.
Dopo la chiamata WdfDriverCreate , il framework collabora con il driver client per comunicare con Windows. Il framework funge da livello di astrazione tra Windows e il driver e gestisce la maggior parte delle attività complesse del driver. Il driver client viene registrato con il framework per gli eventi a cui è interessato il driver. Quando si verificano determinati eventi, Windows invia una notifica al framework. Se il driver ha registrato un callback di eventi per un determinato evento, il framework invia una notifica al driver richiamando il callback dell'evento registrato. In questo modo, il driver ha la possibilità di gestire l'evento, se necessario. Se il driver non ha registrato il callback dell'evento, il framework procede con la gestione predefinita dell'evento.
Uno dei callback degli eventi che il driver deve registrare è EvtDriverDeviceAdd. Il framework richiama l'implementazione EvtDriverDeviceAdd del driver quando il framework è pronto per creare un oggetto dispositivo. In Windows, un oggetto dispositivo è una rappresentazione logica della funzione del dispositivo fisico per cui viene caricato il driver client (descritto più avanti in questo argomento).
Altri callback di eventi che il driver può registrare sono EvtDriverUnload, EvtCleanupCallback e EvtDestroyCallback.
Nel codice del modello il driver client esegue la registrazione per due eventi: EvtDriverDeviceAdd e EvtCleanupCallback. Il driver specifica un puntatore all'implementazione di EvtDriverDeviceAdd nella struttura WDF_DRIVER_CONFIG e il callback dell'evento EvtCleanupCallback nella struttura WDF_OBJECT_ATTRIBUTES.
Quando Windows è pronto per rilasciare la struttura DRIVER_OBJECT e scaricare il driver, il framework segnala tale evento al driver client richiamando l'implementazione evtCleanupCallback del driver. Il framework richiama il callback subito prima che elimini l'oggetto driver del framework. Il driver client può liberare tutte le risorse globali allocate nel driverEntry. Ad esempio, nel codice modello, il driver client arresta la traccia WPP attivata in DriverEntry.
L'esempio di codice seguente mostra l'implementazione del callback dell'evento EvtCleanupCallback del driver client.
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) );
}
Dopo che il dispositivo viene riconosciuto dallo stack di driver USB, il driver del bus crea un oggetto dispositivo fisico (PDO) per il dispositivo e associa il PDO al nodo del dispositivo. Il nodo del dispositivo si trova in una formazione dello stack, in cui il PDO si trova nella parte inferiore. Ogni stack deve avere un PDO e può avere oggetti dispositivo di filtro (DO filtro) e un oggetto dispositivo funzione (FDO) sopra di esso. Per altre informazioni, vedere Nodi del dispositivo e stack di dispositivi.
Questa illustrazione mostra lo stack di dispositivi per il driver modello MyUSBDriver_.sys.
Si noti lo stack di dispositivi denominato "My USB Device". Lo stack di driver USB crea il PDO per lo stack di dispositivi. Nell'esempio il PDO è associato a Usbhub3.sys, che è uno dei driver inclusi nello stack di driver USB. Come driver di funzione per il dispositivo, il driver client deve prima creare l'fdO per il dispositivo e quindi collegarlo all'inizio dello stack di dispositivi.
Per un driver client basato su KMDF, il framework esegue tali attività per conto del driver client. Per rappresentare l'oggetto FDO per il dispositivo, il framework crea un oggetto dispositivo framework. Il driver client può tuttavia specificare determinati parametri di inizializzazione usati dal framework per configurare il nuovo oggetto. Tale opportunità viene data al driver client quando il framework richiama l'implementazione EvtDriverDeviceAdd del driver. Dopo la creazione dell'oggetto e l'fdO viene collegato all'inizio dello stack di dispositivi, il framework fornisce al driver client un handle WDFDEVICE per l'oggetto dispositivo framework. Usando questo handle, il driver client può eseguire varie operazioni correlate al dispositivo.
L'esempio di codice seguente mostra l'implementazione del callback dell'evento EvtDriverDeviceAdd del driver client.
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;
}
Durante l'esecuzione, l'implementazione di EvtDriverDeviceAdd usa la macro PAGED_CODE per verificare che la routine venga chiamata in un ambiente appropriato per il codice pageable. Assicurarsi di chiamare la macro dopo aver dichiarato tutte le variabili; in caso contrario, la compilazione non riesce perché i file di origine generati sono file con estensione c e non .cpp file.
L'implementazione evtDriverDeviceAdd del driver client chiama la funzione helper MyUSBDriver_CreateDevice per eseguire le attività necessarie.
Nell'esempio di codice seguente viene illustrata la funzione helper MyUSBDriver_CreateDevice. MyUSBDriver_CreateDevice è definito 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 ha due parametri: un handle per l'oggetto driver framework creato nella chiamata precedente a DriverEntry e un puntatore a una struttura WDFDEVICE_INIT. Il framework alloca la struttura WDFDEVICE_INIT e lo passa a un puntatore in modo che il driver client possa popolare la struttura con parametri di inizializzazione per l'oggetto dispositivo framework da creare.
Nell'implementazione EvtDriverDeviceAdd il driver client deve eseguire queste attività:
Chiamare il metodo WdfDeviceCreate per recuperare un handle WDFDEVICE al nuovo oggetto dispositivo.
Il metodo WdfDeviceCreate fa sì che il framework crei un oggetto dispositivo framework per l'oggetto FDO e lo allega alla parte superiore dello stack di dispositivi. Nella chiamata WdfDeviceCreate il driver client deve eseguire queste attività:
- Specificare i puntatori alle routine di callback di alimentazione Plug and play (PnP) del driver client nella struttura di WDFDEVICE_INIT specificata dal framework. Le routine vengono prima impostate nella struttura WDF_PNPPOWER_EVENT_CALLBACKS e quindi associate a WDFDEVICE_INIT chiamando il metodo WdfDeviceInitSetPnpPowerEventCallbacks.
Componenti di Windows, PnP e power manager, inviano richieste correlate ai dispositivi ai driver in risposta alle modifiche apportate allo stato PnP (ad esempio avviato, arrestato e rimosso) e allo stato di alimentazione (ad esempio funzionante o sospeso). Per i driver basati su KMDF, il framework intercetta tali richieste. Il driver client può ricevere notifiche sulle richieste registrando routine di callback denominate callback di eventi di alimentazione PnP con il framework, usando la chiamata WdfDeviceCreate . Quando i componenti di Windows inviano richieste, il framework li gestisce e chiama il callback dell'evento di alimentazione PnP corrispondente, se il driver client è registrato.
Una delle routine di callback degli eventi di alimentazione PnP che il driver client deve implementare è EvtDevicePrepareHardware. Il callback dell'evento viene richiamato quando il gestore PnP avvia il dispositivo. L'implementazione per EvtDevicePrepareHardware è illustrata nella sezione seguente.
- Specificare un puntatore alla struttura del contesto di dispositivo del driver. Il puntatore deve essere impostato nella struttura WDF_OBJECT_ATTRIBUTES inizializzata chiamando la macro WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE.
Un contesto di dispositivo (talvolta denominato estensione del dispositivo) è una struttura di dati (definita dal driver client) per l'archiviazione di informazioni su un oggetto dispositivo specifico. Il driver client passa un puntatore al contesto di dispositivo al framework. Il framework alloca un blocco di memoria in base alle dimensioni della struttura e archivia un puntatore a tale posizione di memoria nell'oggetto dispositivo framework. Il driver client può usare il puntatore per accedere e archiviare le informazioni nei membri del contesto di dispositivo. Per altre informazioni sui contesti di dispositivo, vedere Framework Object Context Space.For more information about device contexts, see Framework Object Context Space.
Al termine della chiamata WdfDeviceCreate , il driver client riceve un handle per il nuovo oggetto dispositivo framework, che archivia un puntatore al blocco di memoria allocato dal framework per il contesto di dispositivo. Il driver client può ora ottenere un puntatore al contesto di dispositivo chiamando la macro WdfObjectGet_DEVICE_CONTEXT .
Registrare un GUID dell'interfaccia dispositivo per il driver client chiamando il metodo WdfDeviceCreateDeviceInterface. Le applicazioni possono comunicare con il driver usando questo GUID. La costante GUID viene dichiarata nell'intestazione public.h.
Configurare le code per i trasferimenti di I/O nel dispositivo. Il codice del modello definisce MyUSBDriver_QueueInitialize, una routine helper per la configurazione delle code, descritta nella sezione Codice sorgente della coda.
Codice sorgente del dispositivo
L'oggetto dispositivo rappresenta l'istanza del dispositivo per cui il driver client viene caricato in memoria. Il codice sorgente completo per l'oggetto dispositivo si trova in Device.h e Device.c.
Device.h
Il file di intestazione Device.h include public.h, che contiene dichiarazioni comuni usate da tutti i file nel progetto.
Il blocco successivo in Device.h dichiara il contesto di dispositivo per il driver client.
typedef struct _DEVICE_CONTEXT
{
WDFUSBDEVICE UsbDevice;
ULONG PrivateDeviceData; // just a placeholder
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT)
La struttura DEVICE_CONTEXT è definita dal driver client e archivia informazioni su un oggetto dispositivo framework. Viene dichiarato in Device.h e contiene due membri: un handle per l'oggetto dispositivo di destinazione USB di un framework (descritto più avanti) e un segnaposto. Questa struttura verrà espansa negli esercizi successivi.
Device.h include anche la macro WDF_DECLARE_CONTEXT_TYPE , che genera una funzione inline, WdfObjectGet_DEVICE_CONTEXT. Il driver client può chiamare tale funzione per recuperare un puntatore al blocco di memoria dall'oggetto dispositivo framework.
La riga di codice seguente dichiara MyUSBDriver_CreateDevice, una funzione helper che recupera un handle WDFUSBDEVICE nell'oggetto dispositivo di destinazione USB.
NTSTATUS
MyUSBDriver_CreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
);
USBCreate accetta un puntatore a una struttura WDFDEVICE_INIT come parametro. Si tratta dello stesso puntatore passato dal framework quando ha richiamato l'implementazione EvtDriverDeviceAdd del driver client. Fondamentalmente, MyUSBDriver_CreateDevice esegue le attività di EvtDriverDeviceAdd. Il codice sorgente per EvtDriverDeviceAdd implementazione è illustrato nella sezione precedente.
La riga successiva in Device.h dichiara una dichiarazione del tipo di ruolo funzione per la routine di callback dell'evento EvtDevicePrepareHardware . Il callback dell'evento viene implementato dal driver client ed esegue attività come la configurazione del dispositivo USB.
EVT_WDF_DEVICE_PREPARE_HARDWARE MyUSBDriver_EvtDevicePrepareHardware;
Device.c
Il file di implementazione Device.c contiene il blocco di codice seguente che usa alloc_text
pragma per specificare che l'implementazione del driver di EvtDevicePrepareHardware è in memoria impaginabile.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, MyUSBDriver_CreateDevice)
#pragma alloc_text (PAGE, MyUSBDriver_EvtDevicePrepareHardware)
#endif
Nell'implementazione di EvtDevicePrepareHardware, il driver client esegue le attività di inizializzazione specifiche dell'USB. Tali attività includono la registrazione del driver client, l'inizializzazione di oggetti di destinazione di I/O specifici di USB e la selezione di una configurazione USB. La tabella seguente illustra gli oggetti di destinazione di I/O specializzati forniti dal framework. Per altre informazioni, vedere Destinazioni I/O USB.
Oggetto di destinazione I/O USB (handle) | Ottenere un handle chiamando... | Descrizione |
---|---|---|
Oggetto dispositivo di destinazione USB (WDFUSBDEVICE) | WdfUsbTargetDeviceCreateWithParameters | Rappresenta un dispositivo USB e fornisce metodi per recuperare il descrittore del dispositivo e inviare richieste di controllo al dispositivo. |
Oggetto interfaccia di destinazione USB (WDFUSBINTERFACE) | WdfUsbTargetDeviceGetInterface | Rappresenta una singola interfaccia e fornisce metodi che un driver client può chiamare per selezionare un'impostazione alternativa e recuperare informazioni sull'impostazione. |
Oggetto pipe di destinazione USB (WDFUSBPIPE) | WdfUsbInterfaceGetConfiguredPipe | Rappresenta una singola pipe per un endpoint configurato nell'impostazione alternativa corrente per un'interfaccia. Lo stack di driver USB seleziona ogni interfaccia nella configurazione selezionata e configura un canale di comunicazione per ogni endpoint all'interno dell'interfaccia. Nella terminologia USB, tale canale di comunicazione è noto come pipe. |
Questo esempio di codice mostra l'implementazione per 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;
}
Ecco un'analisi più attenta delle attività del driver client implementate dal codice del modello:
Specifica la versione del contratto del driver client in preparazione alla registrazione con lo stack di driver USB sottostante, caricato da Windows.
Windows può caricare lo stack di driver USB 3.0 o USB 2.0, a seconda del controller host a cui è collegato il dispositivo USB. Lo stack di driver USB 3.0 è una novità di Windows 8 e supporta diverse nuove funzionalità definite dalla specifica USB 3.0, ad esempio la funzionalità dei flussi. Il nuovo stack di driver implementa anche diversi miglioramenti, ad esempio il rilevamento e l'elaborazione migliori dei blocchi di richieste USB (URB), disponibili tramite un nuovo set di routine DI FRAMEWORK. Un driver client che intende usare tali funzionalità o chiamare le nuove routine deve specificare la versione del contratto USBD_CLIENT_CONTRACT_VERSION_602. Un driver client USBD_CLIENT_CONTRACT_VERSION_602 deve rispettare un determinato set di regole. Per altre informazioni su queste regole, vedere Procedure consigliate: uso degli URL.
Per specificare la versione del contratto, il driver client deve inizializzare una struttura WDF_USB_DEVICE_CREATE_CONFIG con la versione del contratto chiamando la macro WDF_USB_DEVICE_CREATE_CONFIG_INIT.
Chiama il metodo WdfUsbTargetDeviceCreateWithParameters. Il metodo richiede un handle per l'oggetto dispositivo framework ottenuto in precedenza dal driver client chiamando WdfDeviceCreate nell'implementazione del driver di EvtDriverDeviceAdd. Il metodo WdfUsbTargetDeviceCreateWithParameters :
- Registra il driver client con lo stack di driver USB sottostante.
- Recupera un handle WDFUSBDEVICE nell'oggetto dispositivo di destinazione USB creato dal framework. Il codice del modello archivia l'handle per l'oggetto dispositivo di destinazione USB nel contesto del dispositivo. Usando tale handle, il driver client può ottenere informazioni specifiche dell'USB sul dispositivo.
È necessario chiamare WdfUsbTargetDeviceCreate anziché WdfUsbTargetDeviceCreateWithParameters se:
Il driver client non chiama il nuovo set di routine ODBC disponibili con la versioneWindows 8 di WDK.
Se il driver client chiama WdfUsbTargetDeviceCreateWithParameters, lo stack di driver USB presuppone che tutti gli URB vengano allocati chiamando WdfUsbTargetDeviceCreateUrb o WdfUsbTargetDeviceCreateIsochUrb. Gli URI allocati da tali metodi hanno blocchi di contesto ODBC opachi usati dallo stack di driver USB per un'elaborazione più rapida. Se il driver client utilizza un valore DISA CHE non viene allocato da tali metodi, il driver USB genera un controllo di bug.
Per altre informazioni sulle allocazioni DI FRAMEWORK, vedere Allocazione e compilazione di URI.
Il driver client non intende rispettare il set di regole descritte in Procedure consigliate: uso degli URI.
Tali driver non sono necessari per specificare una versione del contratto client e pertanto deve ignorare il passaggio 1.
Seleziona una configurazione USB.
Nel codice del modello il driver client seleziona la configurazione predefinita nel dispositivo USB. La configurazione predefinita include La configurazione 0 del dispositivo e l'impostazione alternativa 0 di ogni interfaccia all'interno di tale configurazione.
Per selezionare la configurazione predefinita, il driver client configura la struttura WDF_USB_DEVICE_SELECT_CONFIG_PARAMS chiamando la funzione WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES. La funzione inizializza il membro Type in WdfUsbTargetDeviceSelectConfigTypeMultiInterface per indicare che, se sono disponibili più interfacce, è necessario selezionare un'impostazione alternativa in ognuna di queste interfacce. Poiché la chiamata deve selezionare la configurazione predefinita, il driver client specifica NULL nel parametro SettingPairs e 0 nel parametro NumberInterfaces . Al termine, il membro MultiInterface.NumberOfConfiguredInterfaces di WDF_USB_DEVICE_SELECT_CONFIG_PARAMS indica il numero di interfacce per cui è stata selezionata l'impostazione alternativa 0. Gli altri membri non vengono modificati.
Nota Se il driver client vuole selezionare impostazioni alternative diverse dall'impostazione predefinita, il driver deve creare una matrice di strutture WDF_USB_INTERFACE_SETTING_PAIR . Ogni elemento nella matrice specifica il numero di interfaccia definito dal dispositivo e l'indice dell'impostazione alternativa da selezionare. Queste informazioni vengono archiviate nei descrittori di configurazione e interfaccia del dispositivo che possono essere ottenuti chiamando il metodo WdfUsbTargetDeviceRetrieveConfigDescriptor. Il driver client deve quindi chiamare WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_MULTIPLE_INTERFACES e passare la matrice di WDF_USB_INTERFACE_SETTING_PAIR al framework.
Codice sorgente della coda
L'oggetto coda del framework rappresenta la coda di I/O per un oggetto dispositivo framework specifico. Il codice sorgente completo per l'oggetto queue si trova in Queue.h e Queue.c.
Queue.h
Dichiara una routine di callback di eventi per l'evento generato dall'oggetto queue del framework.
Il primo blocco in Queue.h dichiara un contesto di coda.
typedef struct _QUEUE_CONTEXT {
ULONG PrivateDeviceData; // just a placeholder
} QUEUE_CONTEXT, *PQUEUE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext)
Analogamente a un contesto di dispositivo, un contesto di coda è una struttura di dati definita dal client per archiviare informazioni su una determinata coda.
La riga di codice successiva dichiara MyUSBDriver_QueueInitialize funzione, la funzione helper che crea e inizializza l'oggetto coda del framework.
NTSTATUS
MyUSBDriver_QueueInitialize(
_In_ WDFDEVICE Device
);
L'esempio di codice successivo dichiara una dichiarazione del tipo di ruolo della funzione per la routine di callback dell'evento EvtIoDeviceControl . Il callback dell'evento viene implementato dal driver client e viene richiamato quando il framework elabora una richiesta di controllo di I/O del dispositivo.
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MyUSBDriver_EvtIoDeviceControl;
Queue.c
Il file di implementazione Queue.c contiene il blocco di codice seguente che usa alloc_text
pragma per specificare che l'implementazione del driver di MyUSBDriver_QueueInitialize è in memoria impaginabile.
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, MyUSBDriver_QueueInitialize)
#endif
WDF fornisce l'oggetto coda del framework per gestire il flusso di richiesta al driver client. Il framework crea un oggetto coda del framework quando il driver client chiama il metodo WdfIoQueueCreate. In tale chiamata, il driver client può specificare determinate opzioni di configurazione prima che il framework crei code. Queste opzioni includono se la coda è gestita dall'alimentazione, consente richieste di lunghezza zero o è la coda predefinita per il driver. Un singolo oggetto coda del framework può gestire diversi tipi di richieste, ad esempio il controllo di lettura, scrittura e I/O del dispositivo. Il driver client può specificare callback di eventi per ognuna di queste richieste.
Il driver client deve specificare anche il tipo di invio. Il tipo di invio di un oggetto coda determina il modo in cui il framework recapita le richieste al driver client. Il meccanismo di recapito può essere sequenziale, in parallelo o da un meccanismo personalizzato definito dal driver client. Per una coda sequenziale, una richiesta non viene recapitata finché il driver client non completa la richiesta precedente. In modalità dispatch parallela, il framework inoltra le richieste non appena arrivano da Gestione I/O. Ciò significa che il driver client può ricevere una richiesta durante l'elaborazione di un'altra. Nel meccanismo personalizzato, il client esegue manualmente il pull della richiesta successiva dall'oggetto coda del framework quando il driver è pronto per elaborarlo.
In genere, il driver client deve configurare le code nel callback dell'evento EvtDriverDeviceAdd del driver. Il codice del modello fornisce la routine helper, MyUSBDriver_QueueInitialize, che inizializza l'oggetto coda del framework.
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;
}
Per configurare le code, il driver client esegue queste attività:
- Specifica le opzioni di configurazione della coda in una struttura WDF_IO_QUEUE_CONFIG . Il codice del modello usa la funzione WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE per inizializzare la struttura. La funzione specifica l'oggetto queue come oggetto coda predefinito, è gestito dall'alimentazione e riceve le richieste in parallelo.
- Aggiunge i callback degli eventi del driver client per le richieste di I/O per la coda. Nel modello, il driver client specifica un puntatore al callback dell'evento per una richiesta di controllo di I/O del dispositivo.
- Chiama WdfIoQueueCreate per recuperare un handle WDFQUEUE all'oggetto coda del framework creato dal framework.
Ecco come funziona il meccanismo di accodamento. Per comunicare con il dispositivo USB, un'applicazione apre innanzitutto un handle al dispositivo chiamando le routine SetDixxx e CreateHandle. Usando questo handle, l'applicazione chiama la funzione DeviceIoControl con un codice di controllo specifico. A seconda del tipo di codice di controllo, l'applicazione può specificare buffer di input e output in tale chiamata. La chiamata viene infine ricevuta da I/O Manager, che crea quindi una richiesta (IRP) e la inoltra al driver client. Il framework intercetta la richiesta, crea un oggetto richiesta framework e lo aggiunge all'oggetto coda del framework. In questo caso, poiché il driver client ha registrato il callback dell'evento per la richiesta di controllo di I/O del dispositivo, il framework richiama il callback. Inoltre, poiché l'oggetto queue è stato creato con il flag WdfIoQueueDispatchParallel, il callback viene richiamato non appena la richiesta viene aggiunta alla coda.
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;
}
Quando il framework richiama il callback dell'evento del driver client, passa un handle all'oggetto richiesta framework che contiene la richiesta (e i relativi buffer di input e output) inviati dall'applicazione. Inoltre, invia un handle all'oggetto coda del framework che contiene la richiesta. Nel callback degli eventi, il driver client elabora la richiesta in base alle esigenze. Il codice del modello completa semplicemente la richiesta. Il driver client può eseguire attività più coinvolte. Ad esempio, se un'applicazione richiede determinate informazioni sul dispositivo, nel callback degli eventi, il driver client può creare una richiesta di controllo USB e inviarla allo stack di driver USB per recuperare le informazioni sul dispositivo richieste. Le richieste di controllo USB sono descritte in Trasferimento di controlli USB.