Linux UVC Gadget 驱动程序

概述

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

在设备系统上,一旦驱动程序绑定,它会显示为具有输出功能的 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 设备的许多设置都可以通过这种方式控制。

并非所有可用的属性都在这里描述。有关完整的枚举,请参阅 Documentation/ABI/testing/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,其详细信息位于Packed 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 项目和表示其他描述符的配置项目之间进行链接来实现,如下所示

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 configfs 中的通常位置定义为 USB 设备的各个部分提供文本描述的字符串描述符,然后可以从 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