Skip to content

Commit 837c40d

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 b3260c5 commit 837c40d

File tree

10 files changed

+262
-0
lines changed

10 files changed

+262
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# SPDX-FileCopyrightText: Copyright 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

subsys/usb/host/CMakeLists.txt

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

21+
zephyr_library_sources_ifdef(
22+
CONFIG_USBH_VIDEO_CLASS
23+
class/usbh_uvc.c
24+
)
25+
2126
zephyr_library_sources_ifdef(
2227
CONFIG_USBIP
2328
usbip.c

subsys/usb/host/Kconfig

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

5555
rsource "Kconfig.usbip"
56+
rsource "class/Kconfig"
5657

5758
endif # USB_HOST_STACK

subsys/usb/host/class/Kconfig

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

subsys/usb/host/class/Kconfig.uvc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# SPDX-FileCopyrightText: Copyright Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config USBH_VIDEO_CLASS
5+
bool "USB Video Class implementation [EXPERIMENTAL]"
6+
depends on DT_HAS_ZEPHYR_UVC_HOST_ENABLED
7+
select EXPERIMENTAL
8+
help
9+
USB Host Video Class (UVC) implementation.
10+
11+
if USBH_VIDEO_CLASS
12+
13+
module = USBH_VIDEO
14+
module-str = usbh uvc
15+
default-count = 1
16+
source "subsys/logging/Kconfig.template.log_config"
17+
18+
endif # USBH_VIDEO_CLASS
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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright Nordic Semiconductor ASA
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
/ {
7+
virtual_uhc0: uhc-virtual {
8+
compatible = "zephyr,uhc-virtual";
9+
maximum-speed = "full-speed";
10+
11+
virtual_udc0: udc-virtual {
12+
compatible = "zephyr,udc-virtual";
13+
num-bidir-endpoints = <4>;
14+
maximum-speed = "full-speed";
15+
};
16+
};
17+
18+
uvc_device: uvc-device {
19+
compatible = "zephyr,uvc-device";
20+
};
21+
22+
uvc_host: uvc-host {
23+
compatible = "zephyr,uvc-host";
24+
};
25+
};

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

tests/subsys/usb/uvc/src/main.c

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright Nordic Semiconductor ASA
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include <zephyr/device.h>
7+
#include <zephyr/logging/log.h>
8+
#include <zephyr/usb/usbd.h>
9+
#include <zephyr/usb/usbh.h>
10+
#include <zephyr/usb/class/usbd_uvc.h>
11+
#include <zephyr/ztest.h>
12+
13+
#include "../../../../../drivers/video/video_common.h"
14+
15+
LOG_MODULE_REGISTER(app, LOG_LEVEL_INF);
16+
17+
/*
18+
* Minimal USB Host initialization.
19+
*/
20+
21+
USBH_CONTROLLER_DEFINE(app_usbh,
22+
DEVICE_DT_GET(DT_NODELABEL(virtual_uhc0)));
23+
24+
struct usbh_context *app_usbh_init_controller(void)
25+
{
26+
int err;
27+
28+
err = usbh_init(&app_usbh);
29+
if (err) {
30+
LOG_ERR("Failed to initialize host support");
31+
return NULL;
32+
}
33+
34+
return &app_usbh;
35+
}
36+
37+
USBD_DEVICE_DEFINE(app_usbd, DEVICE_DT_GET(DT_NODELABEL(virtual_udc0)), 0x2fe3, 0x9999);
38+
USBD_DESC_LANG_DEFINE(app_lang);
39+
USBD_DESC_MANUFACTURER_DEFINE(app_mfr, "Nordic");
40+
USBD_DESC_PRODUCT_DEFINE(app_product, "Virtual UVC device");
41+
USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration");
42+
USBD_CONFIGURATION_DEFINE(app_fs_config, 0, 200, &fs_cfg_desc);
43+
44+
/*
45+
* Minimal USB Device initialization.
46+
*/
47+
48+
struct usbd_context *app_usbd_init_device(void)
49+
{
50+
int ret;
51+
52+
ret = usbd_add_descriptor(&app_usbd, &app_lang);
53+
if (ret != 0) {
54+
LOG_ERR("Failed to initialize language descriptor (%d)", ret);
55+
return NULL;
56+
}
57+
58+
ret = usbd_add_descriptor(&app_usbd, &app_mfr);
59+
if (ret != 0) {
60+
LOG_ERR("Failed to initialize manufacturer descriptor (%d)", ret);
61+
return NULL;
62+
}
63+
64+
ret = usbd_add_descriptor(&app_usbd, &app_product);
65+
if (ret != 0) {
66+
LOG_ERR("Failed to initialize product descriptor (%d)", ret);
67+
return NULL;
68+
}
69+
70+
ret = usbd_add_configuration(&app_usbd, USBD_SPEED_FS, &app_fs_config);
71+
if (ret != 0) {
72+
LOG_ERR("Failed to add Full-Speed configuration");
73+
return NULL;
74+
}
75+
76+
ret = usbd_register_all_classes(&app_usbd, USBD_SPEED_FS, 1, NULL);
77+
if (ret != 0) {
78+
LOG_ERR("Failed to add register classes");
79+
return NULL;
80+
}
81+
82+
usbd_device_set_code_triple(&app_usbd, USBD_SPEED_FS, USB_BCC_MISCELLANEOUS, 0x02, 0x01);
83+
84+
usbd_self_powered(&app_usbd, USB_SCD_SELF_POWERED);
85+
86+
ret = usbd_init(&app_usbd);
87+
if (ret != 0) {
88+
LOG_ERR("Failed to initialize device support");
89+
return NULL;
90+
}
91+
92+
return &app_usbd;
93+
}
94+
95+
/*
96+
* Test using this host connected to this device via UVB
97+
*/
98+
99+
const struct video_format test_formats[] = {
100+
{.pixelformat = VIDEO_PIX_FMT_YUYV, .width = 640, .height = 480},
101+
{.pixelformat = VIDEO_PIX_FMT_YUYV, .width = 320, .height = 240},
102+
{.pixelformat = VIDEO_PIX_FMT_YUYV, .width = 160, .height = 120},
103+
};
104+
105+
const struct device *const uvc_dev = DEVICE_DT_GET(DT_NODELABEL(uvc_device));
106+
const struct device *const video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
107+
108+
ZTEST(usb_uvc, test_virtual_device_virtual_host)
109+
{
110+
struct usbd_context *usbd_ctx;
111+
struct usbh_context *usbh_ctx;
112+
int ret;
113+
114+
uvc_set_video_dev(uvc_dev, video_dev);
115+
116+
for (size_t i = 0; i < ARRAY_SIZE(test_formats); i++) {
117+
struct video_format fmt = test_formats[i];
118+
119+
ret = video_estimate_fmt_size(&fmt);
120+
zassert_ok(ret);
121+
122+
ret = uvc_add_format(uvc_dev, &fmt);
123+
zassert_ok(ret);
124+
}
125+
126+
usbd_ctx = app_usbd_init_device();
127+
zassert_not_null(usbd_ctx, "USB device initialization must succeed");
128+
129+
usbh_ctx = app_usbh_init_controller();
130+
zassert_not_null(usbh_ctx, "USB host initialization must succeed");
131+
132+
ret = usbd_enable(usbd_ctx);
133+
zassert_ok(ret, "USB device error code %d must be 0", ret);
134+
135+
ret = usbh_enable(usbh_ctx);
136+
zassert_ok(ret, "USB host enable error code %d must be 0", ret);
137+
138+
k_sleep(K_MSEC(500));
139+
140+
/* TODO: test the video devices here. */
141+
142+
ret = usbh_disable(usbh_ctx);
143+
zassert_ok(ret, "USB host disable error code %d must be 0", ret);
144+
145+
ret = usbd_disable(usbd_ctx);
146+
zassert_ok(ret, "USB device disable error code %d must be 0", ret);
147+
148+
ret = usbh_shutdown(usbh_ctx);
149+
zassert_ok(ret, "USB host shutdown error code %d must be 0", ret);
150+
151+
ret = usbd_shutdown(usbd_ctx);
152+
zassert_ok(ret, "USB device shutdown error code %d must be 0", ret);
153+
}
154+
155+
ZTEST_SUITE(usb_uvc, NULL, NULL, NULL, NULL, NULL);

tests/subsys/usb/uvc/testcase.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
tests:
2+
usb.uvc:
3+
tags:
4+
- usb
5+
- video
6+
platform_allow:
7+
- native_sim/native
8+
integration_platforms:
9+
- native_sim/native
10+
extra_args:
11+
- platform:native_sim/native:SNIPPET="video-sw-generator"

0 commit comments

Comments
 (0)