Linux UVC Gadget 驱动

概述

UVC Gadget 驱动是用于 USB 连接设备端的硬件的驱动程序。它旨在运行在具有 USB 设备端硬件(例如带有 OTG 端口的板卡)的 Linux 系统上。

在设备系统上,一旦驱动绑定,它将表现为一个具有输出能力的 V4L2 设备。

在主机端(通过 USB 电缆连接后),运行 UVC Gadget 驱动并由适当的用户空间程序控制的设备应显示为符合 UVC 规范的摄像头,并能与任何设计用于处理它们的程序正常运行。在设备系统上运行的用户空间程序可以将来自各种来源的图像缓冲区排队,通过 USB 连接传输。通常这意味着转发来自摄像头传感器外设的缓冲区,但缓冲区的来源完全取决于用户空间伴随程序。

配置设备内核

必须选择 Kconfig 选项 USB_CONFIGFS、USB_LIBCOMPOSITE、USB_CONFIGFS_F_UVC 和 USB_F_UVC 以启用 UVC gadget 的支持。

通过 configfs 配置 gadget

UVC Gadget 期望通过使用 UVC 函数的 configfs 进行配置。这提供了很大的灵活性,因为 UVC 设备的许多设置都可以通过这种方式控制。

并非所有可用属性都在此处描述。有关完整枚举,请参阅ABI 文件测试/configfs-usb-gadget-uvc

假设

本节假设您已将 configfs 挂载到 /sys/kernel/config 并将 gadget 创建为 /sys/kernel/config/usb_gadget/g1

UVC 函数

第一步是创建 UVC 函数

# These variables will be assumed throughout the rest of the document
CONFIGFS="/sys/kernel/config"
GADGET="$CONFIGFS/usb_gadget/g1"
FUNCTION="$GADGET/functions/uvc.0"

mkdir -p $FUNCTION

格式和帧

您必须通过告知 gadget 支持哪些格式,以及每种格式支持的帧大小和帧间隔来配置 gadget。在当前的实现中,gadget 无法拒绝设置主机指示其设置的格式,因此,准确地完成此步骤以确保主机永远不会请求无法提供的格式非常重要。

格式在 streaming/uncompressed 和 streaming/mjpeg configfs 组下创建,帧大小在格式下创建,结构如下

uvc.0 +
      |
      + streaming +
                  |
                  + mjpeg +
                  |       |
                  |       + mjpeg +
                  |            |
                  |            + 720p
                  |            |
                  |            + 1080p
                  |
                  + uncompressed +
                                 |
                                 + yuyv +
                                        |
                                        + 720p
                                        |
                                        + 1080p

然后可以为每个帧配置宽度和高度,加上存储单个帧所需的最大缓冲区大小,最后是该格式和帧大小支持的帧间隔。宽度和高度以像素为单位枚举,帧间隔以 100ns 为单位。例如,要创建上述结构,并为每个帧大小设置 2、15 和 100 fps 的帧间隔,您可以这样做

create_frame() {
        # Example usage:
        # create_frame <width> <height> <group> <format name>

        WIDTH=$1
        HEIGHT=$2
        FORMAT=$3
        NAME=$4

        wdir=$FUNCTION/streaming/$FORMAT/$NAME/${HEIGHT}p

        mkdir -p $wdir
        echo $WIDTH > $wdir/wWidth
        echo $HEIGHT > $wdir/wHeight
        echo $(( $WIDTH * $HEIGHT * 2 )) > $wdir/dwMaxVideoFrameBufferSize
        cat <<EOF > $wdir/dwFrameInterval
666666
100000
5000000
EOF
}

create_frame 1280 720 mjpeg mjpeg
create_frame 1920 1080 mjpeg mjpeg
create_frame 1280 720 uncompressed yuyv
create_frame 1920 1080 uncompressed yuyv

目前唯一支持的未压缩格式是 YUYV,详情请参阅打包的 YUV 格式

颜色匹配描述符

