编写 USB 类型 C 端口控制器驱动程序

如果 USB Type-C 硬件实现 USB 类型 C 或电源交付 (PD) 物理层,但未实现电源传送所需的状态机,则需要编写 USB Type-C 端口控制器驱动程序。

在Windows 10版本 1703 中,USB 类型 C 体系结构已得到改进,以支持实现 USB 类型 C 或电源交付 (PD) 物理层的硬件设计,但没有相应的 PD 策略引擎或协议层实现。 对于这些设计,Windows 10版本 1703 通过名为“USB 连接器管理器类型 C 端口控制器接口类扩展”的新类扩展 (UcmTcpciCx) 提供基于软件的 PD 策略引擎和设备策略管理器。 由 IHV 或 OEM/ODM 编写的客户端驱动程序与 UcmTcpciCx 通信,以便提供有关 UcmTcpciCx 中 PD 策略引擎和设备策略管理器所需的硬件事件的信息。 通过本主题和参考部分中介绍的一组编程接口启用该通信。

usb 连接器管理器。

UcmTcpciCx 类扩展本身是 UcmCx 的客户端驱动程序。 有关电源协定、数据角色的策略决策是在 UcmCx 中做出的,并转发到 UcmTcpciCx。 UcmTcpciCx 使用 UcmTcpciCx 客户端驱动程序提供的端口控制器接口实现这些策略并管理 Type-C 和 PD 状态机。

摘要

  • UcmTcpci 类扩展提供的服务
  • 客户端驱动程序的预期行为

官方规范

适用于:

  • Windows 10

WDF 版本

  • KMDF 版本 1.15

上次更新时间:

  • 2017 年 5 月

重要的 API

USB 类型 C 端口控制器接口驱动程序类扩展参考

UcmTcpciCx 客户端驱动程序模板

UcmTcpciCx 客户端驱动程序模板

准备工作

  • 根据硬件还是固件实现 PD 状态机,确定需要写入的驱动程序类型。 有关详细信息,请参阅 开发适用于 USB 类型 C 连接器的 Windows 驱动程序

  • 在目标计算机上安装适用于桌面版 (家庭版、专业版、企业版和教育版) ,或者使用 USB 类型 C 连接器Windows 10 移动版安装。 Windows 10

  • 在开发 计算机上安装最新的 Windows 驱动程序工具包 (WDK) 。 该工具包具有用于编写客户端驱动程序所需的标头文件和库,具体而言,你需要:

    • 存根库, (UcmTcpciCxStub.lib) 。 库转换客户端驱动程序发出的调用,并将其传递给类扩展。
    • 头文件 UcmTcpciCx.h。

    客户端驱动程序在内核模式下运行,并绑定到 KMDF 1.15 库。

    ucm 的 visual studio 配置。

  • 确定客户端驱动程序是否支持警报。

  • 不需要端口控制器符合 TCPCI。 该接口捕获任何 Type-C 端口控制器的功能。 为不符合 TCPCI 的硬件编写 UcmTcpciCx 客户端驱动程序只涉及将 TCPCI 规范中的寄存器和命令的含义映射到硬件的含义。

  • 大多数 TCPCI 控制器都是 I2C 连接的。 客户端驱动程序将使用串行外围总线 (SPB) 连接资源和中断线来与硬件通信。 驱动程序将使用 SPB 框架扩展 (SpbCx) 编程方面。 阅读以下主题,熟悉 SpbCx:

    • [简单的外围总线 (SPB) 驱动程序设计指南]
    • [SPB 驱动程序编程参考]
  • 熟悉 Windows Driver Foundation (WDF) 。 推荐阅读: 使用 Windows Driver Foundation 开发驱动程序,由佩妮·奥里克和盖伊·史密斯撰写。

UcmTcpci 类扩展的行为

  • 作为状态机执行的一部分,UcmTcpciCx 会将 IOCTL 请求发送到端口控制器。 例如,在 PD 消息传送中,它会发送IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER请求来设置传输缓冲区。 该请求 (TRANSMIT_BUFFER) 移交给客户端驱动程序。 然后,驱动程序使用类扩展提供的详细信息设置传输缓冲区。

  • UcmTcpciCx 实现有关电力协定、数据角色等的策略。

客户端驱动程序的预期行为

UcmTcpciCx 的客户端驱动程序应为:

  • 是电源策略所有者。 UcmTcpciCx 不参与端口控制器的电源管理。

  • 将从 UcmTcpciCx 接收的请求转换为硬件读取或写入命令。 命令必须是异步的,因为 DPM 无法阻止等待硬件传输完成。

  • 提供包含框架请求对象的框架队列对象。 对于 UcmTcpci 类扩展要发送到客户端驱动程序的每个请求,该扩展会在驱动程序的队列对象中添加请求对象。 驱动程序完成处理请求后,会调用 WdfRequestComplete。 客户端驱动程序有责任及时完成请求。

  • 发现并报告端口控制器的功能。 这些功能包括端口控制器可以在 (中运行的角色等信息,例如仅限源、仅限接收器、DRP) 。 但是,连接器的其他功能 (查看有关功能存储) 和整个系统的注释,需要 DPM 知道才能正确实现 USB 类型 C 和 PD 策略。 例如,DPM 需要知道系统/连接器的源功能才能将其播发到端口合作伙伴。

    注意功能存储

    除了与客户端驱动程序相关的功能,其他信息还来自称为 “功能存储”的系统全局位置。 此系统全局功能存储存储在 ACPI 中。 它是系统及其每个 USB 类型 C 连接器功能的静态说明,DPM 用来确定要实现的策略。

    通过在端口控制器 () 的客户端驱动程序中分离系统功能的说明,设计允许在具有不同功能的不同系统上使用驱动程序。 UcmCx,而不是 UcmTcpciCx,与功能存储接口。 UcmTcpciCx (或其客户端驱动程序) 不与功能存储进行交互。

    无论适用,功能存储中的信息都会替代来自端口控制器客户端驱动程序的信息。 例如,端口控制器能够仅接收器操作,客户端驱动程序报告该信息。 但是,系统其余部分可能无法正确配置为仅接收器操作。 在这种情况下,系统制造商可以报告连接器能够在功能存储中仅源操作。 功能存储中的设置优先于驱动程序报告的信息。

  • 使用与警报相关的所有相关数据通知 UcmTcpciCx。

  • 可选。 在进入/退出备用模式后执行一些额外的处理。 驱动程序通过 IOCTL 请求通过类扩展通知这些状态。

1.向 UcmTcpciCx 注册客户端驱动程序

示例参考:请参阅 EvtPrepareHardwareDevice.cpp

  1. 在EVT_WDF_DRIVER_DEVICE_ADD实现中,调用 UcmTcpciDeviceInitInitialize 初始化WDFDEVICE_INIT不透明结构。 该调用将客户端驱动程序与框架相关联。

  2. 创建框架设备对象 (WDFDEVICE) 后,调用 UcmTcpciDeviceInitialize 以向 UcmTcpciCx 注册客户端 diver。

2.将 I2C 通信通道初始化到端口控制器硬件。

示例参考:请参阅 EvtCreateDeviceDevice.cpp

在EVT_WDF_DEVICE_PREPARE_HARDWARE实现中,读取硬件资源以打开信道。 这是检索 PD 功能并收到有关警报的通知所必需的。

大多数 TCPCI 控制器都是 I2C 连接的。 在参考示例中,客户端驱动程序使用 SPB 框架扩展 (SpbCx) 编程方面打开 I2 通道。

客户端驱动程序通过调用 WdfCmResourceListGetDescriptor 枚举硬件资源。

警报作为中断接收。 因此,驱动程序创建框架中断对象并注册将处理警报的 ISR。 ISR 执行硬件读取和写入操作,直到硬件访问完成为止。 由于在 DIRQL 上等待是不能接受的,因此驱动程序会在PASSIVE_LEVEL执行 ISR。

3.初始化端口控制器的类型 C 和 PD 功能

示例参考:请参阅 EvtDeviceD0EntryDevice.cpp

在EVT_WDF_DEVICE_D0_EXIT实现中,

  1. 通过读取各种寄存器与端口控制器硬件通信并检索设备标识和功能。

  2. 使用检索的信息初始化UCMTCPCI_PORT_CONTROLLER_IDENTIFICATION和UCMTCPCI_PORT_CONTROLLER_CAPABILITIES。

  3. 通过将初始化的结构传递给UCMTCPCI_PORT_CONTROLLER_CONFIG_INIT,使用上述信息初始化UCMTCPCI_PORT_CONTROLLER_CONFIG结构。

  4. 调用 UcmTcpciPortControllerCreate 以创建端口控制器对象并检索 UCMTCPCIPORTCONTROLLER 句柄。

4. 设置框架队列对象以接收来自 UcmTcpciCx 的请求

Sample reference: See EvtDeviceD0Entry in Device.cpp and HardwareRequestQueueInitialize in Queue.cpp.

  1. 在EVT_WDF_DEVICE_D0_EXIT实现中,通过调用 WdfIoQueueCreate 创建框架队列对象。 在该调用中,需要注册回调实现来处理 UcmTpciCx 发送的 IOCTL 请求。 客户端驱动程序可以使用电源管理的队列。

    在执行 Type-C 和 PD 状态机期间,UcmTpciCx 会将命令发送到要执行的客户端驱动程序。 UcmTcpciCx 保证在任何给定时间最多有一个未完成的端口控制器请求。

  2. 调用 UcmTcpciPortControllerSetHardwareRequestQueue 以向 UcmTpciCx 注册新的框架队列对象。 调用成功后,UcmTcpciCx 将框架队列对象 (WDFREQUEST) 在此队列中,当它需要驱动程序的操作时。

  3. 实现 EvtIoDeviceControl 回调函数来处理这些 IOCTL。

控制代码 说明
IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_STATUS 根据通用串行总线类型 C 端口控制器接口规范获取所有状态寄存器的值。 客户端驱动程序必须检索CC_STATUS、POWER_STATUS和FAULT_STATUS寄存器的值。
IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_CONTROL 获取根据通用串行总线类型 C 端口控制器接口规范定义的所有控件寄存器的值。
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONTROL 设置根据通用串行总线类型 C 端口控制器接口规范定义的控件寄存器的值。
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT 设置根据通用串行总线类型 C 端口控制器接口规范定义的 TRANSMIT 寄存器。
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER 设置根据通用串行总线类型 C 端口控制器接口规范定义的TRANSMIT_BUFER寄存器。
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_RECEIVE_DETECT 设置根据通用串行总线类型 C 端口控制器接口规范定义的RECEIVE_DETECT寄存器。
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONFIG_STANDARD_OUTPUT 设置根据通用串行总线类型 C 端口控制器接口规范定义的CONFIG_STANDARD_OUTPUT寄存器。
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_COMMAND 设置根据通用串行总线类型 C 端口控制器接口规范定义的命令寄存器的值。
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_MESSAGE_HEADER_INFO 设置根据通用串行总线类型 C 端口控制器接口规范定义的MESSAGE_HEADER_INFO寄存器的值。
IOCTL_UCMTCPCI_PORT_CONTROLLER_ALTERNATE_MODE_ENTERED 通知客户端驱动程序输入备用模式,以便驱动程序可以执行其他任务。
IOCTL_UCMTCPCI_PORT_CONTROLLER_ALTERNATE_MODE_EXITED 通知客户端驱动程序退出备用模式,以便驱动程序可以执行其他任务。
IOCTL_UCMTCPCI_PORT_CONTROLLER_DISPLAYPORT_CONFIGURED 通知客户端驱动程序,合作伙伴设备上的 DisplayPort 备用模式已配置固定分配,以便驱动程序可以执行其他任务。
IOCTL_UCMTCPCI_PORT_CONTROLLER_DISPLAYPORT_HPD_STATUS_CHANGED 通知客户端驱动程序,显示端口连接的热插拔检测状态已更改,以便驱动程序可以执行其他任务。
  1. 调用 UcmTcpciPortControllerStart 以指示 UcmTcpciCx 启动端口控制器。 UcmTcpciCx 假定控制 USB 类型 C 和电源传递。 启动端口控制器后,UcmTcpciCx 可能会开始将请求放入硬件请求队列。

