Skip to content

Commit 94c43f4

Browse files
author
Josuah Demangeon
committed
uvc: device: move helper functions to a common directory
Move UVC helper functions to a file shared between UVC host and device. The arrays are not visible anymore from either USB host or device, but instead accessed through a front-end funciton. Signed-off-by: Josuah Demangeon <[email protected]>
1 parent d6ba924 commit 94c43f4

File tree

5 files changed

+343
-260
lines changed

5 files changed

+343
-260
lines changed

subsys/usb/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33

44
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK device)
55
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK_NEXT device_next)
6+
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK_NEXT common)
67
add_subdirectory_ifdef(CONFIG_USB_HOST_STACK host)
8+
add_subdirectory_ifdef(CONFIG_USB_HOST_STACK common)
79
add_subdirectory_ifdef(CONFIG_USBC_STACK usb_c)

subsys/usb/common/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
zephyr_library()
5+
zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
6+
7+
zephyr_library_sources_ifdef(CONFIG_USBD_VIDEO_CLASS usb_common_uvc.c)
8+
zephyr_library_sources_ifdef(CONFIG_USBH_VIDEO_CLASS usb_common_uvc.c)

subsys/usb/common/usb_common_uvc.c

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdint.h>
8+
9+
#include <zephyr/drivers/video.h>
10+
#include <zephyr/drivers/video-controls.h>
11+
#include <zephyr/usb/class/usb_uvc.h>
12+
#include <zephyr/sys/byteorder.h>
13+
14+
#include "usb_common_uvc.h"
15+
16+
static const struct uvc_guid_quirk uvc_guid_quirks[] = {
17+
{
18+
.fourcc = VIDEO_PIX_FMT_YUYV,
19+
.guid = UVC_FORMAT_GUID("YUY2"),
20+
},
21+
{
22+
.fourcc = VIDEO_PIX_FMT_GREY,
23+
.guid = UVC_FORMAT_GUID("Y800"),
24+
},
25+
};
26+
27+
static const struct uvc_control_map uvc_control_map_ct[] = {
28+
{
29+
.size = 1,
30+
.bit = 1,
31+
.selector = UVC_CT_AE_MODE_CONTROL,
32+
.cid = VIDEO_CID_EXPOSURE_AUTO,
33+
.type = UVC_CONTROL_UNSIGNED,
34+
},
35+
{
36+
.size = 1,
37+
.bit = 2,
38+
.selector = UVC_CT_AE_PRIORITY_CONTROL,
39+
.cid = VIDEO_CID_EXPOSURE_AUTO_PRIORITY,
40+
.type = UVC_CONTROL_UNSIGNED,
41+
},
42+
{
43+
.size = 4,
44+
.bit = 3,
45+
.selector = UVC_CT_EXPOSURE_TIME_ABS_CONTROL,
46+
.cid = VIDEO_CID_EXPOSURE,
47+
.type = UVC_CONTROL_UNSIGNED,
48+
},
49+
{
50+
.size = 2,
51+
.bit = 5,
52+
.selector = UVC_CT_FOCUS_ABS_CONTROL,
53+
.cid = VIDEO_CID_FOCUS_ABSOLUTE,
54+
.type = UVC_CONTROL_UNSIGNED,
55+
},
56+
{
57+
.size = 2,
58+
.bit = 6,
59+
.selector = UVC_CT_FOCUS_REL_CONTROL,
60+
.cid = VIDEO_CID_FOCUS_RELATIVE,
61+
.type = UVC_CONTROL_SIGNED,
62+
},
63+
{
64+
.size = 2,
65+
.bit = 7,
66+
.selector = UVC_CT_IRIS_ABS_CONTROL,
67+
.cid = VIDEO_CID_IRIS_ABSOLUTE,
68+
.type = UVC_CONTROL_UNSIGNED,
69+
},
70+
{
71+
.size = 1,
72+
.bit = 8,
73+
.selector = UVC_CT_IRIS_REL_CONTROL,
74+
.cid = VIDEO_CID_IRIS_RELATIVE,
75+
.type = UVC_CONTROL_SIGNED,
76+
},
77+
{
78+
.size = 2,
79+
.bit = 9,
80+
.selector = UVC_CT_ZOOM_ABS_CONTROL,
81+
.cid = VIDEO_CID_ZOOM_ABSOLUTE,
82+
.type = UVC_CONTROL_UNSIGNED,
83+
},
84+
{
85+
.size = 3,
86+
.bit = 10,
87+
.selector = UVC_CT_ZOOM_REL_CONTROL,
88+
.cid = VIDEO_CID_ZOOM_RELATIVE,
89+
.type = UVC_CONTROL_SIGNED,
90+
},
91+
};
92+
93+
static const struct uvc_control_map uvc_control_map_pu[] = {
94+
{
95+
.size = 2,
96+
.bit = 0,
97+
.selector = UVC_PU_BRIGHTNESS_CONTROL,
98+
.cid = VIDEO_CID_BRIGHTNESS,
99+
.type = UVC_CONTROL_SIGNED,
100+
},
101+
{
102+
.size = 1,
103+
.bit = 1,
104+
.selector = UVC_PU_CONTRAST_CONTROL,
105+
.cid = VIDEO_CID_CONTRAST,
106+
.type = UVC_CONTROL_UNSIGNED,
107+
},
108+
{
109+
.size = 2,
110+
.bit = 9,
111+
.selector = UVC_PU_GAIN_CONTROL,
112+
.cid = VIDEO_CID_GAIN,
113+
.type = UVC_CONTROL_UNSIGNED,
114+
},
115+
{
116+
.size = 2,
117+
.bit = 3,
118+
.selector = UVC_PU_SATURATION_CONTROL,
119+
.cid = VIDEO_CID_SATURATION,
120+
.type = UVC_CONTROL_UNSIGNED,
121+
},
122+
{
123+
.size = 2,
124+
.bit = 6,
125+
.selector = UVC_PU_WHITE_BALANCE_TEMP_CONTROL,
126+
.cid = VIDEO_CID_WHITE_BALANCE_TEMPERATURE,
127+
.type = UVC_CONTROL_UNSIGNED,
128+
},
129+
};
130+
131+
static const struct uvc_control_map uvc_control_map_su[] = {
132+
{
133+
.size = 1,
134+
.bit = 0,
135+
.selector = UVC_SU_INPUT_SELECT_CONTROL,
136+
.cid = VIDEO_CID_TEST_PATTERN,
137+
.type = UVC_CONTROL_UNSIGNED,
138+
},
139+
};
140+
141+
static const struct uvc_control_map uvc_control_map_xu[] = {
142+
{
143+
.size = 4,
144+
.bit = 0,
145+
.selector = UVC_XU_BASE_CONTROL + 0,
146+
.cid = VIDEO_CID_PRIVATE_BASE + 0,
147+
.type = UVC_CONTROL_UNSIGNED,
148+
},
149+
{
150+
.size = 4,
151+
.bit = 1,
152+
.selector = UVC_XU_BASE_CONTROL + 1,
153+
.cid = VIDEO_CID_PRIVATE_BASE + 1,
154+
.type = UVC_CONTROL_UNSIGNED,
155+
},
156+
{
157+
.size = 4,
158+
.bit = 2,
159+
.selector = UVC_XU_BASE_CONTROL + 2,
160+
.cid = VIDEO_CID_PRIVATE_BASE + 2,
161+
.type = UVC_CONTROL_UNSIGNED,
162+
},
163+
{
164+
.size = 4,
165+
.bit = 3,
166+
.selector = UVC_XU_BASE_CONTROL + 3,
167+
.cid = VIDEO_CID_PRIVATE_BASE + 3,
168+
.type = UVC_CONTROL_UNSIGNED,
169+
},
170+
};
171+
172+
int uvc_get_control_map(uint8_t subtype, const struct uvc_control_map **map, size_t *length)
173+
{
174+
switch (subtype) {
175+
case UVC_VC_INPUT_TERMINAL:
176+
*map = uvc_control_map_ct;
177+
*length = ARRAY_SIZE(uvc_control_map_ct);
178+
break;
179+
case UVC_VC_SELECTOR_UNIT:
180+
*map = uvc_control_map_su;
181+
*length = ARRAY_SIZE(uvc_control_map_su);
182+
break;
183+
case UVC_VC_PROCESSING_UNIT:
184+
*map = uvc_control_map_pu;
185+
*length = ARRAY_SIZE(uvc_control_map_pu);
186+
break;
187+
case UVC_VC_EXTENSION_UNIT:
188+
*map = uvc_control_map_xu;
189+
*length = ARRAY_SIZE(uvc_control_map_xu);
190+
break;
191+
default:
192+
return -EINVAL;
193+
}
194+
195+
return 0;
196+
}
197+
198+
void uvc_fourcc_to_guid(uint8_t guid[16], const uint32_t fourcc)
199+
{
200+
uint32_t fourcc_le;
201+
202+
/* Lookup in the "quirk table" if the UVC format GUID is custom */
203+
for (int i = 0; i < ARRAY_SIZE(uvc_guid_quirks); i++) {
204+
if (uvc_guid_quirks[i].fourcc == fourcc) {
205+
memcpy(guid, uvc_guid_quirks[i].guid, 16);
206+
return;
207+
}
208+
}
209+
210+
/* By default, UVC GUIDs are the four character code followed by a common suffix */
211+
fourcc_le = sys_cpu_to_le32(fourcc);
212+
/* Copy the common suffix with the GUID set to 'XXXX' */
213+
memcpy(guid, UVC_FORMAT_GUID("XXXX"), 16);
214+
/* Replace the 'XXXX' by the actual GUID of the format */
215+
memcpy(guid, &fourcc_le, 4);
216+
}
217+
218+
uint32_t uvc_guid_to_fourcc(const uint8_t guid[16])
219+
{
220+
uint32_t fourcc;
221+
222+
/* Lookup in the "quirk table" if the UVC format GUID is custom */
223+
for (int i = 0; i < ARRAY_SIZE(uvc_guid_quirks); i++) {
224+
if (memcmp(guid, uvc_guid_quirks[i].guid, 16) == 0) {
225+
return uvc_guid_quirks[i].fourcc;
226+
}
227+
}
228+
229+
/* Extract the four character code out of the leading 4 bytes of the GUID */
230+
memcpy(&fourcc, guid, 4);
231+
fourcc = sys_le32_to_cpu(fourcc);
232+
233+
return fourcc;
234+
}

