4.3.18. USB/USB Gadget调试指南

4.3.18.1. 概述

X5 内置了 USB2.0 和 USB3.0 控制器,其特性如下

USB 2.0 OTG

  • 支持 Host 模式和 Device 模式

  • 兼容性标准: USB 通用串行总线规范 (USB Specification Revision 2.0)

  • 支持高速 (HS), 全速 (FS), 和低速 (LS) 模式

  • 支持 LS 模式的 Keep-Alive 特性,以及 HS/FS 模式使用 (micro-)SOF (Start of Frame) 信号

  • 硬件支持总线数据包错误处理

USB 3.0 OTG

  • 支持 Host 模式和 Device 模式

  • 兼容性标准: USB 通用串行总线规范 (USB Specification Revision 3.0)

  • 支持超高速 (SS), 高速 (HS), 全速 (FS), 和低速 (LS) 模式

  • 支持 PIPE3 PHY (125/250/500 MHz)

  • 支持 LS 模式的 Keep-Alive 特性,以及 HS/FS 模式使用 (micro-)SOF (Start of Frame) 信号

  • 硬件支持总线数据包错误处理

相关术语

术语 说明
USB Universal Serial Bus, 通用串行总线
Gadget 配件
HCD Host Controller Driver,主机控制器驱动
UDC USB Device Controller, USB 设备控制器
HCI Host Controller Interface,主机控制器接口
EHCI Enhanced Host Controller Interface,增强型主机控制器接口
OHCI Open Host Controller Interface,开放式主机控制器接口
ADB Android Debug Bridge,Android 调试
HID Human Interface Device,人机接口设备
Rndis Remote NDIS,远程 NDIS
ECM Ethernet Networking Control Model
ACM Abstract Control Model
MSD Mass Storage Device,大容量存储设备
UVC USB video class
UAC USB Audio class

4.3.18.2. 功能描述

典型应用

USB Gadget 是一种用于模拟 USB 复合设备技术。在 Linux 系统中,”gadget” 通常通过 USB 接口连接主机虚拟设备,例如 USB 存储设备、网络适配器、串口设备、音频设备、HID 设备等。

USB Gadget 的关键特性之一通过 configfs 提供灵活动态配置管理方式。configfs 是一个用户空间操作文件系统接口,开发者可以通过文件系统命令动态创建配置 USB 设备各个组件,如设备描述符、接口、端点等。不同于传统需要编译内核重启系统方式,configfs 使得 USB gadget 配置变得更加灵活,能够系统运行动态调整修改,极大简化开发调试过程。

总之,USB Gadget 提供一种无需修改内核代码即可快速实现管理虚拟 USB 设备方式,增强了 Linux 系统可扩展性灵活性,使能够方便模拟各种 USB 设备功能,满足不同应用场景需求。

功能原理

USB Gadget 驱动框架如下
USB_Host_Device.png

在 USB Gadget 框架中,USB 设备功能行为通过以下几个层次实现

  • USB Function Driver
    USB Function Driver 负责实现设备具体功能。它定义了 USB 设备连接主机行为功能,例如虚拟串口、USB 存储、网络适配器等。每个 USB Gadget 设备通常一个多个功能驱动,表示设备支持不同功能。

  • configfs
    configfs 是一个用户空间接口,通过文件系统方式用户动态配置 USB 设备。configfs 提供一个非常灵活方式设置 USB Gadget 设备各种参数,包括设备描述符、配置、接口以及功能。

  • UDC Driver
    UDC 驱动(USB Device Controller Driver)是硬件 USB 控制器交互驱动程序,负责管理 USB 设备物理连接数据传输。它充当 USB 控制器底层接口,与 USB 设备硬件层面交互。

4.3.18.3. 代码分析

USB Gadget 驱动

内核选项

内核开启以下选项

CONFIG_USB_GADGET
CONFIG_USB_CONFIGFS

USB Gadget 功能通过模块加载的,可以根据的 USB 功能启用相应的 USB function

