USB Descriptor Rework

This commit is contained in:
PhosphorosVR
2025-08-16 18:52:14 +02:00
parent 9a1f55d012
commit 18a4b73428
4 changed files with 110 additions and 266 deletions

View File

@@ -22,14 +22,26 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* ---------------------------------------------------------------------------
* USB Video Class (UVC) Descriptor Definitions for TinyUSB device stack.
*
* This file describes the device, configuration, and string descriptors for
* a single UVC camera. Build-time options (Kconfig) select whether UVC data
* streams use BULK or ISO endpoints, the pixel format (MJPEG / H.264 /
* Uncompressed), and whether multiple frame sizes are exposed.
*
* Goal: keep the same behavior while making the structure easier to read and
* maintain. Comments explain why each block exists and what each macro does.
*/
#include "tusb.h"
#include "usb_descriptors.h"
#include <string.h> // memcpy, strlen
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
// Device descriptor: identifies this as a composite device using IAD for UVC
tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@@ -63,6 +75,16 @@ uint8_t const *tud_descriptor_device_cb(void)
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
// String descriptor indices used in interface descriptors
#define STRID_LANGID 0
#define STRID_MANUFACTURER 1
#define STRID_PRODUCT 2
#define STRID_SERIAL 3
#define STRID_UVC_CAM1 4
// Endpoint numbers for UVC video IN endpoints (device -> host)
#define EPNUM_CAM1_VIDEO_IN 0x81
#if CFG_TUD_CAM1_VIDEO_STREAMING_BULK
#if CONFIG_UVC_CAM1_MULTI_FRAMESIZE
@@ -101,76 +123,38 @@ uint8_t const *tud_descriptor_device_cb(void)
#endif // CFG_TUD_CAM1_VIDEO_STREAMING_BULK
#if CONFIG_UVC_SUPPORT_TWO_CAM
#if CFG_TUD_CAM2_VIDEO_STREAMING_BULK
// Total length of this configuration
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CAM1_VIDEO_CAPTURE_DESC_LEN)
#if CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#if CONFIG_FORMAT_MJPEG_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_MULTI_MJPEG_BULK_LEN(4))
#elif CONFIG_FORMAT_H264_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_MULTI_FRAME_BASED_BULK_LEN(4))
#endif
#else
#if CONFIG_FORMAT_MJPEG_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_MJPEG_BULK_LEN)
#elif CONFIG_FORMAT_H264_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_FRAME_BASED_BULK_LEN)
#else
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_UNCOMPR_BULK_LEN)
#endif
#endif // CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#else
#if CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#if CONFIG_FORMAT_MJPEG_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_MULTI_MJPEG_LEN(4))
#elif CONFIG_FORMAT_H264_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_MULTI_FRAME_BASED_LEN(4))
#endif
#else
#if CONFIG_FORMAT_MJPEG_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_MJPEG_LEN)
#elif CONFIG_FORMAT_H264_CAM2
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_FRAME_BASED_LEN)
#else
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN (TUD_VIDEO_CAPTURE_DESC_UNCOMPR_LEN)
#endif
#endif // CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#endif // CFG_TUD_CAM2_VIDEO_STREAMING_BULK
#else
#define TUD_CAM2_VIDEO_CAPTURE_DESC_LEN 0
#endif
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CAM1_VIDEO_CAPTURE_DESC_LEN + TUD_CAM2_VIDEO_CAPTURE_DESC_LEN)
#define EPNUM_CAM1_VIDEO_IN 0x81
#if CONFIG_UVC_SUPPORT_TWO_CAM
#define EPNUM_CAM2_VIDEO_IN 0x82
#endif
uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
// Full-speed configuration descriptor
static uint8_t const desc_fs_configuration[] = {
// TUD_CONFIG_DESCRIPTOR(config_number, interface_count, string_index,
// total_length, attributes, power_mA)
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500),
// IAD for Video Control
#if CFG_TUD_CAM1_VIDEO_STREAMING_BULK
#if CONFIG_UVC_CAM1_MULTI_FRAMESIZE
#if CONFIG_FORMAT_MJPEG_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_MJPEG_BULK(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
// Camera 1, multi-size MJPEG over BULK
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_MJPEG_BULK(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_H264_BULK(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
// Camera 1, multi-size H.264 over BULK
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_H264_BULK(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#endif
#else
#if CONFIG_FORMAT_MJPEG_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
// Camera 1, single-size MJPEG over BULK
TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
UVC_CAM1_FRAME_WIDTH, UVC_CAM1_FRAME_HEIGHT, UVC_CAM1_FRAME_RATE,
CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_H264_BULK(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
// Camera 1, single-size H.264 over BULK
TUD_VIDEO_CAPTURE_DESCRIPTOR_H264_BULK(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
UVC_CAM1_FRAME_WIDTH, UVC_CAM1_FRAME_HEIGHT, UVC_CAM1_FRAME_RATE,
CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#else
TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
// Camera 1, single-size Uncompressed (YUY2/etc) over BULK
TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
UVC_CAM1_FRAME_WIDTH, UVC_CAM1_FRAME_HEIGHT, UVC_CAM1_FRAME_RATE,
CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#endif
@@ -178,75 +162,32 @@ uint8_t const desc_fs_configuration[] = {
#else
#if CONFIG_UVC_CAM1_MULTI_FRAMESIZE
#if CONFIG_FORMAT_MJPEG_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_MJPEG(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
// Camera 1, multi-size MJPEG over ISO
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_MJPEG(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_H264(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
// Camera 1, multi-size H.264 over ISO
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_H264(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN, CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#endif
#else
#if CONFIG_FORMAT_MJPEG_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
// Camera 1, single-size MJPEG over ISO
TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
UVC_CAM1_FRAME_WIDTH, UVC_CAM1_FRAME_HEIGHT, UVC_CAM1_FRAME_RATE,
CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM1
TUD_VIDEO_CAPTURE_DESCRIPTOR_H264(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
// Camera 1, single-size H.264 over ISO
TUD_VIDEO_CAPTURE_DESCRIPTOR_H264(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
UVC_CAM1_FRAME_WIDTH, UVC_CAM1_FRAME_HEIGHT, UVC_CAM1_FRAME_RATE,
CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#else
TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
// Camera 1, single-size Uncompressed over ISO
TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(STRID_UVC_CAM1, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM1_VIDEO_IN,
UVC_CAM1_FRAME_WIDTH, UVC_CAM1_FRAME_HEIGHT, UVC_CAM1_FRAME_RATE,
CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE),
#endif
#endif // CONFIG_UVC_CAM1_MULTI_FRAMESIZE
#endif // CFG_TUD_CAM1_VIDEO_STREAMING_BULK
#if CONFIG_UVC_SUPPORT_TWO_CAM
#if CFG_TUD_CAM2_VIDEO_STREAMING_BULK
#if CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#if CONFIG_FORMAT_MJPEG_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_MJPEG_BULK(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN, CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_H264_BULK(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN, CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#endif
#else
#if CONFIG_FORMAT_MJPEG_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN,
UVC_CAM2_FRAME_WIDTH, UVC_CAM2_FRAME_HEIGHT, UVC_CAM2_FRAME_RATE,
CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_H264_BULK(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN,
UVC_CAM2_FRAME_WIDTH, UVC_CAM2_FRAME_HEIGHT, UVC_CAM2_FRAME_RATE,
CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#else
TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN,
UVC_CAM2_FRAME_WIDTH, UVC_CAM2_FRAME_HEIGHT, UVC_CAM2_FRAME_RATE,
CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#endif
#endif // CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#else
#if CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#if CONFIG_FORMAT_MJPEG_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_MJPEG(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN, CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_MULTI_H264(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN, CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#endif
#else
#if CONFIG_FORMAT_MJPEG_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN,
UVC_CAM2_FRAME_WIDTH, UVC_CAM2_FRAME_HEIGHT, UVC_CAM2_FRAME_RATE,
CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#elif CONFIG_FORMAT_H264_CAM2
TUD_VIDEO_CAPTURE_DESCRIPTOR_H264(4, ITF_NUM_VIDEO_CONTROL_2, EPNUM_CAM2_VIDEO_IN,
UVC_CAM2_FRAME_WIDTH, UVC_CAM2_FRAME_HEIGHT, UVC_CAM2_FRAME_RATE,
CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#else
TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(4, ITF_NUM_VIDEO_CONTROL, EPNUM_CAM2_VIDEO_IN,
UVC_CAM2_FRAME_WIDTH, UVC_CAM2_FRAME_HEIGHT, UVC_CAM2_FRAME_RATE,
CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE),
#endif
#endif // CONFIG_UVC_CAM2_MULTI_FRAMESIZE
#endif // CFG_TUD_CAM2_VIDEO_STREAMING_BULK
#endif
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -263,30 +204,26 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
// Array of pointers to string literals. Indices must match STRID_* above.
char const *string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
(const char[]){0x09, 0x04}, // 0: Supported language: English (0x0409)
CONFIG_TUSB_MANUFACTURER, // 1: Manufacturer
CONFIG_TUSB_PRODUCT, // 2: Product
CONFIG_TUSB_SERIAL_NUM, // 3: Serials, should use chip ID, overridden with get_serial_number()
"UVC CAM1", // 4: UVC Interface, default, because we're overriding it get_uvc_device_name(), but we still have to keep the structure
#if CONFIG_UVC_SUPPORT_TWO_CAM
"UVC CAM2", // 5: UVC Interface
#endif
CONFIG_TUSB_SERIAL_NUM, // 3: Serial (overridden by get_serial_number())
"UVC CAM1", // 4: UVC Interface name for Cam1 (overridden by get_uvc_device_name())
};
static uint16_t _desc_str[32];
__attribute__((weak)) const char *get_uvc_device_name(void)
{
// ETVR Override, by default we're reporting ourselves as this, users can override it
// Default UVC device name, can be overridden by application
return "UVC OpenIris Camera";
}
__attribute__((weak)) const char *get_serial_number(void)
{
// ETVR Override, by default we're reporting ourselves as the predefined serial number
// this should get overwritten with a better implementation
// Default serial number, can be overridden by application (e.g., chip ID)
return CONFIG_TUSB_SERIAL_NUM;
}
@@ -294,7 +231,6 @@ __attribute__((weak)) const char *get_serial_number(void)
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
printf("am I being asked for this?");
(void)langid;
uint8_t chr_count;
@@ -309,15 +245,16 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
{
return NULL;
}
const char *str = string_desc_arr[index];
if (index == 3)
// Allow dynamic overrides for specific indices
if (index == STRID_SERIAL)
str = get_serial_number();
if (index == 4)
if (index == STRID_UVC_CAM1)
str = get_uvc_device_name();
if (str == NULL)
str = string_desc_arr[index];
@@ -340,4 +277,4 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}
}