4.3.19. USB/USB Gadget调试指南

4.3.19.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.19.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.19.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.19.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.19.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