编写 USB 类型 C 连接器驱动程序

在以下情况下,需要编写 USB Type-C 连接器驱动程序:

总结

  • 类扩展和客户端驱动程序使用的 UCM 对象
  • UCM 类扩展提供的服务
  • 客户端驱动程序的预期行为

官方规范

适用于

  • Windows 10

WDF 版本

  • KMDF 版本 1.15
  • UMDF 版本 2.15

重要的 API

介绍 USB 连接器管理器 (UCM) ,用于管理 USB Type-C 连接器和连接器驱动程序的预期行为。

UCM 是使用 WDF 类扩展客户端驱动程序模型设计的。 UcmCx) (类扩展是 Microsoft 提供的 WDF 驱动程序,它提供客户端驱动程序可以调用这些接口来报告有关连接器的信息。 UCM 客户端驱动程序使用连接器的硬件接口,并使类扩展能够识别连接器上发生的事件。 相反,类扩展调用客户端驱动程序为响应操作系统事件而实现的回调函数。

若要在系统上启用 USB Type-C 连接器,必须编写客户端驱动程序。

usb 连接器管理器。

准备阶段

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

    • 存根库, (UcmCxstub.lib) 。 该库转换客户端驱动程序发出的调用,并将其传递给 UcmCx。

    • 头文件 UcmCx.h。

      可以编写在用户模式或内核模式下运行的 UCM 客户端驱动程序。 对于用户模式,它与 UMDF 2.x 库绑定;对于内核模式,则为 KMDF 1.15。 对于任一模式,编程接口都是相同的。

      ucm 的 visual Studio 配置。

  • 确定客户端驱动程序是否支持 USB Type-C 连接器和 USB 电源输送的高级功能。

    此支持使你能够构建具有 USB Type-C 连接器、USB Type-C 扩展坞和附件以及 USB Type-C 充电器的 Windows 设备。 客户端驱动程序报告连接器事件,这些事件允许操作系统针对系统中的 USB 和功耗实施策略。

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

  • 熟悉 UCM 及其与其他 Windows 驱动程序的交互方式。 请参阅 体系结构:Windows 系统的 USB 类型 C 设计

  • 熟悉 Windows Driver Foundation (WDF) 。 推荐阅读: 使用 Windows Driver Foundation 开发驱动程序,由 Penny Orwick 和 Guy Smith 撰写。

UCM 类扩展提供的服务摘要

UCM 类扩展使操作系统随时了解数据和电源角色、充电级别以及协商的 PD 协定的更改。 当客户端驱动程序与硬件交互时,它必须在发生这些更改时通知类扩展。 类扩展提供了一组方法,客户端驱动程序可以使用这些方法发送本主题 () 中讨论的通知。 以下是提供的服务:

数据角色配置

在 USB 类型 C 系统上, (主机或功能) 的数据角色取决于连接器的 CC 引脚的状态。 客户端驱动程序读取 CC 线路 (请参阅 体系结构:Windows 系统的 USB 类型 C 设计) 端口控制器的状态,以确定端口是否已解析为面向上游的端口 (UFP) 或面向下游的端口 (UFP) 。 它将该信息报告给类扩展,以便它可以向 USB 角色切换驱动程序报告当前角色。

注意

USB 角色切换驱动程序用于Windows 10 移动版系统。 在桌面版系统的Windows 10上,类扩展和角色切换驱动程序之间的通信是可选的。 此类系统可能不使用双角色控制器,在这种情况下,不使用角色切换驱动程序。

电源角色和充电

客户端驱动程序读取 USB 类型 C 当前播发,或与合作伙伴连接器协商 PD 电源合同。

  • 在Windows 10 移动版系统上,选择适当的充电器的决定是由软件辅助的。 客户端驱动程序将协定信息报告给类扩展,以便它可以将充电级别发送到充电仲裁驱动程序 (CAD.sys) 。 CAD 选择要使用的当前电量并将充电级别信息转发到电池子系统。
  • 在桌面版系统的Windows 10上,硬件选择适当的充电器。 客户端驱动程序可以选择获取该信息并将其转发到类扩展。 或者,该逻辑可能由其他驱动程序实现。

数据和电源角色更改

协商 PD 合同后,数据角色和权力角色可能会更改。 该更改可能由客户端驱动程序或合作伙伴连接器启动。 客户端驱动程序向类扩展报告该信息,以便它可以相应地重新配置内容。

数据和/或电源角色更新

操作系统可能会确定当前数据角色不正确。 在这种情况下,类扩展会调用驱动程序的回调函数来执行必要的角色交换操作。

Microsoft 提供的 USB 类型 C 策略管理器监视 USB 类型 C 连接器的活动。 Windows 版本 1809 引入了一组编程接口,可用于将客户端驱动程序写入策略管理器。 客户端驱动程序可以参与 USB 类型 C 连接器的策略决策。 使用此集,可以选择编写内核模式导出驱动程序或用户模式驱动程序。 有关详细信息,请参阅 编写 USB 类型 C 策略管理器客户端驱动程序

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

客户端驱动程序负责执行以下任务:

  • 检测抄送行上的更改,并确定合作伙伴的类型,例如 UFP、DFP 等。 为此,驱动程序必须实现 USB 类型 C 规范中定义的完整 Type-C 状态机。
  • 根据在 CC 线上检测到的方向配置 Mux。 这包括打开 PD 发送器/接收器以及处理和响应 PD 消息。 为此,驱动程序必须实现 USB 电源交付 2.0 规范中定义的完整 PD 接收器和发射器状态机。
  • 制定 PD 策略决策,例如将合同协商 (为源或接收器) 、角色交换等。 客户端驱动程序负责确定最合适的协定。
  • 播发和协商备用模式,如果检测到备用模式,则配置 Mux。 客户端驱动程序负责决定要协商的备用模式。
  • 通过连接器控制 VBus/VConn。

1. (UCMCONNECTOR) 初始化 UCM 连接器对象

UCM 连接器对象 (UCMCONNECTOR) 表示 USB 类型 C 连接器,是 UCM 类扩展和客户端驱动程序之间的main句柄。 对象跟踪连接器的操作模式和电源功能。

下面是客户端驱动程序检索连接器的 UCMCONNECTOR 句柄的序列摘要。 在驱动程序的 中执行这些任务

  1. 通过将引用传递给 UCM_MANAGER_CONFIG 结构来调用 UcmInitializeDevice。 在调用 WdfDeviceCreate 之前,驱动程序必须在 EVT_WDF_DRIVER_DEVICE_ADD 回调函数中调用此方法。

  2. UCM_CONNECTOR_TYPEC_CONFIG 结构中指定 USB Type-C 连接器的初始化参数。 这包括连接器的操作模式,无论是面向下游的端口、面向上游端口,还是支持双重角色。 它还指定连接器为电源时的 USB 类型 C 电流级别。 可以设计 USB Type-C 连接器,使其可以充当 3.5 毫米音频插孔。 如果硬件支持该功能,则必须相应地初始化连接器对象。

    在 结构中,还必须注册客户端驱动程序的回调函数以处理数据角色。

    此回调函数与连接器对象相关联,连接器对象由 UCM 类扩展调用。 此函数必须由客户端驱动程序实现。

    EVT_UCM_CONNECTOR_SET_DATA_ROLE 在附加到合作伙伴连接器时,将连接器的数据角色交换为指定角色。

  3. 如果客户端驱动程序希望支持 PD,即处理连接器的 Power Delivery 2.0 硬件实现,则还必须初始化指定 PD 初始化参数 的 UCM_CONNECTOR_PD_CONFIG 结构。 这包括电源流,无论连接器是电源接收器还是电源。

    在 结构中,还必须注册客户端驱动程序的回调函数来处理电源角色。

    此回调函数与连接器对象相关联,连接器对象由 UCM 类扩展调用。 此函数必须由客户端驱动程序实现。

    EVT_UCM_CONNECTOR_SET_POWER_ROLE 在连接到合作伙伴连接器时,将连接器的电源角色设置为指定角色。

  4. 调用 UcmConnectorCreate 并检索连接器的 UCMCONNECTOR 句柄。 请确保在客户端驱动程序通过调用 WdfDeviceCreate 创建框架设备对象后调用此方法。 此调用的适当位置可以位于驱动程序 EVT_WDF_DEVICE_PREPARE_HARDWAREEVT_WDF_DEVICE_D0_ENTRY

EVT_UCM_CONNECTOR_SET_DATA_ROLE     EvtSetDataRole;

NTSTATUS
EvtDevicePrepareHardware(
    WDFDEVICE Device,
    WDFCMRESLIST ResourcesRaw,
    WDFCMRESLIST ResourcesTranslated
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    PDEVICE_CONTEXT devCtx;
    UCM_MANAGER_CONFIG ucmCfg;
    UCM_CONNECTOR_CONFIG connCfg;
    UCM_CONNECTOR_TYPEC_CONFIG typeCConfig;
    UCM_CONNECTOR_PD_CONFIG pdConfig;
    WDF_OBJECT_ATTRIBUTES attr;
    PCONNECTOR_CONTEXT connCtx;

    UNREFERENCED_PARAMETER(ResourcesRaw);
    UNREFERENCED_PARAMETER(ResourcesTranslated);

    TRACE_FUNC_ENTRY();

    devCtx = GetDeviceContext(Device);

    if (devCtx->Connector)
    {
        goto Exit;
    }

    //
    // Initialize UCM Manager
    //
    UCM_MANAGER_CONFIG_INIT(&ucmCfg);

    status = UcmInitializeDevice(Device, &ucmCfg);
    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(
            "UcmInitializeDevice failed with %!STATUS!.",
            status);
        goto Exit;
    }

    TRACE_INFO("UcmInitializeDevice() succeeded.");

    //
    // Create a USB Type-C connector #0 with PD
    //
    UCM_CONNECTOR_CONFIG_INIT(&connCfg, 0);

    UCM_CONNECTOR_TYPEC_CONFIG_INIT(
        &typeCConfig,
        UcmTypeCOperatingModeDrp,
        UcmTypeCCurrentDefaultUsb | UcmTypeCCurrent1500mA | UcmTypeCCurrent3000mA);

    typeCConfig.EvtSetDataRole = EvtSetDataRole;

    UCM_CONNECTOR_PD_CONFIG_INIT(&pdConfig, UcmPowerRoleSink | UcmPowerRoleSource);

    connCfg.TypeCConfig = &typeCConfig;
    connCfg.PdConfig = &pdConfig;

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CONNECTOR_CONTEXT);

    status = UcmConnectorCreate(Device, &connCfg, &attr, &devCtx->Connector);
    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(
            "UcmConnectorCreate failed with %!STATUS!.",
            status);
        goto Exit;
    }

    connCtx = GetConnectorContext(devCtx->Connector);

    UcmEventInitialize(&connCtx->EventSetDataRole);

    TRACE_INFO("UcmConnectorCreate() succeeded.");

