Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo spiega come:
- Scrivere un driver sorgente HID del Driver Framework Kernel-Mode (KMDF) che invia report di lettura HID a Windows.
- Carica il driver VHF come filtro di basso livello per il driver di origine HID nello stack di dispositivi HID virtuali.
Informazioni sulla scrittura di un driver di origine HID che segnala i dati HID al sistema operativo.
Un dispositivo di input HID, ad esempio una tastiera, un mouse, una penna, un tocco o un pulsante, invia vari report al sistema operativo in modo che possa comprendere lo scopo del dispositivo e intraprendere le azioni necessarie. I rapporti sono sotto forma di raccolte HID e usi HID . Il dispositivo invia tali report su vari trasporti, alcuni dei quali Windows supporta, ad esempio HID su I2C e HID su USB. In alcuni casi, Windows non supporta il trasporto o i report non vengono mappati direttamente all'hardware reale. Può trattarsi di un flusso di dati nel formato HID inviato da un altro componente software per l'hardware virtuale, ad esempio per pulsanti o sensori non GPIO. Si considerino, ad esempio, i dati dell'accelerometro di un telefono che si comportano come controller di gioco, inviati in modalità wireless a un PC. In un altro esempio, un computer può ricevere l'input remoto da un dispositivo Miracast usando il protocollo UIBC.
Nelle versioni precedenti di Windows, per supportare nuovi trasporti (hardware reale o software), è necessario scrivere un minidriver di trasporto HID e associarlo al driver della classe in-box fornito da Microsoft, Hidclass.sys. La coppia di driver classe/mini ha fornito le raccolte HID, come le raccolteTop-Level, ai driver superiori e alle applicazioni in modalità utente. In questo modello, la sfida era scrivere il minidriver, che può essere un'attività complessa.
A partire da Windows 10, il nuovo VHF (Virtual HID Framework) elimina la necessità di scrivere un minidriver di trasporto. È invece possibile scrivere un driver di origine HID usando KMDF o interfacce di programmazione WDM. Il framework è costituito da una libreria statica fornita da Microsoft che espone gli elementi di programmazione usati dal driver. Include anche un driver integrato fornito da Microsoft che enumera uno o più dispositivi figlio e procede alla compilazione e alla gestione di un albero HID virtuale.
Nota
In questa versione VHF supporta un driver di origine HID solo in modalità kernel.
Questo articolo descrive l'architettura del framework, l'albero dei dispositivi HID virtuale e gli scenari di configurazione.
Albero dei dispositivi HID virtuale
In questa immagine l'albero dei dispositivi mostra i driver e i relativi oggetti dispositivo associati.
Driver di origine HID (il tuo driver)
Il driver di origine HID si collega a Vhfkm.lib e include Vhf.h nel progetto di compilazione. Il driver può essere scritto usando Windows Driver Model (WDM) o Kernel-Mode Driver Framework (KMDF) che fa parte di Windows Driver Framework (WDF). Il driver può essere caricato come driver di filtro o driver di funzione nello stack di dispositivi.
Libreria statica VHF (vhfkm.lib)
La libreria statica è inclusa in Windows Driver Kit (WDK) per Windows 10. La libreria espone interfacce di programmazione come routine e funzioni di callback usate dal driver di origine HID. Quando il driver chiama una funzione, la libreria statica inoltra la richiesta al driver VHF che gestisce la richiesta.
Driver VHF (Vhf.sys)
Driver di sistema fornito da Microsoft. Questo driver deve essere caricato come driver di filtro inferiore sotto il driver nello stack di dispositivi di origine HID. Il driver VHF enumera dinamicamente i sottodispositivi e crea oggetti dispositivo fisico (PDO) per uno o più dispositivi HID specificati dal driver di origine HID. Implementa inoltre la funzionalità mini-driver del trasporto HID dei dispositivi secondari enumerati.
Insieme di driver della classe HID (Hidclass.sys, Mshidkmdf.sys)
La coppia Hidclass/Mshidkmdf enumera Top-Level Collections (TLC) in modo analogo a come enumera tali raccolte per un dispositivo HID reale. Un client HID può continuare a richiedere e consumare i TLC proprio come farebbe un dispositivo HID reale. Questa coppia di driver viene installata come driver di funzione nello stack di dispositivi.
Nota
In alcuni scenari, un client HID potrebbe dover identificare l'origine dei dati HID. Ad esempio, un sistema ha un sensore predefinito e riceve i dati da un sensore remoto dello stesso tipo. Il sistema potrebbe voler scegliere un sensore per essere più affidabile. Per distinguere i due sensori connessi al sistema, il client HID esegue una query per l'ID contenitore del TLC. In questo caso, un driver di origine HID può fornire l'ID contenitore, il quale è riportato da VHF come ID contenitore del dispositivo HID virtuale.
Client HID (applicazione)
Interroga e utilizza i TLC segnalati dallo stack di dispositivi HID.
Requisiti di intestazione e libreria
Questa procedura descrive come scrivere un driver di origine HID che segnala i pulsanti del visore VR al sistema operativo. In questo caso, il driver che implementa questo codice può essere un driver audio KMDF esistente e modificato, fungendo da sorgente HID per la segnalazione dei pulsanti delle cuffie utilizzando VHF.
Includi Vhf.h dal WDK.
Collegamento a vhfkm.lib incluso nel WDK.
Creare un descrittore di report HID che il dispositivo vuole segnalare al sistema operativo. In questo esempio, il descrittore di report HID descrive i pulsanti delle cuffie. Il report specifica un report di input HID, dimensione 8 bit (1 byte). I primi tre bit sono per i pulsanti centrale del visore, l'aumento del volume e la diminuzione del volume. I bit rimanenti sono inutilizzati.
UCHAR HeadSetReportDescriptor[] = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop Controls) 0x09, 0x0D, // USAGE (Portable Device Buttons) 0xA1, 0x01, // COLLECTION (Application) 0x85, 0x01, // REPORT_ID (1) 0x05, 0x09, // USAGE_PAGE (Button Page) 0x09, 0x01, // USAGE (Button 1 - HeadSet : middle button) 0x09, 0x02, // USAGE (Button 2 - HeadSet : volume up button) 0x09, 0x03, // USAGE (Button 3 - HeadSet : volume down button) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x03, // REPORT_COUNT (3) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x05, // REPORT_COUNT (5) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0xC0, // END_COLLECTION };
Creare un dispositivo HID virtuale
Inizializzare una struttura VHF_CONFIG chiamando la macro VHF_CONFIG_INIT e quindi chiamare il metodo VhfCreate . Il driver deve chiamare VhfCreate in PASSIVE_LEVEL dopo la chiamata WdfDeviceCreate , in genere, nella funzione di callback EvtDriverDeviceAdd del driver.
Nella chiamata VhfCreate il driver può specificare determinate opzioni di configurazione, ad esempio le operazioni che devono essere elaborate in modo asincrono o impostando le informazioni sul dispositivo (ID fornitore/prodotto).
Ad esempio, un'applicazione richiede un TLC. Quando la coppia di driver di classe HID riceve tale richiesta, la coppia determina il tipo di richiesta e crea una richiesta IOCTL HID Minidriver appropriata, inoltrandola a VHF. Dopo aver ottenuto la richiesta IOCTL, VHF può gestire la richiesta, affidarsi al driver di origine HID per elaborarla o completare la richiesta con STATUS_NOT_SUPPORTED.
VHF gestisce questi IOCTLs:
- IOCTL_HID_GET_STRING
- IOCTL_HID_GET_DEVICE_ATTRIBUTES
- IOCTL_HID_GET_DEVICE_DESCRIPTOR
- IOCTL_HID_GET_REPORT_DESCRIPTOR
Se la richiesta è GetFeature, SetFeature, WriteReport o GetInputReport e il driver di origine HID ha registrato una funzione di callback corrispondente, VHF richiama la funzione di callback. All'interno di tale funzione, il driver di origine HID può ottenere o impostare i dati HID per il dispositivo virtuale HID. Se il driver non registra un callback, VHF completa la richiesta con lo stato STATUS_NOT_SUPPORTED.
VHF richiama le funzioni di callback degli eventi implementate dal driver sorgente HID per questi IOCTL:
-
Se il driver vuole gestire i criteri di buffering durante l'invio di un buffer per ottenere il report di input HID, deve implementare EvtVhfReadyForNextReadReport e specificare un puntatore nel membro EvtVhfAsyncOperationGetInputReport . Per altre informazioni, vedere Inviare il report di input HID.
IOCTL_HID_GET_FEATURE o IOCTL_HID_SET_FEATURE
Se il driver vuole ottenere o impostare un report delle funzionalità HID in modo asincrono, il driver deve implementare la funzione EvtVhfAsyncOperation e specificare un puntatore alla funzione di implementazione get o set nel membro EvtVhfAsyncOperationOperationGetFeature o EvtVhfAsyncOperationSetFeature di VHF_CONFIG.
-
Se il driver vuole ottenere un report di input HID in modo asincrono, il driver deve implementare la funzione EvtVhfAsyncOperation e specificare un puntatore alla funzione nel membro EvtVhfAsyncOperationGetInputReport di VHF_CONFIG.
-
Se il driver vuole scrivere un report di input HID asincrono, il driver deve implementare la funzione EvtVhfAsyncOperation e specificare un puntatore alla funzione nel membro EvtVhfAsyncOperationWriteReport di VHF_CONFIG.
Per qualsiasi altro minidriver IOCTL HID, VHF completa la richiesta con STATUS_NOT_SUPPORTED.
Il dispositivo HID virtuale viene eliminato chiamando VhfDelete. Il callback EvtVhfCleanup è necessario se il driver ha allocato le risorse per il dispositivo HID virtuale. Il driver deve implementare la funzione EvtVhfCleanup e specificare un puntatore a tale funzione nel membro EvtVhfCleanup di VHF_CONFIG. EvtVhfCleanup viene richiamato prima del completamento della chiamata VhfDelete . Per altre informazioni, vedere Eliminare il dispositivo HID virtuale.
Nota
Al termine di un'operazione asincrona, il driver deve chiamare VhfAsyncOperationComplete per impostare i risultati dell'operazione. È possibile chiamare il metodo dal callback dell'evento o in un secondo momento dopo essere ritornati dal callback.
NTSTATUS
VhfSourceCreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
WDF_OBJECT_ATTRIBUTES deviceAttributes;
PDEVICE_CONTEXT deviceContext;
VHF_CONFIG vhfConfig;
WDFDEVICE device;
NTSTATUS status;
PAGED_CODE();
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
deviceAttributes.EvtCleanupCallback = VhfSourceDeviceCleanup;
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (NT_SUCCESS(status))
{
deviceContext = DeviceGetContext(device);
VHF_CONFIG_INIT(&vhfConfig,
WdfDeviceWdmGetDeviceObject(device),
sizeof(VhfHeadSetReportDescriptor),
VhfHeadSetReportDescriptor);
status = VhfCreate(&vhfConfig, &deviceContext->VhfHandle);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfCreate failed %!STATUS!", status);
goto Error;
}
status = VhfStart(deviceContext->VhfHandle);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfStart failed %!STATUS!", status);
goto Error;
}
}
Error:
return status;
}
Inviare il rapporto di input HID
Inviare il report di input HID chiamando VhfReadReportSubmit.
Generalmente, un dispositivo HID invia informazioni sulle modifiche di stato attraverso la trasmissione di report di input tramite interrupt. Ad esempio, il dispositivo vr potrebbe inviare un report quando lo stato di un pulsante cambia. In questo caso, la routine del servizio interrupt (ISR) del driver viene richiamata. In tale routine, il driver potrebbe pianificare una chiamata di procedura posticipata (DPC) che elabora il report di input e lo invia a VHF, che invia le informazioni al sistema operativo. Per impostazione predefinita, VHF memorizza nel buffer il report e il driver di origine HID può iniziare a inviare i report di input HID man mano che arrivano. Questo buffering elimina la necessità del driver di origine HID di implementare una sincronizzazione complessa.
Il driver sorgente HID può inviare report di input implementando i criteri di buffering per i report in sospeso. Per evitare il buffering duplicato, il driver di origine HID può implementare la funzione di callback EvtVhfReadyForNextReadReport e tenere traccia del fatto che VHF abbia richiamato questo callback. Se è stato richiamato in precedenza, il driver di origine HID può chiamare VhfReadReportSubmit per inviare un report. Deve attendere che EvtVhfReadyForNextReadReport venga richiamato prima di poter chiamare nuovamente VhfReadReportSubmit .
VOID
MY_SubmitReadReport(
PMY_CONTEXT Context,
BUTTON_TYPE ButtonType,
BUTTON_STATE ButtonState
)
{
PDEVICE_CONTEXT deviceContext = (PDEVICE_CONTEXT)(Context);
if (ButtonState == ButtonStateUp) {
deviceContext->VhfHidReport.ReportBuffer[0] &= ~(0x01 << ButtonType);
} else {
deviceContext->VhfHidReport.ReportBuffer[0] |= (0x01 << ButtonType);
}
status = VhfReadReportSubmit(deviceContext->VhfHandle, &deviceContext->VhfHidReport);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,"VhfReadReportSubmit failed %!STATUS!", status);
}
}
Eliminare il dispositivo HID virtuale
Eliminare il dispositivo HID virtuale chiamando VhfDelete.
VhfDelete può essere chiamato sincrono o in modo asincrono specificando il parametro Wait. Per una chiamata sincrona, il metodo deve essere chiamato in PASSIVE_LEVEL, ad esempio da EvtCleanupCallback dell'oggetto dispositivo. VhfDelete restituisce dopo l'eliminazione del dispositivo HID virtuale. Se il driver chiama VhfDelete in modo asincrono, restituisce immediatamente e VHF richiama EvtVhfCleanup al termine dell'operazione di eliminazione. Il metodo può essere chiamato al massimo DISPATCH_LEVEL. In questo caso, il driver doveva registrare e implementare una funzione di callback EvtVhfCleanup quando in precedenza si chiamava VhfCreate. Ecco la sequenza di eventi quando il driver di origine HID vuole eliminare il dispositivo HID virtuale:
- Il driver di origine HID interrompe l'avvio delle chiamate in VHF.
- L'origine HID chiama VhfDelete con Wait impostato su FALSE.
- VHF smette di richiamare le funzioni di callback implementate dal driver di origine HID.
- VHF inizia a segnalare il dispositivo come mancante al PnP Manager. A questo punto, la chiamata VhfDelete potrebbe restituire.
- Quando il dispositivo viene segnalato come dispositivo mancante, VHF richiama EvtVhfCleanup se il driver di origine HID ha registrato l'implementazione.
- Dopo che EvtVhfCleanup restituisce, VHF esegue il processo di pulizia.
VOID
VhfSourceDeviceCleanup(
_In_ WDFOBJECT DeviceObject
)
{
PDEVICE_CONTEXT deviceContext;
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");
deviceContext = DeviceGetContext(DeviceObject);
if (deviceContext->VhfHandle != WDF_NO_HANDLE)
{
VhfDelete(deviceContext->VhfHandle, TRUE);
}
}
Installare il driver di origine HID
Nel file INF che installa il driver di origine HID assicurarsi di dichiarare Vhf.sys come driver di filtro inferiore al driver di origine HID usando la direttiva AddReg.
[HIDVHF_Inst.NT.HW]
AddReg = HIDVHF_Inst.NT.AddReg
[HIDVHF_Inst.NT.AddReg]
HKR,,"LowerFilters",0x00010000,"vhf"