From 5489c8f2c8bd454f8f5155af7b34ee4bd12fb472 Mon Sep 17 00:00:00 2001 From: Josuah Demangeon Date: Fri, 27 Jun 2025 15:00:29 +0000 Subject: [PATCH 1/2] usb: uvc: allow the host to use short probe/commit messages Some OSes like MacOS use shorter UVC 1.1 probe/commit messages even when UVC 1.5 is supported, without bUsage, bBitDepthLuma, bmSettings, bMaxNumberOfRefFramesPlus1, bmRateControlModes bmLayoutPerStream. Accept messages of arbitrary size to safely be processed, ignoring all missing fields, improving standard compliance. Signed-off-by: Josuah Demangeon (cherry picked from commit ed9327dec24592c475796c807af10a87ee53ebca) --- subsys/usb/device_next/class/usbd_uvc.c | 48 +++++++++++-------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_uvc.c b/subsys/usb/device_next/class/usbd_uvc.c index 556627aff854d..629b5df73b49e 100644 --- a/subsys/usb/device_next/class/usbd_uvc.c +++ b/subsys/usb/device_next/class/usbd_uvc.c @@ -694,7 +694,8 @@ static int uvc_get_vs_probe(const struct device *dev, struct net_buf *const buf, const struct usb_setup_packet *const setup) { struct uvc_data *data = dev->data; - size_t size = MIN(sizeof(struct uvc_probe), net_buf_tailroom(buf)); + const size_t size = MIN(sizeof(struct uvc_probe), net_buf_tailroom(buf)); + struct uvc_probe probe = {0}; int ret; switch (setup->bRequest) { @@ -702,19 +703,18 @@ static int uvc_get_vs_probe(const struct device *dev, struct net_buf *const buf, if (size < 1) { return -ENOTSUP; } + net_buf_add_u8(buf, UVC_INFO_SUPPORTS_GET); return 0; case UVC_GET_LEN: if (size < 2) { return -ENOTSUP; } + net_buf_add_le16(buf, sizeof(struct uvc_probe)); return 0; case UVC_GET_DEF: - if (size < sizeof(struct uvc_probe)) { - return -ENOTSUP; - } - net_buf_add_mem(buf, &data->default_probe, sizeof(data->default_probe)); + net_buf_add_mem(buf, &data->default_probe, size); return 0; case UVC_GET_MIN: __fallthrough; @@ -723,16 +723,12 @@ static int uvc_get_vs_probe(const struct device *dev, struct net_buf *const buf, case UVC_GET_MAX: __fallthrough; case UVC_GET_CUR: - if (size < sizeof(struct uvc_probe)) { - return -ENOTSUP; - } - - ret = uvc_get_vs_probe_struct(dev, (struct uvc_probe *)buf->data, setup->bRequest); + ret = uvc_get_vs_probe_struct(dev, &probe, setup->bRequest); if (ret != 0) { return ret; } - net_buf_add(buf, sizeof(struct uvc_probe)); + net_buf_add_mem(buf, &probe, size); return 0; default: return -EINVAL; @@ -742,45 +738,41 @@ static int uvc_get_vs_probe(const struct device *dev, struct net_buf *const buf, static int uvc_set_vs_probe(const struct device *dev, const struct net_buf *const buf) { struct uvc_data *data = dev->data; - struct uvc_probe *probe; + const size_t size = MIN(sizeof(struct uvc_probe), buf->len); + struct uvc_probe probe = {0}; struct uvc_probe max = {0}; int ret; - if (buf->len != sizeof(*probe)) { - LOG_ERR("Expected probe message of %u bytes got %u", sizeof(*probe), buf->len); - return -EINVAL; - } - - probe = (struct uvc_probe *)buf->data; + memcpy(&probe, buf->data, size); ret = uvc_get_vs_probe_struct(dev, &max, UVC_GET_MAX); if (ret != 0) { return ret; } - if (probe->bFrameIndex > max.bFrameIndex) { + if (probe.bFrameIndex > max.bFrameIndex) { LOG_WRN("The bFrameIndex %u requested is beyond the max %u", - probe->bFrameIndex, max.bFrameIndex); + probe.bFrameIndex, max.bFrameIndex); return -ERANGE; } - if (probe->bFormatIndex > max.bFormatIndex) { + if (probe.bFormatIndex > max.bFormatIndex) { LOG_WRN("The bFormatIndex %u requested is beyond the max %u", - probe->bFormatIndex, max.bFormatIndex); + probe.bFormatIndex, max.bFormatIndex); return -ERANGE; } - if (probe->dwFrameInterval != 0) { - data->video_frmival.numerator = sys_le32_to_cpu(probe->dwFrameInterval); + if (probe.dwFrameInterval != 0) { + data->video_frmival.numerator = sys_le32_to_cpu(probe.dwFrameInterval); data->video_frmival.denominator = USEC_PER_SEC * 100; } - if (probe->bFrameIndex != 0) { - data->frame_id = probe->bFrameIndex; + if (probe.bFrameIndex != 0) { + data->frame_id = probe.bFrameIndex; } - if (probe->bFormatIndex != 0) { - data->format_id = probe->bFormatIndex; + if (probe.bFormatIndex != 0) { + data->format_id = probe.bFormatIndex; } return 0; From cb5058bb6d7133b2f21e6a58bc22c02a150af77c Mon Sep 17 00:00:00 2001 From: Josuah Demangeon Date: Fri, 18 Jul 2025 19:11:55 +0000 Subject: [PATCH 2/2] usb: uvc: respect setup->wLength in responses Due to the alignment and granularity requirements of memory allocation, setup->wLength is shorter than the allocated buffer size. This lead to responses larger than what the host requested, which it rejected. Fix it by using the minimum between the allocated size, the struct size, and the wLength requested. Signed-off-by: Josuah Demangeon (cherry picked from commit 366c64eca02d38db47e443aee34c4f93f010d5fa) --- subsys/usb/device_next/class/usbd_uvc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_uvc.c b/subsys/usb/device_next/class/usbd_uvc.c index 629b5df73b49e..9038a8e0d3f15 100644 --- a/subsys/usb/device_next/class/usbd_uvc.c +++ b/subsys/usb/device_next/class/usbd_uvc.c @@ -694,7 +694,8 @@ static int uvc_get_vs_probe(const struct device *dev, struct net_buf *const buf, const struct usb_setup_packet *const setup) { struct uvc_data *data = dev->data; - const size_t size = MIN(sizeof(struct uvc_probe), net_buf_tailroom(buf)); + const size_t size = MIN(net_buf_tailroom(buf), + MIN(sizeof(struct uvc_probe), setup->wLength)); struct uvc_probe probe = {0}; int ret; @@ -912,7 +913,8 @@ static int uvc_get_vc_ctrl(const struct device *dev, struct net_buf *const buf, const struct device *video_dev = data->video_dev; struct video_ctrl_query cq = {.id = map->cid, .dev = video_dev}; struct video_control ctrl = {.id = map->cid}; - size_t size = MIN(setup->wLength, net_buf_tailroom(buf)); + const size_t size = MIN(net_buf_tailroom(buf), + MIN(sizeof(struct uvc_probe), setup->wLength)); int64_t val64; int ret; @@ -1104,7 +1106,8 @@ static int uvc_get_errno(const struct device *dev, struct net_buf *const buf, const struct usb_setup_packet *const setup) { struct uvc_data *data = dev->data; - size_t size = MIN(setup->wLength, net_buf_tailroom(buf)); + const size_t size = MIN(net_buf_tailroom(buf), + MIN(sizeof(struct uvc_probe), setup->wLength)); switch (setup->bRequest) { case UVC_GET_INFO: