Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usb: device_next: new USB Video Class (UVC) implementation #76798

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ supported:
- gpio
- spi
- i2c
- usbd
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move it to a separate commit "boards: nicla_vision: add usbd test feature"

vendor: arduino
2 changes: 2 additions & 0 deletions doc/connectivity/usb/device/usb_device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,8 @@ The following Product IDs are currently used:
+----------------------------------------------------+--------+
| :zephyr:code-sample:`uac2-implicit-feedback` | 0x000F |
+----------------------------------------------------+--------+
| :zephyr:code-sample:`uvc` | 0x0011 |
+----------------------------------------------------+--------+
| :zephyr:code-sample:`usb-dfu` (DFU Mode) | 0xFFFF |
+----------------------------------------------------+--------+

Expand Down
4 changes: 4 additions & 0 deletions doc/connectivity/usb/device_next/usb_device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Samples

* :zephyr:code-sample:`uac2-implicit-feedback`

* :zephyr:code-sample:`uvc`

Samples ported to new USB device support
----------------------------------------

Expand Down Expand Up @@ -223,6 +225,8 @@ instance (``n``) and is used as an argument to the :c:func:`usbd_register_class`
+-----------------------------------+-------------------------+-------------------------+
| Bluetooth HCI USB transport layer | :ref:`bt_hci_raw` | :samp:`bt_hci_{n}` |
+-----------------------------------+-------------------------+-------------------------+
| USB Video Class (UVC) | Video device | :samp:`uvc_{n}` |
+-----------------------------------+-------------------------+-------------------------+

CDC ACM UART
============
Expand Down
54 changes: 54 additions & 0 deletions dts/bindings/usb/zephyr,uvc-device.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (c) 2025 tinyVision.ai Inc.
# SPDX-License-Identifier: Apache-2.0

description: |
USB Video Class device

The Video Class will present a video endpoint to interconnect with any video
device. It supports multiple endpoints, each becoming its own video streaming
interface to the host.

The source video devices connected to UVC will receive API calls asking about
the formats, frame intervals, video controls supported. This information is
used to generate the USB device descriptors latter sent to the host.

They will then be controlled according to the commands sent by the USB host.

Example for single endpoint connected to mipi0:

uvc0: uvc {
compatible = "zephyr,uvc-device";
port {
uvc0_ep_in: endpoint {
remote-endpoint-label = "mipi0_ep_out";
};
};
};

Example for multiple endpoints connected to mipi0 and mipi1:

uvc0: uvc {
compatible = "zephyr,uvc-device";
port {
#address-cells = <1>;
#size-cells = <0>;

uvc0_ep0_in: endpoint@0 {
reg = <0>;
remote-endpoint-label = "mipi0_ep_out";
};

uvc0_ep1_in: endpoint@1 {
reg = <1>;
remote-endpoint-label = "mipi1_ep_out";
};
};
};

compatible: "zephyr,uvc-device"

include: base.yaml

child-binding:
child-binding:
include: video-interfaces.yaml
120 changes: 120 additions & 0 deletions include/zephyr/usb/class/usbd_uvc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @file
* @brief USB Device Firmware Upgrade (DFU) public header
*
* Header exposes API for registering DFU images.
*/

#ifndef ZEPHYR_INCLUDE_USB_CLASS_USBD_UVC_H
#define ZEPHYR_INCLUDE_USB_CLASS_USBD_UVC_H

#include <zephyr/kernel.h>

struct uvc_probe {
uint16_t bmHint;
uint8_t bFormatIndex;
uint8_t bFrameIndex;
uint32_t dwFrameInterval;
uint16_t wKeyFrameRate;
uint16_t wPFrameRate;
uint16_t wCompQuality;
uint16_t wCompWindowSize;
uint16_t wDelay;
uint32_t dwMaxVideoFrameSize;
uint32_t dwMaxPayloadTransferSize;
uint32_t dwClockFrequency;
uint8_t bmFramingInfo;
#define UVC_BMFRAMING_INFO_FID BIT(0)
#define UVC_BMFRAMING_INFO_EOF BIT(1)
#define UVC_BMFRAMING_INFO_EOS BIT(2)
uint8_t bPreferedVersion;
uint8_t bMinVersion;
uint8_t bMaxVersion;
uint8_t bUsage;
uint8_t bBitDepthLuma;
uint8_t bmSettings;
uint8_t bMaxNumberOfRefFramesPlus1;
uint16_t bmRateControlModes;
uint64_t bmLayoutPerStream;
} __packed;