CONFIG_USB_CONFIGFS_SERIAL
CONFIG_USB_CONFIGFS_ACM
CONFIG_USB_CONFIGFS_OBEX
CONFIG_USB_CONFIGFS_NCM
CONFIG_USB_CONFIGFS_ECM
CONFIG_USB_CONFIGFS_ECM_SUBSET
CONFIG_USB_CONFIGFS_RNDIS
CONFIG_USB_CONFIGFS_EEM
CONFIG_USB_CONFIGFS_MASS_STORAGE
CONFIG_USB_CONFIGFS_F_LB_SS
CONFIG_USB_CONFIGFS_F_FS
CONFIG_USB_CONFIGFS_F_UAC1
CONFIG_USB_CONFIGFS_F_UAC2
CONFIG_USB_CONFIGFS_F_HID
CONFIG_USB_CONFIGFS_F_UVC

驱动代码

USB 驱动源代码位于内核 drivers/usb 目录

.
├── Kconfig
├── Makefile
├── core            // USB 子系统核心代码
├── dwc3            // USB 控制器 dwc3 驱动程序
└── gadget          // gadget 目录包含了与 USB gadget 相关驱动,实现了 USB Gadge 功能(如存储设备、串口、网络适配器等)
    ├── Kconfig
    ├── Makefile
    ├── composite.c // 创建和管理 USB 复合设备
    ├── config.c
    ├── configfs.c  // configfs 文件系统源码
    ├── configfs.h
    ├── function    // USB 设备端功能驱动程序,实现不同类型的 USB 设备功能
    │   ├── Makefile
    │   ├── f_acm.c
    │   ├── f_ecm.c
    │   ├── f_eem.c
    │   ├── f_fs.c
    │   ├── f_hid.c
    │   ├── f_loopback.c
    │   ├── f_mass_storage.c
    │   ├── f_mass_storage.h
    │   ├── f_midi.c
    │   ├── f_ncm.c
    │   ├── f_obex.c
    │   ├── f_phonet.c
    │   ├── f_printer.c
    │   ├── f_rndis.c
    │   ├── f_serial.c
    │   ├── f_sourcesink.c
    │   ├── f_subset.c
    │   ├── f_tcm.c
    │   ├── f_uac1.c
    │   ├── f_uac1_legacy.c
    │   ├── f_uac2.c
    │   ├── f_uvc.c
    │   ├── f_uvc.h
    │   ├── g_zero.h
    │   ├── ndis.h
    │   ├── rndis.c
    │   ├── rndis.h
    │   ├── storage_common.c
    │   ├── storage_common.h
    │   ├── tcm.h
    │   ├── u_audio.c
    │   ├── u_audio.h
    │   ├── u_ecm.h
    │   ├── u_eem.h
    │   ├── u_ether.c
    │   ├── u_ether.h
    │   ├── u_ether_configfs.h
    │   ├── u_fs.h
    │   ├── u_gether.h
    │   ├── u_hid.h
    │   ├── u_midi.h
    │   ├── u_ncm.h
    │   ├── u_phonet.h
    │   ├── u_printer.h
    │   ├── u_rndis.h
    │   ├── u_serial.c
    │   ├── u_serial.h
    │   ├── u_tcm.h
    │   ├── u_uac1.h
    │   ├── u_uac1_legacy.c
    │   ├── u_uac1_legacy.h
    │   ├── u_uac2.h
    │   ├── u_uvc.h
    │   ├── uac_common.h
    │   ├── uvc.h
    │   ├── uvc_configfs.c
    │   ├── uvc_configfs.h
    │   ├── uvc_queue.c
    │   ├── uvc_queue.h
    │   ├── uvc_v4l2.c
    │   ├── uvc_v4l2.h
    │   ├── uvc_video.c
    │   └── uvc_video.h
    ├── functions.c      // 创建和管理 USB Function
    ├── udc              // UDC 驱动程序
    └── usbstring.c      // 创建和管理 USB 设备字符串描述符

USB Gadget 脚本

usb-gadget.sh脚本配置流程

usb-gadget.sh 脚本位于/etc/init.d/usb-gadget.sh,主要作用简化 USB 设备功能启用过程,并提供一种自动化方式配置管理 USB gadget 设备,其功能如下:

  • 根据预设的 USB 配置文件,初始化 USB Gadget 驱动

  • 配置 USB 复合设备功能描述符,并通过 CONFIGFS 接口这些配置应用系统

  • 启用 USB 复合设备功能

