Compartir a través de


Cómo registrar un controlador compuesto

En este artículo se describe cómo un controlador de un dispositivo multifunción USB, denominado controlador compuesto, puede registrar y anular el registro del dispositivo compuesto con la pila de controladores USB subyacente. Windows carga el controlador proporcionado por Microsoft, Usbccgp.sys como controlador compuesto predeterminado. El procedimiento de este artículo se aplica a un controlador compuesto personalizado basado en el modelo de controladores de Windows (WDM) que reemplaza Usbccgp.sys.

Un dispositivo de bus serie universal (USB) puede proporcionar varias funciones que están activas simultáneamente. Estos dispositivos multifunción también se conocen como dispositivos compuestos. Por ejemplo, un dispositivo compuesto podría definir una función para la funcionalidad del teclado y otra función para el mouse. El controlador compuesto enumera las funciones del dispositivo. El controlador compuesto puede administrar esas funciones en un modelo monolítico o crear objetos de dispositivo físico (PPO) para cada una de las funciones. Los controladores de función USB, como el controlador de teclado y el controlador del mouse, administran sus respectivos PPO individuales.

La especificación USB 3.0 define la característica de suspensión y reactivación remota de funciones, que permite que funciones individuales entren y salgan de estados de baja potencia sin afectar al estado de energía de otras funciones o al dispositivo completo. Para obtener más información sobre la característica, consulte Cómo implementar la suspensión de funciones en un controlador compuesto.

Para usar la característica, el controlador compuesto debe registrar el dispositivo con la pila de controladores USB subyacente. Dado que la característica se aplica a dispositivos USB 3.0, el controlador compuesto debe asegurarse de que la pila subyacente admite la versión USBD_INTERFACE_VERSION_602. A través de la solicitud de registro, el controlador compuesto:

  • Informa a la pila del controlador USB subyacente que el controlador es responsable de enviar una solicitud para armar una función para la reactivación remota. La pila de controladores USB procesa la solicitud de reactivación remota, que envía las solicitudes de protocolo necesarias al dispositivo.
  • Obtiene una lista de identificadores de función (uno por función) asignado por la pila de controladores USB. Después, el controlador compuesto puede usar un identificador de función en la solicitud de reactivación remota de la función asociada al identificador.

Normalmente, un controlador compuesto envía la solicitud de registro a AddDevice del controlador o a la rutina start-device para controlar IRP_MN_START_DEVICE. Por lo tanto, el controlador compuesto libera los recursos asignados para el registro en las rutinas de descarga del controlador, como la rutina stop-device (IRP_MN_STOP_DEVICE) o remove-device (IRP_MN_REMOVE_DEVICE).

Requisitos previos

Antes de enviar la solicitud de registro, asegúrese de que:

  • Tiene el número de funciones en el dispositivo. Ese número se puede obtener de los descriptores recuperados por la solicitud get-configuration.
  • Ha obtenido un identificador USBD en una llamada anterior a USBD_CreateHandle.
  • La pila de controladores USB subyacente admite dispositivos USB 3.0. Para ello, llame a USBD_IsInterfaceVersionSupported y pase USBD_INTERFACE_VERSION_602 como versión que se va a comprobar.

Para obtener un ejemplo de código, consulte Cómo implementar la suspensión de funciones en un controlador compuesto.

Registro de un dispositivo compuesto

En el procedimiento siguiente se describe cómo se debe compilar y enviar una solicitud de registro para asociar un controlador compuesto a la pila de controladores USB.

  1. Asigne una estructura COMPOSITE_DEVICE_CAPABILITIES e inicialícela llamando a la macro COMPOSITE_DEVICE_CAPABILITIES_INIT.

  2. Establezca el miembro CapabilityFunctionSuspend de COMPOSITE_DEVICE_CAPABILITIES en 1.

  3. Asigne una estructura REGISTER_COMPOSITE_DEVICE e inicialice la estructura llamando a la rutina USBD_BuildRegisterCompositeDevice. En la llamada, especifique el identificador USBD, la estructura COMPOSITE_DEVICE_CAPABILITIES inicializada y el número de funciones.

  4. Asigne un paquete de solicitud de E/S (IRP) llamando a IoAllocateIrp y obtenga un puntero a la primera ubicación de pila del IRP (IO_STACK_LOCATION) llamando a IoGetNextIrpStackLocation.

  5. Asigne memoria para un búfer lo suficientemente grande como para contener una matriz de identificadores de función (USBD_FUNCTION_HANDLE). El número de elementos de la matriz debe ser el número de PDO.

  6. Compile la solicitud estableciendo los siguientes miembros de IO_STACK_LOCATION:

    • Especifique el tipo de solicitud estableciendo Parameters.DeviceIoControl.IoControlCode en IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE.
    • Especifique el parámetro de entrada estableciendo Parameters.Others.Argument1 en la dirección de la estructura REGISTER_COMPOSITE_DEVICE inicializada.
    • Especifique el parámetro de salida estableciendo AssociatedIrp.SystemBuffer en el búfer asignado en el paso 5.
  7. Llame a IoCallDriver para enviar la solicitud pasando el IRP a la siguiente ubicación de pila.

Al finalizar, inspeccione la matriz de identificadores de función devueltos por la pila del controlador USB. Puede almacenar la matriz en el contexto del dispositivo del controlador para su uso futuro.

En el ejemplo de código siguiente se muestra cómo compilar y enviar una solicitud de registro. En el ejemplo se supone que el controlador compuesto almacena el número obtenido previamente de funciones y el controlador USBD en el contexto del dispositivo del controlador.

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

Anulación del registro de un dispositivo compuesto

  1. Asigne un IRP llamando a IoAllocateIrp y obtenga un puntero a la primera ubicación de pila del IRP (IO_STACK_LOCATION) llamando a IoGetNextIrpStackLocation.
  2. Compile la solicitud estableciendo el miembro Parameters.DeviceIoControl.IoControlCode de IO_STACK_LOCATION en IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE.
  3. Llame a IoCallDriver para enviar la solicitud pasando el IRP a la siguiente ubicación de pila.

El controlador compuesto envía una vez la solicitud IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE en el contexto de la rutina remove-device. El propósito de la solicitud es quitar la asociación entre la pila del controlador USB y el controlador compuesto y su función enumerada. La solicitud también limpia los recursos creados para mantener esa asociación y todos los identificadores de función que se devolvieron en la solicitud de registro anterior.

En el ejemplo de código siguiente se muestra cómo compilar y enviar una solicitud para anular el registro del dispositivo compuesto. En el ejemplo se supone que el controlador compuesto se registró anteriormente a través de una solicitud de registro, como se describió anteriormente en este artículo.

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