可以为您创建的每种格式指定一些色彩信息。此步骤是可选的,如果跳过此步骤,将包含默认信息;这些默认值遵循 UVC 规范的颜色匹配描述符部分中定义的那些值。

要创建颜色匹配描述符,请创建一个 configfs 项并将其三个属性设置为您想要的设置,然后从您希望与之关联的格式链接到它

# Create a new Color Matching Descriptor

mkdir $FUNCTION/streaming/color_matching/yuyv
pushd $FUNCTION/streaming/color_matching/yuyv

echo 1 > bColorPrimaries
echo 1 > bTransferCharacteristics
echo 4 > bMatrixCoefficients

popd

# Create a symlink to the Color Matching Descriptor from the format's config item
ln -s $FUNCTION/streaming/color_matching/yuyv $FUNCTION/streaming/uncompressed/yuyv

有关有效值的详细信息,请查阅 UVC 规范。请注意,存在一个默认的颜色匹配描述符,任何未链接到不同颜色匹配描述符的格式都将使用它。可以更改默认描述符的属性设置,因此请记住,如果您这样做,您将更改任何未链接到不同描述符的格式的默认值。

头部链接

UVC 规范要求格式和帧描述符前面有头部,详细说明后续不同格式描述符的数量和累积大小等。此操作和类似操作通过 configfs 中代表头部的 configfs 项与代表其他描述符的 config 项之间的链接来实现,方式如下

mkdir $FUNCTION/streaming/header/h

# This section links the format descriptors and their associated frames
# to the header
cd $FUNCTION/streaming/header/h
ln -s ../../uncompressed/yuyv
ln -s ../../mjpeg/mjpeg

# This section ensures that the header will be transmitted for each
# speed's set of descriptors. If support for a particular speed is not
# needed then it can be skipped here.
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

扩展单元支持

UVC 扩展单元 (XU) 基本上提供了一个独立的单元,可以向其发送控制设置和获取请求。这些控制请求的含义完全取决于实现,但可用于控制 UVC 规范之外的设置(例如启用或禁用视频效果)。XU 可以插入到 UVC 单元链中,也可以保持自由悬挂。

配置扩展单元涉及在相应目录中创建条目并适当设置其属性,如下所示

mkdir $FUNCTION/control/extensions/xu.0
pushd $FUNCTION/control/extensions/xu.0

# Set the bUnitID of the Processing Unit as the source for this
# Extension Unit
echo 2 > baSourceID

# Set this XU as the source of the default output terminal. This inserts
# the XU into the UVC chain between the PU and OT such that the final
# chain is IT > PU > XU.0 > OT
cat bUnitID > ../../terminal/output/default/baSourceID

# Flag some controls as being available for use. The bmControl field is
# a bitmap with each bit denoting the availability of a particular
# control. For example to flag the 0th, 2nd and 3rd controls available:
echo 0x0d > bmControls

# Set the GUID; this is a vendor-specific code identifying the XU.
echo -e -n "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" > guidExtensionCode

popd

bmControls 属性和 baSourceID 属性是多值属性。这意味着您可以向它们写入多个以换行符分隔的值。例如,要将第 1、2、9 和 10 个控制标记为可用,您需要向 bmControls 写入两个值,如下所示

cat << EOF > bmControls
0x03
0x03
EOF

baSourceID 属性的多值特性表明 XU 可以是多输入的,但请注意,这目前没有显著影响。

bControlSize 属性反映 bmControls 属性的大小,类似地 bNrInPins 反映 baSourceID 属性的大小。当您设置 bmControls 和 baSourceID 时,这两个属性都会自动增加/减少。也可以手动增加或减少 bControlSize,这将导致条目截断到新大小,或用 0x00 填充条目,例如

$ cat bmControls
0x03
0x05

$ cat bControlSize
2

$ echo 1 > bControlSize
$ cat bmControls
0x03

$ echo 2 > bControlSize
$ cat bmControls
0x03
0x00

bNrInPins 和 baSourceID 的功能相同。