usb-gadget.sh 的使用帮助信息,支持如下类型 USB 设备

  • USB 单一设备,包括 ADB / Rndis / ECM / HID / ACM /MSD / UVC / UAC

  • USB 复合设备,即通过同一 USB 设备同时支持多种设备功能

Usage: /etc/init.d/usb-gadget.sh {start|stop|restart} [options]
 options:
 detail gadget-composite config, using .usb/.default-config in default
      adb                         launch adbd
      msd                         run as gadget mass storage device
      msd-ram                     run as gadget mass storage device(with ddr storage)
      hid                         run as hid gadget
      rndis                       run as rndis gadget
      ecm                         run as cdc ether gadget
      uvc                         run as uvc gadget
      uac1                        usb audio class specification 1
      uac2                        usb audio class specification 2
      uvc-hid                     uvc + hid composite gadget
      uvc-uac1                    uvc + uac1 composite gadget
      uvc-uac2                    uvc + uac2 composite gadget
      uvc-hid-uac1                uvc + hid + uac1 composite gadget
      uvc-hid-uac2                uvc + hid + uac2 composite gadget
      uvc-rndis                   uvc + rndis composite gadget
      uvc-ecm                     uvc + cdc ether composite gadget
      uvc-acm                     uvc + acm(serial) composite gadget
      uvc-rndis-uac1              uvc + rndis + uac1 composite gadget
      uvc-rndis-uac2              uvc + rndis + uac2 composite gadget
      uvc-ecm-uac1                uvc + ecm + uac1 composite gadget
      uvc-ecm-uac2                uvc + ecm + uac2 composite gadget
      rndis-hid                   rndis + hid composite gadget
      rndis-uac1                  rndis + uac1 composite gadget
      msd-uac1                    msd + uac1 composite gadget
      hid-uac1                    hid + uac1 composite gadget
      uvc-adb                     uvc + adb composite gadget

usb-gadget.sh 脚本配置流程如下

  • 挂载 configfs,configfs 提供一个用户空间接口,用于配置 USB 复合设备

  • 创建 USB Gadget 目录:实例一个的 USB Gadget 设备。

  • 配置 USB Gadget 信息:设置 USB 设备配置,包括厂商 ID (VID)、产品 ID (PID)、设备描述符基本信息

  • 创建配置 USB 功能:根据设备功能(如虚拟串口、存储、网络等),创建配置相应的 USB 功能

  • 绑定 USB 配置功能:将配置文件功能进行关联,完成设备功能配置

  • 启用 USB 控制器:启动使能 USB 控制器,开始主机进行通信

start_usb_gadget()
{
    echo "Creating the USB gadget"

    # 加载 libcomposite 驱动,usb gadget 依赖 libcomposite 模块
    echo "Loading composite module"
    modprobe libcomposite

    # 挂载 configfs
    echo "Mount ConfigFS and create Gadget"
    CONFIGFS_MOUNT_POINT=$(mount | grep configfs | awk '{ print $3 }')
    echo "configfs mount point: " ${CONFIGFS_MOUNT_POINT}
    if [[ -n ${CONFIGFS_MOUNT_POINT} ]]; then
        echo "Configfs already mounted..."
    else
        mount -t configfs none /sys/kernel/config
    fi

    # 创建 USB Gadget
    echo "Creating gadget directory g_comp"
    mkdir -p $USB_CONFIGFS
    cd $USB_CONFIGFS
    if [ $? -ne 0 ]; then
        echo "Error creating usb gadget in configfs"
        exit 1
    else
        echo "OK"
    fi

    # 创建并配置 USB Gadget configuration
    echo "init configfs..."
    configfs_init

    # 配置 USB Function
    echo "Init functions..."
    function_init
    echo "OK"

    # 将 USB configuration 与 Function 绑定
    echo "Bind functions..."
    bind_functions

    # 加载 USB Function 所需的后台程序
    echo "Pre run userspace daemons(eg. adb)..."
    pre_run_binary

    # just with userspace daemon, needs to wait some time.
    echo "waiting"
    for i in `seq 0 $?`
    do
        echo "."
        sleep 0.1
    done

    echo "OK"

    # 使能 UDC
    echo "Binding USB Device Controller"
    echo $UDC > UDC
    echo peripheral > $UDC_ROLE
    cat $UDC_ROLE
    echo "OK"

    echo "Run some userspace daemons(eg. usb_camera)..."
    run_binary
}

通过 configfs_init() 配置 USB Gadget 设备信息

configfs_init()
{
    # 配置 USB PID/VID
    echo "Setting Vendor and Product ID's"
    echo $USB_VID > idVendor
    echo $USB_PID > idProduct
    echo "OK"

    if $USE_RNDIS; then
        echo 0100 > bcdDevice   # v1.0.0, otherwise win10 rndis couldn't install
    fi

    # 配置 USB 设备类
    if $IS_MULTI_FUNC; then
        echo "Setting Multi-func Gadget for Windows"
        echo 0xEF > bDeviceClass
        echo 0x02 > bDeviceSubClass
        echo 0x01 > bDeviceProtocol
    elif $USE_UAC1; then
        echo 0xEF > bDeviceClass
        echo 0x02 > bDeviceSubClass
        echo 0x01 > bDeviceProtocol
    else
        echo "single function gadget"
        if $USE_RNDIS; then
            echo 0x02 > bDeviceClass        # same with legacy/ether.c
        fi
    fi

    # 配置 USB 字符串描述符
    echo "Setting English strings"
    mkdir -p strings/0x409
    echo $SERIAL > strings/0x409/serialnumber  # 序列号
    echo $MANUF > strings/0x409/manufacturer   # 产商
    echo $PRODUCT > strings/0x409/product      # 设备型号
    echo "OK"

    # 创建 USB 配置
    echo "Creating Config"
    mkdir configs/c.1
    mkdir configs/c.1/strings/0x409
    echo "Conf 1" > configs/c.1/strings/0x409/configuration
    echo 0xC0 > configs/c.1/bmAttributes
    echo 0x01 > configs/c.1/MaxPower
}

4.3.18.4. USB Gadget 功能使用

ADB (Android Debug Bridge)

ADB(Android Debug Bridge) 是一个多功能的 Android 命令行工具,允许开发者设备进行交互,通常用于嵌入式设备开发调试。

代码分析

通过 create_adb() 函数创建 ADB Function

create_adb()
{
    CONFIG=$1
    FUNCTION=$2

    echo "Creating ADB gadget functionality"
    mkdir functions/$FUNCTION

    ln -s functions/$FUNCTION configs/c.1
}

USB ADB 功能依赖于 ADBD 后台服务

mkdir -p /dev/usb-ffs/adb
mount -o uid=2000,gid=2000 -t functionfs adb /dev/usb-ffs/adb  # 挂载 FunctionFS 文件系统
start-stop-daemon -S -b -q -n adbd -a /usr/bin/adbd            # 启动 ADBD 后台服务

使用方法

板端运行命令

/etc/init.d/usb-gadget.sh restart adb

USB ADB 使用方法可以参考 使用 adb 章节

Rndis 网卡

RNDIS(Remote Network Driver Interface Specification) 是一种用于通过 USB 接口实现网络通信标准协议,通常用于设备(如智能手机、嵌入式设备等)通过USB连接计算机,并通过 USB 连接提供网络功能。RNDIS 允许设备通过 USB 与计算机进行数据传输,表现一个虚拟以太网接口,从而使设备可以通过传统网络一样计算机进行数据通信。

代码分析

通过 create_rndis() 创建 USB Rndis Function

create_rndis configs/c.1 rndis.0

create_rndis()
{
    CONFIG=$1
    FUNCTION=$2

    echo "Creating RNDIS gadget functionality"
    mkdir functions/$FUNCTION                 # 创建 USB Rndis Function 目录

    mkdir -p os_desc
    echo 1 > os_desc/use
    echo 0xcd > os_desc/b_vendor_code
    echo MSFT100 > os_desc/qw_sign

    mkdir -p functions/$FUNCTION/os_desc/interface.rndis  # 创建 Rndis 目录,用于存储 Rndis 相关的设备描述信息
    echo RNDIS > functions/$FUNCTION/os_desc/interface.rndis/compatible_id  # 写入 Rndis 协议标识符
    echo 5162001 > functions/$FUNCTION/os_desc/interface.rndis/sub_compatible_id # 设置 Rndis 兼容ID与设备版本

    ln -s functions/$FUNCTION configs/c.1    # 将 Rndis Function 与 config 绑定一起
    ln -s configs/c.1/ os_desc
}

