Share via


Escritura de un controlador de cliente UCSI

Un controlador usb de tipo C Conectar or system Software Interface (UCSI) actúa como controlador de controlador para un sistema USB Type-C con un controlador incrustado (EC).

Si el sistema que implementa Platform Policy Manager (PPM), como se describe en la especificación UCSI, en una EC que está conectada al sistema a través de:

  • Un transporte ACPI, no es necesario escribir un controlador. Cargue el controlador incluido en microsoft (UcmUcsiCx.sys y UcmUcsiAcpiClient.sys). (Consulte Controlador UCSI).

  • Un transporte que no sea ACPI, como USB, PCI, I2C o UART, debe escribir un controlador cliente para el controlador.

Nota:

Si el hardware USB type-C no tiene la capacidad de controlar la máquina de estado de entrega de energía (PD), considere la posibilidad de escribir un controlador de controlador de puerto usb de tipo C. Para obtener más información, consulte Escritura de un controlador de puerto usb de tipo C.

A partir de Windows 10, versión 1809, se ha agregado una nueva extensión de clase para UCSI (UcmUcsiCx.sys), que implementa la especificación UCSI de forma independiente del transporte. Con una cantidad mínima de código, el controlador, que es un cliente a UcmUcsiCx, puede comunicarse con el hardware USB Type-C a través del transporte no ACPI. En este tema se describen los servicios proporcionados por la extensión de clase UCSI y el comportamiento esperado del controlador cliente.

Especificaciones oficiales

Se aplica a:

  • Windows 10, versión 1809

Versión de WDF

  • KMDF versión 1.27

API importantes

Referencia de extensiones de clase UcmUcsiCx

Ejemplo

Ejemplo de controlador de cliente UcmUcsiCx

Reemplace las partes ACPI por la implementación del bus necesario.

Arquitectura de extensión de clase UCSI

La extensión de clase UCSI, UcmUcsiCx, le permite escribir un controlador que se comunique con su controlador incrustado mediante el transporte que no sea ACPI. El controlador del controlador es un controlador cliente a UcmUcsiCx. UcmUcsiCx es, a su vez, un cliente al administrador de conectores USB (UCM). Por lo tanto, UcmUcsiCx no toma ninguna decisión política propia. En su lugar, implementa directivas proporcionadas por UCM. UcmUcsiCx implementa máquinas de estado para controlar las notificaciones del Administrador de directivas de plataforma (PPM) desde el controlador cliente y envía comandos para implementar decisiones de directiva de UCM, lo que permite una detección de problemas y control de errores más confiable.

Arquitectura de extensión de clase UCSI.

Administrador de directivas del sistema operativo (OPM)

OS Policy Manager (OPM) implementa la lógica para interactuar con PPM, como se describe en la especificación UCSI. OPM es responsable de:

  • Convertir directivas de UCM en comandos UCSI y notificaciones UCSI en notificaciones UCM.
  • Envío de comandos UCSI necesarios para inicializar PPM, detectar errores y mecanismos de recuperación.

Control de comandos UCSI

Una operación típica implica varios comandos que debe completar el hardware UCSI-complicante. Por ejemplo, consideremos el comando GET_CONNECTOR_STATUS.

  1. El firmware PPM envía una notificación de cambio de conexión al controlador UcmUcsiCx/client.
  2. En respuesta, el controlador UcmUcsiCx/client envía un comando GET_CONNECTOR_STATUS al firmware PPM.
  3. El firmware PPM se ejecuta GET_CONNECTOR_STATUS y envía de forma asincrónica una notificación de comando completa al controlador UcmUcsiCx/client. Esa notificación contiene datos sobre el estado de conexión real.
  4. El controlador UcmUcsiCx/client procesa esa información de estado y envía un ACK_CC_CI al firmware PPM.
  5. El firmware PPM ejecuta ACK_CC_CI y envía de forma asincrónica una notificación completa de comandos al controlador UcmUcsiCx/client.
  6. El controlador UcmUcsiCx/client considera que el comando GET_CONNECTOR_STATUS está completo.

Comunicación con Platform Policy Manager (PPM)

UcmUcsiCx abstrae los detalles del envío de comandos UCSI desde OPM al firmware PPM y la recepción de notificaciones del firmware PPM. Convierte comandos PPM en objetos WDFREQUEST y los reenvía al controlador cliente.

  • Notificaciones ppm

    El controlador cliente notifica a UcmUcsiCx sobre las notificaciones PPM del firmware. El controlador proporciona el bloque de datos UCSI que contiene CCI. UcmUcsiCx reenvía las notificaciones a OPM y a otros componentes que realizan las acciones adecuadas en función de los datos.

  • ICTLs al controlador de cliente

    UcmUcsiCx envía comandos UCSI (a través de solicitudes IOCTL) al controlador cliente para enviarlo al firmware PPM. El controlador es responsable de completar la solicitud después de haber enviado el comando UCSI al firmware.

Control de transiciones de energía

El controlador cliente es el propietario de la directiva de energía.

Si el controlador cliente entra en un estado Dx debido a S0-Idle, WDF lleva el controlador a D0 cuando UcmUcsiCx envía un IOCTL que contiene un comando UCSI a la cola administrada por energía del controlador cliente. El controlador cliente en S0-Idle debe volver a escribir un estado con tecnología cuando hay una notificación PPM desde el firmware porque en S0-Idle, las notificaciones PPM todavía están habilitadas.

Antes de empezar

  • Determine el tipo de controlador que necesita escribir en función de si el hardware o el firmware implementa la máquina de estado pd y el transporte.

    Decisión para elegir la extensión de clase correcta. Para obtener más información, consulte Desarrollo de controladores de Windows para conectores usb de tipo C.

  • Instale Windows 10 para las ediciones de escritorio (Home, Pro, Enterprise y Education).

  • Instale el kit de controladores de Windows (WDK) más reciente en el equipo de desarrollo. El kit tiene los archivos y bibliotecas de encabezado necesarios para escribir el controlador cliente, en concreto, necesitará lo siguiente:

    • La biblioteca de código auxiliar (UcmUcsiCxStub.lib). La biblioteca traduce las llamadas realizadas por el controlador cliente y las pasa a la extensión de clase.
    • El archivo de encabezado, Ucmucsicx.h.
    • El controlador cliente se ejecuta en modo kernel y se enlaza a la biblioteca KMDF 1.27.
  • Familiarícese con Windows Driver Foundation (WDF). Lectura recomendada: Desarrollar controladores con Windows Driver Foundation escrito por Penny Orwick y Guy Smith.

1. Registrar el controlador de cliente con UcmUcsiCx

En la implementación de EVT_WDF_DRIVER_DEVICE_ADD.

  1. Después de establecer las funciones de devolución de llamada de eventos plug and Play y de administración de energía (WdfDeviceInitSetPnpPowerEventCallbacks), llame a UcmUcsiDeviceInitInitialize para inicializar la estructura WDFDEVICE_INIT opaca. La llamada asocia el controlador de cliente con el marco de trabajo.

  2. Después de crear el objeto de dispositivo de marco (WDFDEVICE), llame a UcmUcsiDeviceInitialize para registrar el buzón de cliente con UcmUcsiCx.

2. Crear el objeto PPM con UcmUcsiCx