Exit:

    TRACE_FUNC_EXIT();
    return status;
}

2.报告合作伙伴连接器附加事件

检测到与合作伙伴连接器的连接时,客户端驱动程序必须调用 UcmConnectorTypeCAttach 。 此调用通知 UCM 类扩展,这会进一步通知操作系统。 此时,系统可能会开始在 USB 类型 C 级别充电。

UCM 类扩展还会通知 USB 角色切换驱动程序 (URS) 。 根据合作伙伴的类型,URS 在主机角色或函数角色中配置控制器。 在调用此方法之前,请确保系统上的 Mux 已正确配置。 否则,如果系统处于功能角色,它将以不正确的速度连接 (高速而不是 SuperSpeed) 。

        UCM_CONNECTOR_TYPEC_ATTACH_PARAMS attachParams;

        UCM_CONNECTOR_TYPEC_ATTACH_PARAMS_INIT(
            &attachParams,
            UcmTypeCPortStateDfp);
        attachParams.CurrentAdvertisement = UcmTypeCCurrent1500mA;

        status = UcmConnectorTypeCAttach(
                    Connector,
                    &attachParams);
        if (!NT_SUCCESS(status))
        {
            TRACE_ERROR(
                "UcmConnectorTypeCAttach() failed with %!STATUS!.",
                status);
            goto Exit;
        }

        TRACE_INFO("UcmConnectorTypeCAttach() succeeded.");