其他参数说明

  • dev_addr 设置网卡 MAC 地址
    例如: echo "00:11:22:33:44:55" > functions/$FUNCTION/dev_addr

使用方法

板端运行命令

/etc/init.d/usb-gadget.sh restart rndis

USB Rndis 使用方法可以参考 虚拟网卡(rndis) 章节

ECM 网卡

ECM(Ethernet Control Model) 是一种通过USB连接进行以太网通信协议,允许设备(如手机、嵌入式系统、路由器等) 通过 USB 与主机(通常计算机其他网络设备)建立虚拟以太网连接。ECM 协议使得通过 USB 的网络连接类似传统以太网连接,它定义如何通过 USB 端口传输以太网数据包,使得设备能够主机进行以太网通信。

代码分析

通过 create_ecm() 创建 USB ECM Function

create_ecm()
{
    CONFIG=$1
    FUNCTION=$2

    echo "Creating CDC ECM gadget functionality"

    mkdir functions/$FUNCTION                  # 创建 USB ECM Function 目录
    ln -s functions/$FUNCTION configs/c.1      # 将 ECM Function 与 config 绑定一起
}

使用方法

板端运行命令

/etc/init.d/usb-gadget.sh restart ecm

USB ECM 使用方法可以参考 虚拟网卡(cdc-ecm) 章节

MSD (Mass Storage Device)

MSD(Mass Storage Device)是通过 USB 端口连接大容量存储设备。它允许设备硬盘、U盘、SD卡存储设备一样,提供数据存储读取功能,并主机(如个人电脑、智能手机、嵌入式设备等)进行通信。

代码分析

MSD 需要板端分配一块存储空间作为存储源。这块存储空间可以文件系统某个目录、一个存储镜像,或是一个 ramfs 镜像。主机可以访问本地存储一样读写存储源。选择存储类型(目录、镜像或 ramfs)取决于具体应用需求系统架构。ramfs 镜像通常用于需要高速读写临时存储场景,而文件系统目录镜像适用持久存储。
BSP源码默认使用文件系统镜像和 ramfs 镜像作为存储源。如果需要修改存储路径,请分别修改变量 $MSD_FILE$MSD_BLOCK_SIZE

  • 使用文件系统镜像作为存储

镜像路径 镜像大小 挂载目录 文件系统格式
/userdata/mass_storage.img 128M /media/mass_storage FAT32

执行命令

/etc/init.d/usb-gadget.sh restart msd
  • 使用 ramfs 镜像作为存储

镜像路径 镜像大小 挂载目录 文件系统格式
/dev/shm/msd_ram.img 512M /media/mass_storage FAT32

执行命令

/etc/init.d/usb-gadget.sh restart msd-ram

使用方法

Host 端显示大容量存储外设插入
USB_Gadget_MSD_Insert.png

对比源目录与 Host 端目录文件一致

  • 源目录

root@buildroot:/media/mass_storage# ls -al
total 11282
drwxr-xr-x 3 root root    16384 Jan  1 00:00 .
drwxrwxrwt 3 root root       60 Jan  1 00:00 ..
-rwxr-xr-x 1 root root        0 Jan  1  1980 file1
-rwxr-xr-x 1 root root  1048576 Jan  1  1980 file2
-rwxr-xr-x 1 root root 10485760 Jan  1  1980 file3
drwxr-xr-x 2 root root     2048 Jan  1  1980 folder
  • Host 端
    USB_Gadget_MSD_Host_Folder.png

HID (Human Interface Device)

USB HID(Human Interface Device) 是 USB 协议定义一种设备类,用于连接各种人机交互设备(如鼠标、键盘、游戏控制器、触摸屏等)到计算机其他主机系统。

代码分析

HID 设备创建 HID 功能时,需要提供报告描述符(Report Descriptor)来定义 HID 报告格式。SDK 默认提供一个报告描述符镜像文件:/etc/init.d/hid_report_desc.bin,用户可以根据自己的 HID 报告格式需求,替换镜像文件。
报告描述符是 HID 设备主机之间通信核心,它定义设备输入、输出特性信息,因此,正确配置报告描述符对于确保 HID 设备正常工作至关重要。

