Come scrivere il primo driver client USB (UMDF)

In questo articolo si userà il modello Driver in modalità utente, USB (UMDF V2) fornito con Microsoft Visual Studio 2022 per scrivere un driver basato su UMDF (User Mode Driver Framework). Dopo aver compilato e installato il driver client, si visualizzerà il driver client in Gestione dispositivi e si visualizzerà l'output del driver in un debugger.

UMDF (definito framework in questo articolo) è basato sul modello a oggetti del componente (COM). Per impostazione predefinita, ogni oggetto framework deve implementare IUnknown e i relativi metodi , QueryInterface, AddRef e Release. I metodi AddRef e Release gestiscono la durata dell'oggetto, quindi il driver client non deve mantenere il conteggio dei riferimenti. Il metodo QueryInterface consente al driver client di ottenere puntatori di interfaccia ad altri oggetti framework nel modello a oggetti WDF (Windows Driver Framework). Gli oggetti Framework eseguono attività di driver complesse e interagiscono con Windows. Alcuni oggetti framework espongono interfacce che consentono a un driver client di interagire con il framework.

Un driver client basato su UMDF viene implementato come server COM (DLL) in-process e C++ è il linguaggio preferito per la scrittura di un driver client per un dispositivo USB. In genere, il driver client implementa diverse interfacce esposte dal framework. Questo articolo fa riferimento a una classe definita dal driver client che implementa le interfacce del framework come classe di callback. Dopo aver creato un'istanza di queste classi, gli oggetti di callback risultanti vengono associati a oggetti framework specifici. Questa partnership offre al driver client la possibilità di rispondere a eventi correlati al dispositivo o al sistema segnalati dal framework. Ogni volta che Windows invia una notifica al framework su determinati eventi, il framework richiama il callback del driver client, se disponibile. In caso contrario, il framework procede con l'elaborazione predefinita dell'evento. Il codice modello definisce classi di callback driver, dispositivo e coda.

Per una spiegazione del codice sorgente generato dal modello, vedere Informazioni sul codice del modello UMDF per il driver client USB.

Prima di iniziare

Per lo sviluppo, il debug e l'installazione di un driver in modalità utente, sono necessari due computer:

  • Un computer host che esegue Windows 10 o una versione successiva del sistema operativo Windows. Il computer host è l'ambiente di sviluppo, in cui si scrive e si esegue il debug del driver.
  • Un computer di destinazione che esegue la versione del sistema operativo in cui si vuole testare il driver, ad esempio Windows 11 versione 22H2. Il computer di destinazione ha il driver in modalità utente di cui si vuole eseguire il debug e uno dei debugger.

In alcuni casi, in cui i computer host e di destinazione eseguono la stessa versione di Windows, è possibile avere un solo computer che esegue Windows 10 o una versione successiva di Windows. Questo articolo presuppone che si usino due computer per lo sviluppo, il debug e l'installazione del driver in modalità utente.

Prima di iniziare, assicurarsi di soddisfare i requisiti seguenti:

Requisiti software

  • Il computer host ha Visual Studio 2022.

  • Il computer host ha la versione più recente di Windows Driver Kit (WDK) per Windows 11, versione 22H2.

    Il kit include intestazioni, librerie, strumenti, documentazione e strumenti di debug necessari per sviluppare, compilare ed eseguire il debug di un driver client USB. È possibile ottenere la versione più recente di WDK da Come ottenere il wdk.

  • Il computer host ha la versione più recente degli strumenti di debug per Windows. È possibile ottenere la versione più recente da WDK oppure scaricare e installare gli strumenti di debug per Windows.

  • Se si usano due computer, è necessario configurare l'host e i computer di destinazione per il debug in modalità utente. Per altre informazioni, vedere Configurazione User-Mode debug in Visual Studio.

Requisiti hardware

Ottenere un dispositivo USB per il quale si scriverà il driver client. Nella maggior parte dei casi, viene fornito un dispositivo USB e la specifica hardware. La specifica descrive le funzionalità del dispositivo e i comandi del fornitore supportati. Usare la specifica per determinare la funzionalità del driver USB e le decisioni di progettazione correlate.

Se non si ha familiarità con lo sviluppo di driver USB, usare il kit di apprendimento OSR USB FX2 per studiare esempi USB inclusi in WDK. Contiene il dispositivo USB FX2 e tutte le specifiche hardware necessarie per implementare un driver client.

Passaggio 1: Generare il codice del driver

Per informazioni dettagliate sulla scrittura di codice driver UMDF, vedere Scrittura di un driver UMDF basato su un modello.

