root/drivers/usb/gadget/legacy/webcam.c
// SPDX-License-Identifier: GPL-2.0+
/*
 *      webcam.c -- USB webcam gadget driver
 *
 *      Copyright (C) 2009-2010
 *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 */

#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/usb/video.h>

#include "u_uvc.h"
#include "uvc_configfs.h"

USB_GADGET_COMPOSITE_OPTIONS();

/*-------------------------------------------------------------------------*/

/* module parameters specific to the Video streaming endpoint */
static unsigned int streaming_interval = 1;
module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_interval, "1 - 16");

static unsigned int streaming_maxpacket = 1024;
module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");

static unsigned int streaming_maxburst;
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");

/* --------------------------------------------------------------------------
 * Device descriptor
 */

#define WEBCAM_VENDOR_ID                0x1d6b  /* Linux Foundation */
#define WEBCAM_PRODUCT_ID               0x0102  /* Webcam A/V gadget */
#define WEBCAM_DEVICE_BCD               0x0010  /* 0.10 */

static char webcam_vendor_label[] = "Linux Foundation";
static char webcam_product_label[] = "Webcam gadget";
static char webcam_config_label[] = "Video";

/* string IDs are assigned dynamically */

#define STRING_DESCRIPTION_IDX          USB_GADGET_FIRST_AVAIL_IDX

static struct usb_string webcam_strings[] = {
        [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
        [USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
        [USB_GADGET_SERIAL_IDX].s = "",
        [STRING_DESCRIPTION_IDX].s = webcam_config_label,
        {  }
};

static struct usb_gadget_strings webcam_stringtab = {
        .language = 0x0409,     /* en-us */
        .strings = webcam_strings,
};

static struct usb_gadget_strings *webcam_device_strings[] = {
        &webcam_stringtab,
        NULL,
};

static struct usb_function_instance *fi_uvc;
static struct usb_function *f_uvc;

static struct usb_device_descriptor webcam_device_descriptor = {
        .bLength                = USB_DT_DEVICE_SIZE,
        .bDescriptorType        = USB_DT_DEVICE,
        /* .bcdUSB = DYNAMIC */
        .bDeviceClass           = USB_CLASS_MISC,
        .bDeviceSubClass        = 0x02,
        .bDeviceProtocol        = 0x01,
        .bMaxPacketSize0        = 0, /* dynamic */
        .idVendor               = cpu_to_le16(WEBCAM_VENDOR_ID),
        .idProduct              = cpu_to_le16(WEBCAM_PRODUCT_ID),
        .bcdDevice              = cpu_to_le16(WEBCAM_DEVICE_BCD),
        .iManufacturer          = 0, /* dynamic */
        .iProduct               = 0, /* dynamic */
        .iSerialNumber          = 0, /* dynamic */
        .bNumConfigurations     = 0, /* dynamic */
};

static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
        .bLength                = UVC_DT_HEADER_SIZE(1),
        .bDescriptorType        = USB_DT_CS_INTERFACE,
        .bDescriptorSubType     = UVC_VC_HEADER,
        .bcdUVC                 = cpu_to_le16(0x0110),
        .wTotalLength           = 0, /* dynamic */
        .dwClockFrequency       = cpu_to_le32(48000000),
        .bInCollection          = 0, /* dynamic */
        .baInterfaceNr[0]       = 0, /* dynamic */
};

static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
        .bLength                = UVC_DT_CAMERA_TERMINAL_SIZE(3),
        .bDescriptorType        = USB_DT_CS_INTERFACE,
        .bDescriptorSubType     = UVC_VC_INPUT_TERMINAL,
        .bTerminalID            = 1,
        .wTerminalType          = cpu_to_le16(0x0201),
        .bAssocTerminal         = 0,
        .iTerminal              = 0,
        .wObjectiveFocalLengthMin       = cpu_to_le16(0),
        .wObjectiveFocalLengthMax       = cpu_to_le16(0),
        .wOcularFocalLength             = cpu_to_le16(0),
        .bControlSize           = 3,
        .bmControls[0]          = 2,
        .bmControls[1]          = 0,
        .bmControls[2]          = 0,
};

static const struct uvc_processing_unit_descriptor uvc_processing = {
        .bLength                = UVC_DT_PROCESSING_UNIT_SIZE(2),
        .bDescriptorType        = USB_DT_CS_INTERFACE,
        .bDescriptorSubType     = UVC_VC_PROCESSING_UNIT,
        .bUnitID                = 2,
        .bSourceID              = 1,
        .wMaxMultiplier         = cpu_to_le16(16*1024),
        .bControlSize           = 2,
        .bmControls[0]          = 1,
        .bmControls[1]          = 0,
        .iProcessing            = 0,
        .bmVideoStandards       = 0,
};

static const struct uvc_output_terminal_descriptor uvc_output_terminal = {
        .bLength                = UVC_DT_OUTPUT_TERMINAL_SIZE,
        .bDescriptorType        = USB_DT_CS_INTERFACE,
        .bDescriptorSubType     = UVC_VC_OUTPUT_TERMINAL,
        .bTerminalID            = 3,
        .wTerminalType          = cpu_to_le16(0x0101),
        .bAssocTerminal         = 0,
        .bSourceID              = 2,
        .iTerminal              = 0,
};

DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2);

