USB 驱动开发 --- Gadget 设备连接 Windows 免驱

打印 上一主题 下一主题

主题 1028|帖子 1028|积分 3084

环境信息

测试使用 DuoS(Arm CA53, Linux 5.10) 搭建方案验证环境,使用 USB sniff + Wirekshark 抓包分析,合照如下:

注:左侧图中设备:1. 蓝色,USB sniff 非侵入工 USB 抓包工具;2. 绿色,DuoS 开发板,
系统初始化

查看 DuoS 上作为 USB设备时,初始化流程
  1. # cat /etc/inittab
  2. ...
  3. # now run any rc scripts
  4. ::sysinit:/etc/init.d/rcS
  5. # cat /etc/init.d/rcS
  6. ...
  7. for i in /etc/init.d/S??* ;do
  8.         ...
  9.         case "$i" in       
  10.                 ...
  11.                 *)
  12.                         $i start
  13. # cat /etc/init.d/S99user
  14. ...
  15. export USERDATAPATH=/mnt/data/
  16. export SYSTEMPATH=/mnt/system/
  17. case "$1" in
  18.   start)
  19.     ...
  20.       if [ -f $SYSTEMPATH/usb.sh ]; then
  21.         . $SYSTEMPATH/usb.sh &
  22.       fi
  23. # ll /mnt/system/usb.sh
  24. lrwxrwxrwx 1 1000 1000 10 Dec 19  2024 /mnt/system/usb.sh -> usb-ncm.sh*
复制代码
由软链可知,当前 DuoS 作为 USB 设备工作,功能为 NCM。具体查看ncm.sh脚本实现
  1. #----> device/generic/rootfs_overlay/duos/mnt/system/usb-ncm.sh
  2. ...                # GPIO 相关控制,用于切换 USB 通道与 HUB 控制
  3. /etc/uhubon.sh     device >> /tmp/ncm.log 2>&1
  4. /etc/run_usb.sh probe ncm >> /tmp/ncm.log 2>&1
  5. /etc/run_usb.sh start ncm >> /tmp/ncm.log 2>&1
复制代码
可知,除了配置外部 GPIO 修改以 USB 通路和电源配置外,初始化过程大抵分为两个阶段:

  • USB OTG(ID 管脚)控制;
  • Gadget 设备创建与启用;
阶段一、USB OTG 控制

初始化脚本中 OTG 控制实现如下:
  1. #----> device/generic/br_overlay/common/etc/uhubon.sh
  2. ...
  3. case "$1" in
  4.   ...
  5.   device)
  6.         echo device > /proc/cviusb/otg_role
  7.         ;;       
复制代码
查找 /proc 子系统下otg_role节点功能归属
  1. $ grep -wrn "otg_role" linux_5.10/drivers/
  2. linux_5.10/drivers/usb/dwc2/platform.c:395:#define CVIUSB_ROLE_PROC_NAME "cviusb/otg_role"
