Skip to content

Commit b3260c5

Browse files
author
Josuah Demangeon
committed
usb: host: uvc: placeholder implementation
Loop through each of the VideoStreaming and VideoControl descriptor to parse them. This is meant as a stub for the purpose of testing the class API. Signed-off-by: Josuah Demangeon <[email protected]>
1 parent e51fbbf commit b3260c5

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

subsys/usb/host/class/usbh_uvc.c

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* Copyright (c) 2025 tinyVision.ai Inc.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#define DT_DRV_COMPAT zephyr_uvc_host
7+
8+
#include <stdlib.h>
9+
10+
#include <zephyr/init.h>
11+
#include <zephyr/devicetree.h>
12+
#include <zephyr/kernel.h>
13+
#include <zephyr/sys/byteorder.h>
14+
#include <zephyr/sys/atomic.h>
15+
#include <zephyr/usb/usbh.h>
16+
#include <zephyr/usb/usb_ch9.h>
17+
#include <zephyr/drivers/usb/udc.h>
18+
#include <zephyr/drivers/video.h>
19+
#include <zephyr/drivers/video-controls.h>
20+
#include <zephyr/logging/log.h>
21+
22+
#include <zephyr/devicetree.h>
23+
#include <zephyr/sys/util.h>
24+
#include <zephyr/usb/usb_ch9.h>
25+
26+
#include "usbh_ch9.h"
27+
#include "usbh_class.h"
28+
#include "usbh_desc.h"
29+
#include "usbh_device.h"
30+
#include "usb_uvc.h"
31+
32+
#include "../../../drivers/video/video_ctrls.h"
33+
#include "../../../drivers/video/video_device.h"
34+
35+
LOG_MODULE_REGISTER(usbh_uvc, CONFIG_USBH_VIDEO_LOG_LEVEL);
36+
37+
struct usbh_uvc_data {
38+
int todo;
39+
};
40+
41+
/*
42+
* Descriptor parsing utilities
43+
* Validate and parse the video streaming and video control descriptors.
44+
*/
45+
46+
static bool usbh_uvc_desc_is_valid_vs_header(const void *const desc,
47+
const void *const desc_end)
48+
{
49+
const struct uvc_stream_header_descriptor *const header_desc = desc;
50+
51+
return usbh_desc_is_valid(desc, desc_end, sizeof(struct uvc_stream_header_descriptor)) &&
52+
header_desc->bDescriptorType == USB_DESC_CS_INTERFACE &&
53+
(header_desc->bDescriptorSubtype == UVC_VS_OUTPUT_HEADER ||
54+
header_desc->bDescriptorSubtype == UVC_VS_INPUT_HEADER);
55+
}
56+
57+
static bool usbh_uvc_desc_is_valid_vc_header(const void *const desc,
58+
const void *const desc_end)
59+
{
60+
const struct uvc_control_header_descriptor *const header_desc = desc;
61+
62+
return usbh_desc_is_valid(desc, desc_end, sizeof(struct uvc_control_header_descriptor)) &&
63+
header_desc->bDescriptorType == USB_DESC_CS_INTERFACE &&
64+
header_desc->bDescriptorSubtype == UVC_VC_HEADER;
65+
}
66+
67+
const void *usbh_uvc_desc_get_vs_end(const struct usb_if_descriptor *if_desc,
68+
const void *const desc_end)
69+
{
70+
const struct uvc_stream_header_descriptor *const header_desc =
71+
usbh_desc_get_next(if_desc, desc_end);
72+
const void *vs_end;
73+
74+
if (!usbh_uvc_desc_is_valid_vs_header(header_desc, desc_end)) {
75+
return NULL;
76+
}
77+
78+
vs_end = (uint8_t *)header_desc + header_desc->wTotalLength;
79+
if (vs_end > desc_end) {
80+
return NULL;
81+
}
82+
83+
return vs_end;
84+
}
85+
86+
const void *usbh_uvc_desc_get_vc_end(const struct usb_if_descriptor *if_desc,
87+
const void *const desc_end)
88+
{
89+
const struct uvc_control_header_descriptor *const header_desc =
90+
usbh_desc_get_next(if_desc, desc_end);
91+
const void *vc_end;
92+
93+
if (!usbh_uvc_desc_is_valid_vc_header(header_desc, desc_end)) {
94+
return NULL;
95+
}
96+
97+
vc_end = (uint8_t *)header_desc + header_desc->wTotalLength;
98+
if (vc_end > desc_end) {
99+
LOG_WRN("vc_end %p > desc_end %p", vc_end, desc_end);
100+
return NULL;
101+
}
102+
103+
return vc_end;
104+
}
105+
106+
static int usbh_uvc_parse_vc_desc(struct usbh_class_data *const c_data,
107+
const void *const desc_ptr, const void *const desc_end)
108+
{
109+
return 0;
110+
}
111+
112+
static int usbh_uvc_parse_vs_desc(struct usbh_class_data *const c_data,
113+
const void *const desc_ptr, const void *const desc_end)
114+
{
115+
return 0;
116+
}
117+
118+
static int usbh_uvc_probe(struct usbh_class_data *const c_data,
119+
struct usb_device *const udev, const uint8_t iface)
120+
{
121+
return 0;
122+
}
123+
124+
static int usbh_uvc_removed(struct usbh_class_data *const c_data)
125+
{
126+
return 0;
127+
}
128+
129+
static int usbh_uvc_init(struct usbh_class_data *const c_data,
130+
struct usbh_context *const uhs_ctx)
131+
{
132+
return 0;
133+
}
134+
135+
static int usbh_uvc_completion_cb(struct usbh_class_data *const c_data,
136+
struct uhc_transfer *const xfer)
137+
{
138+
return 0;
139+
}
140+
141+
static int usbh_uvc_suspended(struct usbh_class_data *const c_data)
142+
{
143+
return 0;
144+
}
145+
146+
static int usbh_uvc_resumed(struct usbh_class_data *const c_data)
147+
{
148+
return 0;
149+
}
150+
151+
static int usbh_uvc_preinit(const struct device *dev)
152+
{
153+
return 0;
154+
}
155+
156+
static struct usbh_class_api uvc_class_api = {
157+
.init = usbh_uvc_init,
158+
.completion_cb = usbh_uvc_completion_cb,
159+
.probe = usbh_uvc_probe,
160+
.removed = usbh_uvc_removed,
161+
.suspended = usbh_uvc_suspended,
162+
.resumed = usbh_uvc_resumed,
163+
};
164+
165+
int usbh_uvc_get_caps(const struct device *const dev, struct video_caps *const caps)
166+
{
167+
return 0;
168+
}
169+
170+
int usbh_uvc_get_format(const struct device *const dev, struct video_format *const fmt)
171+
{
172+
return 0;
173+
}
174+
175+
int usbh_uvc_set_stream(const struct device *const dev, bool enable, enum video_buf_type type)
176+
{
177+
return 0;
178+
}
179+
180+
int usbh_uvc_enqueue(const struct device *const dev, struct video_buffer *const vbuf)
181+
{
182+
return 0;
183+
}
184+
185+
int usbh_uvc_dequeue(const struct device *const dev, struct video_buffer **const vbuf,
186+
k_timeout_t timeout)
187+
{
188+
return 0;
189+
}
190+
191+
static DEVICE_API(video, uvc_video_api) = {
192+
.get_caps = usbh_uvc_get_caps,
193+
.get_format = usbh_uvc_get_format,
194+
.set_stream = usbh_uvc_set_stream,
195+
.enqueue = usbh_uvc_enqueue,
196+
.dequeue = usbh_uvc_dequeue,
197+
};
198+
199+
static struct usbh_class_filter usbh_uvc_filters[] = {
200+
{
201+
.flags = USBH_CLASS_MATCH_CLASS | USBH_CLASS_MATCH_SUB,
202+
.class = USB_BCC_VIDEO,
203+
.sub = UVC_SC_VIDEO_INTERFACE_COLLECTION,
204+
},
205+
};
206+
207+
#define USBH_VIDEO_DT_DEVICE_DEFINE(n) \
208+
struct usbh_uvc_data usbh_uvc_data_##n = { \
209+
}; \
210+
\
211+
USBH_DEFINE_CLASS(uvc_c_data_##n, &uvc_class_api, \
212+
(void *)DEVICE_DT_INST_GET(n), \
213+
usbh_uvc_filters, ARRAY_SIZE(usbh_uvc_filters)); \
214+
\
215+
DEVICE_DT_INST_DEFINE(n, usbh_uvc_preinit, NULL, \
216+
&usbh_uvc_data_##n, NULL, \
217+
POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \
218+
&uvc_video_api); \
219+
\
220+
VIDEO_DEVICE_DEFINE(uvc_host_##n, DEVICE_DT_INST_GET(n), NULL);
221+
222+
DT_INST_FOREACH_STATUS_OKAY(USBH_VIDEO_DT_DEVICE_DEFINE)

0 commit comments

Comments
 (0)