Partilhar via


Como registar um dispositivo composto

Este artigo descreve como um driver de um dispositivo multifuncional USB, chamado de driver composto, pode registrar e desregistrar o dispositivo composto com a pilha de drivers USB subjacente. O Windows carrega o driver fornecido pela Microsoft, Usbccgp.sys Como o driver composto padrão. O procedimento descrito neste artigo aplica-se a um driver composto personalizado baseado no Modelo de Driver do Windows (WDM) que substitui Usbccgp.sys.

Um dispositivo USB (Universal Serial Bus) pode fornecer várias funções que estão ativas simultaneamente. Esses dispositivos multifuncionais 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. O driver composto enumera as funções do dispositivo. O driver composto pode gerenciar essas funções em um modelo monolítico ou criar objetos de dispositivo físico (DOP) para cada uma das funções. Os drivers de função USB, como o driver do teclado e o driver do mouse, gerenciam seus respetivos PDOs individuais.

A especificação USB 3.0 define o recurso de suspensão de função e despertar remoto permitindo que funções individuais entrem e saiam de estados de baixo consumo de 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 Como implementar a suspensão de função em um driver composto.

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 certificar-se de que a pilha subjacente suporta a versão USBD_INTERFACE_VERSION_602. Através do pedido de registo, o condutor composto:

  • Informa a pilha de drivers USB subjacente que o driver é responsável por enviar uma solicitação para ativar uma função de despertar remoto. A pilha de drivers USB processa a solicitação de ativação remota, 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 drivers USB. O driver composto pode então usar uma alça de função na solicitação do motorista para despertar remoto da função associada à alça.

Normalmente, um driver composto envia a solicitação de registro no AddDevice do driver ou na rotina do dispositivo de inicialização para lidar com IRP_MN_START_DEVICE. Assim, o driver composto liberta os recursos que são alocados para o registo nas rotinas de descarregamento do driver, como a rotina de parar dispositivo (IRP_MN_STOP_DEVICE) ou a rotina de remover dispositivo (IRP_MN_REMOVE_DEVICE).

Pré-requisitos

Antes de enviar o pedido de registo, certifique-se de que:

  • O dispositivo possui um número de funções. 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 drivers USB subjacente suporta dispositivos USB 3.0. Para fazer isso, chame USBD_IsInterfaceVersionSupported e passe USBD_INTERFACE_VERSION_602 como a versão a ser verificada.

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

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 drivers USB.

  1. Aloque uma estrutura COMPOSITE_DEVICE_CAPABILITIES e inicialize-a utilizando 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 COMPOSITE_DEVICE_CAPABILITIES inicializada e o número de funções.

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

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

  6. Crie 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 para o endereço da estrutura REGISTER_COMPOSITE_DEVICE inicializada.
    • Especifique o parâmetro de saída definindo AssociatedIrp.SystemBuffer para o buffer que foi alocado na etapa 5.
  7. Chame IoCallDriver para passar o IRP para a próxima posição da pilha e enviar a solicitação.

Após a conclusão, inspecione a matriz de alças de função retornadas pela pilha de drivers 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 de funções obtido anteriormente 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 de um dispositivo composto

  1. Aloque um IRP chamando IoAllocateIrp e obtenha um ponteiro para o primeiro local de pilha (IO_STACK_LOCATION) do IRP chamando IoGetNextIrpStackLocation.
  2. Crie 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 de IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE é enviada uma vez pelo driver composto no contexto da rotina de remoção de dispositivo. O objetivo do pedido é remover a associação entre a pilha de controladores USB e o controlador composto, bem como a 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 artigo.

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