Per il codice specifico per USB, selezionare le opzioni seguenti in Visual Studio 2022

  1. Nella casella di ricerca nella parte superiore della finestra di dialogo Nuovo progetto digitare USB.
  2. Nel riquadro centrale selezionare Driver modalità utente, USB (UMDF V2).
  3. Selezionare Avanti.
  4. Immettere un nome di progetto, scegliere un percorso di salvataggio e selezionare Crea.

Gli screenshot seguenti illustrano la finestra di dialogo Nuovo progetto per il modello driver di User-Mode USB .

Screenshot delle opzioni di creazione del progetto di Visual Studio.

Screenshot della schermata di configurazione del progetto di creazione di Visual Studio.

Questo articolo presuppone che il nome del progetto sia MyUSBDriver_UMDF_. Contiene i file seguenti:

File Descrizione
Driver.h; Driver.c Contiene l'implementazione del punto di ingresso del modulo driver. Funzionalità e callback correlati a DriverEntry e WDFDRIVER.
Device.h; Device.c Funzionalità e callback correlati a WDFDEVICE e WDFUSBDEVICE.
Queue.h; Queue.c Funzionalità e callback correlati a WDFQUEUE.
Trace.h Definisce il GUID dell'interfaccia del dispositivo. Dichiara inoltre le funzioni di traccia e le macro.
<>Nome progetto.inf File INF necessario per installare il driver client nel computer di destinazione.

Passaggio 2: Aggiungere informazioni sul dispositivo

Prima di compilare il driver, è necessario aggiungere informazioni sul dispositivo, in particolare l'ID hardware. Per specificare l'ID hardware:

  1. Nella finestra Esplora soluzioni fare clic con il pulsante destro del mouse su MyUSBDriver_UMDF_ e scegliere Proprietà.
  2. Nella finestra MyUSBDriver_UMDF_ Pagine delle proprietà passare a Proprietà > di configurazione Installazione driver>, come illustrato di seguito. Screenshot della finestra delle pagine delle proprietà di Visual Studio 2022.
  3. Selezionare Rimuovi versioni precedenti del driver prima della distribuzione.
  4. In Target Device Name (Nome dispositivo di destinazione) selezionare il nome del computer configurato per il test e il debug.
  5. Selezionare Aggiornamento driver ID hardware e immettere l'ID hardware per il driver. In questo esercizio l'ID hardware è Root\MyUSBDriver_UMDF_. Selezionare OK.

Nota

In questo esercizio l'ID hardware non identifica un componente hardware reale. Identifica un dispositivo immaginario che verrà assegnato un posto nell'albero dei dispositivi come figlio del nodo radice. Per l'hardware reale, non selezionare Aggiornamento driver ID hardware. Selezionare invece Installa e verifica. È possibile visualizzare l'ID hardware nel file di informazioni (INF) del driver. Nella finestra Esplora soluzioni passare a MyUSBDriver_UMDF_ > File driver e fare doppio clic su MyUSBDriver_UMDF_.inf. L'ID hardware è in [Standard.NT$ARCH$].

Tutti i driver client USB basati su UMDF richiedono due driver forniti da Microsoft, il reflector e WinUSB.

  • Reflector: se il driver viene caricato correttamente, il riflettore viene caricato come driver principale nello stack in modalità kernel. Il riflettore deve essere il driver principale nello stack della modalità kernel. Per soddisfare questo requisito, il file INF del modello specifica il reflectionor come servizio e WinUSB come driver di filtro inferiore nel file INF:

    [MyDevice_Install.NT.Services]
    AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall  ; flag 0x2 sets this as the service for the device
    AddService=WinUsb,0x000001f8,WinUsb_ServiceInstall  ; this service is installed because its a filter.
    
  • WinUSB: il pacchetto di installazione deve contenere coinstallatori per Winusb.sys perché per il driver client, WinUSB è il gateway allo stack di driver USB in modalità kernel. Un altro componente che viene caricato è una DLL in modalità utente, denominata WinUsb.dll, nel processo host del driver client (Wudfhost.exe). Winusb.dll espone Funzioni WinUSB che semplificano il processo di comunicazione tra il driver client e WinUSB.

Passaggio 3: Compilare il codice del driver client USB

Per compilare il driver:

  1. Aprire il progetto o la soluzione driver in Visual Studio 2022.
  2. Fare clic con il pulsante destro del mouse sulla soluzione nel Esplora soluzioni e selezionare Configuration Manager.
  3. Nella Configuration Manager selezionare la configurazione della soluzione attiva (ad esempio Debug o Versione) e la piattaforma di soluzioni attiva (ad esempio x64) che corrispondono al tipo di compilazione a cui si è interessati.
  4. Verificare che il GUID dell'interfaccia del dispositivo sia accurato in tutto il progetto.
    • Il GUID dell'interfaccia del dispositivo è definito in Trace.h e viene fatto riferimento da MyUSBDriverUMDFCreateDevice in Device.c. Quando si crea il progetto con il nome MyUSBDriver_UMDF_, Visual Studio 2022 definisce il GUID dell'interfaccia del dispositivo con il nome GUID_DEVINTERFACE_MyUSBDriver_UMDF_ ma chiama WdfDeviceCreateDeviceInterface con il parametro &GUID_DEVINTERFACE_MyUSBDriverUMDFnon corretto . Sostituire il parametro non corretto con il nome definito in Trace.h per assicurarsi che il driver venga compilato correttamente.
  5. Dal menu Compila selezionare Compila soluzione.

Per altre informazioni, vedere Compilazione di un driver.

Passaggio 4: Configurare un computer per il test e il debug

Per testare e eseguire il debug di un driver, eseguire il debugger nel computer host e il driver nel computer di destinazione. Finora, è stato usato Visual Studio nel computer host per compilare un driver. È quindi necessario configurare un computer di destinazione. Per configurare un computer di destinazione, seguire le istruzioni riportate in Effettuare il provisioning di un computer per la distribuzione e il test dei driver.

Passaggio 5: Abilitare la traccia per il debug del kernel

Il codice modello contiene diversi messaggi di traccia (TraceEvents) che consentono di tenere traccia delle chiamate di funzione. Tutte le funzioni nel codice sorgente contengono messaggi di traccia che contrassegnano la voce e l'uscita di una routine. Per gli errori, il messaggio di traccia contiene il codice di errore e una stringa significativa. Poiché la traccia WPP è abilitata per il progetto driver, il file di simboli PDB creato durante il processo di compilazione contiene istruzioni di formattazione dei messaggi di traccia. Se si configurano gli host e i computer di destinazione per la traccia WPP, il driver può inviare messaggi di traccia a un file o al debugger.

Per configurare il computer host per la traccia WPP

  1. Creare file TMF (Trace Message Format) estraendo le istruzioni di formattazione dei messaggi di traccia dal file di simboli PDB.

    È possibile usare Tracepdb.exe per creare file TMF. Lo strumento si trova nella <cartella di installazionedi Windows Kits\10\bin\<architecture> della cartella> WDK. Il comando seguente crea file TMF per il progetto driver.

    tracepdb -f <PDBFiles> -p <TMFDirectory>
    

    L'opzione -f specifica il percorso e il nome del file di simboli PDB. L'opzione -p specifica il percorso per i file TMF creati da Tracepdb. Per altre informazioni, vedere Comandi tracepdb.

    Nel progetto sono presenti tre file nel percorso specificato, uno per ogni file di codice C. Vengono assegnati nomi di file GUID.

  2. Nel debugger digitare i comandi seguenti:

    .load Wmitrace
    .chain
    !wmitrace.searchpath + <TMF file location>
    

Questi comandi:

  • Caricare l'estensione Wmitrace.dll.
  • Verifica che l'estensione del debugger venga caricata.
  • Aggiunge il percorso dei file TMF al percorso di ricerca dell'estensione del debugger.

L'output è simile al seguente:

Trace Format search path is: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE;c:\drivers\tmf

Per configurare il computer di destinazione per la traccia WPP

  1. Assicurarsi di avere lo strumento Tracelog nel computer di destinazione. Lo strumento si trova nella <install_folder> cartellaWindows Kits\10\Tools\<arch> del WDK. Per altre informazioni, vedere Sintassi dei comandi tracelog.

  2. Aprire una finestra dei comandi ed eseguire come amministratore.

  3. Digitare il comando seguente:

    tracelog -start MyTrace -guid \#c918ee71-68c7-4140-8f7d-c907abbcb05d -flag 0xFFFF -level 7-rt -kd
    

Il comando avvia una sessione di traccia denominata MyTrace.

L'argomento guid specifica il GUID del provider di traccia, ovvero il driver client. È possibile ottenere il GUID da Trace.h nel progetto Visual Studio 2022. Come altra opzione, è possibile digitare il comando seguente e specificare il GUID in un file con estensione guid. Il file contiene il GUID in formato trattino:

tracelog -start MyTrace -guid c:\\drivers\\Provider.guid -flag 0xFFFF -level 7-rt -kd

È possibile arrestare la sessione di traccia digitando il comando seguente:

tracelog -stop MyTrace