配置摄像头终端和处理单元支持的控制

UVC 链中的摄像头终端和处理单元也有 bmControls 属性,其功能与扩展单元中的相同字段类似。然而,与 XU 不同的是,这些单元的位标志的含义在 UVC 规范中定义;您应该查阅“摄像头终端描述符”和“处理单元描述符”部分以获取标志的枚举。

# Set the Processing Unit's bmControls, flagging Brightness, Contrast
# and Hue as available controls:
echo 0x05 > $FUNCTION/control/processing/default/bmControls

# Set the Camera Terminal's bmControls, flagging Focus Absolute and
# Focus Relative as available controls:
echo 0x60 > $FUNCTION/control/terminal/camera/default/bmControls

如果您不设置这些字段,则默认情况下,摄像头终端的自动曝光模式控制和处理单元的亮度控制将被标记为可用;如果它们不受支持,您应将该字段设置为 0x00。

请注意,摄像头终端或处理单元的 bmControls 字段的大小由 UVC 规范固定,因此 bControlSize 属性在此处是只读的。

自定义字符串支持

提供 USB 设备各个部分的文本描述的字符串描述符可以在 USB configfs 中的常用位置定义,然后可以从 UVC 函数根目录或扩展单元目录链接到它们,以将这些字符串指定为描述符

# Create a string descriptor in us-EN and link to it from the function
# root. The name of the link is significant here, as it declares this
# descriptor to be intended for the Interface Association Descriptor.
# Other significant link names at function root are vs0_desc and vs1_desc
# For the VideoStreaming Interface 0/1 Descriptors.

mkdir -p $GADGET/strings/0x409/iad_desc
echo -n "Interface Associaton Descriptor" > $GADGET/strings/0x409/iad_desc/s
ln -s $GADGET/strings/0x409/iad_desc $FUNCTION/iad_desc

# Because the link to a String Descriptor from an Extension Unit clearly
# associates the two, the name of this link is not significant and may
# be set freely.

mkdir -p $GADGET/strings/0x409/xu.0
echo -n "A Very Useful Extension Unit" > $GADGET/strings/0x409/xu.0/s
ln -s $GADGET/strings/0x409/xu.0 $FUNCTION/control/extensions/xu.0

中断端点

VideoControl 接口有一个可选的中断端点,默认情况下是禁用的。这旨在支持 UVC 的延迟响应控制设置请求(应该通过中断端点而不是占用端点 0 进行响应)。目前缺少通过此端点发送数据的支持,因此为了避免混淆,它保持禁用状态。如果您希望启用它,可以通过 configfs 属性进行

echo 1 > $FUNCTION/control/enable_interrupt_ep

带宽配置

有三个属性控制 USB 连接的带宽。它们位于功能根目录中,可以在限制范围内设置

# streaming_interval sets bInterval. Values range from 1..255
echo 1 > $FUNCTION/streaming_interval

# streaming_maxpacket sets wMaxPacketSize. Valid values are 1024/2048/3072
echo 3072 > $FUNCTION/streaming_maxpacket

# streaming_maxburst sets bMaxBurst. Valid values are 1..15
echo 1 > $FUNCTION/streaming_maxburst

此处传递的值将根据 UVC 规范(取决于 USB 连接的速度)钳制到有效值。要了解这些设置如何影响带宽,您应该查阅 UVC 规范,但一个经验法则是,增加 streaming_maxpacket 设置将改善带宽(从而提高最大可能帧率),而 streaming_maxburst 在 USB 连接以超高速运行时也是如此。增加 streaming_interval 将降低带宽和帧率。

用户空间应用程序

UVC Gadget 驱动本身无法做任何特别有趣的事情。它必须与一个用户空间程序配对,该程序响应 UVC 控制请求并填充缓冲区以排队到驱动程序创建的 V4L2 设备。这些如何实现取决于具体实现,并且超出了本文档的范围,但可以在https://gitlab.freedesktop.org/camera/uvc-gadget找到一个参考应用程序