create_hid()
{
    CONFIG=$1
    FUNCTION=$2

    echo "Creating HID gadget functionality"
    mkdir functions/$FUNCTION
    echo 1 > functions/$FUNCTION/protocol
    echo 1 > functions/$FUNCTION/subclass
    echo 1024 > functions/$FUNCTION/report_length            # 设置 HID report 长度
    cat $HID_REPORT_DESC > functions/$FUNCTION/report_desc   # 设置 HID 报告描述符,用于设置 HID report 格式

    ln -s functions/$FUNCTION configs/c.1

    echo "OK"
}

使用方法

板端执行命令

/etc/init.d/usb-gadget.sh restart hid

Host 端显示 HID 外设插入
USB_Gadget_HID_Insert.png

ACM 串口

ACM (Abstract Control Model) 是USB通信设备类(CDC,Communication Device Class)中一种协议,主要用于支持基于串口通信设备。通过 USB ACM 协议,设备可以模拟一个标准串行端口(如RS-232串口)。 USB ACM 通常用于替代传统的 RS-232 或其他串行接口通信。

代码分析

create_acm()
{
    CONFIG=$1
    FUNCTION=$2

    echo "Creating ACM gadget functionality"
    mkdir functions/$FUNCTION                   # 创建 USB ACM Function 目录
    ln -s functions/$FUNCTION configs/c.1       # 创建 USB ACM Function 目录
}

使用方法

板端执行命令

/etc/init.d/usb-gadget.sh restart acm

板端生成 tty 节点 /dev/ttyGS0

Host 端显示串口插入 (本示例为 COM4)
USB_Gadget_ACM_Insert.png

以下通过简单串口收发测试验证 USB ACM 功能

  • ACM 通路测试 - 方向 Device->Host
    板端执行测试命令

echo "USB ACM Test: Device -> Host." > /dev/ttyGS0

Host 打开对应虚拟串口 (本示例为 COM4,默认波特率9600),并接收相同字符串
USB_Gadget_ACM_Test_Dev2Host.png

  • ACM 通路测试 - 方向 Host -> Device
    Host 打开对应虚拟串口 (本示例为 COM4,默认波特率9600),并发送字符串
    USB_Gadget_ACM_Test_Host2Dev.png

板端执行测试命令,并接收相同字符串

root@buildroot:/userdata# cat /dev/ttyGS0
USB ACM Test: Host -> Device.

UAC (USB Audio Class)

UAC(USB Audio Class) 是 USB 设备一部分,专门用于通过 USB 连接音频设备标准协议。UAC 允许设备(如耳机、麦克风、扬声器、音频接口、USB 声卡等)通过 USB 总线计算机其他 USB 主机进行音频数据传输。USB UAC 提供一个通用、标准化方式实现音频设备连接,从而使用户可以各种操作系统上(如 Windows、Linux、macOS 等)以即插即用方式使用这些设备。

代码分析

usb-gadget.sh 脚本通过函数 create_uac1() 配置 UAC 参数

create_uac1()
{
    CONFIG=$1
    FUNCTION=$2

    echo "Creating UAC1 gadget functionality : $FUNCTION"

    mkdir functions/$FUNCTION
    echo 0x3 > functions/$FUNCTION/p_chmask    # 使能声卡放音左右声道
    echo 48000 > functions/$FUNCTION/c_srate   # 设置采样率,示例 48K
    echo 0x3 > functions/$FUNCTION/c_chmask    # 使能声卡录音左右声道

    ln -s functions/$FUNCTION configs/c.1      # 绑定 UAC Function 到 config

    echo "OK"
}

常用参数接口设置如下

  • p_srate, c_srate: 采样率,支持采样率配置,采样率之间逗号隔开,例如 echo 8000,16000,32000 > c_srate

  • c_chmask, p_chmask: 声道掩码,支持多声道删除对应声道

  • p_ssize, c_ssize: 数据位宽,例如: 16bit 位宽值为2

使用方法

板端执行命令生成 UAC 设备