static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
        .bLength                = UVC_DT_INPUT_HEADER_SIZE(1, 2),
        .bDescriptorType        = USB_DT_CS_INTERFACE,
        .bDescriptorSubType     = UVC_VS_INPUT_HEADER,
        .bNumFormats            = 2,
        .wTotalLength           = 0, /* dynamic */
        .bEndpointAddress       = 0, /* dynamic */
        .bmInfo                 = 0,
        .bTerminalLink          = 3,
        .bStillCaptureMethod    = 0,
        .bTriggerSupport        = 0,
        .bTriggerUsage          = 0,
        .bControlSize           = 1,
        .bmaControls[0][0]      = 0,
        .bmaControls[1][0]      = 4,
};

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(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(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(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(UVCG_FRM_INTERV_0_720P),
};

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);
DECLARE_UVC_FRAME_MJPEG(3);

static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
        .bLength                = UVC_DT_FRAME_MJPEG_SIZE(3),
        .bDescriptorType        = USB_DT_CS_INTERFACE,
        .bDescriptorSubType     = UVC_VS_FRAME_MJPEG,
        .bFrameIndex            = 1,
        .bmCapabilities         = 0,
        .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(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 = {
        .bLength                = UVC_DT_FRAME_MJPEG_SIZE(1),
        .bDescriptorType        = USB_DT_CS_INTERFACE,
        .bDescriptorSubType     = UVC_VS_FRAME_MJPEG,
        .bFrameIndex            = 2,
        .bmCapabilities         = 0,
        .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(UVCG_FRM_INTERV_0_720P),
};

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[] = {
        (const struct uvc_descriptor_header *) &uvc_control_header,
        (const struct uvc_descriptor_header *) &uvc_camera_terminal,
        (const struct uvc_descriptor_header *) &uvc_processing,
        (const struct uvc_descriptor_header *) &uvc_output_terminal,
        NULL,
};

static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
        (const struct uvc_descriptor_header *) &uvc_control_header,
        (const struct uvc_descriptor_header *) &uvc_camera_terminal,
        (const struct uvc_descriptor_header *) &uvc_processing,
        (const struct uvc_descriptor_header *) &uvc_output_terminal,
        NULL,
};

static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
        (const struct uvc_descriptor_header *) &uvc_input_header,
        (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 *) &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 *) &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 *) &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 *) &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 *) &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 *) &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 *) &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 *) &uvcg_color_matching.desc,
        NULL,
};

/* --------------------------------------------------------------------------
 * USB configuration
 */

static int
webcam_config_bind(struct usb_configuration *c)
{
        int status = 0;

        f_uvc = usb_get_function(fi_uvc);
        if (IS_ERR(f_uvc))
                return PTR_ERR(f_uvc);

        status = usb_add_function(c, f_uvc);
        if (status < 0)
                usb_put_function(f_uvc);

        return status;
}

static struct usb_configuration webcam_config_driver = {
        .label                  = webcam_config_label,
        .bConfigurationValue    = 1,
        .iConfiguration         = 0, /* dynamic */
        .bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
        .MaxPower               = CONFIG_USB_GADGET_VBUS_DRAW,
};

static int
webcam_unbind(struct usb_composite_dev *cdev)
{
        if (!IS_ERR_OR_NULL(f_uvc))
                usb_put_function(f_uvc);
        if (!IS_ERR_OR_NULL(fi_uvc))
                usb_put_function_instance(fi_uvc);
        return 0;
}

static int
webcam_bind(struct usb_composite_dev *cdev)
{
        struct f_uvc_opts *uvc_opts;
        int ret;

        fi_uvc = usb_get_function_instance("uvc");
        if (IS_ERR(fi_uvc))
                return PTR_ERR(fi_uvc);

        uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);

        uvc_opts->streaming_interval = streaming_interval;
        uvc_opts->streaming_maxpacket = streaming_maxpacket;
        uvc_opts->streaming_maxburst = streaming_maxburst;

        uvc_opts->fs_control = uvc_fs_control_cls;
        uvc_opts->ss_control = uvc_ss_control_cls;
        uvc_opts->fs_streaming = uvc_fs_streaming_cls;
        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.
         */
        ret = usb_string_ids_tab(cdev, webcam_strings);
        if (ret < 0)
                goto error;
        webcam_device_descriptor.iManufacturer =
                webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
        webcam_device_descriptor.iProduct =
                webcam_strings[USB_GADGET_PRODUCT_IDX].id;
        webcam_config_driver.iConfiguration =
                webcam_strings[STRING_DESCRIPTION_IDX].id;

        /* Register our configuration. */
        if ((ret = usb_add_config(cdev, &webcam_config_driver,
                                        webcam_config_bind)) < 0)
                goto error;

        usb_composite_overwrite_options(cdev, &coverwrite);
        INFO(cdev, "Webcam Video Gadget\n");
        return 0;

error:
        usb_put_function_instance(fi_uvc);
        return ret;
}

/* --------------------------------------------------------------------------
 * Driver
 */

static struct usb_composite_driver webcam_driver = {
        .name           = "g_webcam",
        .dev            = &webcam_device_descriptor,
        .strings        = webcam_device_strings,
        .max_speed      = USB_SPEED_SUPER,
        .bind           = webcam_bind,
        .unbind         = webcam_unbind,
};

module_usb_composite_driver(webcam_driver);

MODULE_AUTHOR("Laurent Pinchart");
MODULE_DESCRIPTION("Webcam Video Gadget");
MODULE_LICENSE("GPL");