Partilhar via


Como registrar um dispositivo composto

Este tópico descreve como um driver de um dispositivo usb de várias funções, chamado de driver composto, pode registrar e cancelar o registro do dispositivo composto com a pilha de driver USB subjacente. O driver fornecido pela Microsoft, Usbccgp.sys, é o driver composto padrão que é carregado pelo Windows. O procedimento neste tópico se aplica a um driver composto personalizado baseado em WDM (Modelo de Driver do Windows) que substitui Usbccgp.sys.

Um dispositivo USB (Barramento Serial Universal) pode fornecer várias funções que estão ativas simultaneamente. Esses dispositivos de várias funções também são conhecidos como dispositivos compostos. Por exemplo, um dispositivo composto pode definir uma função para a funcionalidade do teclado e outra função para o mouse. As funções do dispositivo são enumeradas pelo driver composto. O driver composto pode gerenciar essas funções em si em um modelo monolítico ou criar PDOs (objetos de dispositivo físico) para cada uma das funções. Esses PDOs individuais são gerenciados por seus respectivos drivers de função USB, o driver de teclado e o driver do mouse.

A especificação USB 3.0 define o recurso de suspensão de função e ativação remota que permite que funções individuais entrem e saiam de estados de baixa energia sem afetar o estado de energia de outras funções ou de todo o dispositivo. Para obter mais informações sobre o recurso, consulte How to Implement Function Suspend in a Composite Driver.

Para usar o recurso, o driver composto precisa registrar o dispositivo com a pilha de driver USB subjacente. Como o recurso se aplica a dispositivos USB 3.0, o driver composto deve garantir que a pilha subjacente dê suporte à versão USBD_INTERFACE_VERSION_602. Por meio da solicitação de registro, o driver composto:

  • Informa à pilha de driver USB subjacente que o driver é responsável por enviar uma solicitação para armar uma função para ativação remota. A solicitação de ativação remota é processada pela pilha de driver USB, que envia as solicitações de protocolo necessárias para o dispositivo.
  • Obtém uma lista de identificadores de função (um por função) atribuídos pela pilha de driver USB. Em seguida, o driver composto pode usar um identificador de função na solicitação do driver para ativação remota da função associada ao identificador.

Normalmente, um driver composto envia a solicitação de registro no AddDevice do driver ou na rotina do dispositivo inicial para lidar com IRP_MN_START_DEVICE. Consequentemente, o driver composto libera os recursos alocados para o registro nas rotinas de descarregamento do driver, como stop-device (IRP_MN_STOP_DEVICE) ou rotina remove-device (IRP_MN_REMOVE_DEVICE).

Pré-requisitos

Antes de enviar a solicitação de registro, verifique se:

  • Você tem o número de funções no dispositivo. Esse número pode ser derivado dos descritores recuperados pela solicitação get-configuration.
  • Você obteve um identificador USBD em uma chamada anterior para USBD_CreateHandle.
  • A pilha de driver USB subjacente dá suporte a dispositivos USB 3.0. Para fazer isso, chame USBD_IsInterfaceVersionSupported e passe USBD_INTERFACE_VERSION_602 como a versão para marcar.

Para obter um exemplo de código, consulte Como implementar a suspensão de função em um driver composto.

Instruções

Registrar um dispositivo composto

O procedimento a seguir descreve como você deve criar e enviar uma solicitação de registro para associar um driver composto à pilha de driver USB.

  1. Aloque uma estrutura COMPOSITE_DEVICE_CAPABILITIES e inicialize-a chamando a macro COMPOSITE_DEVICE_CAPABILITIES_INIT .

  2. Defina o membro CapabilityFunctionSuspend de COMPOSITE_DEVICE_CAPABILITIES como 1.

  3. Aloque uma estrutura REGISTER_COMPOSITE_DEVICE e inicialize a estrutura chamando a rotina USBD_BuildRegisterCompositeDevice . Na chamada, especifique o identificador USBD, a estrutura de COMPOSITE_DEVICE_CAPABILITIES inicializada e o número de funções.

  4. Aloque um IRP (pacote de solicitação de E/S) chamando IoAllocateIrp e obtenha um ponteiro para o primeiro local de pilha (IO_STACK_LOCATION) do IRP chamando IoGetNextIrpStackLocation.

  5. Aloque memória para um buffer grande o suficiente para conter uma matriz de identificadores de função (USBD_FUNCTION_HANDLE). O número de elementos na matriz deve ser o número de PDOs.

  6. Compile a solicitação definindo os seguintes membros do IO_STACK_LOCATION:

    • Especifique o tipo de solicitação definindo Parameters.DeviceIoControl.IoControlCode como IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE.
    • Especifique o parâmetro de entrada definindo Parameters.Others.Argument1 como o endereço da estrutura de REGISTER_COMPOSITE_DEVICE inicializada.
    • Especifique o parâmetro de saída definindo AssociatedIrp.SystemBuffer como o buffer alocado na etapa 5.
  7. Chame IoCallDriver para enviar a solicitação passando o IRP para o próximo local da pilha.

Após a conclusão, inspecione a matriz de identificadores de função que é retornada pela pilha de driver USB. Você pode armazenar a matriz no contexto do dispositivo do driver para uso futuro.

O exemplo de código a seguir mostra como criar e enviar uma solicitação de registro. O exemplo pressupõe que o driver composto armazena o número obtido anteriormente de funções e o identificador USBD no contexto do dispositivo do 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,  
        &registerInfo);  

    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 = &registerInfo;  

    //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;  
}

Cancelar o registro do dispositivo composto

  1. Aloque um IRP chamando IoAllocateIrp e obtenha um ponteiro para o primeiro local de pilha do IRP (IO_STACK_LOCATION) chamando IoGetNextIrpStackLocation.
  2. Compile a solicitação definindo o membro Parameters.DeviceIoControl.IoControlCode do IO_STACK_LOCATION como IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE.
  3. Chame IoCallDriver para enviar a solicitação passando o IRP para o próximo local da pilha.

A solicitação IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE é enviada uma vez pelo driver composto no contexto da rotina remove-device. A finalidade da solicitação é remover a associação entre a pilha de driver USB e o driver composto e sua função enumerada. A solicitação também limpa todos os recursos que foram criados para manter essa associação e todos os identificadores de função que foram retornados na solicitação de registro anterior.

O exemplo de código a seguir mostra como criar e enviar uma solicitação para cancelar o registro do dispositivo composto. O exemplo pressupõe que o driver composto foi registrado anteriormente por meio de uma solicitação de registro, conforme descrito anteriormente neste tópico.

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;  
}

IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE
IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE