Написание драйвера клиента UCSI

Драйвер USB Type-C Подключение or System Software Interface (UCSI) служит драйвером контроллера для системы USB Type-C с внедренным контроллером (EC).

Если система, реализующая диспетчер политик платформы (система УПП), как описано в спецификации UCSI, в ec, подключенном к системе, выполняется:

  • Транспорт ACPI не требуется писать драйвер. Загрузите встроенный драйвер Корпорации Майкрософт (UcmUcsiCx.sys и UcmUcsiAcpiClient.sys). (См. раздел Драйвер UCSI).

  • Транспорт, отличный от ACPI, например USB, PCI, I2C или UART, необходимо написать драйвер клиента для контроллера.

Примечание.

Если оборудование USB Type-C не имеет возможности обработки устройства состояния доставки питания (PD), попробуйте написать драйвер контроллера порта USB Type-C. Дополнительные сведения см. в статье "Запись драйвера контроллера порта USB Type-C".

Начиная с Windows 10 версии 1809, добавлено новое расширение класса для UCSI (UcmUcsiCx.sys), которое реализует спецификацию UCSI в транспортном не зависящем от транспорта. С минимальным объемом кода драйвер, который является клиентом UcmUcsiCx, может взаимодействовать с оборудованием USB Type-C через транспорт, отличный от ACPI. В этом разделе описываются службы, предоставляемые расширением класса UCSI, и ожидаемое поведение драйвера клиента.

Официальные спецификации

Относится к:

  • Windows 10, версия 1809

Версия WDF

  • KMDF версии 1.27

Важные API

Справочник по расширениям классов UcmUcsiCx

Пример

Пример драйвера клиента UcmUcsiCx

Замените части ACPI реализацией требуемой шины.

Архитектура расширения класса UCSI

Расширение класса UCSI, UcmUcsiCx, позволяет писать драйвер, взаимодействующий со своим внедренным контроллером с помощью транспорта, отличного от ACPI. Драйвер контроллера — это драйвер клиента для UcmUcsiCx. UcmUcsiCx в свою очередь является клиентом диспетчера соединителей USB (UCM). Поэтому UcmUcsiCx не принимает собственные решения политики. Вместо этого она реализует политики, предоставляемые UCM. UcmUcsiCx реализует компьютеры состояний для обработки уведомлений диспетчера политик платформы (система УПП) от драйвера клиента и отправляет команды для реализации решений политики UCM, что позволяет более надежно обнаруживать проблемы и обработку ошибок.

Архитектура расширения класса UCSI.

Диспетчер политик ОС (OPM)

Диспетчер политик ОС (OPM) реализует логику взаимодействия с система УПП, как описано в спецификации UCSI. OPM отвечает за:

  • Преобразование политик UCM в команды UCSI и уведомления UCSI в уведомления UCM.
  • Отправка команд UCSI, необходимых для инициализации система УПП, обнаружения ошибок и механизмов восстановления.

Обработка команд UCSI

Типичная операция включает несколько команд для выполнения оборудованием UCSI-complicant. Например, рассмотрим команду GET_CONNECTOR_STATUS.

  1. Встроенное ПО система УПП отправляет уведомление об изменении подключения к драйверу UcmUcsiCx/client.
  2. В ответ драйвер UcmUcsiCx/client отправляет команду GET_CONNECTOR_STATUS обратно в встроенное ПО система УПП.
  3. Встроенное ПО система УПП выполняет GET_CONNECTOR_STATUS и асинхронно отправляет уведомление о выполнении команд в драйвер UcmUcsiCx/client. Это уведомление содержит данные о фактическом состоянии подключения.
  4. Драйвер UcmUcsiCx/client обрабатывает сведения о состоянии и отправляет ACK_CC_CI встроенному ПО система УПП.
  5. Встроенное ПО система УПП выполняет ACK_CC_CI и асинхронно отправляет уведомление о выполнении команды в драйвер UcmUcsiCx/client.
  6. Драйвер UcmUcsiCx/client считает, что команда GET_CONNECTOR_STATUS будет завершена.

Обмен данными с диспетчером политик платформы (система УПП)

UcmUcsiCx абстрагирует сведения о отправке команд UCSI из OPM в встроенное ПО система УПП и получение уведомлений из встроенного ПО система УПП. Он преобразует команды система УПП в объекты WDFREQUEST и перенаправит их в драйвер клиента.

  • Уведомления система УПП

    Драйвер клиента уведомляет UcmUcsiCx о система УПП уведомлениях из встроенного ПО. Драйвер предоставляет блок данных UCSI, содержащий CCI. UcmUcsiCx перенаправит уведомления в OPM и другие компоненты, которые выполняют соответствующие действия на основе данных.

  • IoCTLs для драйвера клиента

    UcmUcsiCx отправляет команды UCSI (через запросы IOCTL) драйверу клиента для отправки в система УПП встроенного ПО. Драйвер отвечает за выполнение запроса после отправки команды UCSI встроенному ПО.

Обработка переходов питания

Драйвер клиента является владельцем политики питания.

Если драйвер клиента входит в состояние Dx из-за S0-Idle, WDF переносит драйвер в D0, когда UcmUcsiCx отправляет IOCTL, содержащую команду UCSI, в очередь управляемого клиентом драйвера клиента. Драйвер клиента в S0-Idle должен повторно включать состояние питания при наличии уведомления система УПП из встроенного ПО, так как в S0-Idle система УПП уведомления по-прежнему включены.

Подготовка к работе

  • Определите тип драйвера, который необходимо написать, в зависимости от того, реализуется ли оборудование или встроенное ПО, а также транспорт.

    Решение о выборе правильного расширения класса. Дополнительные сведения см. в статье "Разработка драйверов Windows для соединителей USB Type-C".

  • Установите Windows 10 для классических выпусков (Home, Pro, Enterprise и Education).

  • Установите последнюю версию комплекта драйверов Windows (WDK) на компьютере разработки. В комплекте есть необходимые файлы заголовков и библиотеки для написания драйвера клиента, в частности, вам потребуется:

    • Библиотека заглушки (UcmUcsiCxStub.lib). Библиотека преобразует вызовы, выполненные драйвером клиента, и передает их в расширение класса.
    • Файл заголовка Ucmucsicx.h.
    • Драйвер клиента выполняется в режиме ядра и привязывается к библиотеке KMDF 1.27.
  • Ознакомьтесь с Windows Driver Foundation (WDF). Рекомендуемое чтение: разработка драйверов с помощью Windows Driver Foundation , написанная Пенни Орвик и Гай Смит.

1. Регистрация драйвера клиента в UcmUcsiCx

В реализации EVT_WDF_DRIVER_DEVICE_ADD .

  1. После задания функций обратного вызова событий самонастраивающийся и управления питанием (WdfDeviceInitSetPnpPowerEventCallbacks), вызовите UcmUcsiDeviceInitInitialize, чтобы инициализировать непрозрачную структуру WDFDEVICE_INIT. Вызов связывает драйвер клиента с платформой.

  2. После создания объекта устройства платформы (WDFDEVICE) вызовите UcmUcsiDeviceInitialize , чтобы зарегистрировать делитель клиента в UcmUcsiCx.

2. Создание объекта система УПП с помощью UcmUcsiCx

В реализации EVT_WDF_DEVICE_PREPARE_HARDWARE после получения списка необработанных и переведенных ресурсов используйте ресурсы для подготовки оборудования. Например, если транспорт является I2C, прочитайте аппаратные ресурсы, чтобы открыть канал связи. Затем создайте объект система УПП. Чтобы создать объект, необходимо задать определенные параметры конфигурации.

  1. Укажите дескриптор коллекции соединителей на устройстве.

    1. Создайте коллекцию соединителей, вызвав UcmUcsi Подключение orCollectionCreate.

    2. Перечислите соединители на устройстве и добавьте их в коллекцию путем вызова UcmUcsi Подключение orCollectionAdd Подключение 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. Определите, следует ли включить контроллер устройства.

  3. Настройте и создайте объект система УПП.

    1. Инициализация структуры UCMUCSI_система УПП_CONFIG путем предоставления дескриптора соединителя, созданного на шаге 1.

    2. Задайте для элемента UsbDeviceControllerEnabled логическое значение, определенное на шаге 2.

    3. Задайте обратные вызовы событий в WDF_OBJECT_ATTRIBUTES.

    4. Вызовите UcmUcsiPpmCreate , передав все настроенные структуры.

      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. Настройка очередей ввода-вывода

UcmUcsiCx отправляет команды UCSI драйверу клиента для отправки в встроенное ПО система УПП. Команды отправляются в виде этих запросов IOCTL в очереди WDF.

Драйвер клиента отвечает за создание и регистрацию этой очереди в UcmUcsiCx путем вызова UcmUcsiPpmSetUcsiCommandRequestQueue. Очередь должна быть управляемой питанием.

UcmUcsiCx гарантирует, что в очереди WDF может быть не более одного выдающегося запроса. Драйвер клиента также отвечает за выполнение запроса WDF после отправки команды UCSI встроенному ПО.

Как правило, драйвер настраивает очереди в реализации 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);

Кроме того, драйвер клиента также должен вызвать UcmUcsiPpmStart , чтобы уведомить UcmUcsiCx о том, что драйвер готов к получению запросов IOCTL. Мы рекомендуем выполнить этот вызов в EVT_WDF_DEVICE_PREPARE_HARDWARE после создания дескриптора WDFQUEUE для получения команд UCSI через UcmUcsiPpmSetUcsiCommandRequestQueue. И наоборот, если драйвер не хочет обрабатывать больше запросов, он должен вызывать UcmUcsiPpmStop. Сделайте это в вашей EVT_WDF_DEVICE_RELEASE_HARDWARE реализации.

4. Обработка запросов IOCTL

Рассмотрим эту последовательность событий, возникающих при подключении партнера USB Type-C к соединителю.

  1. система УПП встроенное ПО определяет событие подключения и отправляет уведомление драйверу клиента.
  2. Драйвер клиента вызывает UcmUcsiPpmNotification , чтобы отправить это уведомление в UcmUcsiCx.
  3. UcmUcsiCx уведомляет компьютер состояния OPM и отправляет команду Get Подключение or Status в UcmUcsiCx.
  4. UcmUcsiCx создает запрос и отправляет IOCTL_UCMUCSI_система УПП_SEND_UCSI_DATA_BLOCK драйверу клиента.
  5. Драйвер клиента обрабатывает запрос и отправляет команду в встроенное ПО система УПП. Драйвер завершает этот запрос асинхронно и отправляет другое уведомление в UcmUcsiCx.
  6. При успешном завершении выполнения команды компьютер состояния OPM считывает полезные данные (содержащие сведения о состоянии соединителя) и уведомляет UCM о событии подключения Type-C.

В этом примере полезные данные также указывают на то, что изменение состояния согласования поставок питания между встроенной ПО и партнером порта успешно выполнено. Компьютер состояния OPM отправляет другую команду UCSI: получить PDOS. Аналогично команде Get Подключение or Status, когда команда Get PDOs успешно завершается, компьютер состояния OPM уведомляет UCM об этом событии.

Обработчик драйвера клиента для EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL похож на этот пример кода. Сведения об обработке запросов см. в разделе "Обработчики запросов"

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