5. 处理来自端口控制器硬件的警报

示例参考:请参阅 ProcessAndSendAlertsAlert.cpp

客户端驱动程序必须处理从端口控制器硬件接收 (或事件) 的警报,并使用与事件相关的数据将其发送到 UcmTcpciCx。

发生硬件警报时,端口控制器硬件会驱动警报引脚高。 这会导致在步骤 2) 中注册的客户端驱动程序 ISR (调用。 例程服务硬件在PASSIVE_LEVEL中断。 例程确定中断是否是来自端口控制器硬件的警报;如果是这样,它将完成警报的处理,并通过调用 UcmTcpciPortControllerAlert 通知 UcmTcpciCx。

在调用 UcmTcpciPortControllerAlert 之前,客户端负责在UCMTCPCI_PORT_CONTROLLER_ALERT_DATA结构中包含与警报相关的所有相关数据。 客户端提供一组处于活动状态的所有警报,因为硬件可能会同时断言多个警报。

下面是一个任务流,用于报告 CC 状态中的更改。

  1. 客户端收到硬件警报。

  2. 客户端读取警报寄存器并确定处于活动状态的类型警报。

  3. 客户端读取 CC STATUS 寄存器,并在UCMTCPCI_PORT_CONTROLLER_ALERT_DATA中描述 CC STATUS 寄存器的内容。 驱动程序将 AlertType 成员设置为注册的 UcmTcpciPortControllerAlertCCStatus 和 CCStatus 成员。

  4. 客户端调用 UcmPortControllerAlert 将阵列硬件警报发送到 UcmTcpciCx。

  5. 客户端在客户端检索警报信息) 后随时清除警报 (这种情况可能发生

6. 处理从 UcmTcpciCx 接收的请求

示例参考:请参阅 PortControllerInterface.cpp

作为状态机执行的一部分,UcmTcpciCx 需要将请求发送到端口控制器。 例如,它需要设置TRANSMIT_BUFFER。 此请求将移交给客户端驱动程序。 驱动程序使用 UcmTcpciCx 提供的详细信息设置传输缓冲区。 其中大多数请求都转换为客户端驱动程序读取或写入的硬件。 命令必须是异步的,因为 DPM 无法阻止等待硬件传输完成。

UcmTcpciCx 将命令作为 I/O 控制代码发送,描述客户端驱动程序所需的 get/set 操作。 在客户端驱动程序的队列设置中,驱动程序使用 UcmTcpciCx 注册了其队列。 UcmTcpciCx 开始将框架请求对象放置在需要驱动程序操作的队列中。 I/O 控制代码列在步骤 4 的表中。

客户端驱动程序有责任及时完成请求。

客户端驱动程序在完成请求操作后,对框架请求对象调用 WdfRequestComplete,其完成状态为完成状态。

客户端驱动程序可能需要将 I/O 请求发送到另一个驱动程序来执行硬件操作。 例如,在示例中,驱动程序将 SPB 请求发送到 I2C 连接的端口控制器。 在这种情况下,驱动程序无法转发从 UcmTcpciCx 收到的框架请求对象,因为请求对象在 WDM IRP 中可能没有正确的堆栈位置数。 客户端驱动程序必须创建另一个框架请求对象,并将其转发到另一个驱动程序。 客户端驱动程序可以在初始化期间预先分配所需的请求对象,而不是每次从 UcmTcpciCx 获取请求时创建一个请求。 这是可能的,因为 UcmTcpciCx 保证在任何给定时间只有一个未完成的请求。

另请参阅

USB Type-C 端口控制器接口驱动程序类扩展参考