Skip to content

Commit e524c88

Browse files
author
Josuah Demangeon
committed
tests: usb: uvc: connect USB device and host via UVB
Add a test to run the USB Video Class host support by using the existing Zephyr USB Video Class device support. This allows running implementing the host side from the device side. Signed-off-by: Josuah Demangeon <[email protected]>
1 parent badef2d commit e524c88

File tree

12 files changed

+416
-0
lines changed

12 files changed

+416
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
USB Video Class (UVC) host instance.
6+
7+
Each UVC instance added to the USB Host Controller (UHC) node will be visible
8+
as a new camera from Zephyr point of view.
9+
10+
as soon as a camera is connected to USB this device will be usable by the application as a
11+
video device, following the video API.
12+
13+
compatible: "zephyr,uvc-host"
14+
15+
include: base.yaml

include/zephyr/usb/usbh.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <zephyr/drivers/usb/uhc.h>
2323
#include <zephyr/sys/iterable_sections.h>
2424

25+
extern const struct usbh_class_data *usbh_class_data;
26+
2527
#ifdef __cplusplus
2628
extern "C" {
2729
#endif

subsys/usb/host/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ zephyr_library_sources_ifdef(
1616
usbh_shell.c
1717
)
1818

19+
zephyr_library_sources_ifdef(
20+
CONFIG_USBH_VIDEO_CLASS
21+
class/usbh_uvc.c
22+
)
23+
1924
zephyr_library_sources_ifdef(
2025
CONFIG_USBIP
2126
usbip.c

subsys/usb/host/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,6 @@ config USBH_MAX_UHC_MSG
5454
Maximum number of USB host controller events that can be queued.
5555

5656
rsource "Kconfig.usbip"
57+
rsource "class/Kconfig"
5758

5859
endif # USB_HOST_STACK

subsys/usb/host/class/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
rsource "Kconfig.uvc"

subsys/usb/host/class/Kconfig.uvc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config USBH_VIDEO_CLASS
6+
bool "USB Video Class implementation [EXPERIMENTAL]"
7+
depends on DT_HAS_ZEPHYR_UVC_HOST_ENABLED
8+
select EXPERIMENTAL
9+
help
10+
USB Host Video Class (UVC) implementation.
11+
12+
if USBH_VIDEO_CLASS
13+
14+
module = USBH_VIDEO
15+
module-str = usbh uvc
16+
default-count = 1
17+
source "subsys/logging/Kconfig.template.log_config"
18+
19+
endif # USBH_VIDEO_CLASS

subsys/usb/host/class/usbh_uvc.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright (c) 2025 tinyVision.ai Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT zephyr_uvc_host
8+
9+
#include <stdlib.h>
10+
11+
#include <zephyr/init.h>
12+
#include <zephyr/devicetree.h>
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/sys/byteorder.h>
15+
#include <zephyr/sys/atomic.h>
16+
#include <zephyr/usb/usbh.h>
17+
#include <zephyr/usb/usb_ch9.h>
18+
#include <zephyr/drivers/usb/udc.h>
19+
#include <zephyr/drivers/video.h>
20+
#include <zephyr/drivers/video-controls.h>
21+
#include <zephyr/logging/log.h>
22+
23+
#include <zephyr/devicetree.h>
24+
#include <zephyr/sys/util.h>
25+
#include <zephyr/usb/usb_ch9.h>
26+
#include <zephyr/usb/class/usb_uvc.h>
27+
28+
#include "usbh_device.h"
29+
#include "usbh_ch9.h"
30+
31+
#include "../../../drivers/video/video_ctrls.h"
32+
#include "../../../drivers/video/video_device.h"
33+
34+
LOG_MODULE_REGISTER(usbh_uvc, CONFIG_USBH_VIDEO_LOG_LEVEL);
35+
36+
struct usbh_uvc_config {
37+
struct usbh_contex *uhs_ctx;
38+
};
39+
40+
static int usbh_uvc_request(struct usbh_contex *const uhs_ctx, struct uhc_transfer *const xfer, int err)
41+
{
42+
LOG_INF("%p %p %d", uhs_ctx, xfer, err);
43+
44+
return 0;
45+
}
46+
47+
static int usbh_uvc_connected(struct usbh_contex *const uhs_ctx)
48+
{
49+
/* TODO for now, assume the root device is matching the class (no HUB, just one class) */
50+
struct usb_device *const udev = uhs_ctx->root;
51+
const size_t len = 512;
52+
struct net_buf *buf;
53+
int ret;
54+
55+
LOG_INF("%p", uhs_ctx);
56+
57+
buf = usbh_xfer_buf_alloc(udev, len);
58+
if (buf == NULL) {
59+
LOG_ERR("Failed to allocate a host transfer buffer");
60+
return -ENOMEM;
61+
}
62+
63+
ret = usbh_req_desc(udev, USB_DESC_DEVICE, 0, 0, len, buf);
64+
if (ret != 0) {
65+
LOG_ERR("Failed to request descriptor");
66+
return ret;
67+
}
68+
69+
LOG_HEXDUMP_INF(buf->data, buf->len, "buf");
70+
71+
return 0;
72+
}
73+
74+
static int usbh_uvc_removed(struct usbh_contex *const uhs_ctx)
75+
{
76+
LOG_INF("%p", uhs_ctx);
77+
78+
return 0;
79+
}
80+
81+
static int usbh_uvc_rwup(struct usbh_contex *const uhs_ctx)
82+
{
83+
LOG_INF("%p", uhs_ctx);
84+
85+
return 0;
86+
}
87+
88+
static int usbh_uvc_suspended(struct usbh_contex *const uhs_ctx)
89+
{
90+
LOG_INF("%p", uhs_ctx);
91+
92+
return 0;
93+
}
94+
95+
static int usbh_uvc_resumed(struct usbh_contex *const uhs_ctx)
96+
{
97+
LOG_INF("%p", uhs_ctx);
98+
99+
return 0;
100+
}
101+
102+
static int usbh_uvc_preinit(const struct device *dev)
103+
{
104+
LOG_INF("%s", dev->name);
105+
106+
return 0;
107+
}
108+
109+
static const struct usbh_class_data usbh_uvc_class = {
110+
.code = {0, 0, 0},
111+
.request = usbh_uvc_request,
112+
.connected = usbh_uvc_connected,
113+
.removed = usbh_uvc_removed,
114+
.rwup = usbh_uvc_rwup,
115+
.suspended = usbh_uvc_suspended,
116+
.resumed = usbh_uvc_resumed,
117+
};
118+
119+
/* This hardcodes a single class instance as only class of the system */
120+
/* TODO replace this by something letting the host stack decide which class to call. */
121+
const struct usbh_class_data *usbh_class_data = &usbh_uvc_class;
122+
123+
int usbh_uvc_get_caps(const struct device *const dev, struct video_caps *const caps)
124+
{
125+
return 0;
126+
}
127+
128+
int usbh_uvc_get_format(const struct device *const dev, struct video_format *const fmt)
129+
{
130+
return 0;
131+
}
132+
133+
int usbh_uvc_set_stream(const struct device *const dev, bool enable, enum video_buf_type type)
134+
{
135+
return 0;
136+
}
137+
138+
int usbh_uvc_enqueue(const struct device *const dev, struct video_buffer *const vbuf)
139+
{
140+
return 0;
141+
}
142+
143+
int usbh_uvc_dequeue(const struct device *const dev, struct video_buffer **const vbuf,
144+
k_timeout_t timeout)
145+
{
146+
return 0;
147+
}
148+
149+
static DEVICE_API(video, uvc_video_api) = {
150+
.get_caps = usbh_uvc_get_caps,
151+
.get_format = usbh_uvc_get_format,
152+
.set_stream = usbh_uvc_set_stream,
153+
.enqueue = usbh_uvc_enqueue,
154+
.dequeue = usbh_uvc_dequeue,
155+
};
156+
157+
#define USBH_VIDEO_DT_DEVICE_DEFINE(n) \
158+
\
159+
DEVICE_DT_INST_DEFINE(n, usbh_uvc_preinit, NULL, \
160+
NULL, &usbh_uvc_class, \
161+
POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \
162+
&uvc_video_api); \
163+
\
164+
VIDEO_DEVICE_DEFINE(uvc_host_##n, DEVICE_DT_INST_GET(n), NULL);
165+
166+
DT_INST_FOREACH_STATUS_OKAY(USBH_VIDEO_DT_DEVICE_DEFINE)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(uvc_uvb)
6+
7+
# This sample cannot use the common USB sample infrastructure as we do not want
8+
# to initialize the board default `zephyr_udc0` but instead an unrelated `virtual_udc0`.
9+
#include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
10+
11+
FILE(GLOB app_sources src/*.c)
12+
target_sources(app PRIVATE ${app_sources})
13+
zephyr_include_directories(${ZEPHYR_BASE}/subsys/usb/host)

tests/subsys/usb/uvc/app.overlay

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/ {
8+
virtual_uhc0: uhc-virtual {
9+
compatible = "zephyr,uhc-virtual";
10+
maximum-speed = "full-speed";
11+
12+
virtual_udc0: udc-virtual {
13+
compatible = "zephyr,udc-virtual";
14+
num-bidir-endpoints = <4>;
15+
maximum-speed = "full-speed";
16+
};
17+
};
18+
19+
uvc_device: uvc-device {
20+
compatible = "zephyr,uvc-device";
21+
};
22+
23+
uvc_host: uvc-host {
24+
compatible = "zephyr,uvc-host";
25+
};
26+
};

tests/subsys/usb/uvc/prj.conf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CONFIG_LOG=y
2+
CONFIG_UDC_BUF_POOL_SIZE=2048
3+
CONFIG_UDC_DRIVER_LOG_LEVEL_INF=y
4+
CONFIG_UHC_DRIVER_LOG_LEVEL_INF=y
5+
CONFIG_USBD_VIDEO_CLASS=y
6+
CONFIG_USBD_VIDEO_LOG_LEVEL_INF=y
7+
CONFIG_USBH_LOG_LEVEL_INF=y
8+
CONFIG_USBH_VIDEO_CLASS=y
9+
CONFIG_USBH_VIDEO_LOG_LEVEL_INF=y
10+
CONFIG_USB_DEVICE_STACK_NEXT=y
11+
CONFIG_USB_HOST_STACK=y
12+
CONFIG_UVB_LOG_LEVEL_INF=y
13+
CONFIG_VIDEO=y
14+
CONFIG_VIDEO_LOG_LEVEL_INF=y
15+
CONFIG_ZTEST=y

0 commit comments

Comments
 (0)