/* This is a particular variant of this struct that is used by the Zephyr implementation. Other
* organization of the fields are allowed by the standard.
*/
struct uvc_payload_header {
uint8_t bHeaderLength;
uint8_t bmHeaderInfo;
uint32_t dwPresentationTime; /* optional */
uint32_t scrSourceClockSTC; /* optional */
uint16_t scrSourceClockSOF; /* optional */
} __packed;

/* Information used for declaring and operating a video stream */
struct uvc_stream_data {
/* Global state of the UVC video device endpoint */
atomic_t state;
/* Input buffers to which enqueued video buffers land */
struct k_fifo fifo_in;
/* Output buffers from which dequeued buffers are picked */
struct k_fifo fifo_out;
/* VideoControl unit descriptors */
struct usb_desc_header **desc_vc;
/* VideoStreaming format and frame descriptors */
struct usb_desc_header **desc_vs;
/* Pointer to the endpoint descriptor of this stream */
struct usb_ep_descriptor *desc_vs_ep_fs;
struct usb_ep_descriptor *desc_vs_ep_hs;
/* Default video probe stored at boot time and sent back to the host when requested. */
struct uvc_probe default_probe;
/* Video payload header content sent before every frame, updated between every frame */
struct uvc_payload_header payload_header;
/* Format currently selected by the host */
uint8_t format_id;
/* Frame currently selected by the host */
uint8_t frame_id;
/* Video device that is connected to this UVC stream */
const struct device *video_dev;
/* Video format cached locally for efficiency */
struct video_format video_fmt;
/* Current frame interval selected by the host */
struct video_frmival video_frmival;
/* Byte offset within the currently transmitted video buffer */
size_t vbuf_offset;
/* Makes sure flushing the stream only happens in one context at a time. */
struct k_mutex mutex;
/* Zero Length packet used to reset a stream when restarted */
struct net_buf zlp;
/* Signal to alert video devices of buffer-related evenets */
struct k_poll_signal *signal;
};

/* Instance of a VideoStreaming interface */
struct uvc_stream {
const struct device *dev;
/* Name of the stream, matching the string descriptor */
const char *name;
/* Pointer to memory for variable stream data */
struct uvc_stream_data *data;
};

#define USBD_UVC_DEFINE_STREAM(_id, _uvc_dev, _name) \
static struct uvc_stream_data uvc_stream_data_##_id; \
\
const static STRUCT_SECTION_ITERABLE(uvc_stream, _id) = { \
.dev = _uvc_dev, \
.name = _name, \
.data = &uvc_stream_data_##_id, \
};

static inline void uvc_set_video_dev(const struct uvc_stream *strm, const struct device *dev)
{
strm->data->video_dev = dev;
}

#endif /* ZEPHYR_INCLUDE_USB_CLASS_USBD_UVC_H */
3 changes: 2 additions & 1 deletion samples/subsys/usb/common/sample_usbd_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ static void sample_fix_code_triple(struct usbd_context *uds_ctx,
IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) ||
IS_ENABLED(CONFIG_USBD_MIDI2_CLASS) ||
IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) {
IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS) ||
IS_ENABLED(CONFIG_USBD_VIDEO_CLASS)) {
/*
* Class with multiple interfaces have an Interface
* Association Descriptor available, use an appropriate triple
Expand Down
8 changes: 8 additions & 0 deletions samples/subsys/usb/uvc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(usb_video)

include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
target_sources(app PRIVATE src/main.c)
9 changes: 9 additions & 0 deletions samples/subsys/usb/uvc/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

# Source common USB sample options used to initialize new experimental USB
# device stack. The scope of these options is limited to USB samples in project
# tree, you cannot use them in your own application.
source "samples/subsys/usb/common/Kconfig.sample_usbd"

source "Kconfig.zephyr"
Loading
Loading