Passaggio 6: Distribuire il driver nel computer di destinazione

  1. Nella finestra Esplora soluzioni fare clic con il pulsante destro del mouse sul nome del progetto (MyUSBDriver_UMDF_) e scegliere Proprietà.
  2. Nel riquadro sinistro passare a Configuration Properties > Driver Install Deployment (Installazione > driver di configurazione).
  3. Per Nome dispositivo di destinazione specificare il nome del computer di destinazione.
  4. Selezionare Installa/Reinstalla e Verifica.
  5. Selezionare OK.
  6. Nel menu Debug scegliere Avvia debug oppure premere F5 sulla tastiera.

Nota

Non specificare l'ID hardware del dispositivo in Aggiornamento driver ID hardware. L'ID hardware deve essere specificato solo nel file INF (Information) del driver.

Passaggio 7: Visualizzare il driver in Gestione dispositivi

  1. Immettere il comando seguente per aprire Gestione dispositivi.

    devmgmt
    
  2. Verificare che Gestione dispositivi mostra il nodo seguente.

    Dispositivo USB

    MyUSBDriver_UMDF_Device

Passaggio 8: Visualizzare l'output nel debugger

Verificare che i messaggi di traccia vengano visualizzati nella finestra Immediata debugger nel computer host.

L'output avrà un aspetto analogo al seguente:

[0]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::OnPrepareHardware Entry
[0]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::OnPrepareHardware Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::CreateInstanceAndInitialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Initialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Initialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::CreateInstanceAndInitialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Configure Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::CreateInstanceAndInitialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::Initialize Entry
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::Initialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyIoQueue::CreateInstanceAndInitialize Exit
[1]0744.05F0::00/00/0000-00:00:00.000 [MyUSBDriver_UMDF_]CMyDevice::Configure Exit

Commenti

Esaminiamo il modo in cui il framework e il driver client interagiscono per interagire con Windows e gestire le richieste inviate al dispositivo USB. Questa figura mostra i moduli caricati nel sistema per un driver client USB basato su UMDF.

Diagramma dell'architettura del driver client in modalità utente.

Lo scopo di ogni modulo è descritto qui:

  • Applicazione: un processo in modalità utente che genera richieste di I/O per comunicare con il dispositivo USB.
  • Gestione I/O: un componente Windows che crea pacchetti di richieste di I/O (IRP) per rappresentare le richieste di applicazione ricevute e li inoltra all'inizio dello stack di dispositivi in modalità kernel per il dispositivo di destinazione.
  • Reflectionor: un driver in modalità kernel fornito da Microsoft installato nella parte superiore dello stack di dispositivi in modalità kernel (WUDFRd.sys). Il reflectionor reindirizza gli indirizzi IP ricevuti dal gestore di I/O al processo host del driver client. Al momento della ricezione della richiesta, il framework e il driver client gestiscono la richiesta.
  • Processo host: processo in cui viene eseguito il driver in modalità utente (Wudfhost.exe). Ospita anche il framework e il dispatcher di I/O.
  • Driver client: driver di funzione in modalità utente per il dispositivo USB.
  • UMDF: il modulo framework che gestisce la maggior parte delle interazioni con Windows per conto del driver client. Espone le interfacce del driver di dispositivo in modalità utente (DDI) che il driver client può usare per eseguire attività di driver comuni.
  • Dispatcher: meccanismo eseguito nel processo host; determina come inoltrare una richiesta alla modalità kernel dopo che è stata elaborata dai driver in modalità utente e ha raggiunto la parte inferiore dello stack in modalità utente. Nell'illustrazione il dispatcher inoltra la richiesta alla DLL in modalità utente, Winusb.dll.
  • Winusb.dll : una DLL in modalità utente fornita da Microsoft che espone Funzioni WinUSB che semplificano il processo di comunicazione tra il driver client e WinUSB (Winusb.sys, caricato in modalità kernel).
  • Winusb.sys : un driver fornito da Microsoft richiesto da tutti i driver client UMDF per i dispositivi USB. Il driver deve essere installato sotto il reflectionor e funge da gateway per lo stack di driver USB nella modalità kernel. Per altre informazioni, vedere WinUSB.
  • Stack di driver USB: un set di driver, fornito da Microsoft, che gestisce la comunicazione a livello di protocollo con il dispositivo USB. Per altre informazioni, vedere Driver sul lato host USB in Windows.

Ogni volta che un'applicazione effettua una richiesta per lo stack di driver USB, windows I/O manager invia la richiesta al reflectionor, che lo indirizza al driver client in modalità utente. Il driver client gestisce la richiesta chiamando metodi UMDF specifici, che chiamano internamente Funzioni WinUSB per inviare la richiesta a WinUSB. Al momento della ricezione della richiesta, WinUSB elabora la richiesta o la inoltra allo stack di driver USB.