Condividi tramite


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.

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 di 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 del 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 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 driverEntry e callback degli eventi 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 rimosso non appena il driver restituisce 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. Nel codice del modello, ad esempio, il driver client alloca le risorse necessarie per la traccia software WPP chiamando la macro WPP_INIT_TRACING .

  • Registra determinate routine di callback degli 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 strutturadi windows DRIVER_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 il driver è interessato. Quando si verificano determinati eventi, Windows invia una notifica al framework. Se il driver ha registrato un callback di evento 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 degli eventi, 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 di 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 dell'eliminazione dell'oggetto driver del framework. Il driver client può liberare tutte le risorse globali allocate nel driverEntry. Nel codice del modello, ad esempio, 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 stack, in cui il PDO si trova nella parte inferiore. Ogni stack deve avere un PDO e può avere oggetti dispositivo di filtro (DO di filtro) e un oggetto dispositivo funzione (FDO) sopra di esso. Per altre informazioni, vedere Nodi del dispositivo e stack di dispositivi.

Questa figura mostra lo stack di dispositivi per il driver modello MyUSBDriver_.sys.

stack di dispositivi per il driver modello.

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 il fdO per il dispositivo e quindi collegarlo alla parte superiore dello stack di dispositivi.

Per un driver client basato su KMDF, il framework esegue tali attività per conto del driver client. Per rappresentare l'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. Questa opportunità viene data al driver client quando il framework richiama l'implementazione EvtDriverDeviceAdd del driver. Dopo aver creato l'oggetto e aver collegato l'fdO 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 di eventi 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 di paging. 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 file cpp.

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 la 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 colleghi alla parte superiore dello stack di dispositivi. Nella chiamata WdfDeviceCreate il driver client deve eseguire queste attività:

    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, funzionamento o sospensione). 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 callback 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 dell'evento 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 viene illustrata nella sezione seguente.

    Un contesto del dispositivo (talvolta denominato estensione del dispositivo) è una struttura di dati (definita dal driver client) per archiviare informazioni su un oggetto dispositivo specifico. Il driver client passa un puntatore al relativo contesto del 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 del dispositivo. Per altre informazioni sui contesti dei dispositivi, vedere 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 del dispositivo. Il driver client può ora ottenere un puntatore al contesto del dispositivo chiamando la macro WdfObjectGet_DEVICE_CONTEXT .

  • Registrare un GUID dell'interfaccia del 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 modello definisce MyUSBDriver_QueueInitialize, una routine helper per la configurazione delle code, illustrata nella sezione Codice sorgente 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 è 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 del progetto.

Il blocco successivo in Device.h dichiara il contesto del 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 (illustrato in seguito) e un segnaposto. Questa struttura verrà espansa in 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 all'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 EvtDriverDevice del driver client. Fondamentalmente, MyUSBDriver_CreateDevice esegue le attività di EvtDriverDeviceAdd. Il codice sorgente per EvtDriverDeviceAdd implementazione viene illustrato nella sezione precedente.

La riga successiva in Device.h dichiara una dichiarazione di tipo di ruolo 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 di USB. Queste 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. Nella tabella seguente vengono illustrati gli oggetti di destinazione di I/O specializzati forniti dal framework. Per altre informazioni, vedere Destinazioni di 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 un'interfaccia singola 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 a 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'occhiata più attenta alle attività del driver client come implementato dal codice modello:

  1. Specifica la versione del contratto del driver client in preparazione per registrarsi 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 è nuovo in Windows 8 e supporta diverse nuove funzionalità definite dalla specifica USB 3.0, ad esempio la funzionalità flussi. Il nuovo stack di driver implementa anche diversi miglioramenti, ad esempio il rilevamento e l'elaborazione migliori dei blocchi di richiesta USB (URB), disponibili tramite un nuovo set di routine DI URB. 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 di 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 .

  2. Chiama il metodo WdfUsbTargetDeviceCreateWithParameters . Il metodo richiede un handle per l'oggetto del dispositivo framework ottenuto in precedenza dal driver client chiamando WdfDeviceCreate nell'implementazione del driver di EvtDriverDeviceAdd. 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 modello archivia l'handle all'oggetto dispositivo di destinazione USB nel contesto del dispositivo. Usando tale handle, il driver client può ottenere informazioni specifiche usb sul dispositivo.

    È necessario chiamare WdfUsbTargetDeviceCreate anziché WdfUsbTargetDeviceCreateWithParameters se:

    • Il driver client non chiama il nuovo set di routine ODBC disponibile con la versioneWindows 8 del WDK.

      Se il driver client chiama WdfUsbTargetDeviceCreateWithParameters, lo stack di driver USB presuppone che tutti gli URL vengano allocati chiamando WdfUsbTargetDeviceCreateUrb o WdfUsbTargetDeviceCreateIsochUrb. Gli URL allocati da tali metodi hanno blocchi di contesto DI ODBC opachi usati dallo stack di driver USB per l'elaborazione più veloce. Se il driver client usa un VALORE DI VALORE non allocato da tali metodi, il driver USB genera un controllo di bug.

      Per altre informazioni sulle allocazioni DI FRAMEWORK, vedere Allocazione e compilazione di URB.

    • Il driver client non intende rispettare il set di regole descritte in Procedure consigliate : Uso di URB.

    Tali driver non sono necessari per specificare una versione del contratto client e pertanto deve ignorare il passaggio 1.

  3. Seleziona una configurazione USB.

    Nel codice modello, il driver client seleziona la configurazione predefinita nel dispositivo USB. La configurazione predefinita include 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 WdfUsbTargetDeviceSelectConfigConfigTypeMultiInterface 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 coda è in Queue.h e Queue.c.

Queue.h

Dichiara una routine di callback evento per l'evento generato dall'oggetto coda 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 coda specifica.

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 di tipo di ruolo per la routine di callback degli eventi EvtIoDeviceControl . Il callback dell'evento viene implementato dal driver client e viene richiamato quando il framework elabora una richiesta di controllo 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 di framework quando il driver client chiama il metodo WdfIoQueueCreate . In tale chiamata, il driver client può specificare alcune 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 eventi per ognuna di queste richieste.

Il driver client deve anche specificare 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à di distribuzione 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 code nel callback dell'evento EvtDriverDevice del driver. Il codice 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à:

  1. Specifica le opzioni di configurazione della coda in una struttura WDF_IO_QUEUE_CONFIG . Il codice 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.
  2. 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 I/O del dispositivo.
  3. Chiama WdfIoQueueCreate per recuperare un handle WDFQUEUE all'oggetto coda del framework creato dal framework.

Ecco come funziona il meccanismo di coda. Per comunicare con il dispositivo USB, un'applicazione apre prima 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 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 evento, il driver client elabora la richiesta in base alle esigenze. Il codice 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 evento, 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 vengono illustrate in Trasferimento di controlli USB.