3. 报告 USB 类型 C 播发更改

在初始附加事件中,合作伙伴连接器发送当前播发。 如果播发指定合作伙伴连接器的当前级别,则合作伙伴是面向 USB 类型 C 的端口。 否则,播发指定本地连接器的当前级别,由本地连接器) (UCMCONNECTOR 句柄表示。 此初始播发可能会在连接的生存期内更改。 这些更改必须由客户端驱动程序监视。

如果本地连接器是电源接收器,并且当前播发发生了更改,则客户端驱动程序必须检测当前播发中的更改,并将其报告给类扩展。 在Windows 10 移动版系统上,CAD.sys 和电池子系统使用该信息来调整从源中抽取的电流量。 若要向类扩展报告当前级别的更改,客户端驱动程序必须调用 UcmConnectorTypeCCurrentAdChanged

4. 报告新的协商 PD 合同

如果连接器支持 PD,在初始附加事件之后,连接器及其合作伙伴连接器之间传输了 PD 消息。 在两个合作伙伴之间,协商 PD 合同,确定连接器可以绘制或允许合作伙伴绘制的当前级别。 每次 PD 协定更改时,客户端驱动程序都必须调用这些方法来向类扩展报告更改。

  • 每当客户端驱动程序获取源功能播发 (未经请求或从合作伙伴) 时,都必须调用这些方法。 仅当合作伙伴是源时,本地连接器 (接收器) 从合作伙伴获取未经请求的广告。 此外,即使合作伙伴当前是接收器) ,本地连接器也可以显式请求能够成为源 (的合作伙伴的源功能。 通过向合作伙伴发送 Get_Source_Caps 消息来完成交换。
  • 相反,每次本地连接器 (源) 向合作伙伴播发源功能时,客户端驱动程序都必须调用这些方法。 此外,当本地连接器收到来自合作伙伴 的Get_Source_Caps 消息时,它必须使用本地连接器的源功能进行响应。

5. 报告电池充电状态

如果充电级别不足,客户端驱动程序可以通知 UCM 类扩展。 类扩展将此信息报告给操作系统。 系统使用该信息向用户显示充电器未以最佳方式为系统充电的通知。 可以通过以下方法报告充电状态:

这些方法指定充电状态。 如果报告的级别为 UcmChargingStateSlowChargingUcmChargingStateTrickleCharging (请参阅 UCM_CHARGING_STATE) ,操作系统会显示用户通知。

6. 报告PR_Swap/DR_Swap事件

如果连接器收到电源角色 (PR_Swap) 或数据角色 (DR_Swap) 交换来自合作伙伴的消息,则客户端驱动程序必须通知 UCM 类扩展。

  • UcmConnectorDataDirectionChanged

    处理 PD DR_Swap消息后调用此方法。 完成此调用后,操作系统会将新角色报告给 URS,这会删除现有角色驱动程序并加载新角色的驱动程序。

  • UcmConnectorPowerDirectionChanged

    在处理 PD PR_Swap消息后调用此方法。 PR_Swap后,需要重新谈判 PD 协定。 客户端驱动程序必须通过调用 步骤 4 中所述的方法来报告 PD 协定协商。

7. 实现回调函数以处理电源和数据角色交换请求

UCM 类扩展可能会收到更改连接器的数据或电源方向的请求。 在这种情况下,如果连接器实现 PD) ,它将 (调用客户端驱动程序实现EVT_UCM_CONNECTOR_SET_DATA_ROLE 和EVT_UCM_CONNECTOR_SET_POWER_ROLE 回调函数。 客户端驱动程序之前在调用 UcmConnectorCreate 时注册了这些函数。

客户端驱动程序使用硬件接口执行角色交换操作。

  • EVT_UCM_CONNECTOR_SET_DATA_ROLE

    在回调实现中,客户端驱动程序应:

    1. 向端口伙伴发送 PD DR_Swap消息。
    2. 调用 UcmConnectorDataDirectionChanged 以通知类扩展消息序列已成功完成或未成功。
    EVT_UCM_CONNECTOR_SET_DATA_ROLE     EvtSetDataRole;
    
    NTSTATUS
    EvtSetDataRole(
        UCMCONNECTOR  Connector,
        UCM_TYPE_C_PORT_STATE DataRole
        )
    {
        PCONNECTOR_CONTEXT connCtx;
    
        TRACE_INFO("EvtSetDataRole(%!UCM_TYPE_C_PORT_STATE!) Entry", DataRole);
    
        connCtx = GetConnectorContext(Connector);
    
        TRACE_FUNC_EXIT();
    
        return STATUS_SUCCESS;
    }
    
  • EVT_UCM_CONNECTOR_SET_POWER_ROLE

    在回调实现中,客户端驱动程序应:

    1. 向端口合作伙伴发送 PD PR_Swap消息。
    2. 调用 UcmConnectorPowerDirectionChanged 以通知类扩展消息序列已成功完成或未成功。
    EVT_UCM_CONNECTOR_SET_POWER_ROLE     EvtSetPowerRole;
    
    NTSTATUS
    EvtSetPowerRole(
        UCMCONNECTOR Connector,
        UCM_POWER_ROLE PowerRole
        )
    {
        PCONNECTOR_CONTEXT connCtx;
    
        TRACE_INFO("EvtSetPowerRole(%!UCM_POWER_ROLE!) Entry", PowerRole);
    
        connCtx = GetConnectorContext(Connector);
    
        //PR_Swap operation.
    
        TRACE_FUNC_EXIT();
    
        return STATUS_SUCCESS;
    }
    

注意

客户端驱动程序可以异步调用 UcmConnectorDataDirectionChangedUcmConnectorPowerDirectionChanged ,这并非来自回调线程。 在典型的实现中, 类扩展调用回调函数,导致客户端驱动程序启动硬件事务以发送消息。 事务完成后,硬件会通知驱动程序。 驱动程序调用这些方法以通知类扩展。

8.报告合作伙伴连接器分离事件

当与合作伙伴连接器的连接结束时,客户端驱动程序必须调用 UcmConnectorTypeCDetach 。 此调用通知 UCM 类扩展,这会进一步通知操作系统。

用例示例:连接到电脑的移动设备

当运行 Windows 10 移动版 的设备通过 USB Type-C 连接连接到运行 Windows 10 桌面版的电脑时,操作系统将确保移动设备是面向上游的端口 (UFP) ,因为 MTP 仅在该方向上工作。 在此方案中,数据角色更正的顺序如下:

  1. 在移动设备上运行的客户端驱动程序通过调用 UcmConnectorTypeCAttach 报告附加事件,并将合作伙伴连接器报告为面向下游的端口 (UFP) 。
  2. 客户端驱动程序通过调用 UcmConnectorPdPartnerSourceCapsUcmConnectorPdConnectionStateChanged 报告 PD 协定。
  3. UCM 类扩展通知 USB 设备端驱动程序,导致这些驱动程序响应来自主机的枚举。 操作系统信息通过 USB 交换。
  4. UCM 类扩展 UcmCx 调用客户端驱动程序的回调函数以更改角色: EVT_UCM_CONNECTOR_SET_DATA_ROLEEVT_UCM_CONNECTOR_SET_POWER_ROLE

注意

如果两个Windows 10 移动版设备相互连接,则不会执行角色交换,并且通知用户该连接不是有效的连接。