usb: gadget: webcam: Make g_webcam loadable again
authorAndrzej Pietrasiewicz <andrzej.p@collabora.com>
Fri, 15 Dec 2023 13:16:14 +0000 (14:16 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jan 2024 23:35:53 +0000 (15:35 -0800)
[ Upstream commit f1fd91a0924b6bff91ca1287461fb8e3b3b61d92 ]

commit 588b9e85609b ("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: 588b9e85609b ("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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/gadget/function/f_uvc.c
drivers/usb/gadget/function/u_uvc.h
drivers/usb/gadget/legacy/webcam.c

index 3d5c03e1361e4b99f04b541cb88157329af6eab8..2e6bafb2a55492c25bd15332969f8d081b2319fc 100644 (file)
@@ -966,7 +966,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);
 }
@@ -1058,25 +1059,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;
-
-       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;
-
-       uvc->header = to_uvcg_streaming_header(h);
-       if (!uvc->header->linked) {
-               mutex_unlock(&opts->lock);
-               kfree(uvc);
-               return ERR_PTR(-EBUSY);
+       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;
+
+               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->desc.extension_units = &opts->extension_units;
index 1ce58f61253c9afaa53f51bf3e6164bb87a90321..3ac392cbb779495434f6e8a19d073f0c91d2ce23 100644 (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 */
index c06dd1af7a0c506a17210bbc9ea41ac5355985d9..c395438d39780ec07b46344ef004596995cc04d1 100644 (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,43 +157,112 @@ 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),
@@ -202,28 +270,66 @@ static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
        .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.
         */