En la implementación de EVT_WDF_DEVICE_PREPARE_HARDWARE, después de haber recibido la lista de recursos sin procesar y traducidos, use los recursos para preparar el hardware. Por ejemplo, si el transporte es I2C, lea los recursos de hardware para abrir un canal de comunicación. A continuación, cree un objeto PPM. Para crear el objeto, debe establecer determinadas opciones de configuración.

  1. Proporcione un identificador para la recopilación del conector en el dispositivo.

    1. Cree la colección de conectores llamando a UcmUcsi Conectar orCollectionCreate.

    2. Enumere los conectores en el dispositivo y agréguelos a la colección llamando a UcmUcsi Conectar orCollectionAdd Conectar or

      // Create the connector collection.
      
      UCMUCSI_CONNECTOR_COLLECTION* ConnectorCollectionHandle;
      
      status = UcmUcsiConnectorCollectionCreate(Device, //WDFDevice
               WDF_NO_OBJECT_ATTRIBUTES,
               ConnectorCollectionHandle);
      
      // Enumerate the connectors on the device.
      // ConnectorId of 0 is reserved for the parent device.
      // In this example, we assume the parent has no children connectors.
      
      UCMUCSI_CONNECTOR_INFO_INIT(&connectorInfo);
      connectorInfo.ConnectorId = 0;
      
      status = UcmUcsiConnectorCollectionAddConnector ( &ConnectorCollectionHandle,
                   &connectorInfo);
      
  2. Decida si desea habilitar el controlador de dispositivo.

  3. Configure y cree el objeto PPM.

    1. Inicialice una estructura de UCMUCSI_PPM_CONFIG proporcionando el identificador del conector que creó en el paso 1.

    2. Establezca el miembro UsbDeviceControllerEnabled en un valor booleano determinado en el paso 2.

    3. Establezca las devoluciones de llamada de eventos en WDF_OBJECT_ATTRIBUTES.

    4. Llame a UcmUcsiPpmCreate pasando todas las estructuras configuradas.

      UCMUCSIPPM ppmObject = WDF_NO_HANDLE;
      PUCMUCSI_PPM_CONFIG UcsiPpmConfig;
      WDF_OBJECT_ATTRIBUTES attrib;
      
      UCMUCSI_PPM_CONFIG_INIT(UcsiPpmConfig, ConnectorCollectionHandle);
      
      UcsiPpmConfig->UsbDeviceControllerEnabled = TRUE;
      
      WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrib, Ppm);
      attrib->EvtDestroyCallback = &EvtObjectContextDestroy;
      
      status = UcmUcsiPpmCreate(wdfDevice, UcsiPpmConfig, &attrib, &ppmObject);
      

3. Configuración de colas de E/S

UcmUcsiCx envía comandos UCSI al controlador cliente para enviarlo al firmware PPM. Los comandos se envían en forma de estas solicitudes IOCTL en una cola de WDF.

El controlador cliente es responsable de crear y registrar esa cola en UcmUcsiCx mediante una llamada a UcmUcsiPpmSetUcsiCommandRequestQueue. La cola debe administrarse con energía.

UcmUcsiCx garantiza que puede haber como máximo una solicitud pendiente en la cola de WDF. El controlador cliente también es responsable de completar la solicitud WDF después de que haya enviado el comando UCSI al firmware.

Normalmente, el controlador configura colas en su implementación de EVT_WDF_DEVICE_PREPARE_HARDWARE.

WDFQUEUE UcsiCommandRequestQueue = WDF_NO_HANDLE;
WDF_OBJECT_ATTRIBUTES attrib;
WDF_IO_QUEUE_CONFIG queueConfig;

WDF_OBJECT_ATTRIBUTES_INIT(&attrib);
attrib.ParentObject = GetObjectHandle();

// In this example, even though the driver creates a sequential queue,
// UcmUcsiCx guarantees that will not send another request
// until the previous one has been completed.


WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchSequential);

// The queue must be power-managed.

queueConfig.PowerManaged = WdfTrue;
queueConfig.EvtIoDeviceControl = EvtIoDeviceControl;

status = WdfIoQueueCreate(device, &queueConfig, &attrib, &UcsiCommandRequestQueue);

UcmUcsiPpmSetUcsiCommandRequestQueue(ppmObject, UcsiCommandRequestQueue);