/etc/init.d/usb-gadget.sh restart uac1
  • 板端生成 UAC1 声卡
    查看 UAC1 声卡方法

root@buildroot:~# cat /proc/asound/cards
 0 [guaaudio       ]: gua-audio - gua-audio
                      gua-audio
 1 [UAC1Gadget     ]: UAC1_Gadget - UAC1_Gadget
                      UAC1_Gadget 0

root@buildroot:~# ls -al /proc/asound/card1
total 0
dr-xr-xr-x  5 root root 0 Jan  1 00:06 .
dr-xr-xr-x 11 root root 0 Jan  1 00:04 ..
-r--r--r--  1 root root 0 Jan  1 00:06 id
dr-xr-xr-x  4 root root 0 Jan  1 00:06 pcm0c
dr-xr-xr-x  4 root root 0 Jan  1 00:06 pcm0p

root@buildroot:~# ls /dev/snd/pcmC1D0*
/dev/snd/pcmC1D0c  /dev/snd/pcmC1D0p

可以看到 UAC1 对应 card1(UAC1Gadget),具有一个 playback 设备节点(pcmC1D0p),一个 capture 设备节点(pcmC1D0c)

  • Host 端显示音频设备接入
    USB_Gadget_UAC1_Insert.png

  • 测试 UAC1 放音功能
    注意: 由于 X5 EVB 主板没有音频播放设备,所以采用录制音频文件方式验证 UAC1 放音功能。
    Host端打开音频播放器,并选择 UAC1 对应放音设备。
    以 Audacity 为例,选择一段测试音频,并选择 UAC1 的放音设备(本示例为 2-AC Interface),点击播放。
    USB_Gadget_UAC1_Playback.png

板端执行命令

arecord -f dat -t wav -r 48000 -c 2 -D hw:1,0 /userdata/rec.wav

以上命令表示从 Card1(UAC1Gadget) 录音,指定格式为 wav,采样率48K,双声道,并保存文件 /userdata/rec.wav

  • 测试 UAC1 录音功能
    注意: 由于 X5 EVB 主板没有麦克风,所以采用播放音频文件方式验证 UAC1 录音功能。
    使用 Windows 的录音侦听功能,实时播放录音音频,方法如下:
    Host端打开 声音设置 -> 声音控制面板 -> 录制 -> 属性 -> 侦听,勾选侦听设备(本示例为 2-AC Interface),并选择 Host 端用于播放扬声器。
    USB_Gadget_UAC1_Capture.png

板端执行命令

aplay /userdata/play.wav -c 2 -r 48000 -D hw:1,0

以上命令表示通过 Card1(UAC1Gadget) 播放音频文件,指定格式为 wav,采样率48K,双声道。

UVC (USB Video Class)

UVC(USB Video Class) 是 USB 设备之一,用于支持视频设备(如摄像头、视频采集卡、网络摄像头等)通过 USB 总线计算机其他 USB 主机进行视频数据传输标准协议。UVC 允许视频设备操作系统(如 Windows、Linux、macOS 等)以即插即用方式进行连接,并提供视频流实时传输控制。

代码分析

当前 X5 UVC 支持分辨率以及格式

格式 分辨率
YUV 1280 x 720
H264 640 x 480
1088 x 1280
1280 x 720
1920 x 1080
MJPEG 1280 x 720
1920 x 1080

UVC 分辨率列表通过以下两处代码设置,如果用户自定义分辨率列表,需注意列表内容顺序保持一致。

  1. 通过 usb-gadget.sh 的 create_frame() 函数添加

