[v2] media: uvcvideo: Work around too little video controls size
Commit Message
The size of the video probe & commit controls depends on the UVC
protocol version. Some devices, namely the Quanta ACER HD User Facing
(0408:4033 and 0408:4035), claim to support the UVC 1.5 protocol, but
return only 26 bytes of data when queried with GET_CUR for those
controls. This causes probing of the device to fail.
Work around this non-compliance by lowering the size of the control when
the device returns less data than expected, but still a valid size for
one of the existing UVC versions. The size is cached and used for
subsequent queries of the probe and commit controls.
Reported-by: Giuliano Lotta <giuliano.lotta@gmail.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/usb/uvc/uvc_video.c | 48 +++++++++++++++++++------------
drivers/media/usb/uvc/uvcvideo.h | 1 +
include/uapi/linux/usb/video.h | 4 +++
3 files changed, 35 insertions(+), 18 deletions(-)
Comments
Hi Laurent
Just minor nipitks.
On Mon, 16 Jan 2023 at 17:04, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> The size of the video probe & commit controls depends on the UVC
> protocol version. Some devices, namely the Quanta ACER HD User Facing
> (0408:4033 and 0408:4035), claim to support the UVC 1.5 protocol, but
> return only 26 bytes of data when queried with GET_CUR for those
> controls. This causes probing of the device to fail.
>
> Work around this non-compliance by lowering the size of the control when
> the device returns less data than expected, but still a valid size for
> one of the existing UVC versions. The size is cached and used for
> subsequent queries of the probe and commit controls.
>
> Reported-by: Giuliano Lotta <giuliano.lotta@gmail.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> drivers/media/usb/uvc/uvc_video.c | 48 +++++++++++++++++++------------
> drivers/media/usb/uvc/uvcvideo.h | 1 +
> include/uapi/linux/usb/video.h | 4 +++
> 3 files changed, 35 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> index 9634596f3dc7..23f3179a013f 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -272,24 +272,10 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
> }
> }
>
> -static size_t uvc_video_ctrl_size(struct uvc_streaming *stream)
> -{
> - /*
> - * Return the size of the video probe and commit controls, which depends
> - * on the protocol version.
> - */
> - if (stream->dev->uvc_version < 0x0110)
> - return 26;
> - else if (stream->dev->uvc_version < 0x0150)
> - return 34;
> - else
> - return 48;
> -}
> -
> static int uvc_get_video_ctrl(struct uvc_streaming *stream,
> struct uvc_streaming_control *ctrl, int probe, u8 query)
> {
> - u16 size = uvc_video_ctrl_size(stream);
> + u16 size = stream->ctrl_size;
> u8 *data;
> int ret;
>
> @@ -329,6 +315,20 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
> "Enabling workaround.\n");
> ret = -EIO;
> goto out;
> + } else if (ret < size &&
what about (ret != size) {
switch (ret) {
case UVC_STREAMING_CONTROL_SIZE_V1_0:
case UVC_STREAMING_CONTROL_SIZE_V1_1:
case UVC_STREAMING_CONTROL_SIZE_V1_5:
printk(stream at vendor)
stream->ctrl_size = ret;
size = ret;
break;
default:
printk(fatal error)
return -EIO;
break;
}
}
> + (ret == UVC_STREAMING_CONTROL_SIZE_V1_0 ||
> + ret == UVC_STREAMING_CONTROL_SIZE_V1_1)) {
> + /*
> + * Some Quanta cameras (for instance 0408:4033 and 0408:4035)
> + * advertise UVC 1.5 compliance but only returns 26 bytes of
> + * data for the video probe and commit controls.
> + */
> + dev_warn(&stream->intf->dev,
> + "UVC non compliance: video control size %u < %u as required by UVC v%u.%02x. Enabling workaround.\n",
> + ret, size, stream->dev->uvc_version >> 8,
> + stream->dev->uvc_version & 0xff);
> + stream->ctrl_size = ret;
> + size = ret;
> } else if (ret != size) {
> dev_err(&stream->intf->dev,
> "Failed to query (%u) UVC %s control : %d (exp. %u).\n",
> @@ -349,7 +349,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
> ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
> ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
>
> - if (size >= 34) {
> + if (size >= UVC_STREAMING_CONTROL_SIZE_V1_1) {
> ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
> ctrl->bmFramingInfo = data[30];
> ctrl->bPreferedVersion = data[31];
> @@ -379,7 +379,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
> static int uvc_set_video_ctrl(struct uvc_streaming *stream,
> struct uvc_streaming_control *ctrl, int probe)
> {
> - u16 size = uvc_video_ctrl_size(stream);
> + u16 size = stream->ctrl_size;
> u8 *data;
> int ret;
>
> @@ -399,7 +399,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream,
> put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
> put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
>
> - if (size >= 34) {
> + if (size >= UVC_STREAMING_CONTROL_SIZE_V1_1) {
> put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
> data[30] = ctrl->bmFramingInfo;
> data[31] = ctrl->bPreferedVersion;
> @@ -2148,6 +2148,18 @@ int uvc_video_init(struct uvc_streaming *stream)
>
> atomic_set(&stream->active, 0);
>
> + /*
> + * Set the probe & commit control size based on the UVC protocol
> + * version. It may be overridden by uvc_get_video_ctrl() for devices
> + * that don't comply with the UVC version they report.
> + */
> + if (stream->dev->uvc_version < 0x0110)
> + stream->ctrl_size = UVC_STREAMING_CONTROL_SIZE_V1_0;
> + else if (stream->dev->uvc_version < 0x0150)
> + stream->ctrl_size = UVC_STREAMING_CONTROL_SIZE_V1_1;
> + else
> + stream->ctrl_size = UVC_STREAMING_CONTROL_SIZE_V1_5;
> +
> /*
> * Alternate setting 0 should be the default, yet the XBox Live Vision
> * Cam (and possibly other devices) crash or otherwise misbehave if
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 72189249719e..151d8d27cbbc 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -441,6 +441,7 @@ struct uvc_streaming {
> struct uvc_format *format;
>
> struct uvc_streaming_control ctrl;
> + unsigned int ctrl_size;
> struct uvc_format *def_format;
> struct uvc_format *cur_format;
> struct uvc_frame *cur_frame;
> diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
> index 6e8e572c2980..7cb7713db757 100644
> --- a/include/uapi/linux/usb/video.h
> +++ b/include/uapi/linux/usb/video.h
> @@ -454,6 +454,10 @@ struct uvc_streaming_control {
> __u8 bMaxVersion;
> } __attribute__((__packed__));
>
> +#define UVC_STREAMING_CONTROL_SIZE_V1_0 26
> +#define UVC_STREAMING_CONTROL_SIZE_V1_1 34
> +#define UVC_STREAMING_CONTROL_SIZE_V1_5 48
Maybe a future improvement, convert this to a nice structure
> +
> /* Uncompressed Payload - 3.1.1. Uncompressed Video Format Descriptor */
> struct uvc_format_uncompressed {
> __u8 bLength;
> --
> Regards,
>
> Laurent Pinchart
>
@@ -272,24 +272,10 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
}
}
-static size_t uvc_video_ctrl_size(struct uvc_streaming *stream)
-{
- /*
- * Return the size of the video probe and commit controls, which depends
- * on the protocol version.
- */
- if (stream->dev->uvc_version < 0x0110)
- return 26;
- else if (stream->dev->uvc_version < 0x0150)
- return 34;
- else
- return 48;
-}
-
static int uvc_get_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl, int probe, u8 query)
{
- u16 size = uvc_video_ctrl_size(stream);
+ u16 size = stream->ctrl_size;
u8 *data;
int ret;
@@ -329,6 +315,20 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
"Enabling workaround.\n");
ret = -EIO;
goto out;
+ } else if (ret < size &&
+ (ret == UVC_STREAMING_CONTROL_SIZE_V1_0 ||
+ ret == UVC_STREAMING_CONTROL_SIZE_V1_1)) {
+ /*
+ * Some Quanta cameras (for instance 0408:4033 and 0408:4035)
+ * advertise UVC 1.5 compliance but only returns 26 bytes of
+ * data for the video probe and commit controls.
+ */
+ dev_warn(&stream->intf->dev,
+ "UVC non compliance: video control size %u < %u as required by UVC v%u.%02x. Enabling workaround.\n",
+ ret, size, stream->dev->uvc_version >> 8,
+ stream->dev->uvc_version & 0xff);
+ stream->ctrl_size = ret;
+ size = ret;
} else if (ret != size) {
dev_err(&stream->intf->dev,
"Failed to query (%u) UVC %s control : %d (exp. %u).\n",
@@ -349,7 +349,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
- if (size >= 34) {
+ if (size >= UVC_STREAMING_CONTROL_SIZE_V1_1) {
ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
ctrl->bmFramingInfo = data[30];
ctrl->bPreferedVersion = data[31];
@@ -379,7 +379,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
static int uvc_set_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl, int probe)
{
- u16 size = uvc_video_ctrl_size(stream);
+ u16 size = stream->ctrl_size;
u8 *data;
int ret;
@@ -399,7 +399,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream,
put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
- if (size >= 34) {
+ if (size >= UVC_STREAMING_CONTROL_SIZE_V1_1) {
put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
@@ -2148,6 +2148,18 @@ int uvc_video_init(struct uvc_streaming *stream)
atomic_set(&stream->active, 0);
+ /*
+ * Set the probe & commit control size based on the UVC protocol
+ * version. It may be overridden by uvc_get_video_ctrl() for devices
+ * that don't comply with the UVC version they report.
+ */
+ if (stream->dev->uvc_version < 0x0110)
+ stream->ctrl_size = UVC_STREAMING_CONTROL_SIZE_V1_0;
+ else if (stream->dev->uvc_version < 0x0150)
+ stream->ctrl_size = UVC_STREAMING_CONTROL_SIZE_V1_1;
+ else
+ stream->ctrl_size = UVC_STREAMING_CONTROL_SIZE_V1_5;
+
/*
* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
@@ -441,6 +441,7 @@ struct uvc_streaming {
struct uvc_format *format;
struct uvc_streaming_control ctrl;
+ unsigned int ctrl_size;
struct uvc_format *def_format;
struct uvc_format *cur_format;
struct uvc_frame *cur_frame;
@@ -454,6 +454,10 @@ struct uvc_streaming_control {
__u8 bMaxVersion;
} __attribute__((__packed__));
+#define UVC_STREAMING_CONTROL_SIZE_V1_0 26
+#define UVC_STREAMING_CONTROL_SIZE_V1_1 34
+#define UVC_STREAMING_CONTROL_SIZE_V1_5 48
+
/* Uncompressed Payload - 3.1.1. Uncompressed Video Format Descriptor */
struct uvc_format_uncompressed {
__u8 bLength;