Además, el controlador de cliente también debe llamar a UcmUcsiPpmStart para notificar a UcmUcsiCx que el controlador está listo para recibir las solicitudes de IOCTL. Se recomienda realizar esa llamada en la EVT_WDF_DEVICE_PREPARE_HARDWARE después de crear el identificador WDFQUEUE para recibir comandos UCSI, a través de UcmUcsiPpmSetUcsiCommandRequestQueue. Por el contrario, cuando el controlador no desea procesar más solicitudes, debe llamar a UcmUcsiPpmStop. Haga esto en la implementación de EVT_WDF_DEVICE_RELEASE_HARDWARE .

4. Control de las solicitudes de IOCTL

Considere esta secuencia de ejemplo de los eventos que se producen cuando un asociado de tipo C USB está conectado a un conector.

  1. El firmware PPM determina un evento de asociación y envía una notificación al controlador cliente.
  2. El controlador cliente llama a UcmUcsiPpmNotification para enviar esa notificación a UcmUcsiCx.
  3. UcmUcsiCx notifica al equipo de estado de OPM y envía un comando Get Conectar or Status a UcmUcsiCx.
  4. UcmUcsiCx crea una solicitud y envía IOCTL_UCMUCSI_PPM_SEND_UCSI_DATA_BLOCK al controlador cliente.
  5. El controlador cliente procesa esa solicitud y envía el comando al firmware PPM. El controlador completa esta solicitud de forma asincrónica y envía otra notificación a UcmUcsiCx.
  6. Cuando el comando se ha completado correctamente, la máquina de estado de OPM lee la carga (que contiene información de estado del conector) y notifica al UCM del evento de asociación de Type-C.

En este ejemplo, la carga también indicó que un cambio en el estado de negociación de entrega de energía entre el firmware y el asociado de puerto se realizó correctamente. La máquina de estado de OPM envía otro comando UCSI: Obtener PPO. De forma similar al comando Get Conectar or Status, cuando el comando Get PDOs se completa correctamente, la máquina de estado de OPM notifica a UCM de este evento.

El controlador del controlador de cliente para EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL es similar a este código de ejemplo. Para obtener información sobre el control de solicitudes, consulte Controladores de solicitudes.

void EvtIoDeviceControl(
    _In_ WDFREQUEST Request,
    _In_ ULONG IoControlCode
    )
{
...
    switch (IoControlCode)
    {
    case IOCTL_UCMUCSI_PPM_SEND_UCSI_DATA_BLOCK:
        EvtSendData(Request);
        break;

    case IOCTL_UCMUCSI_PPM_GET_UCSI_DATA_BLOCK:
        EvtReceiveData(Request);
        break;

    default:
        status = STATUS_NOT_SUPPORTED;
        goto Exit;
    }

    status = STATUS_SUCCESS;

Exit:

    if (!NT_SUCCESS(status))
    {
        WdfRequestComplete(Request, status);
    }

}

VOID EvtSendData(
    WDFREQUEST Request
    )
{
    NTSTATUS status;
    PUCMUCSI_PPM_SEND_UCSI_DATA_BLOCK_IN_PARAMS inParams;

    status = WdfRequestRetrieveInputBuffer(Request, sizeof(*inParams),
        reinterpret_cast<PVOID*>(&inParams), nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    // Build a UCSI command request and send to the PPM firmware.

Exit:
    WdfRequestComplete(Request, status);
}

VOID EvtReceiveData(
    WDFREQUEST Request
    )
{

    NTSTATUS status;

    PUCMUCSI_PPM_GET_UCSI_DATA_BLOCK_IN_PARAMS inParams;
    PUCMUCSI_PPM_GET_UCSI_DATA_BLOCK_OUT_PARAMS outParams;

    status = WdfRequestRetrieveInputBuffer(Request, sizeof(*inParams),
        reinterpret_cast<PVOID*>(&inParams), nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(*outParams),
        reinterpret_cast<PVOID*>(&outParams), nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    // Receive data from the PPM firmware.
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }
    WdfRequestSetInformation(Request, sizeof(*outParams));

Exit:
    WdfRequestComplete(Request, status);
}