一次失败的问题解决经历

2022 年 12 月 22 日

这周因为染上 Omicron,获得了几天在家办公的时间。状态不是太好,很难做些实际的工作,所以整理一下以前的笔记。大部分都没法发出来,只有这篇还有一点点价值。

问题背景

正在开发的 Open Harmony 设备,使用 USB 连接 PC 时,默认是作为一个 hdc 的客户端设备连接的。但项目上,希望这个 USB 同时可以作为一个串口来与设备通信,也就是说,希望能够做到 hdc 和 串口 功能同时可用。

经过研究,我们发现可以通过 configfs 去设置设备端,使得 windows 上可以识别到一个 USB 复合设备,该复合设备同时包含了 hdc 接口和串口。并且串口可用。

但经过如此设置,hdc 的 windows 版本完全无法连接到设备,连 "list targets" 都无法正常工作。

问题重现方式

首先,使用调试串口或 hdc shell 连接到开发板,执行如下命令以配置 USB 设备为复合设备:

# hdc shell or debug serial
cd /config/usb_gadget/g1/functions
mkdir acm.0
cd /config/usb_gadget/g1
ln -s functions/acm.0 configs/b.1/f2

然后重新插拔 USB,或者继续在 hdc shell 中使用命令重置 USB

# hdc shell or debug serial
echo none > /config/usb_gadget/g1/UDC
echo fcc00000.dwc3 > /config/usb_gadget/g1/UDC

此时在 windows 中按照连接组织设备,可以看到原来的 "HDC 设备变成了一个 USB Composite Device,下面包含两个设备,HDC InterfaceUSB 串行设备 (COM8)(后者对应的串口可能会有所变化)。现在如果使用调试串口在设备端打开该串口,再用 PC 端串口工具连接,可以验证出串口设备是正常工作的。命令是:

# debug serial
microcom -s 115200 /dev/ttyGS0

但此时 hdc 常规命令都已经无法正常使用。

原因分析

经过分析,发现问题出在 hdc host,也就是其 PC 端的实现上。下文的 hdc 均指的是 hdc host。

hdc 使用了 libusb 来跟 USB 设备通信。libusb 是一个跨平台的库,hdc 也是跨平台的应用,编译时使用了 mingw。

hdc 在后台有一个线程在不断扫描 USB 节点变化,将合法 USB 设备的序列号加入一个本地列表。(简化后的关键流程)按照如下顺序执行:

  1. libusb_get_device_list,获取 USB 设备列表。
  2. libusb_open,打开 USB 设备。
  3. libusb_get_device_descriptor 获取设备描述,判断合法性后,将设备序列号并加入本地列表。

常规情况下,libusb 获取到 hdc 设备句柄,打开都不会有问题。

但在设备端配置后,libusb 只能获取到 Composite Device 句柄,然后在 libusb_open 时就会失败,错误是:“Entity not found”。

有几个相关的问题的讨论,但似乎没有人去解决此问题。参考以下链接。

对于此问题,有人提出将驱动强制切换为 winusb 可以解决此问题。经过实验,这样做虽然可以保证 libusb_open 成功,但 hdc 后续代码仍有问题。另外,这意味着要重新生成 windows 侧的 hdc 驱动,因此我没有在此方向上继续分析。

针对 libusb 的接口做了一些分析,我发现 libusb 似乎无法正常处理 Windows 上的 Composite Device。作为参考,在上面的第一个链接里可以看到如下回复:

At worst, it is an impedance mismatch between the Linux philosophy and the Windows philosophy. In the Windows world. your hardware really is exposed as 3 separate devices: the composite device, a WinUSB device, and a communication device. You, as a driver writer, would write an INF that matches exactly the device you want to target. The fact that it happened to be part of a larger composite device is supposed to be irrelevant. Windows wants to abstract away those details.

In the libusb world, things have been geared to think of the whole VID/PID as one device, and then you claim the interfaces you happen to want to drive.

简单说,回答者认为这个问题来源于 Windows/Linux 设计理念的差异。提出的一种解决思路是,作为 windows driver developer,写一个特定的 INF 来描述这个 composite device。

结论

hdc 与 串口 作为 Composite Device 存在时,hdc 所依赖的 libusb 无法正确处理与 hdc 接口的连接。

要解决此问题,有几种可能的方向,其工作量都难以预估:

  • 专用 INF 驱动,需要研究 Windows Driver 相关知识,且不保证不需要修改 hdc 源码。从产品角度而言,增加了驱动安装的操作步骤。
  • 修改 hdc 源码,将 libusb 替换为 windows native API。这个工作量比重写 hdc 可能还要大一些。

因此,我最终放弃了 hdc 和 串口作为复合设备同时工作的想法。通过显式的界面切换 USB 功能方式解决了这个 USB 通信问题。

Top