Skip to content

Commit b319828

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. A draft implementation of UVC is added leveraging this test. Signed-off-by: Josuah Demangeon <[email protected]>
1 parent a4563b4 commit b319828

File tree

13 files changed

+416
-3
lines changed

13 files changed

+416
-3
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include <zephyr/drivers/usb/uhc.h>
2323
#include <zephyr/sys/iterable_sections.h>
2424

25-
#ifdef __cplusplus
25+
_data#ifdef __cplusplus
2626
extern "C" {
2727
#endif
2828

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: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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,
41+
int err)
42+
{
43+
LOG_INF("%p %p %d", uhs_ctx, xfer, err);
44+
45+
return 0;
46+
}
47+
48+
static int usbh_uvc_connected(struct usbh_contex *const uhs_ctx)
49+
{
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+
/* TODO this hardcodes a single class instance as only class of the system */
120+
const struct usbh_class_data *usbh_class = &usbh_uvc_class;
121+
122+
int usbh_uvc_get_caps(const struct device *const dev, struct video_caps *const caps)
123+
{
124+
return 0;
125+
}
126+
127+
int usbh_uvc_get_format(const struct device *const dev, struct video_format *const fmt)
128+
{
129+
return 0;
130+
}
131+
132+
int usbh_uvc_set_stream(const struct device *const dev, bool enable, enum video_buf_type type)
133+
{
134+
return 0;
135+
}
136+
137+
int usbh_uvc_enqueue(const struct device *const dev, struct video_buffer *const vbuf)
138+
{
139+
return 0;
140+
}
141+
142+
int usbh_uvc_dequeue(const struct device *const dev, struct video_buffer **const vbuf,
143+
k_timeout_t timeout)
144+
{
145+
return 0;
146+
}
147+
148+
static DEVICE_API(video, uvc_video_api) = {
149+
.get_caps = usbh_uvc_get_caps,
150+
.get_format = usbh_uvc_get_format,
151+
.set_stream = usbh_uvc_set_stream,
152+
.enqueue = usbh_uvc_enqueue,
153+
.dequeue = usbh_uvc_dequeue,
154+
};
155+
156+
#define USBH_VIDEO_DT_DEVICE_DEFINE(n) \
157+
\
158+
DEVICE_DT_INST_DEFINE(n, usbh_uvc_preinit, NULL, \
159+
NULL, &usbh_uvc_class, \
160+
POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \
161+
&uvc_video_api); \
162+
\
163+
VIDEO_DEVICE_DEFINE(uvc_host_##n, DEVICE_DT_INST_GET(n), NULL);
164+
165+
DT_INST_FOREACH_STATUS_OKAY(USBH_VIDEO_DT_DEVICE_DEFINE)

subsys/usb/host/usbh_core.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ K_MSGQ_DEFINE(usbh_msgq, sizeof(struct uhc_event),
2929
K_MSGQ_DEFINE(usbh_bus_msgq, sizeof(struct uhc_event),
3030
CONFIG_USBH_MAX_UHC_MSG, sizeof(uint32_t));
3131

32-
extern const struct usbh_class_data *usbh_class_data;
32+
extern const struct usbh_class_data *usbh_class;
3333

3434
static int usbh_event_carrier(const struct device *dev,
3535
const struct uhc_event *const event)
@@ -75,7 +75,7 @@ static void dev_connected_handler(struct usbh_contex *const ctx,
7575
return;
7676
}
7777

78-
if (usbh_class_data->connected(ctx)) {
78+
if (usbh_class->connected(ctx)) {
7979
LOG_ERR("The class failed to handle the connection");
8080
return;
8181
}
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+
};

0 commit comments

Comments
 (0)