复制代码
源码文件
  1. //----> linux_5.10/drivers/usb/dwc2/platform.c
  2. #define CVIUSB_ROLE_PROC_NAME "cviusb/otg_role"
  3. static int dwc2_driver_probe(struct platform_device *dev)
  4.     ...
  5.         hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
  6.         ...
  7.         hsotg->dev = &dev->dev;
  8.         ...
  9.         dwc2_lowlevel_hw_init(hsotg);
  10.         ...
  11.         cviusb_proc_dir = proc_mkdir("cviusb", NULL);                                        // 创建 /proc/cviusb 目录; 创建其下节点:otg_role
  12.         cviusb_role_proc_entry = proc_create_data(CVIUSB_ROLE_PROC_NAME, 0644, NULL, &role_proc_ops, hsotg);
  13. #define CVIUSB_ROLE_PROC_NAME "cviusb/otg_role"
  14. static const struct proc_ops role_proc_ops = {
  15.         ...
  16.         .proc_write                = role_proc_write,
  17. static ssize_t role_proc_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
  18.     ...
  19.         sel_role_hdler(hsotg, procdata);
  20.                 n = ARRAY_SIZE(sel_role);
  21.                 for (i = 0; i < n; i++) {
  22.                         if (!strcmp(str, sel_role[i])) {
  23.                                 t = i;
  24.                                 break;   
  25.                 hsotg->cviusb.id_override = t;
  26.                 dwc2_set_hw_id(hsotg, t);                                                                // is_dev = t;
  27.                         if (is_dev) {
  28.                                 iowrite32((ioread32((void *)hsotg->cviusb.usb_pin_regs) & ~0x0000C0) | 0xC0, (void *)hsotg->cviusb.usb_pin_regs);
  29. // 截取
  30. //        cviusb->usb_pin_regs = ioremap(0x03000048, 0x4);               
复制代码
参考 SG2000 技术手册,查看 usb_phy_ctrl_reg 寄存器如下:

可知:向 otg_role 节点写入 device,在硬件上控制了 USB PHY ID 线的驱动和控制方式,影响 USB OTG 功能中 ID 线的使用。
阶段二、USB Gadget 设备创建

初始化脚本中 Gadget 设备创建与启动实现如下,可分为 probe、start 两个动作:
  1. #----> device/generic/br_overlay/common/etc/run_usb.sh
  2. CVI_DIR=/tmp/usb
  3. CVI_GADGET=$CVI_DIR/usb_gadget/cvitek
  4. case "$1" in
  5.   start)
  6.         start
  7.         ;;
  8.   ...
  9.   probe)
  10.         probe
  11. probe() {
  12.   mkdir $CVI_DIR
  13.   if [ ! -d $CVI_DIR/usb_gadget ]; then  
  14.     mount none $CVI_DIR -t configfs                                  # 挂载 USB Configfs
  15.     mkdir $CVI_GADGET                                                                # 创建gadget设备:cvitek
  16.     echo $VID             > $CVI_GADGET/idVendor        #        设置设备信息:VID、PID
  17.     echo $PID             > $CVI_GADGET/idProduct
  18.     mkdir $CVI_GADGET/strings/0x409                                    # 创建dadget设备 语言信息
  19.     ...
  20.     mkdir $CVI_GADGET/configs/c.1                                        # 创建gadget设备 配置
  21.     ...
  22.     echo 0xEF             > $CVI_GADGET/bDeviceClass                        # 设备类型、子类型、协议信息
  23.     echo 0x02             > $CVI_GADGET/bDeviceSubClass
  24.     echo 0x01             > $CVI_GADGET/bDeviceProtocol  
  25.         ...
  26.   if [ "$CLASS" = "ffs.adb" ] ; then ...
  27.   else
  28.             mkdir $CVI_GADGET/functions/$CLASS.usb$FUNC_NUM                        # 当前为 NCM 功能设备
  29.   if [ "$CLASS" = "ncm" ] ; then
  30.     ln -s $CVI_FUNC/ncm.usb$FUNC_NUM $CVI_GADGET/configs/c.1        # 关联  function
  31.   ...            
  32. start() {
  33.   ...
  34.   if [ -d $CVI_GADGET/functions/ffs.adb ]; then ... else
  35.     UDC=`ls /sys/class/udc/ | awk '{print $1}'`
  36.     echo ${UDC} >$CVI_GADGET/UDC                                                                # Gadget NCM 功能设备启用,实际对应外设:4340000.usb
复制代码
可知 Gadget 设备依靠以 configfs 格式挂载的目次 /tmp/usb,使用文件操纵(mkdir、ln)情势对 Gadget 设备 cvitek 举行配置与管理,涉及:


  • 配置根本信息:PID、VID和语言信息;
  • 设置配置文件:语言、功率和接口等信息;
  • 创立功能设备:当前仅有一个 NCM 功能;
  • 关联功能设备:关联 配置(configuration) 与 功能(function);
  • 启动功能设备:向 UDC 写入需要启动的 功能设备;
方案配景

参考 USB 中文网相干文档可知:WinUSB是微软提供的一个USB设备的通用驱动程序。使用这个驱动用户不需要编写内核层的驱动程序就能访问USB设备。WCID则是USB驱动一种新的匹配机制,通常USB设备都是通过VID和PID来举行匹配的,而使用了WCID之后,设备不通过VID和PID来匹配驱动,而是通过一个叫做 WCID(Windows Compatible ID) 来匹配,这样就不消为每一个VID和PID差异的设备编写INF文件了。
在完成WCID匹配之后,不需要编写inf文件,系统会根据设备范例来安装驱动,最终实现免驱。
问题近况

收集现有环境信息,已知 DuoS 上电启动后将以 USB 设备 NCM 功能工作,接入 Windows 后无法免驱使用,设备与抓包信息如下:

由设备管理器 其他设备->CDC NCM 可知,当前 DuoS 未能正常免驱使用。
方案开发

参考 《简朴几步,让自定义USB设备也能免驱动运行》,分别测试 微软系统描述符 1.0 与 2.0 方式实现免驱。
开发验证一、OS 1.0 免驱方案

总结 《使用微软系统描述符1.0制作免驱动自定义USB设备》方案,实施步骤如下:

  • 先读取设备描述符和配置描述符,判定设备描述符中的bcdUSB字段,检查设备支持的USB版本号是否大于便是2.0;
  • 假如bcdUSB大于便是 0x0200,主机请求 OS字符串描述符,请求索引 index 值为0xee;
  • 设备应答 OS 字符串描述符(总长度为18,内容unicode编码为 ”MSFT100″,vendor code由厂商自己定义);
  • 主机对OS字符串描述验证通事后,发出功能描述符请求(两种):

    • 设备应答 扩展兼容ID描述符(请求索引 wIndex 值为 0x04);
    • 设备应答 扩展属性描述符(请求索引 wIndex 值为 0x05);

  • 完成枚举,免驱使用;
通过兼容ID,系统已经知道了设备需要WinUSB驱动;通过扩展属性,告诉系统我们设备的GUID是什么。但 Windows系统对扩展“属性描述”和“兼容ID”处理逻辑不太一样,假如设备的“扩展属性“已经有了,就不会再去获取。而是否已经存在“扩展属性”可查看注册表:


  • 路径:[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_$<VID>&ID_$<ID>\$<SPEC>\Device Parameters]
  • 键值对:DeviceInterfaceGUIDs
测试验证

验证方案,对原设备直接抓包测试,文件《DuoS_原USB从设备.pcapng》;

从USB抓包可知:原设备直接插入 Win10 主机,可以抓取枚举过程中的数据包。在此底子上有选择的过滤出 GET DESCRIPTOR 请求包,不能查找到 Index 为 0xEE 的 ”OS字符串描述符“ 请求包,所以只能说明当前测试方式无法触发 Win10 主机的 “微软系统描述符1.0" 机制。
理论中 ”设备支持的USB版本号是否大于便是2.0,即bcdUSB大于便是0x0200“ 是 须要不充实 条件,缘故原由另外再做探索。在 “微软系统描述符1.0" 机制之外还有个 ”微软系统描述符2.0“ 机制可做测试。
开发验证二、OS 2.0 免驱方案

总结《使用微软系统描述符2.0制作免驱动自定义USB设备》,实施步骤如下:

  • 先读取设备描述符和配置描述符,判定设备描述符中的 bcdUSB 字段是否大于便是 0x0210;
  • 假如 bcdUSB 大于便是 0x0210,主机请求 BOS 描述符(范例 bDescriptorType 值为0x0f);
  • 设备应答 BOS 描述符(总长度为33(5 + 28),bDevCapabilityType 值为 0x05,UUID、VendorCode 由厂商自己定义);
  • 主机对 BOS 描述验证通事后,依据 bVendorCode 发出 OS2.0 描述符集请求(索引 wIndex 值为 0xC0),包含:

    • WCID20 兼容ID 描述符(范例 wDescriptorType 值为 0x03; cCID_8 值为 ”WINUSB“);
    • WCID20 注册表属性 描述符(范例 wDescriptorType 值为 0x04;内容为接口 GUID 键对,此中 GUID 值由厂商自己定义);

  • 完成枚举,免驱使用;
bDeviceCapabilityType = 0x05,在 USB 尺度中 5 是保留值,在 Windows 系统中定义为 Platform Capability BOS Descriptor,里面包含了uuid,操纵系统版本,vendor code信息。
比较起来,2.0 不再需要OS字符串描述符,而是使用了USB尺度的 BOS 描述符来获取设备的vendor code。然后再通过一个叫做描述符集的描述符一次性返回全部接口全部配置的compat ID和属性。
测试验证一、bcdUSB ≥ 0x0210

由抓包查看 DEVICE 应答包的 bcdUSB 值

可知当前设备 bcdUSB 值为 0x0200 不符合 ”大于便是 0x0210“ 的要求,所以需要调试 bcdUSB 值实验买通方案步骤2 ---- bcdUSB 大于便是 0x0210,主机请求 BOS 描述符。
修改 bcdUSB 值按递进关系分为两步,分别是:configfs 下 Gadget 设备 bcdUSB 节点修改,若失败,则再从驱动源码级别修改。
修改验证一、configfs 配置,设备节点 bcdUSB

configfs,直接修改 gadget 驱动节点 bcdUSB
  1. # 查看 gadget 设备内容
  2. $ ls /tmp/usb/usb_gadget/cvitek/
  3. UDC              bMaxPacketSize0  functions/       os_desc/
  4. bDeviceClass     bcdDevice        idProduct        strings/
  5. bDeviceProtocol  bcdUSB           idVendor
  6. bDeviceSubClass  configs/         max_speed
  7. # 查看当前 bcdUSB 值
  8. $ cat /tmp/usb/usb_gadget/cvitek/bcdUSB
  9. 0x0200
  10. # 修改当前值为 0x0210
  11. $ echo 0x0210 > /tmp/usb/usb_gadget/cvitek/bcdUSB
  12. # 读出验证
  13. $ cat /tmp/usb/usb_gadget/cvitek/bcdUSB
  14. 0x0210
  15. # 重新插拔后
  16. # 两次读取
  17. $ cat /tmp/usb/usb_gadget/cvitek/bcdUSB
  18. 0x0200
复制代码
由测试可知:configfs 方式生成的 Gadget 设备属性 bcdUSB 无法直接修改。
修改验证二、驱动源码,bcdUSB 初始化

寻找 bcdUSB 赋值时机,在查找之前需要对 USB Gadget 驱动有个大抵认识,以下逻辑框图大抵可转达 Win10 主机与 DuoS 连接与功能层级:

由图可料想:与 configfs 下 Gadget 设备 bcdUSB 节点最直接关联的是 composite framework(以下简称 libcomposite 框架) ,现实查找过程如下:
  1. $ grep -wrn "bcdUSB" linux_5.10/drivers/usb/
  2. ...
  3. linux_5.10/drivers/usb/gadget/configfs.c:335:CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB);
复制代码
源码跟读
  1. //----> linux_5.10/drivers/usb/gadget/configfs.c
  2. CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB);
  3. static struct configfs_attribute *gadget_root_attrs[] = {
  4.         ...
  5.         &gadget_dev_desc_attr_bcdUSB,
  6.         &gadget_dev_desc_attr_UDC,
  7. #define GI_DEVICE_DESC_SIMPLE_R_u16(__name)        \
  8. static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \
  9.                         char *page)        \
  10. {        \
  11.         return sprintf(page, "0x%04x\n", \
  12.                 le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \
  13. }
  14. static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item, const char *page, size_t len) {
  15.     ...
  16.         to_gadget_info(item)->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB);
  17. GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB);                                        # 宏展开后如下:
  18. static ssize_t gadget_dev_desc_bcdUSB_show(struct config_item *item, char *page) {       
  19.         return sprintf(page, "0x%04x\n", le16_to_cpup(&to_gadget_info(item)->cdev.desc.bcdUSB));         # 关键 cdev->desc.bcdUSB     
复制代码
梳理 usb_udc 设备 与 usb_composite_dev 设备的关系,而 usb_composiste_dev 设备 又是 libcomposite 框架的核心结构之一,所以继续在 libcomposite框架下查找:
  1. $ grep -wrn "bcdUSB" linux_5.10/drivers/usb/
  2. ...
  3. linux_5.10/drivers/usb/gadget/composite.c:1773:                                 cdev->desc.bcdUSB = cpu_to_le16(0x0320);
  4. linux_5.10/drivers/usb/gadget/composite.c:1776:                                 cdev->desc.bcdUSB = cpu_to_le16(0x0210);
  5. linux_5.10/drivers/usb/gadget/composite.c:1780:                                 cdev->desc.bcdUSB = cpu_to_le16(0x0201);
  6. linux_5.10/drivers/usb/gadget/composite.c:1782:                                 cdev->desc.bcdUSB = cpu_to_le16(0x0201);
复制代码
源码跟读
  1. //----> linux_5.10/drivers/usb/gadget/composite.c
  2. /*
  3. * The setup() callback implements all the ep0 functionality that's
  4. * not handled lower down, in hardware or the hardware driver(like
  5. * device and endpoint feature flags, and their status).  It's all
  6. * housekeeping for the gadget function we're implementing.  Most of
  7. * the work is in config and function specific setup.
  8. */
  9. int composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) {
  10.         struct usb_composite_dev *cdev = get_gadget_data(gadget);
  11.         struct usb_request       *req = cdev->req;
  12.     ...
  13.         if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)                                // 非标准描述符请求,走 unkown 分支处理
  14.                 goto unknown;
  15.         switch (ctrl->bRequest) {                                                                                                        // 检查 请求类型
  16.         /* we handle all standard USB descriptors */
  17.         case USB_REQ_GET_DESCRIPTOR:                                                                                                // GET 类型
  18.                 if (ctrl->bRequestType != USB_DIR_IN)
  19.                         goto unknown;
  20.                 switch (w_value >> 8) {
  21.                 case USB_DT_DEVICE:                                                                                                                // 设备描述符
  22.                         ...
  23.                         if (gadget_is_superspeed(gadget)) {                                                                        // 超高速设备(USB 3.0 标准)
  24.                                 if (gadget->speed >= USB_SPEED_SUPER) {
  25.                                         cdev->desc.bcdUSB = cpu_to_le16(0x0320);
  26.                                         cdev->desc.bMaxPacketSize0 = 9;
  27.                                 } else {
  28.                                         cdev->desc.bcdUSB = cpu_to_le16(0x0210);
  29.                                 }
  30.                         } else {                                                                                                                        // 高速(USB 2.0)、全速(USB 1.1)、低速(USB 1.0)
  31.                                 if (gadget->lpm_capable)                                                                                // 支持 链路电源管理,USB 2.0 支持部分
  32.                                         cdev->desc.bcdUSB = cpu_to_le16(0x0201);
  33.                                 else
  34.                                         cdev->desc.bcdUSB = cpu_to_le16(0x0200);
  35.                         }
复制代码
结合现实 DuoS SOC 核心计划,其 USB 控制器使用 DWC2 IP核心,支持 USB 2.0 规范所以只能走到最后一个分支 ---- bcdUSB = 0x0200。
可靠方案应考虑 lpm_capable 如何买通以买通倒数第二个分支 ---- bcdUSB = 0x0201。为了快速验证,将最后一个分支修改为 0x020,补丁如下:
  1. diff --git a/linux_5.10/drivers/usb/gadget/composite.c b/linux_5.10/drivers/usb/gadget/composite.c
  2. index 1a556a628..364ef4a0c 100644
  3. --- a/linux_5.10/drivers/usb/gadget/composite.c
  4. +++ b/linux_5.10/drivers/usb/gadget/composite.c
  5. @@ -1685,7 +1685,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
  6.                                 if (gadget->lpm_capable)
  7.                                         cdev->desc.bcdUSB = cpu_to_le16(0x0201);
  8.                                 else
  9. -                                       cdev->desc.bcdUSB = cpu_to_le16(0x0200);
  10. +                                       cdev->desc.bcdUSB = cpu_to_le16(0x0210);
  11.                         }
  12.                         value = min(w_length, (u16) sizeof cdev->desc);
复制代码
稳妥起见,查看相干模块是否纳入构建以保证修改可以最终收效。先查看对应源码的构建 Makefile 文件:
  1. ...
  2. obj-$(CONFIG_USB_LIBCOMPOSITE)        += libcomposite.o                                                                # libcomposite.ko,即是文中提及的 libcomposite 框架
  3. libcomposite-y                        := usbstring.o config.o epautoconf.o
  4. libcomposite-y                        += composite.o functions.o configfs.o u_f.o                                # 当前修改文件对应编译产物:composite.o
  5. obj-$(CONFIG_USB_GADGET)        += udc/ function/ legacy/
复制代码
查看配置文件生成的 .config 文件是否开启 CONFIG_USB_LIBCOMPOSITE:
  1. $ grep -wrn -E "CONFIG_USB_LIBCOMPOSITE|CONFIG_USB_GADGET" linux_5.10/build/sg2000_milkv_duos_glibc_arm64_sd/.config
  2. 2579:CONFIG_USB_GADGET=y
  3. 2605:CONFIG_USB_LIBCOMPOSITE=y
复制代码
可知:libcomposite 内核模块 内嵌至Image镜像中,修改驱动后需要整编Linux内核。编译命令如下:
  1. $ source  build/cvisetup.sh
  2. $ defconfig sg2000_milkv_duos_glibc_arm64_sd
  3. # 编译内核;更新fip.bin文件
  4. build_kernel
复制代码
设备更新内核(包含于 boot.sd),操纵命令:
  1. #        分区挂载
  2. #                 创建boot分区,挂载目录:/mnt/boot
  3. mkdir -p /mnt/boot/ && mount /dev/mmcblk0p1 /mnt/boot/
  4. #         固件下载, SCP
  5. SDK_ROOT=Source/01-SG200x/SDK_SG200x_V2
  6. scp gaoyang3513@192.168.8.100:${SDK_ROOT}/install/soc_sg2000_milkv_duos_glibc_arm64_sd/rawimages/boot.sd /mnt/boot/
  7. #         重启生效
  8. reboot
复制代码
抓包测试:

结论:在bcdUSB调整为0x0201后,Windows 枚举USB设备时会读取 BOS(OS字符串描述符)
至此买通步骤2,紧跟需要买通步骤3 ---- 设备应答 OS 字符串 描述符。
测试验证二、应答 “BOS 描述符”

在 libcomposite 框架下,同步骤2 ---- 应答 Device 描述符处理的 composite_setup 函数下就有 BOS 描述符请求的应答处理逻辑。
可靠方案应考虑: libcomposite 连接了底层 UDC 驱动和上层 Function 驱动,setup 是一个自下由上贯通的流程处理,以何时、何种方式合理地对 BOS 描述符举行应答 。 当前需要快速验证,参考 《使用微软系统描述符2.0制作免驱动自定义USB设备》给出的 BOS 描述符样式,直接在 libcomposite 框架 setup中应答 BOS 请求,补丁如下:
  1. diff --git a/linux_5.10/drivers/usb/Makefile b/linux_5.10/drivers/usb/Makefile
  2. index 1c1c1d659..0da7c468f 100644
  3. --- a/linux_5.10/drivers/usb/Makefile
  4. +++ b/linux_5.10/drivers/usb/Makefile
  5. @@ -66,3 +66,5 @@ obj-$(CONFIG_USBIP_CORE)        += usbip/
  6. obj-$(CONFIG_TYPEC)                += typec/
  7. obj-$(CONFIG_USB_ROLE_SWITCH)        += roles/
  8. +
  9. +subdir-ccflags-y += -DDEBUG -DCONFIG_GAOYANG
  10. diff --git a/linux_5.10/drivers/usb/gadget/composite.c b/linux_5.10/drivers/usb/gadget/composite.c
  11. index 1a556a628..0b772c924 100644
  12. --- a/linux_5.10/drivers/usb/gadget/composite.c
  13. +++ b/linux_5.10/drivers/usb/gadget/composite.c
  14. @@ -664,6 +717,35 @@ static int bos_desc(struct usb_composite_dev *cdev)
  15.         struct usb_ext_cap_descriptor        *usb_ext;
  16.         struct usb_dcd_config_params        dcd_config_params;
  17.         struct usb_bos_descriptor        *bos = cdev->req->buf;
  18. +#if defined (CONFIG_GAOYANG)
  19. +        /* WCID20 device capability descriptor */
  20. +        #define        USB_CAP_TYPE_WCID           5
  21. +        #define USB_CAP_WCID_SIZE           0x1C
  22. +        #define WINUSB20_WCID_VENDOR_CODE   0x00
  23. +        #define WINUSB20_WCID_DESC_SET_SIZE 162
  24. +
  25. +        struct usb_cap_wcid20_descriptor {
  26. +                __u8   bLength;
  27. +                __u8   bDescriptorType;
  28. +                __u8   bDevCapabilityType;
  29. +                __u8   bReserved ;
  30. +                __u8   bPlatformCapabilityUUID_16[16];
  31. +                __u32  dwWindowsVersion;
  32. +                __le32 wDescriptorSetTotalLength;
  33. +                __u8   bVendorCode;
  34. +                __u8   bAltEnumCode;
  35. +        } __attribute__((packed));
  36. +
  37. +        char winusb20_wcidbos_uuid[] = {
  38. +                0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c,
  39. +                0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, 0x9f,
  40. +        };
  41. +
  42. +        uint32_t winusb20_wcidbos_version = 0x06030000;
  43. +
  44. +        struct usb_cap_wcid20_descriptor *usb_cap_wcid;
  45. +
  46. +#endif // defined (CONFIG_GAOYANG)
  47.         unsigned int                        besl = 0;
  48.         bos->bLength = USB_DT_BOS_SIZE;
  49. @@ -708,6 +789,19 @@ static int bos_desc(struct usb_composite_dev *cdev)
  50.         usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT |
  51.                                             USB_BESL_SUPPORT | besl);
  52. +#if defined (CONFIG_GAOYANG)
  53. +        usb_cap_wcid = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
  54. +        le16_add_cpu(&bos->wTotalLength, USB_CAP_WCID_SIZE);
  55. +        bos->bNumDeviceCaps++;
  56. +
  57. +        usb_cap_wcid->bLength                   = USB_CAP_WCID_SIZE;
  58. +        usb_cap_wcid->bDescriptorType           = USB_DT_DEVICE_CAPABILITY;
  59. +        usb_cap_wcid->bDevCapabilityType        = USB_CAP_TYPE_WCID;
  60. +        usb_cap_wcid->dwWindowsVersion          = winusb20_wcidbos_version;
  61. +        usb_cap_wcid->wDescriptorSetTotalLength = cpu_to_le32(WINUSB20_WCID_DESC_SET_SIZE);
  62. +        usb_cap_wcid->bVendorCode               = WINUSB20_WCID_VENDOR_CODE;
  63. +        memcpy(usb_cap_wcid->bPlatformCapabilityUUID_16, winusb20_wcidbos_uuid, sizeof(winusb20_wcidbos_uuid));
  64. +#endif // defined (CONFIG_GAOYANG)
  65.         /*
  66.          * The Superspeed USB Capability descriptor shall be implemented by all
  67.          * SuperSpeed devices.
  68. @@ -1716,11 +1810,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
  69.                                 value = min(w_length, (u16) value);
  70.                         break;
  71.                 case USB_DT_BOS:
  72. +#if !defined (CONFIG_GAOYANG)
  73.                         if (gadget_is_superspeed(gadget) ||
  74.                             gadget->lpm_capable) {
  75.                                 value = bos_desc(cdev);
  76.                                 value = min(w_length, (u16) value);
  77.                         }
  78. +#else
  79. +                        value = bos_desc(cdev);
  80. +                        value = min(w_length, (u16) value);
  81. +#endif // defined (CONFIG_GAOYANG)
  82.                         break;
  83.                 case USB_DT_OTG:
  84.                         if (gadget_is_otg(gadget)) {          
复制代码
补丁简要说明:


  • Makfile,使用 subdir-ccflags-y 对当前 Makefile 下的全部构建过程添加宏:

    • DEBUG,添加调试信息打印;
    • CONFIG_GAOYANG,快速方案 控制宏;

  • composite.c 源码修改:

    • 添加 WCID20 设备能力 描述符结构体定义:usb_cap_wcid20_descriptor;
    • 跳过 超高速设备 检查,在 BOS 描述符请求处理分支直接返回 “BOS 描述符”;
    • 返回 “BOS 描述符” 时(bos_desc 实现中),追加 ”WCID20 设备能力 描述符“;

抓包测试:

结论:应答 BOS 请求并追加 ”WCID20 设备能力 描述符“后,主机新发出 DATA0 请求。
至此,买通步骤3,紧跟需要买通步骤4 ---- 设备应答 “OS2.0 描述符集”。
测试验证三、应答 “OS2.0 描述符集”

应答的“OS2.0 描述符集”应包含:


  • WCID20 兼容ID 描述符(范例 wDescriptorType 值为 0x03; cCID_8 值为 ”WINUSB“);
  • WCID20 注册表属性 描述符(范例 wDescriptorType 值为 0x04;内容为接口 GUID 键对,此中 GUID 值由厂商自己定义);
在 libcomposite 框架下,同步骤3 ---- 应答“BOS 描述符”处理的 composite_setup 函数下就有“OS2.0 描述符集”请求的应答处理逻辑。
可靠方案同样应考虑: 在 libcomposite 框架下,怎么更合理地对“OS2.0 描述符集”举行应答 。 当前需要快速验证,参考 《使用微软系统描述符2.0制作免驱动自定义USB设备》给出的“OS2.0 描述符集”样式,直接在 libcomposite 框架 setup中应答 BOS 请求,补丁如下:
  1. diff --git a/linux_5.10/drivers/usb/gadget/composite.c b/linux_5.10/drivers/usb/gadget/composite.c
  2. index 1a556a628..0b772c924 100644
  3. --- a/linux_5.10/drivers/usb/gadget/composite.c
  4. +++ b/linux_5.10/drivers/usb/gadget/composite.c
  5. @@ -20,6 +20,50 @@
  6. #include "u_os_desc.h"
  7. +#if defined (CONFIG_GAOYANG)
  8. +
  9. +#define  WINUSB_IF0_WCID_PROPERTIES_SIZE  (162)
  10. +
  11. +const uint8_t WINUSB20_WCIDDescriptorSet [162] = {
  12. +  ///
  13. +  /// WCID20 descriptor set descriptor
  14. +  ///
  15. +  0x0a, 0x00,                                       /* wLength */
  16. +  0x00, 0x00,                                       /* wDescriptorType */
  17. +  0x00, 0x00, 0x03, 0x06,                           /* dwWindowsVersion */
  18. +  0xa2, 0x00,                                       /* wDescriptorSetTotalLength */
  19. +  ///
  20. +  /// WCID20 compatible ID descriptor
  21. +  ///
  22. +  0x14, 0x00,                                       /* wLength */
  23. +  0x03, 0x00,                                       /* wDescriptorType */
  24. +  /* WINUSB */
  25. +  'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,         /* cCID_8 */
  26. +  /*  */
  27. +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   /* cSubCID_8 */
  28. +  ///
  29. +  /// WCID20 registry property descriptor
  30. +  ///
  31. +  0x84, 0x00,                                       /* wLength */
  32. +  0x04, 0x00,                                       /* wDescriptorType */
  33. +  0x07, 0x00,                                       /* wPropertyDataType */
  34. +  0x2a, 0x00,                                       /* wPropertyNameLength */
  35. +  /* DeviceInterfaceGUIDs */
  36. +  'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00,       /* wcPropertyName_21 */
  37. +  'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00,       /* wcPropertyName_21 */
  38. +  't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00,       /* wcPropertyName_21 */
  39. +  'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00,       /* wcPropertyName_21 */
  40. +  'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00,       /* wcPropertyName_21 */
  41. +  0x00, 0x00,                                       /* wcPropertyName_21 */
  42. +  0x50, 0x00,                                       /* wPropertyDataLength */
  43. +  /* {1D4B2365-4749-48EA-B38A-7C6FDDDD7E26} */
  44. +  '{', 0x00, '1', 0x00, 'D', 0x00, '4', 0x00,       /* wcPropertyData_40 */
  45. +  'B', 0x00, '2', 0x00, '3', 0x00, '6', 0x00,       /* wcPropertyData_40 */
  46. +  '5', 0x00, '-', 0x00, '4', 0x00, '7', 0x00,       /* wcPropertyData_40 */
  47. +  '4', 0x00, '9', 0x00, '-', 0x00, '4', 0x00,       /* wcPropertyData_40 */
  48. +  '8', 0x00, 'E', 0x00, 'A', 0x00, '-', 0x00,       /* wcPropertyData_40 */
  49. +  'B', 0x00, '3', 0x00, '8', 0x00, 'A', 0x00,       /* wcPropertyData_40 */
  50. +  '-', 0x00, '7', 0x00, 'C', 0x00, '6', 0x00,       /* wcPropertyData_40 */
  51. +  'F', 0x00, 'D', 0x00, 'D', 0x00, 'D', 0x00,       /* wcPropertyData_40 */
  52. +  'D', 0x00, '7', 0x00, 'E', 0x00, '2', 0x00,       /* wcPropertyData_40 */
  53. +  '6', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00,     /* wcPropertyData_40 */
  54. +};
  55. +#endif // defined (CONFIG_GAOYANG)
  56. +
  57. /**
  58.   * struct usb_os_string - represents OS String to be reported by a gadget
  59.   * @bLength: total length of the entire descritor, always 0x12
  60. @@ -1891,6 +1990,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
  61.                 /*
  62.                  * OS descriptors handling
  63.                  */
  64. +#if defined (CONFIG_GAOYANG)
  65. +                 if (ctrl->bRequestType == 0xC0) {
  66. +                        value = min_t(int, w_length, sizeof(WINUSB20_WCIDDescriptorSet));
  67. +                        memcpy(req->buf, WINUSB20_WCIDDescriptorSet, value);
  68. +
  69. +                        goto check_value;
  70. +                 }
  71. +#endif // defined (CONFIG_GAOYANG)
  72.                 if (cdev->use_os_string && cdev->os_desc_config &&
  73.                     (ctrl->bRequestType & USB_TYPE_VENDOR) &&
  74.                     ctrl->bRequest == cdev->b_vendor_code) {  
复制代码
补丁简要说明:


  • 定义“OS2.0 描述符集”变量 WINUSB20_WCIDDescriptorSet,此中包含:

    • “兼容ID 描述符",固定为:”WINUSB“;
    • ”注册表属性 描述符“,新增键值对:DeviceInterfaceGUIDs : 1D4B2365-4749-48EA-B38A-7C6FDDDD7E26;

  • 跳过”OS 描述符集“检查,在”unknown:“处理分支直接返回 “OS2.0 描述符集”,内容为 WINUSB20_WCIDDescriptorSet;
抓包测试:

结论:应答的“OS2.0 描述符集”后,主机完成设备枚举,视设备为 WinUSb 设备而免驱。
至此,完成 DuoS 免驱的快速验证。
方案总结


  • 当前环境(Win10 USB 2.0 主机,Linux USB 2.0 设备)下,无法触发 Win10 主机的 “微软系统描述符1.0" 机制,OS 1.0 免驱方案不收效;
  • 经快速修改{ bcdUSB = 0x0210;应答 ”BOS 描述符“;应答 ”OS2.0 描述符集“}后,可以触发 Win10 主机的 “微软系统描述符2.0" 机制,方案有效;
  • 完整方案需要参考 gadget 驱动框架,考虑更合理的 “微软系统描述符2.0" 免驱方案实现;
调试关注


  • 及时删除注册表中的 usbflags下的对应目次,否则设备不再发出BOS描述符请求。
  • configfs 方式下, DuoS 调试关注:

    • 生成 usb_f_ss_lb.ko 模块,补丁:
      1. diff --git a/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/linux/cvitek_sg2000_milkv_duos_glibc_arm64_sd_defconfig b/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/linux/cvitek_sg2000_milkv_duos_glibc_arm64_sd_defconfig
      2. index 255e7cbd8..e7cf8ad64 100644
      3. --- a/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/linux/cvitek_sg2000_milkv_duos_glibc_arm64_sd_defconfig
      4. +++ b/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/linux/cvitek_sg2000_milkv_duos_glibc_arm64_sd_defconfig
      5. @@ -230,3 +230,6 @@ CONFIG_DEBUG_FS=y
      6. # CONFIG_FTRACE is not set
      7. # CONFIG_STRICT_DEVMEM is not set
      8. # CONFIG_RUNTIME_TESTING_MENU is not set
      9. +CONFIG_USB_ZERO=m
      10. +CONFIG_USB_F_SS_LB=m
      复制代码
    • SourceSink Function 设备命令:
      1. # 加载 f_ss_lb 驱动
      2. insmod /mnt/system/ko/usb_f_ss_lb.ko
      3. # 变量声明
      4. CVI_DIR=/tmp/usb
      5. CVI_GADGET=$CVI_DIR/usb_gadget/cvitek
      6. CVI_FUNC=$CVI_GADGET/functions
      7. MANUFACTURER="Cvitek"
      8. PRODUCT="USB Com Port"
      9. SERIAL="0123456789"
      10. VID=0x3346
      11. PID=0x1003
      12. CLASS=SourceSink
      13. FUNC_NUM=0
      14. # 环境初始化
      15. mkdir $CVI_DIR
      16. # configfs 挂载
      17. mount none $CVI_DIR -t configfs
      18. # 创建 Gadget 设备
      19. mkdir $CVI_GADGET
      20. # Gadget 设备初始化
      21. echo $VID >$CVI_GADGET/idVendor
      22. echo $PID >$CVI_GADGET/idProduct
      23. # 信息初始化
      24. mkdir $CVI_GADGET/strings/0x409
      25. echo $MANUFACTURER>$CVI_GADGET/strings/0x409/manufacturer
      26. echo $PRODUCT>     $CVI_GADGET/strings/0x409/product
      27. echo $SERIAL>      $CVI_GADGET/strings/0x409/serialnumber
      28. # 创建并关联 configuration 与 function
      29. mkdir $CVI_GADGET/configs/c.1
      30. mkdir $CVI_GADGET/functions/$CLASS.usb$FUNC_NUM
      31. ln -s $CVI_FUNC/$CLASS.usb$FUNC_NUM $CVI_GADGET/configs/c.1
      32. # 配置 configuration
      33. mkdir $CVI_GADGET/configs/c.1/strings/0x409
      34. echo "config1">    $CVI_GADGET/configs/c.1/strings/0x409/configuration
      35. echo 120>          $CVI_GADGET/configs/c.1/MaxPower
      36. # 使能 Gadget 设备
      37. echo 4340000.usb > /tmp/usb/usb_gadget/cvitek/UDC
      复制代码

术语缩写

术语 & 编写说明备注UDCUSB设备控制器(UDC)驱动指的是作为其他USB主机控制器外设的USB硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个USB设备依附于一个USB主机控制器上。NCMUSB NCM,属于USB-IF定义的CDC(Communication Device Class)下的一个子类:Network Control Model,用于Host和Device之间交换以太网帧。MSCMass Storage Class, USB大容量存储设备,通常指的是像U盘这样的存储设备。UACUSB Audio Class,USB音频类,比方电话,音乐回放,录音等音频功能等;ACMAbstract Control Model, 主要用于支持模仿调制解调器(Modem)设备在 USB 总线上的通信。 参考



  • 第16章 USB主机、设备与Gadget驱动之USB UDC与Gadget驱动(一)
  • 通过configfs配置的Linux USB gadget
  • Configfs - 用户空间驱动的内核对象配置
  • 简朴几步,让自定义USB设备也能免驱动运行
  • 使用微软系统描述符1.0制作免驱动自定义USB设备
  • 使用微软系统描述符2.0制作免驱动自定义USB设备
  • WinUSB提供的相干USB结构体

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

愛在花開的季節

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表