usb: gadget: webcam: Make g_webcam loadable again

commit 588b9e8560 ("usb: gadget: uvc: add v4l2 enumeration api calls")
has rendered the precomposed (aka legacy) webcam gadget unloadable.

uvc_alloc() since then has depended on certain config groups being
available in configfs tree related to the UVC function. However, legacy
gadgets do not create anything in configfs, so uvc_alloc() must fail
with -ENOENT no matter what.

This patch mimics the required configfs hierarchy to satisfy the code which
inspects formats and frames found in uvcg_streaming_header.

This has been tested with guvcview on the host side, using vivid as a
source of video stream on the device side and using the userspace program
found at https://gitlab.freedesktop.org/camera/uvc-gadget.git.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Fixes: 588b9e8560 ("usb: gadget: uvc: add v4l2 enumeration api calls")
Link: https://lore.kernel.org/r/20231215131614.29132-1-andrzej.p@collabora.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Andrzej Pietrasiewicz 2023-12-15 14:16:14 +01:00 committed by Greg Kroah-Hartman
parent c51ffe929f
commit f1fd91a092
3 changed files with 281 additions and 97 deletions

View File

@ -959,7 +959,8 @@ static void uvc_free(struct usb_function *f)
struct uvc_device *uvc = to_uvc(f);
struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
func_inst);
config_item_put(&uvc->header->item);
if (!opts->header)
config_item_put(&uvc->header->item);
--opts->refcnt;
kfree(uvc);
}
@ -1051,25 +1052,29 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_streaming;
streaming = config_group_find_item(&opts->func_inst.group, "streaming");
if (!streaming)
goto err_config;
if (opts->header) {
uvc->header = opts->header;
} else {
streaming = config_group_find_item(&opts->func_inst.group, "streaming");
if (!streaming)
goto err_config;
header = config_group_find_item(to_config_group(streaming), "header");
config_item_put(streaming);
if (!header)
goto err_config;
header = config_group_find_item(to_config_group(streaming), "header");
config_item_put(streaming);
if (!header)
goto err_config;
h = config_group_find_item(to_config_group(header), "h");
config_item_put(header);
if (!h)
goto err_config;
h = config_group_find_item(to_config_group(header), "h");
config_item_put(header);
if (!h)
goto err_config;
uvc->header = to_uvcg_streaming_header(h);
if (!uvc->header->linked) {
mutex_unlock(&opts->lock);
kfree(uvc);
return ERR_PTR(-EBUSY);
uvc->header = to_uvcg_streaming_header(h);
if (!uvc->header->linked) {
mutex_unlock(&opts->lock);
kfree(uvc);
return ERR_PTR(-EBUSY);
}
}
uvc->desc.extension_units = &opts->extension_units;

View File

@ -98,6 +98,12 @@ struct f_uvc_opts {
*/
struct mutex lock;
int refcnt;
/*
* Only for legacy gadget. Shall be NULL for configfs-composed gadgets,
* which is guaranteed by alloc_inst implementation of f_uvc doing kzalloc.
*/
struct uvcg_streaming_header *header;
};
#endif /* U_UVC_H */

View File

@ -12,6 +12,7 @@
#include <linux/usb/video.h>
#include "u_uvc.h"
#include "uvc_configfs.h"
USB_GADGET_COMPOSITE_OPTIONS();
@ -84,8 +85,6 @@ static struct usb_device_descriptor webcam_device_descriptor = {
.bNumConfigurations = 0, /* dynamic */
};
DECLARE_UVC_HEADER_DESCRIPTOR(1);
static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
.bLength = UVC_DT_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
@ -158,72 +157,179 @@ static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
.bmaControls[1][0] = 4,
};
static const struct uvc_format_uncompressed uvc_format_yuv = {
.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
.bFormatIndex = 1,
.bNumFrameDescriptors = 2,
.guidFormat =
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
.bBitsPerPixel = 16,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
static const struct uvcg_color_matching uvcg_color_matching = {
.desc = {
.bLength = UVC_DT_COLOR_MATCHING_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_COLORFORMAT,
.bColorPrimaries = 1,
.bTransferCharacteristics = 1,
.bMatrixCoefficients = 4,
},
};
static struct uvcg_uncompressed uvcg_format_yuv = {
.fmt = {
.type = UVCG_UNCOMPRESSED,
/* add to .frames and fill .num_frames at runtime */
.color_matching = (struct uvcg_color_matching *)&uvcg_color_matching,
},
.desc = {
.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
.bFormatIndex = 1,
.bNumFrameDescriptors = 2,
.guidFormat = {
'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
},
.bBitsPerPixel = 16,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
},
};
static struct uvcg_format_ptr uvcg_format_ptr_yuv = {
.fmt = &uvcg_format_yuv.fmt,
};
DECLARE_UVC_FRAME_UNCOMPRESSED(1);
DECLARE_UVC_FRAME_UNCOMPRESSED(3);
#define UVCG_WIDTH_360P 640
#define UVCG_HEIGHT_360P 360
#define UVCG_MIN_BITRATE_360P 18432000
#define UVCG_MAX_BITRATE_360P 55296000
#define UVCG_MAX_VIDEO_FB_SZ_360P 460800
#define UVCG_FRM_INTERV_0_360P 666666
#define UVCG_FRM_INTERV_1_360P 1000000
#define UVCG_FRM_INTERV_2_360P 5000000
#define UVCG_DEFAULT_FRM_INTERV_360P UVCG_FRM_INTERV_0_360P
static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 1,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(640),
.wHeight = cpu_to_le16(360),
.dwMinBitRate = cpu_to_le32(18432000),
.dwMaxBitRate = cpu_to_le32(55296000),
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
.dwDefaultFrameInterval = cpu_to_le32(666666),
.wWidth = cpu_to_le16(UVCG_WIDTH_360P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_360P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
.bFrameIntervalType = 3,
.dwFrameInterval[0] = cpu_to_le32(666666),
.dwFrameInterval[1] = cpu_to_le32(1000000),
.dwFrameInterval[2] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P),
.dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P),
.dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P),
};
static u32 uvcg_frame_yuv_360p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_360P,
[1] = UVCG_FRM_INTERV_1_360P,
[2] = UVCG_FRM_INTERV_2_360P,
};
static const struct uvcg_frame uvcg_frame_yuv_360p = {
.fmt_type = UVCG_UNCOMPRESSED,
.frame = {
.b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED,
.b_frame_index = 1,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_360P,
.w_height = UVCG_HEIGHT_360P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_360P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_360P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P,
.b_frame_interval_type = 3,
},
.dw_frame_interval = uvcg_frame_yuv_360p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_360p = {
.frm = (struct uvcg_frame *)&uvcg_frame_yuv_360p,
};
#define UVCG_WIDTH_720P 1280
#define UVCG_HEIGHT_720P 720
#define UVCG_MIN_BITRATE_720P 29491200
#define UVCG_MAX_BITRATE_720P 29491200
#define UVCG_MAX_VIDEO_FB_SZ_720P 1843200
#define UVCG_FRM_INTERV_0_720P 5000000
#define UVCG_DEFAULT_FRM_INTERV_720P UVCG_FRM_INTERV_0_720P
static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 2,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(1280),
.wHeight = cpu_to_le16(720),
.dwMinBitRate = cpu_to_le32(29491200),
.dwMaxBitRate = cpu_to_le32(29491200),
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
.dwDefaultFrameInterval = cpu_to_le32(5000000),
.wWidth = cpu_to_le16(UVCG_WIDTH_720P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_720P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
.bFrameIntervalType = 1,
.dwFrameInterval[0] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P),
};
static const struct uvc_format_mjpeg uvc_format_mjpg = {
.bLength = UVC_DT_FORMAT_MJPEG_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
.bFormatIndex = 2,
.bNumFrameDescriptors = 2,
.bmFlags = 0,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
static u32 uvcg_frame_yuv_720p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_720P,
};
static const struct uvcg_frame uvcg_frame_yuv_720p = {
.fmt_type = UVCG_UNCOMPRESSED,
.frame = {
.b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED,
.b_frame_index = 2,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_720P,
.w_height = UVCG_HEIGHT_720P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_720P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_720P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P,
.b_frame_interval_type = 1,
},
.dw_frame_interval = uvcg_frame_yuv_720p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_720p = {
.frm = (struct uvcg_frame *)&uvcg_frame_yuv_720p,
};
static struct uvcg_mjpeg uvcg_format_mjpeg = {
.fmt = {
.type = UVCG_MJPEG,
/* add to .frames and fill .num_frames at runtime */
.color_matching = (struct uvcg_color_matching *)&uvcg_color_matching,
},
.desc = {
.bLength = UVC_DT_FORMAT_MJPEG_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
.bFormatIndex = 2,
.bNumFrameDescriptors = 2,
.bmFlags = 0,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0,
},
};
static struct uvcg_format_ptr uvcg_format_ptr_mjpeg = {
.fmt = &uvcg_format_mjpeg.fmt,
};
DECLARE_UVC_FRAME_MJPEG(1);
@ -235,16 +341,45 @@ static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 1,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(640),
.wHeight = cpu_to_le16(360),
.dwMinBitRate = cpu_to_le32(18432000),
.dwMaxBitRate = cpu_to_le32(55296000),
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
.dwDefaultFrameInterval = cpu_to_le32(666666),
.wWidth = cpu_to_le16(UVCG_WIDTH_360P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_360P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P),
.bFrameIntervalType = 3,
.dwFrameInterval[0] = cpu_to_le32(666666),
.dwFrameInterval[1] = cpu_to_le32(1000000),
.dwFrameInterval[2] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P),
.dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P),
.dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P),
};
static u32 uvcg_frame_mjpeg_360p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_360P,
[1] = UVCG_FRM_INTERV_1_360P,
[2] = UVCG_FRM_INTERV_2_360P,
};
static const struct uvcg_frame uvcg_frame_mjpeg_360p = {
.fmt_type = UVCG_MJPEG,
.frame = {
.b_length = UVC_DT_FRAME_MJPEG_SIZE(3),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_MJPEG,
.b_frame_index = 1,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_360P,
.w_height = UVCG_HEIGHT_360P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_360P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_360P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P,
.b_frame_interval_type = 3,
},
.dw_frame_interval = uvcg_frame_mjpeg_360p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_360p = {
.frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_360p,
};
static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
@ -253,23 +388,44 @@ static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 2,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(1280),
.wHeight = cpu_to_le16(720),
.dwMinBitRate = cpu_to_le32(29491200),
.dwMaxBitRate = cpu_to_le32(29491200),
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
.dwDefaultFrameInterval = cpu_to_le32(5000000),
.wWidth = cpu_to_le16(UVCG_WIDTH_720P),
.wHeight = cpu_to_le16(UVCG_HEIGHT_720P),
.dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P),
.dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P),
.dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P),
.dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P),
.bFrameIntervalType = 1,
.dwFrameInterval[0] = cpu_to_le32(5000000),
.dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P),
};
static const struct uvc_color_matching_descriptor uvc_color_matching = {
.bLength = UVC_DT_COLOR_MATCHING_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_COLORFORMAT,
.bColorPrimaries = 1,
.bTransferCharacteristics = 1,
.bMatrixCoefficients = 4,
static u32 uvcg_frame_mjpeg_720p_dw_frame_interval[] = {
[0] = UVCG_FRM_INTERV_0_720P,
};
static const struct uvcg_frame uvcg_frame_mjpeg_720p = {
.fmt_type = UVCG_MJPEG,
.frame = {
.b_length = UVC_DT_FRAME_MJPEG_SIZE(1),
.b_descriptor_type = USB_DT_CS_INTERFACE,
.b_descriptor_subtype = UVC_VS_FRAME_MJPEG,
.b_frame_index = 2,
.bm_capabilities = 0,
.w_width = UVCG_WIDTH_720P,
.w_height = UVCG_HEIGHT_720P,
.dw_min_bit_rate = UVCG_MIN_BITRATE_720P,
.dw_max_bit_rate = UVCG_MAX_BITRATE_720P,
.dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P,
.dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P,
.b_frame_interval_type = 1,
},
.dw_frame_interval = uvcg_frame_mjpeg_720p_dw_frame_interval,
};
static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_720p = {
.frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_720p,
};
static struct uvcg_streaming_header uvcg_streaming_header = {
};
static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
@ -290,40 +446,40 @@ static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvcg_format_yuv.desc,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
(const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvcg_color_matching.desc,
NULL,
};
@ -387,6 +543,23 @@ webcam_bind(struct usb_composite_dev *cdev)
uvc_opts->hs_streaming = uvc_hs_streaming_cls;
uvc_opts->ss_streaming = uvc_ss_streaming_cls;
INIT_LIST_HEAD(&uvcg_format_yuv.fmt.frames);
list_add_tail(&uvcg_frame_ptr_yuv_360p.entry, &uvcg_format_yuv.fmt.frames);
list_add_tail(&uvcg_frame_ptr_yuv_720p.entry, &uvcg_format_yuv.fmt.frames);
uvcg_format_yuv.fmt.num_frames = 2;
INIT_LIST_HEAD(&uvcg_format_mjpeg.fmt.frames);
list_add_tail(&uvcg_frame_ptr_mjpeg_360p.entry, &uvcg_format_mjpeg.fmt.frames);
list_add_tail(&uvcg_frame_ptr_mjpeg_720p.entry, &uvcg_format_mjpeg.fmt.frames);
uvcg_format_mjpeg.fmt.num_frames = 2;
INIT_LIST_HEAD(&uvcg_streaming_header.formats);
list_add_tail(&uvcg_format_ptr_yuv.entry, &uvcg_streaming_header.formats);
list_add_tail(&uvcg_format_ptr_mjpeg.entry, &uvcg_streaming_header.formats);
uvcg_streaming_header.num_fmt = 2;
uvc_opts->header = &uvcg_streaming_header;
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/