Come registrare un dispositivo composito
Questo articolo descrive come un driver di un dispositivo a più funzioni USB, denominato driver composito, può registrare e annullare la registrazione del dispositivo composito con lo stack di driver USB sottostante. Windows carica il driver fornito da Microsoft, Usbccgp.sys Come driver composito predefinito. La procedura descritta in questo articolo si applica a un driver composito basato su Windows Driver Model (WDM) personalizzato che sostituisce Usbccgp.sys.
Un dispositivo USB (Universal Serial Bus) può fornire più funzioni attive contemporaneamente. Tali dispositivi multi-funzione sono noti anche come dispositivi compositi. Ad esempio, un dispositivo composito potrebbe definire una funzione per la funzionalità della tastiera e un'altra funzione per il mouse. Il driver composito enumera le funzioni del dispositivo. Il driver composito può gestire tali funzioni in un modello monolitico o creare oggetti dispositivo fisico (PDO) per ognuna delle funzioni. I driver di funzione USB, ad esempio il driver della tastiera e il driver del mouse, gestiscono i rispettivi PDO singoli.
La specifica USB 3.0 definisce la funzionalità di sospensione della funzione e riattivazione remota che consente alle singole funzioni di entrare ed uscire da stati a basso consumo senza influire sullo stato di alimentazione di altre funzioni o dell'intero dispositivo. Per altre informazioni sulla funzionalità, vedere How to Implement Function Suspend in a Composite Driver.For more information about the feature, see How to Implement Function Suspend in a Composite Driver.
Per usare la funzionalità, il driver composito deve registrare il dispositivo con lo stack di driver USB sottostante. Poiché la funzionalità si applica ai dispositivi USB 3.0, il driver composito deve assicurarsi che lo stack sottostante supporti la versione USBD_INTERFACE_VERSION_602. Tramite la richiesta di registrazione, il driver composito:
- Informa lo stack di driver USB sottostante che il driver è responsabile dell'invio di una richiesta a una funzione per la riattivazione remota. Lo stack di driver USB elabora la richiesta di riattivazione remota, che invia le richieste di protocollo necessarie al dispositivo.
- Ottiene un elenco di handle di funzione (uno per funzione) assegnato dallo stack di driver USB. Il driver composito può quindi usare un handle di funzione nella richiesta di riattivazione remota della funzione associata all'handle del driver.
In genere un driver composito invia la richiesta di registrazione nel componente AddDevice del driver o nella routine del dispositivo di avvio per gestire IRP_MN_START_DEVICE. Il driver composito rilascia quindi le risorse allocate per la registrazione nelle routine di scaricamento del driver, ad esempio stop-device (IRP_MN_STOP_DEVICE) o remove-device routine (IRP_MN_REMOVE_DEVICE).
Prerequisiti
Prima di inviare la richiesta di registrazione, assicurarsi che:
- Il numero di funzioni nel dispositivo è disponibile. Tale numero può essere derivato dai descrittori recuperati dalla richiesta get-configuration.
- Hai ottenuto un handle USBD in una chiamata precedente a USBD_CreateHandle.
- Lo stack di driver USB sottostante supporta i dispositivi USB 3.0. A tale scopo, chiamare USBD_IsInterfaceVersionSupported e passare USBD_INTERFACE_VERSION_602 come versione da controllare.
Per un esempio di codice, vedere How to Implement Function Suspend in a Composite Driver.For a code example, see How to Implement Function Suspend in a Composite Driver.
Registrare un dispositivo composito
La procedura seguente descrive come compilare e inviare una richiesta di registrazione per associare un driver composito allo stack di driver USB.
Allocare una struttura COMPOSITE_DEVICE_CAPABILITIES e inizializzarla chiamando la macro COMPOSITE_DEVICE_CAPABILITIES_INIT.
Impostare il membro CapabilityFunctionSuspend di COMPOSITE_DEVICE_CAPABILITIES su 1.
Allocare una struttura REGISTER_COMPOSITE_DEVICE e inizializzare la struttura chiamando la routine USBD_BuildRegisterCompositeDevice. Nella chiamata specificare l'handle USBD, la struttura COMPOSITE_DEVICE_CAPABILITIES inizializzata e il numero di funzioni.
Allocare un pacchetto di richiesta I/O chiamando IoAllocateIrp e ottenere un puntatore alla prima posizione dello stack di IRP (IO_STACK_LOCATION) chiamando IoGetNextIrpStackLocation.
Allocare memoria per un buffer di dimensioni sufficienti per contenere una matrice di handle di funzione (USBD_FUNCTION_HANDLE). Il numero di elementi nella matrice deve essere il numero di PDO.
Compilare la richiesta impostando i membri seguenti del IO_STACK_LOCATION:
- Specificare il tipo di richiesta impostando Parameters.DeviceIoControl.IoControlCode su IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE.
- Specificare il parametro di input impostando Parameters.Others.Argument1 sull'indirizzo della struttura REGISTER_COMPOSITE_DEVICE inizializzata.
- Specificare il parametro di output impostando AssociatedIrp.SystemBuffer sul buffer allocato nel passaggio 5.
Chiamare IoCallDriver per inviare la richiesta passando l'IRP alla posizione successiva dello stack.
Al termine, controllare la matrice di handle di funzione restituiti dallo stack di driver USB. È possibile archiviare la matrice nel contesto di dispositivo del driver per un uso futuro.
Nell'esempio di codice seguente viene illustrato come compilare e inviare una richiesta di registrazione. L'esempio presuppone che il driver composito archivia il numero di funzioni ottenuto in precedenza e l'handle USBD nel contesto di dispositivo del driver.
VOID RegisterCompositeDriver(PPARENT_FDO_EXT parentFdoExt)
{
PIRP irp;
REGISTER_COMPOSITE_DRIVER registerInfo;
COMPOSITE_DRIVER_CAPABILITIES capabilities;
NTSTATUS status;
PVOID buffer;
ULONG bufSize;
PIO_STACK_LOCATION nextSp;
buffer = NULL;
COMPOSITE_DRIVER_CAPABILITIES_INIT(&capabilities);
capabilities.CapabilityFunctionSuspend = 1;
USBD_BuildRegisterCompositeDriver(parentFdoExt->usbdHandle,
capabilities,
parentFdoExt->numFunctions,
®isterInfo);
irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);
if (irp == NULL)
{
//IoAllocateIrp failed.
status = STATUS_INSUFFICIENT_RESOURCES;
goto ExitRegisterCompositeDriver;
}
nextSp = IoGetNextIrpStackLocation(irp);
bufSize = parentFdoExt->numFunctions * sizeof(USBD_FUNCTION_HANDLE);
buffer = ExAllocatePoolWithTag (NonPagedPool, bufSize, POOL_TAG);
if (buffer == NULL)
{
// Memory alloc for function-handles failed.
status = STATUS_INSUFFICIENT_RESOURCES;
goto ExitRegisterCompositeDriver;
}
nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DRIVER;
//Set the input buffer in Argument1
nextSp->Parameters.Others.Argument1 = ®isterInfo;
//Set the output buffer in SystemBuffer field for USBD_FUNCTION_HANDLE.
irp->AssociatedIrp.SystemBuffer = buffer;
// Pass the IRP down to the next device object in the stack. Not shown.
status = CallNextDriverSync(parentFdoExt, irp, FALSE);
if (!NT_SUCCESS(status))
{
//Failed to register the composite driver.
goto ExitRegisterCompositeDriver;
}
parentFdoExt->compositeDriverRegistered = TRUE;
parentFdoExt->functionHandleArray = (PUSBD_FUNCTION_HANDLE) buffer;
End:
if (!NT_SUCCESS(status))
{
if (buffer != NULL)
{
ExFreePoolWithTag (buffer, POOL_TAG);
buffer = NULL;
}
}
if (irp != NULL)
{
IoFreeIrp(irp);
irp = NULL;
}
return;
}
Annullare la registrazione di un dispositivo composito
- Allocare un IRP chiamando IoAllocateIrp e ottenere un puntatore alla prima posizione dello stack di IRP (IO_STACK_LOCATION) chiamando IoGetNextIrpStackLocation.
- Compilare la richiesta impostando il membro Parameters.DeviceIoControl.IoControlCode di IO_STACK_LOCATION su IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE.
- Chiamare IoCallDriver per inviare la richiesta passando l'IRP alla posizione successiva dello stack.
La richiesta di IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE viene inviata una volta dal driver composito nel contesto della routine remove-device. Lo scopo della richiesta è rimuovere l'associazione tra lo stack di driver USB e il driver composito e la relativa funzione enumerata. La richiesta pulisce anche tutte le risorse create per mantenere tale associazione e tutti gli handle di funzione restituiti nella richiesta di registrazione precedente.
L'esempio di codice seguente illustra come compilare e inviare una richiesta per annullare la registrazione del dispositivo composito. Nell'esempio si presuppone che il driver composito sia stato registrato in precedenza tramite una richiesta di registrazione, come descritto in precedenza in questo articolo.
VOID UnregisterCompositeDriver(
PPARENT_FDO_EXT parentFdoExt )
{
PIRP irp;
PIO_STACK_LOCATION nextSp;
NTSTATUS status;
PAGED_CODE();
irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);
if (irp == NULL)
{
//IoAllocateIrp failed.
status = STATUS_INSUFFICIENT_RESOURCES;
return;
}
nextSp = IoGetNextIrpStackLocation(irp);
nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DRIVER;
// Pass the IRP down to the next device object in the stack. Not shown.
status = CallNextDriverSync(parentFdoExt, irp, FALSE);
if (NT_SUCCESS(status))
{
parentFdoExt->compositeDriverRegistered = FALSE;
}
IoFreeIrp(irp);
return;
}