subsys/usb/common/usb_common_uvc.h

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_USB_COMMON_UVC_H
8+
#define ZEPHYR_INCLUDE_USB_COMMON_UVC_H
9+
10+
#include <stdint.h>
11+
12+
/**
13+
* @brief Type of value used by the USB protocol for this control.
14+
*/
15+
enum uvc_control_type {
16+
/** Signed integer control type */
17+
UVC_CONTROL_SIGNED,
18+
/** Unsigned integer control type */
19+
UVC_CONTROL_UNSIGNED,
20+
};
21+
22+
/**
23+
* @brief Mapping between UVC controls and Video controls
24+
*/
25+
struct uvc_control_map {
26+
/* Video CID to use for this control */
27+
uint32_t cid;
28+
/* Size to write out */
29+
uint8_t size;
30+
/* Bit position in the UVC control */
31+
uint8_t bit;
32+
/* UVC selector identifying this control */
33+
uint8_t selector;
34+
/* Whether the UVC value is signed, always false for bitmaps and boolean */
35+
enum uvc_control_type type;
36+
};
37+
38+
/**
39+
* @brief Mapping between UVC GUIDs and standard FourCC.
40+
*/
41+
struct uvc_guid_quirk {
42+
/* A Video API format identifier, for which the UVC format GUID is not standard. */
43+
uint32_t fourcc;
44+
/* GUIDs are 16-bytes long, with the first four bytes being the Four Character Code of the
45+
* format and the rest constant, except for some exceptions listed in this table.
46+
*/
47+
uint8_t guid[16];
48+
};
49+
50+
/**
51+
* @brief Get a conversion table for a given control unit type
52+
*
53+
* The mappings contains information about how UVC control structures are related to
54+
* video control structures.
55+
*
56+
* @param subtype The field bDescriptorSubType of a descriptor of type USB_DESC_CS_INTERFACE.
57+
* @param map Filled with a pointer to the conversion table
58+
* @param length Filled with the number of elements in that conversion table.
59+
*
60+
* @return 0 on success, negative code on error.
61+
*/
62+
int uvc_get_control_map(uint8_t subtype, const struct uvc_control_map **map, size_t *length);
63+
64+
/**
65+
* @brief Convert a standard FourCC to an equivalent UVC GUID.
66+
*
67+
* @param guid Array to a GUID, filled in binary format
68+
* @param fourcc Four character code
69+
*/
70+
void uvc_fourcc_to_guid(uint8_t guid[16], const uint32_t fourcc);
71+
72+
/**
73+
* @brief Convert an UVC GUID to a standard FourCC
74+
*
75+
* @param guid GUID, to convert
76+
*
77+
* @return Four Character Code
78+
*/
79+
uint32_t uvc_guid_to_fourcc(const uint8_t guid[16]);
80+
81+
#endif /* ZEPHYR_INCLUDE_USB_COMMON_UVC_H */

0 commit comments

Comments
 (0)