Skip to content

Commit 982e54f

Browse files
committed
usb: device_next: uvc: remove application decisions from the UVC class
The UVC class was deciding itself which formats were sent to the host. Remove this logic out of the UVC class and introduce uvc_add_format() to give the application the freedom of which format to list. Signed-off-by: Josuah Demangeon <me@josuah.net>
1 parent f15c135 commit 982e54f

File tree

3 files changed

+232
-140
lines changed

3 files changed

+232
-140
lines changed

include/zephyr/usb/class/usbd_uvc.h

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,37 @@
2626
*/
2727

2828
/**
29-
* @brief Set the video device that a UVC instance will use.
29+
* @brief Set the video device that a UVC instance will use for control requests.
3030
*
31-
* It will query its supported controls, formats and frame rates, and use this information to
32-
* generate USB descriptors sent to the host.
33-
*
34-
* At runtime, it will forward all USB controls from the host to this device.
31+
* It will query its supported video controls and frame intervals and use this information to
32+
* generate the USB descriptors presented to the host. In addition, for every supported UVC control
33+
* request from the host to this @p uvc_dev instance, it will issue a matching video API control
34+
* request to @p video_dev.
3535
*
3636
* @note This function must be called before @ref usbd_enable.
3737
*
38-
* @param uvc_dev The UVC device
39-
* @param video_dev The video device that this UVC instance controls
38+
* @param uvc_dev Pointer to the UVC device to configure
39+
* @param video_dev Pointer to the video device to which controls requests are sent
4040
*/
4141
void uvc_set_video_dev(const struct device *uvc_dev, const struct device *video_dev);
4242

43+
/**
44+
* @brief Add a video format that a UVC instance will present to the host.
45+
*
46+
* This information will be used to generate USB descriptors.
47+
* The particular format selected by the host can be queried with @ref video_get_format.
48+
*
49+
* @note This function must be called before @ref usbd_enable.
50+
*
51+
* @note The @p fmt struct field @c size must be set prior to call this function,
52+
* such as calling @ref video_set_format().
53+
*
54+
* @param uvc_dev Pointer to the UVC device to configure
55+
* @param fmt The video format to add to this UVC instance
56+
* @return 0 on success, negative value on error
57+
*/
58+
int uvc_add_format(const struct device *const uvc_dev, const struct video_format *const fmt);
59+
4360
/**
4461
* @}
4562
*/

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

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,72 @@
1717

1818
LOG_MODULE_REGISTER(uvc_sample, LOG_LEVEL_INF);
1919

20-
const struct device *const uvc_dev = DEVICE_DT_GET(DT_NODELABEL(uvc));
21-
const struct device *const video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
20+
const static struct device *const uvc_dev = DEVICE_DT_GET(DT_NODELABEL(uvc));
21+
const static struct device *const video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
22+
23+
/* Format capabilities of video_dev, used everywhere through the sample */
24+
static struct video_caps video_caps = {.type = VIDEO_BUF_TYPE_OUTPUT};
25+
26+
static int app_add_format(uint32_t pixfmt, uint32_t width, uint32_t height)
27+
{
28+
struct video_format fmt = {
29+
.pixelformat = pixfmt,
30+
.width = width,
31+
.height = height,
32+
.type = VIDEO_BUF_TYPE_OUTPUT,
33+
};
34+
int ret;
35+
36+
/* Set the format to get the size */
37+
ret = video_set_format(video_dev, &fmt);
38+
if (ret != 0) {
39+
LOG_ERR("Could not set the format of %s to %s %ux%u (size %u)",
40+
video_dev->name, VIDEO_FOURCC_TO_STR(fmt.pixelformat),
41+
fmt.width, fmt.height, fmt.size);
42+
return ret;
43+
}
44+
45+
if (fmt.size > CONFIG_VIDEO_BUFFER_POOL_SZ_MAX) {
46+
LOG_WRN("Skipping format %ux%u", fmt.width, fmt.height);
47+
return 0;
48+
}
49+
50+
ret = uvc_add_format(uvc_dev, &fmt);
51+
if (ret == -ENOMEM) {
52+
/* If there are too many formats, ignore the error, just list fewer formats */
53+
return 0;
54+
}
55+
return ret;
56+
}
57+
58+
/* Submit to UVC only the formats expected to be working (enough memory for the size, etc.) */
59+
static int app_add_filtered_formats(void)
60+
{
61+
for (int i = 0; video_caps.format_caps[i].pixelformat != 0; i++) {
62+
const struct video_format_cap *vcap = &video_caps.format_caps[i];
63+
64+
ret = app_add_format(vcap->pixelformat, vcap->width_min, vcap->height_min);
65+
if (ret != 0) {
66+
return ret;
67+
}
68+
69+
if (vcap->width_min != vcap->width_max || vcap->height_min != vcap->height_max) {
70+
ret = app_add_format(vcap->pixelformat, vcap->width_max, vcap->height_max);
71+
if (ret != 0) {
72+
return ret;
73+
}
74+
}
75+
}
76+
77+
return 0;
78+
}
2279

