You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Linux FunctionFS USB Gadget在Windows10中识别为WINUSB设备?

问题:BeagleBone Blue USB FunctionFS WINUSB设备识别失败

我正尝试将BeagleBone Blue开发板制作成可与Windows主机通信的小型USB设备。编写了开机自动运行的Shell脚本配置FunctionFS Gadget,脚本内容如下:

#!/bin/bash

sudo modprobe usb_f_fs

cd /sys/kernel/config/usb_gadget/

sudo mkdir -p g_my_gadget
cd g_my_gadget

echo 0x0200 > bcdUSB     # Complies with USB specification 2.0.0
echo 0xXXXX > idVendor   # VID
echo 0xXXXX > idProduct  # PID
echo 0x0100 > bcdDevice  # v1.0.0

# String descriptors (configure these before you need them)
sudo mkdir -p strings/0x0409  # 0x0409 is the code for English
echo "SERIALNUMBER" > strings/0x0409/serialnumber
echo "MY_MANUFACTURER" > strings/0x0409/manufacturer
echo "MY DEVICE" > strings/0x0409/product

# Configuration descriptor 1
sudo mkdir -p configs/c.1/strings/0x0409
echo "My Configuration" > configs/c.1/strings/0x0409/configuration  # This sets iConfiguration in Configuration Descriptor
echo 250 > configs/c.1/MaxPower  # Request 500 mA
echo 0xc0 > configs/c.1/bmAttributes

echo 0x00 > bDeviceClass
echo 0x00 > bDeviceSubClass
echo 0x00 > bDeviceProtocol

# Write OS Descriptors
echo 1 > os_desc/use
echo 0xcd > os_desc/b_vendor_code
echo MSFT100 > os_desc/qw_sign

sudo mkdir -p functions/ffs.usb0  # Calls *_alloc_inst in the kernel module--we can now set functions specific to the class

sudo ln -s functions/ffs.usb0 configs/c.1/  # calls _alloc in the kernel module

# Make the directory that the endpoints will live in + mount to the functionFS file system
sudo mkdir /dev/ffs-usb0
sudo mount -t functionfs usb0 /dev/ffs-usb0

同时,拷贝了ffs-test.c并更新了描述符对象,编译后的二进制文件在脚本运行后执行,更新后的描述符如下:

.header = {
        .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
        .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
                             FUNCTIONFS_HAS_HS_DESC |
                             FUNCTIONFS_HAS_SS_DESC |
                             FUNCTIONFS_HAS_MS_OS_DESC
                             ),
        .length = cpu_to_le32(sizeof descriptors),
    },
    .fs_count = cpu_to_le32(3),
    .fs_descs = {
        .intf = {
            .bLength = sizeof descriptors.fs_descs.intf,
            .bDescriptorType = USB_DT_INTERFACE,
            .bNumEndpoints = 2,
            .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
            .bInterfaceSubClass = 0xFF,
            .bInterfaceProtocol = 0xFF,
            .iInterface = 0,
        },
        .sink = {
            .bLength = sizeof descriptors.fs_descs.sink, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK,
            /* .wMaxPacketSize = autoconfiguration (kernel) */
        },
        .source = {
            .bLength = sizeof descriptors.fs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK,
            /* .wMaxPacketSize = autoconfiguration (kernel) */
        },
    },
    .hs_count = cpu_to_le32(3),
    .hs_descs = {
        .intf = {
            .bLength = sizeof descriptors.fs_descs.intf,
            .bDescriptorType = USB_DT_INTERFACE,
            .bNumEndpoints = 2,
            .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
            .bInterfaceSubClass = 0xFF,
            .bInterfaceProtocol = 0xFF,
            .iInterface = 0,
        },
        .sink = {
            .bLength = sizeof descriptors.hs_descs.sink,
            .bDescriptorType = USB_DT_ENDPOINT,
            .bEndpointAddress = 1 | USB_DIR_IN,
            .bmAttributes = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize = cpu_to_le16(512),
        },
        .source = {
            .bLength = sizeof descriptors.hs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), .bInterval = 1, /* NAK every 1 uframe */
        },
    },
    .ss_count = cpu_to_le32(5),
    .ss_descs = {
        .intf = {
            .bLength = sizeof descriptors.fs_descs.intf,
            .bDescriptorType = USB_DT_INTERFACE,
            .bNumEndpoints = 2,
            .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
            .bInterfaceSubClass = 0xFF,
            .bInterfaceProtocol = 0xFF,
            .iInterface = 0,
        },
        .sink = {
            .bLength = sizeof descriptors.hs_descs.sink,
            .bDescriptorType = USB_DT_ENDPOINT,
            .bEndpointAddress = 1 | USB_DIR_IN,
            .bmAttributes = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize = cpu_to_le16(1024),
        },
        .sink_comp = {
            .bLength = USB_DT_SS_EP_COMP_SIZE,
            .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
            .bMaxBurst = 0,
            .bmAttributes = 0,
            .wBytesPerInterval = 0,
        },
        .source = {
            .bLength = sizeof descriptors.hs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), .bInterval = 1, /* NAK every 1 uframe */
        },
        .source_comp = {
            .bLength = USB_DT_SS_EP_COMP_SIZE,
            .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
            .bMaxBurst = 0,
            .bmAttributes = 0,
            .wBytesPerInterval = 0,
        },
    },
    .os_count = cpu_to_le32(2),
    .os_compat = {
        .header = {
            .interface = 0,
            .dwLength = cpu_to_le32(sizeof descriptors.os_compat),
            .bcdVersion = cpu_to_le16(0x01),
            .wIndex = cpu_to_le16(0x0004),
            .bCount = 0x01,
            .Reserved= 0x00,
        },
        .function = {
            .bFirstInterfaceNumber = 0x0,
            .Reserved1 = 0x01,
            .CompatibleID = "WINUSB\0\0",
            .SubCompatibleID = {0, 0, 0, 0, 0, 0, 0, 0},
            .Reserved2 = {0, 0, 0, 0, 0, 0},
        }
    },
    .os_prop = {
        .header= {
            .interface = 0,
            .dwLength = cpu_to_le32(sizeof descriptors.os_prop),
            .bcdVersion = cpu_to_le16(0x01),
            .wIndex = cpu_to_le16(0x0005),
            .wCount = cpu_to_le16(0x0001),
        },
        .desc = {
            .dwSize=cpu_to_le32(sizeof(descriptors.os_prop.desc)
                    +sizeof(descriptors.os_prop.bPropertyName)
                    +sizeof (descriptors.os_prop.wPropertyDataLength)
                    +sizeof(descriptors.os_prop.bPropertyData)),
            .dwPropertyDataType = cpu_to_le32(0x00000001),
            .wPropertyNameLength = cpu_to_le16(20),
        },
        .bPropertyName = "DeviceInterfaceGUID\0",
        .wPropertyDataLength = cpu_to_le32(39),
        .bPropertyData =  "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\0",
    },
};

将BeagleBone连接到Windows10主机后,设备在设备管理器中显示为“其他设备”,而非“通用串行总线设备”,且无法与设备通信。已配置必要的描述符,但Windows似乎未接收到识别为WINUSB设备所需的信息,请问遗漏了什么配置?


解决方案

以下是几个关键遗漏点和修复步骤:

1. 缺少USB控制器绑定步骤

脚本最后未将USB控制器绑定到gadget,导致Windows无法完成设备枚举。需要添加:

# 先通过`ls /sys/class/udc/`查看可用的UDC节点,替换为你的设备对应名称
echo "musb-hdrc.0.auto" > UDC

这一步会激活gadget,让主机识别到完整的USB设备。

2. OS描述符配置细节错误

  • 确保os_desc目录存在,若不存在需提前创建:mkdir -p os_desc
  • echo MSFT100 > os_desc/qw_sign必须是大写无空格,Windows对该签名的大小写敏感,小写会导致OS描述符不被识别。

3. WINUSB属性描述符长度计算错误

os_prop.desc.dwSize的计算逻辑有误,正确的长度应包含属性名称、数据的实际字节数(包括终止符),而非直接使用sizeof。修改为:

.dwSize = cpu_to_le32(
    sizeof(struct usb_os_desc_prop_header) +
    strlen("DeviceInterfaceGUID") + 1 +  // 包含'\0'
    sizeof(descriptors.os_prop.wPropertyDataLength) +
    strlen("{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}") + 1
),

错误的长度计算会导致Windows解析属性描述符失败,无法识别WINUSB设备。

4. 权限与进程运行问题

挂载functionfs后,/dev/ffs-usb0的默认权限可能无法让普通用户访问,导致ffs-test程序无法操作端点。添加权限配置:

sudo chmod 777 /dev/ffs-usb0

或者直接以root用户运行ffs-test二进制文件,确保程序能正常读写端点文件。

5. 冲突驱动检查

通过lsmod | grep usb_f_查看已加载的USB gadget驱动,确保只有usb_f_fs在运行,避免其他驱动干扰设备枚举。


内容的提问来源于stack exchange,提问作者Ken

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:08:13