create_uvc() {
	CONFIG=$1
	FUNCTION=$2

	echo "	Creating UVC gadget functionality : $FUNCTION"
	mkdir functions/$FUNCTION

    # 添加分辨率与格式到支持列表
	create_frame $FUNCTION 1280 720 uncompressed u
	create_frame $FUNCTION 640 480 uncompressed f h264
	create_frame $FUNCTION 1088 1280 uncompressed f h264
	create_frame $FUNCTION 1280 720 uncompressed f h264
	create_frame $FUNCTION 1920 1080 uncompressed f h264
	create_frame $FUNCTION 1280 720 mjpeg m
	create_frame $FUNCTION 1920 1080 mjpeg m

	mkdir functions/$FUNCTION/streaming/header/h
	cd functions/$FUNCTION/streaming/header/h
	ln -s ../../uncompressed/u
	ln -s ../../uncompressed/f
	ln -s ../../mjpeg/m
	cd ../../class/fs
	ln -s ../../header/h
	cd ../../class/hs
	ln -s ../../header/h
	cd ../../class/ss
	ln -s ../../header/h
	cd ../../../control
	mkdir header/h
	ln -s header/h class/fs
	ln -s header/h class/ss
	cd ../../../

	# Set the packet size: uvc gadget max size is 3k...
	echo 3072 > functions/$FUNCTION/streaming_maxpacket
	echo 2048 > functions/$FUNCTION/streaming_maxpacket
	echo 1024 > functions/$FUNCTION/streaming_maxpacket

	# use usb2.0 isoc max bandwidth, using 3072 max packet size
	echo 3072 > functions/$FUNCTION/streaming_maxpacket

	ln -s functions/$FUNCTION configs/c.1
}
  1. 通过 libguvc 源码添加
    路径 app/samples/platform_samples/sample_usb/gadget/libguvc/src/uvc_gadget.c

// YUV 格式分辨率列表
static struct uvc_frame_info uvc_frames_yuyv[] = {
	{ 1280, 720, { 333333, 0 }, 3072 }, /* Note: 720p */
	{ 0, 0, { 0, }, 0},
};

// MJPEG 格式分辨率列表
static struct uvc_frame_info uvc_frames_mjpeg[] = {
	{ 1280, 720, { 333333, 0 }, 384 }, /* Note: 720p, 80KB */
	{ 1920, 1080, { 333333, 0 }, 800 }, /* Note: 1080p, 200KB */
	{ 0, 0, { 0, }, },
};

// H264 格式分辨率列表
static struct uvc_frame_info uvc_frames_h264[] = {
	{ 640,  480, { 333333, 0 }, 3072 }, /* Note: 480p */
	{ 1088, 1280, { 333333, 0 }, 3072 }, /* Note: 1280p */
	{ 1280, 720, { 333333, 0 }, 3072 }, /* Note: 720p */
	{ 1920, 1080, { 333333, 0 }, 3072 }, /* Note: 1080p */
	{ 0, 0, { 0, }, },
};

// UVC 支持格式与分辨率列表
static struct uvc_format_info uvc_formats[] = {
	{V4L2_PIX_FMT_YUYV, uvc_frames_yuyv},
	{V4L2_PIX_FMT_H264, uvc_frames_h264},
	{V4L2_PIX_FMT_MJPEG, uvc_frames_mjpeg},
};

通过 potplayer 播放器获取 UVC 分辨率列表
USB_Gadget_UVC_Resolution.png

使用方法

X5 UVC Host 端的使用方法可以参考 uvc_gadget_camera 章节

4.3.18.5. 常见问题

X5 EVB 主板 USB2.0 和 3.0 分别哪个接口

  • USB2.0 对应 Micro-USB 接口
    USB2_Interface_MicroUSB.png

  • USB3.0 对应 USB Type-A 接口
    USB3_Interface_TypeA.png

使用 USB2.0 模拟设备提示 “usb-gadget is already running”

X5 EVB 启动默认通过 USB2.0 生成 ADB,如果使用 USB2.0 生成其他 USB 设备提示报错,说明系统已有 USB 设备运行,请执行 /etc/init.d/usb-gadget.sh stop

如何使用 USB 3.0 Gadget

USB3.0 请使用 /etc/init.d/usb3.0-gadget.sh脚本
使用方法与 USB2.0 相同

USB 3.0 切换 Host & Device 模式

USB3.0 默认工作在 Host 模式,用户可以通过软件硬件两种方式设置 USB3.0 工作在 Device 模式

  • 硬件方式

短接插针,启动后 USB3.0 自动切换到 Device 模式

USB3_DeviceMode_Enable_Plug.png

  • 软件方式

设置 USB3.0 为 Host模式

echo host > /sys/class/usb_role/35100000.usb-role-switch/role

设置 USB3.0 为 Device模式

echo device > /sys/class/usb_role/35100000.usb-role-switch/role