2380
int main(void)
2481
{
2582
struct usbd_context *sample_usbd;
2683
struct video_buffer *vbuf;
2784
struct video_format fmt = {0};
28-
struct video_caps caps;
85+
struct video_frmival frmival = {0};
2986
struct k_poll_signal sig;
3087
struct k_poll_event evt[1];
3188
k_timeout_t timeout = K_FOREVER;
@@ -36,16 +93,21 @@ int main(void)
3693
return -ENODEV;
3794
}
3895

39-
caps.type = VIDEO_BUF_TYPE_OUTPUT;
40-
41-
if (video_get_caps(video_dev, &caps)) {
96+
ret = video_get_caps(video_dev, &video_caps);
97+
if (ret != 0) {
4298
LOG_ERR("Unable to retrieve video capabilities");
4399
return 0;
44100
}
45101

46-
/* Must be done before initializing USB */
102+
/* Must be called before usb_enable() */
47103
uvc_set_video_dev(uvc_dev, video_dev);
48104

105+
/* Must be called before usb_enable() */
106+
ret = app_add_filtered_formats();
107+
if (ret != 0) {
108+
return ret;
109+
}
110+
49111
sample_usbd = sample_usbd_init_device(NULL);
50112
if (sample_usbd == NULL) {
51113
return -ENODEV;
@@ -58,7 +120,6 @@ int main(void)
58120

59121
LOG_INF("Waiting the host to select the video format");
60122

61-
/* Get the video format once it is selected by the host */
62123
while (true) {
63124
fmt.type = VIDEO_BUF_TYPE_INPUT;
64125

@@ -74,9 +135,31 @@ int main(void)
74135
k_sleep(K_MSEC(10));
75136
}
76137

77-
LOG_INF("The host selected format '%s' %ux%u, preparing %u buffers of %u bytes",
138+
ret = video_get_frmival(uvc_dev, &frmival);
139+
if (ret != 0) {
140+
LOG_ERR("Failed to get the video frame interval");
141+
return ret;
142+
}
143+
144+
LOG_INF("The host selected format '%s' %ux%u at frame interval %u/%u",
78145
VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height,
79-
CONFIG_VIDEO_BUFFER_POOL_NUM_MAX, fmt.pitch * fmt.height);
146+
frmival.numerator, frmival.denominator);
147+
148+
fmt.type = VIDEO_BUF_TYPE_OUTPUT;
149+
150+
ret = video_set_format(video_dev, &fmt);
151+
if (ret != 0) {
152+
LOG_ERR("Could not set the format of %s to %s %ux%u (size %u)",
153+
video_dev->name, VIDEO_FOURCC_TO_STR(fmt.pixelformat),
154+
fmt.width, fmt.height, fmt.size);
155+
}
156+
157+
ret = video_set_frmival(video_dev, &frmival);
158+
if (ret != 0) {
159+
LOG_WRN("Could not set the framerate of %s", video_dev->name);
160+
}
161+
162+
LOG_INF("Preparing %u buffers of %u bytes", CONFIG_VIDEO_BUFFER_POOL_NUM_MAX, fmt.size);
80163

81164
for (int i = 0; i < CONFIG_VIDEO_BUFFER_POOL_NUM_MAX; i++) {
82165
vbuf = video_buffer_alloc(fmt.size, K_NO_WAIT);

0 commit comments

Comments
 (0)