162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * webcam.c -- USB webcam gadget driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009-2010 662306a36Sopenharmony_ci * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/usb/video.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "u_uvc.h" 1562306a36Sopenharmony_ci#include "uvc_configfs.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS(); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* module parameters specific to the Video streaming endpoint */ 2262306a36Sopenharmony_cistatic unsigned int streaming_interval = 1; 2362306a36Sopenharmony_cimodule_param(streaming_interval, uint, S_IRUGO|S_IWUSR); 2462306a36Sopenharmony_ciMODULE_PARM_DESC(streaming_interval, "1 - 16"); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic unsigned int streaming_maxpacket = 1024; 2762306a36Sopenharmony_cimodule_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); 2862306a36Sopenharmony_ciMODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic unsigned int streaming_maxburst; 3162306a36Sopenharmony_cimodule_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); 3262306a36Sopenharmony_ciMODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 3562306a36Sopenharmony_ci * Device descriptor 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */ 3962306a36Sopenharmony_ci#define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */ 4062306a36Sopenharmony_ci#define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic char webcam_vendor_label[] = "Linux Foundation"; 4362306a36Sopenharmony_cistatic char webcam_product_label[] = "Webcam gadget"; 4462306a36Sopenharmony_cistatic char webcam_config_label[] = "Video"; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* string IDs are assigned dynamically */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic struct usb_string webcam_strings[] = { 5162306a36Sopenharmony_ci [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label, 5262306a36Sopenharmony_ci [USB_GADGET_PRODUCT_IDX].s = webcam_product_label, 5362306a36Sopenharmony_ci [USB_GADGET_SERIAL_IDX].s = "", 5462306a36Sopenharmony_ci [STRING_DESCRIPTION_IDX].s = webcam_config_label, 5562306a36Sopenharmony_ci { } 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct usb_gadget_strings webcam_stringtab = { 5962306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 6062306a36Sopenharmony_ci .strings = webcam_strings, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct usb_gadget_strings *webcam_device_strings[] = { 6462306a36Sopenharmony_ci &webcam_stringtab, 6562306a36Sopenharmony_ci NULL, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct usb_function_instance *fi_uvc; 6962306a36Sopenharmony_cistatic struct usb_function *f_uvc; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic struct usb_device_descriptor webcam_device_descriptor = { 7262306a36Sopenharmony_ci .bLength = USB_DT_DEVICE_SIZE, 7362306a36Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE, 7462306a36Sopenharmony_ci /* .bcdUSB = DYNAMIC */ 7562306a36Sopenharmony_ci .bDeviceClass = USB_CLASS_MISC, 7662306a36Sopenharmony_ci .bDeviceSubClass = 0x02, 7762306a36Sopenharmony_ci .bDeviceProtocol = 0x01, 7862306a36Sopenharmony_ci .bMaxPacketSize0 = 0, /* dynamic */ 7962306a36Sopenharmony_ci .idVendor = cpu_to_le16(WEBCAM_VENDOR_ID), 8062306a36Sopenharmony_ci .idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID), 8162306a36Sopenharmony_ci .bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD), 8262306a36Sopenharmony_ci .iManufacturer = 0, /* dynamic */ 8362306a36Sopenharmony_ci .iProduct = 0, /* dynamic */ 8462306a36Sopenharmony_ci .iSerialNumber = 0, /* dynamic */ 8562306a36Sopenharmony_ci .bNumConfigurations = 0, /* dynamic */ 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { 8962306a36Sopenharmony_ci .bLength = UVC_DT_HEADER_SIZE(1), 9062306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 9162306a36Sopenharmony_ci .bDescriptorSubType = UVC_VC_HEADER, 9262306a36Sopenharmony_ci .bcdUVC = cpu_to_le16(0x0110), 9362306a36Sopenharmony_ci .wTotalLength = 0, /* dynamic */ 9462306a36Sopenharmony_ci .dwClockFrequency = cpu_to_le32(48000000), 9562306a36Sopenharmony_ci .bInCollection = 0, /* dynamic */ 9662306a36Sopenharmony_ci .baInterfaceNr[0] = 0, /* dynamic */ 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { 10062306a36Sopenharmony_ci .bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3), 10162306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 10262306a36Sopenharmony_ci .bDescriptorSubType = UVC_VC_INPUT_TERMINAL, 10362306a36Sopenharmony_ci .bTerminalID = 1, 10462306a36Sopenharmony_ci .wTerminalType = cpu_to_le16(0x0201), 10562306a36Sopenharmony_ci .bAssocTerminal = 0, 10662306a36Sopenharmony_ci .iTerminal = 0, 10762306a36Sopenharmony_ci .wObjectiveFocalLengthMin = cpu_to_le16(0), 10862306a36Sopenharmony_ci .wObjectiveFocalLengthMax = cpu_to_le16(0), 10962306a36Sopenharmony_ci .wOcularFocalLength = cpu_to_le16(0), 11062306a36Sopenharmony_ci .bControlSize = 3, 11162306a36Sopenharmony_ci .bmControls[0] = 2, 11262306a36Sopenharmony_ci .bmControls[1] = 0, 11362306a36Sopenharmony_ci .bmControls[2] = 0, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct uvc_processing_unit_descriptor uvc_processing = { 11762306a36Sopenharmony_ci .bLength = UVC_DT_PROCESSING_UNIT_SIZE(2), 11862306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 11962306a36Sopenharmony_ci .bDescriptorSubType = UVC_VC_PROCESSING_UNIT, 12062306a36Sopenharmony_ci .bUnitID = 2, 12162306a36Sopenharmony_ci .bSourceID = 1, 12262306a36Sopenharmony_ci .wMaxMultiplier = cpu_to_le16(16*1024), 12362306a36Sopenharmony_ci .bControlSize = 2, 12462306a36Sopenharmony_ci .bmControls[0] = 1, 12562306a36Sopenharmony_ci .bmControls[1] = 0, 12662306a36Sopenharmony_ci .iProcessing = 0, 12762306a36Sopenharmony_ci .bmVideoStandards = 0, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct uvc_output_terminal_descriptor uvc_output_terminal = { 13162306a36Sopenharmony_ci .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE, 13262306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 13362306a36Sopenharmony_ci .bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL, 13462306a36Sopenharmony_ci .bTerminalID = 3, 13562306a36Sopenharmony_ci .wTerminalType = cpu_to_le16(0x0101), 13662306a36Sopenharmony_ci .bAssocTerminal = 0, 13762306a36Sopenharmony_ci .bSourceID = 2, 13862306a36Sopenharmony_ci .iTerminal = 0, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciDECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { 14462306a36Sopenharmony_ci .bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2), 14562306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 14662306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_INPUT_HEADER, 14762306a36Sopenharmony_ci .bNumFormats = 2, 14862306a36Sopenharmony_ci .wTotalLength = 0, /* dynamic */ 14962306a36Sopenharmony_ci .bEndpointAddress = 0, /* dynamic */ 15062306a36Sopenharmony_ci .bmInfo = 0, 15162306a36Sopenharmony_ci .bTerminalLink = 3, 15262306a36Sopenharmony_ci .bStillCaptureMethod = 0, 15362306a36Sopenharmony_ci .bTriggerSupport = 0, 15462306a36Sopenharmony_ci .bTriggerUsage = 0, 15562306a36Sopenharmony_ci .bControlSize = 1, 15662306a36Sopenharmony_ci .bmaControls[0][0] = 0, 15762306a36Sopenharmony_ci .bmaControls[1][0] = 4, 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic const struct uvcg_color_matching uvcg_color_matching = { 16162306a36Sopenharmony_ci .desc = { 16262306a36Sopenharmony_ci .bLength = UVC_DT_COLOR_MATCHING_SIZE, 16362306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 16462306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_COLORFORMAT, 16562306a36Sopenharmony_ci .bColorPrimaries = 1, 16662306a36Sopenharmony_ci .bTransferCharacteristics = 1, 16762306a36Sopenharmony_ci .bMatrixCoefficients = 4, 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct uvcg_uncompressed uvcg_format_yuv = { 17262306a36Sopenharmony_ci .fmt = { 17362306a36Sopenharmony_ci .type = UVCG_UNCOMPRESSED, 17462306a36Sopenharmony_ci /* add to .frames and fill .num_frames at runtime */ 17562306a36Sopenharmony_ci .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching, 17662306a36Sopenharmony_ci }, 17762306a36Sopenharmony_ci .desc = { 17862306a36Sopenharmony_ci .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, 17962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 18062306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, 18162306a36Sopenharmony_ci .bFormatIndex = 1, 18262306a36Sopenharmony_ci .bNumFrameDescriptors = 2, 18362306a36Sopenharmony_ci .guidFormat = { 18462306a36Sopenharmony_ci 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, 18562306a36Sopenharmony_ci 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 18662306a36Sopenharmony_ci }, 18762306a36Sopenharmony_ci .bBitsPerPixel = 16, 18862306a36Sopenharmony_ci .bDefaultFrameIndex = 1, 18962306a36Sopenharmony_ci .bAspectRatioX = 0, 19062306a36Sopenharmony_ci .bAspectRatioY = 0, 19162306a36Sopenharmony_ci .bmInterlaceFlags = 0, 19262306a36Sopenharmony_ci .bCopyProtect = 0, 19362306a36Sopenharmony_ci }, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic struct uvcg_format_ptr uvcg_format_ptr_yuv = { 19762306a36Sopenharmony_ci .fmt = &uvcg_format_yuv.fmt, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciDECLARE_UVC_FRAME_UNCOMPRESSED(1); 20162306a36Sopenharmony_ciDECLARE_UVC_FRAME_UNCOMPRESSED(3); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci#define UVCG_WIDTH_360P 640 20462306a36Sopenharmony_ci#define UVCG_HEIGHT_360P 360 20562306a36Sopenharmony_ci#define UVCG_MIN_BITRATE_360P 18432000 20662306a36Sopenharmony_ci#define UVCG_MAX_BITRATE_360P 55296000 20762306a36Sopenharmony_ci#define UVCG_MAX_VIDEO_FB_SZ_360P 460800 20862306a36Sopenharmony_ci#define UVCG_FRM_INTERV_0_360P 666666 20962306a36Sopenharmony_ci#define UVCG_FRM_INTERV_1_360P 1000000 21062306a36Sopenharmony_ci#define UVCG_FRM_INTERV_2_360P 5000000 21162306a36Sopenharmony_ci#define UVCG_DEFAULT_FRM_INTERV_360P UVCG_FRM_INTERV_0_360P 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { 21462306a36Sopenharmony_ci .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), 21562306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 21662306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, 21762306a36Sopenharmony_ci .bFrameIndex = 1, 21862306a36Sopenharmony_ci .bmCapabilities = 0, 21962306a36Sopenharmony_ci .wWidth = cpu_to_le16(UVCG_WIDTH_360P), 22062306a36Sopenharmony_ci .wHeight = cpu_to_le16(UVCG_HEIGHT_360P), 22162306a36Sopenharmony_ci .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P), 22262306a36Sopenharmony_ci .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P), 22362306a36Sopenharmony_ci .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P), 22462306a36Sopenharmony_ci .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P), 22562306a36Sopenharmony_ci .bFrameIntervalType = 3, 22662306a36Sopenharmony_ci .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P), 22762306a36Sopenharmony_ci .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P), 22862306a36Sopenharmony_ci .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P), 22962306a36Sopenharmony_ci}; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic u32 uvcg_frame_yuv_360p_dw_frame_interval[] = { 23262306a36Sopenharmony_ci [0] = UVCG_FRM_INTERV_0_360P, 23362306a36Sopenharmony_ci [1] = UVCG_FRM_INTERV_1_360P, 23462306a36Sopenharmony_ci [2] = UVCG_FRM_INTERV_2_360P, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_yuv_360p = { 23862306a36Sopenharmony_ci .fmt_type = UVCG_UNCOMPRESSED, 23962306a36Sopenharmony_ci .frame = { 24062306a36Sopenharmony_ci .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), 24162306a36Sopenharmony_ci .b_descriptor_type = USB_DT_CS_INTERFACE, 24262306a36Sopenharmony_ci .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED, 24362306a36Sopenharmony_ci .b_frame_index = 1, 24462306a36Sopenharmony_ci .bm_capabilities = 0, 24562306a36Sopenharmony_ci .w_width = UVCG_WIDTH_360P, 24662306a36Sopenharmony_ci .w_height = UVCG_HEIGHT_360P, 24762306a36Sopenharmony_ci .dw_min_bit_rate = UVCG_MIN_BITRATE_360P, 24862306a36Sopenharmony_ci .dw_max_bit_rate = UVCG_MAX_BITRATE_360P, 24962306a36Sopenharmony_ci .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P, 25062306a36Sopenharmony_ci .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P, 25162306a36Sopenharmony_ci .b_frame_interval_type = 3, 25262306a36Sopenharmony_ci }, 25362306a36Sopenharmony_ci .dw_frame_interval = uvcg_frame_yuv_360p_dw_frame_interval, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_yuv_360p = { 25762306a36Sopenharmony_ci .frm = (struct uvcg_frame *)&uvcg_frame_yuv_360p, 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci#define UVCG_WIDTH_720P 1280 26062306a36Sopenharmony_ci#define UVCG_HEIGHT_720P 720 26162306a36Sopenharmony_ci#define UVCG_MIN_BITRATE_720P 29491200 26262306a36Sopenharmony_ci#define UVCG_MAX_BITRATE_720P 29491200 26362306a36Sopenharmony_ci#define UVCG_MAX_VIDEO_FB_SZ_720P 1843200 26462306a36Sopenharmony_ci#define UVCG_FRM_INTERV_0_720P 5000000 26562306a36Sopenharmony_ci#define UVCG_DEFAULT_FRM_INTERV_720P UVCG_FRM_INTERV_0_720P 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { 26862306a36Sopenharmony_ci .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), 26962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 27062306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, 27162306a36Sopenharmony_ci .bFrameIndex = 2, 27262306a36Sopenharmony_ci .bmCapabilities = 0, 27362306a36Sopenharmony_ci .wWidth = cpu_to_le16(UVCG_WIDTH_720P), 27462306a36Sopenharmony_ci .wHeight = cpu_to_le16(UVCG_HEIGHT_720P), 27562306a36Sopenharmony_ci .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P), 27662306a36Sopenharmony_ci .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P), 27762306a36Sopenharmony_ci .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P), 27862306a36Sopenharmony_ci .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P), 27962306a36Sopenharmony_ci .bFrameIntervalType = 1, 28062306a36Sopenharmony_ci .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P), 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic u32 uvcg_frame_yuv_720p_dw_frame_interval[] = { 28462306a36Sopenharmony_ci [0] = UVCG_FRM_INTERV_0_720P, 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_yuv_720p = { 28862306a36Sopenharmony_ci .fmt_type = UVCG_UNCOMPRESSED, 28962306a36Sopenharmony_ci .frame = { 29062306a36Sopenharmony_ci .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), 29162306a36Sopenharmony_ci .b_descriptor_type = USB_DT_CS_INTERFACE, 29262306a36Sopenharmony_ci .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED, 29362306a36Sopenharmony_ci .b_frame_index = 2, 29462306a36Sopenharmony_ci .bm_capabilities = 0, 29562306a36Sopenharmony_ci .w_width = UVCG_WIDTH_720P, 29662306a36Sopenharmony_ci .w_height = UVCG_HEIGHT_720P, 29762306a36Sopenharmony_ci .dw_min_bit_rate = UVCG_MIN_BITRATE_720P, 29862306a36Sopenharmony_ci .dw_max_bit_rate = UVCG_MAX_BITRATE_720P, 29962306a36Sopenharmony_ci .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P, 30062306a36Sopenharmony_ci .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P, 30162306a36Sopenharmony_ci .b_frame_interval_type = 1, 30262306a36Sopenharmony_ci }, 30362306a36Sopenharmony_ci .dw_frame_interval = uvcg_frame_yuv_720p_dw_frame_interval, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_yuv_720p = { 30762306a36Sopenharmony_ci .frm = (struct uvcg_frame *)&uvcg_frame_yuv_720p, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic struct uvcg_mjpeg uvcg_format_mjpeg = { 31162306a36Sopenharmony_ci .fmt = { 31262306a36Sopenharmony_ci .type = UVCG_MJPEG, 31362306a36Sopenharmony_ci /* add to .frames and fill .num_frames at runtime */ 31462306a36Sopenharmony_ci .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching, 31562306a36Sopenharmony_ci }, 31662306a36Sopenharmony_ci .desc = { 31762306a36Sopenharmony_ci .bLength = UVC_DT_FORMAT_MJPEG_SIZE, 31862306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 31962306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, 32062306a36Sopenharmony_ci .bFormatIndex = 2, 32162306a36Sopenharmony_ci .bNumFrameDescriptors = 2, 32262306a36Sopenharmony_ci .bmFlags = 0, 32362306a36Sopenharmony_ci .bDefaultFrameIndex = 1, 32462306a36Sopenharmony_ci .bAspectRatioX = 0, 32562306a36Sopenharmony_ci .bAspectRatioY = 0, 32662306a36Sopenharmony_ci .bmInterlaceFlags = 0, 32762306a36Sopenharmony_ci .bCopyProtect = 0, 32862306a36Sopenharmony_ci }, 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic struct uvcg_format_ptr uvcg_format_ptr_mjpeg = { 33262306a36Sopenharmony_ci .fmt = &uvcg_format_mjpeg.fmt, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ciDECLARE_UVC_FRAME_MJPEG(1); 33662306a36Sopenharmony_ciDECLARE_UVC_FRAME_MJPEG(3); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { 33962306a36Sopenharmony_ci .bLength = UVC_DT_FRAME_MJPEG_SIZE(3), 34062306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 34162306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_FRAME_MJPEG, 34262306a36Sopenharmony_ci .bFrameIndex = 1, 34362306a36Sopenharmony_ci .bmCapabilities = 0, 34462306a36Sopenharmony_ci .wWidth = cpu_to_le16(UVCG_WIDTH_360P), 34562306a36Sopenharmony_ci .wHeight = cpu_to_le16(UVCG_HEIGHT_360P), 34662306a36Sopenharmony_ci .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P), 34762306a36Sopenharmony_ci .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P), 34862306a36Sopenharmony_ci .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P), 34962306a36Sopenharmony_ci .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P), 35062306a36Sopenharmony_ci .bFrameIntervalType = 3, 35162306a36Sopenharmony_ci .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P), 35262306a36Sopenharmony_ci .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P), 35362306a36Sopenharmony_ci .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P), 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic u32 uvcg_frame_mjpeg_360p_dw_frame_interval[] = { 35762306a36Sopenharmony_ci [0] = UVCG_FRM_INTERV_0_360P, 35862306a36Sopenharmony_ci [1] = UVCG_FRM_INTERV_1_360P, 35962306a36Sopenharmony_ci [2] = UVCG_FRM_INTERV_2_360P, 36062306a36Sopenharmony_ci}; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_mjpeg_360p = { 36362306a36Sopenharmony_ci .fmt_type = UVCG_MJPEG, 36462306a36Sopenharmony_ci .frame = { 36562306a36Sopenharmony_ci .b_length = UVC_DT_FRAME_MJPEG_SIZE(3), 36662306a36Sopenharmony_ci .b_descriptor_type = USB_DT_CS_INTERFACE, 36762306a36Sopenharmony_ci .b_descriptor_subtype = UVC_VS_FRAME_MJPEG, 36862306a36Sopenharmony_ci .b_frame_index = 1, 36962306a36Sopenharmony_ci .bm_capabilities = 0, 37062306a36Sopenharmony_ci .w_width = UVCG_WIDTH_360P, 37162306a36Sopenharmony_ci .w_height = UVCG_HEIGHT_360P, 37262306a36Sopenharmony_ci .dw_min_bit_rate = UVCG_MIN_BITRATE_360P, 37362306a36Sopenharmony_ci .dw_max_bit_rate = UVCG_MAX_BITRATE_360P, 37462306a36Sopenharmony_ci .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P, 37562306a36Sopenharmony_ci .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P, 37662306a36Sopenharmony_ci .b_frame_interval_type = 3, 37762306a36Sopenharmony_ci }, 37862306a36Sopenharmony_ci .dw_frame_interval = uvcg_frame_mjpeg_360p_dw_frame_interval, 37962306a36Sopenharmony_ci}; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_360p = { 38262306a36Sopenharmony_ci .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_360p, 38362306a36Sopenharmony_ci}; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { 38662306a36Sopenharmony_ci .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), 38762306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 38862306a36Sopenharmony_ci .bDescriptorSubType = UVC_VS_FRAME_MJPEG, 38962306a36Sopenharmony_ci .bFrameIndex = 2, 39062306a36Sopenharmony_ci .bmCapabilities = 0, 39162306a36Sopenharmony_ci .wWidth = cpu_to_le16(UVCG_WIDTH_720P), 39262306a36Sopenharmony_ci .wHeight = cpu_to_le16(UVCG_HEIGHT_720P), 39362306a36Sopenharmony_ci .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P), 39462306a36Sopenharmony_ci .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P), 39562306a36Sopenharmony_ci .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P), 39662306a36Sopenharmony_ci .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P), 39762306a36Sopenharmony_ci .bFrameIntervalType = 1, 39862306a36Sopenharmony_ci .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P), 39962306a36Sopenharmony_ci}; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic u32 uvcg_frame_mjpeg_720p_dw_frame_interval[] = { 40262306a36Sopenharmony_ci [0] = UVCG_FRM_INTERV_0_720P, 40362306a36Sopenharmony_ci}; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic const struct uvcg_frame uvcg_frame_mjpeg_720p = { 40662306a36Sopenharmony_ci .fmt_type = UVCG_MJPEG, 40762306a36Sopenharmony_ci .frame = { 40862306a36Sopenharmony_ci .b_length = UVC_DT_FRAME_MJPEG_SIZE(1), 40962306a36Sopenharmony_ci .b_descriptor_type = USB_DT_CS_INTERFACE, 41062306a36Sopenharmony_ci .b_descriptor_subtype = UVC_VS_FRAME_MJPEG, 41162306a36Sopenharmony_ci .b_frame_index = 2, 41262306a36Sopenharmony_ci .bm_capabilities = 0, 41362306a36Sopenharmony_ci .w_width = UVCG_WIDTH_720P, 41462306a36Sopenharmony_ci .w_height = UVCG_HEIGHT_720P, 41562306a36Sopenharmony_ci .dw_min_bit_rate = UVCG_MIN_BITRATE_720P, 41662306a36Sopenharmony_ci .dw_max_bit_rate = UVCG_MAX_BITRATE_720P, 41762306a36Sopenharmony_ci .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P, 41862306a36Sopenharmony_ci .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P, 41962306a36Sopenharmony_ci .b_frame_interval_type = 1, 42062306a36Sopenharmony_ci }, 42162306a36Sopenharmony_ci .dw_frame_interval = uvcg_frame_mjpeg_720p_dw_frame_interval, 42262306a36Sopenharmony_ci}; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_720p = { 42562306a36Sopenharmony_ci .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_720p, 42662306a36Sopenharmony_ci}; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic struct uvcg_streaming_header uvcg_streaming_header = { 42962306a36Sopenharmony_ci}; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_fs_control_cls[] = { 43262306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_control_header, 43362306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_camera_terminal, 43462306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_processing, 43562306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_output_terminal, 43662306a36Sopenharmony_ci NULL, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { 44062306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_control_header, 44162306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_camera_terminal, 44262306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_processing, 44362306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_output_terminal, 44462306a36Sopenharmony_ci NULL, 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = { 44862306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_input_header, 44962306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, 45062306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, 45162306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, 45262306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, 45362306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, 45462306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, 45562306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, 45662306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, 45762306a36Sopenharmony_ci NULL, 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { 46162306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_input_header, 46262306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, 46362306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, 46462306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, 46562306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, 46662306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, 46762306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, 46862306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, 46962306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, 47062306a36Sopenharmony_ci NULL, 47162306a36Sopenharmony_ci}; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { 47462306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_input_header, 47562306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, 47662306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, 47762306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, 47862306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, 47962306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, 48062306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, 48162306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, 48262306a36Sopenharmony_ci (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, 48362306a36Sopenharmony_ci NULL, 48462306a36Sopenharmony_ci}; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 48762306a36Sopenharmony_ci * USB configuration 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic int 49162306a36Sopenharmony_ciwebcam_config_bind(struct usb_configuration *c) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int status = 0; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci f_uvc = usb_get_function(fi_uvc); 49662306a36Sopenharmony_ci if (IS_ERR(f_uvc)) 49762306a36Sopenharmony_ci return PTR_ERR(f_uvc); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci status = usb_add_function(c, f_uvc); 50062306a36Sopenharmony_ci if (status < 0) 50162306a36Sopenharmony_ci usb_put_function(f_uvc); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return status; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic struct usb_configuration webcam_config_driver = { 50762306a36Sopenharmony_ci .label = webcam_config_label, 50862306a36Sopenharmony_ci .bConfigurationValue = 1, 50962306a36Sopenharmony_ci .iConfiguration = 0, /* dynamic */ 51062306a36Sopenharmony_ci .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 51162306a36Sopenharmony_ci .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW, 51262306a36Sopenharmony_ci}; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int 51562306a36Sopenharmony_ciwebcam_unbind(struct usb_composite_dev *cdev) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(f_uvc)) 51862306a36Sopenharmony_ci usb_put_function(f_uvc); 51962306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(fi_uvc)) 52062306a36Sopenharmony_ci usb_put_function_instance(fi_uvc); 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int 52562306a36Sopenharmony_ciwebcam_bind(struct usb_composite_dev *cdev) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct f_uvc_opts *uvc_opts; 52862306a36Sopenharmony_ci int ret; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci fi_uvc = usb_get_function_instance("uvc"); 53162306a36Sopenharmony_ci if (IS_ERR(fi_uvc)) 53262306a36Sopenharmony_ci return PTR_ERR(fi_uvc); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci uvc_opts->streaming_interval = streaming_interval; 53762306a36Sopenharmony_ci uvc_opts->streaming_maxpacket = streaming_maxpacket; 53862306a36Sopenharmony_ci uvc_opts->streaming_maxburst = streaming_maxburst; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci uvc_opts->fs_control = uvc_fs_control_cls; 54162306a36Sopenharmony_ci uvc_opts->ss_control = uvc_ss_control_cls; 54262306a36Sopenharmony_ci uvc_opts->fs_streaming = uvc_fs_streaming_cls; 54362306a36Sopenharmony_ci uvc_opts->hs_streaming = uvc_hs_streaming_cls; 54462306a36Sopenharmony_ci uvc_opts->ss_streaming = uvc_ss_streaming_cls; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci INIT_LIST_HEAD(&uvcg_format_yuv.fmt.frames); 54762306a36Sopenharmony_ci list_add_tail(&uvcg_frame_ptr_yuv_360p.entry, &uvcg_format_yuv.fmt.frames); 54862306a36Sopenharmony_ci list_add_tail(&uvcg_frame_ptr_yuv_720p.entry, &uvcg_format_yuv.fmt.frames); 54962306a36Sopenharmony_ci uvcg_format_yuv.fmt.num_frames = 2; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci INIT_LIST_HEAD(&uvcg_format_mjpeg.fmt.frames); 55262306a36Sopenharmony_ci list_add_tail(&uvcg_frame_ptr_mjpeg_360p.entry, &uvcg_format_mjpeg.fmt.frames); 55362306a36Sopenharmony_ci list_add_tail(&uvcg_frame_ptr_mjpeg_720p.entry, &uvcg_format_mjpeg.fmt.frames); 55462306a36Sopenharmony_ci uvcg_format_mjpeg.fmt.num_frames = 2; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci INIT_LIST_HEAD(&uvcg_streaming_header.formats); 55762306a36Sopenharmony_ci list_add_tail(&uvcg_format_ptr_yuv.entry, &uvcg_streaming_header.formats); 55862306a36Sopenharmony_ci list_add_tail(&uvcg_format_ptr_mjpeg.entry, &uvcg_streaming_header.formats); 55962306a36Sopenharmony_ci uvcg_streaming_header.num_fmt = 2; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci uvc_opts->header = &uvcg_streaming_header; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* Allocate string descriptor numbers ... note that string contents 56462306a36Sopenharmony_ci * can be overridden by the composite_dev glue. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci ret = usb_string_ids_tab(cdev, webcam_strings); 56762306a36Sopenharmony_ci if (ret < 0) 56862306a36Sopenharmony_ci goto error; 56962306a36Sopenharmony_ci webcam_device_descriptor.iManufacturer = 57062306a36Sopenharmony_ci webcam_strings[USB_GADGET_MANUFACTURER_IDX].id; 57162306a36Sopenharmony_ci webcam_device_descriptor.iProduct = 57262306a36Sopenharmony_ci webcam_strings[USB_GADGET_PRODUCT_IDX].id; 57362306a36Sopenharmony_ci webcam_config_driver.iConfiguration = 57462306a36Sopenharmony_ci webcam_strings[STRING_DESCRIPTION_IDX].id; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Register our configuration. */ 57762306a36Sopenharmony_ci if ((ret = usb_add_config(cdev, &webcam_config_driver, 57862306a36Sopenharmony_ci webcam_config_bind)) < 0) 57962306a36Sopenharmony_ci goto error; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci usb_composite_overwrite_options(cdev, &coverwrite); 58262306a36Sopenharmony_ci INFO(cdev, "Webcam Video Gadget\n"); 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cierror: 58662306a36Sopenharmony_ci usb_put_function_instance(fi_uvc); 58762306a36Sopenharmony_ci return ret; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 59162306a36Sopenharmony_ci * Driver 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic struct usb_composite_driver webcam_driver = { 59562306a36Sopenharmony_ci .name = "g_webcam", 59662306a36Sopenharmony_ci .dev = &webcam_device_descriptor, 59762306a36Sopenharmony_ci .strings = webcam_device_strings, 59862306a36Sopenharmony_ci .max_speed = USB_SPEED_SUPER, 59962306a36Sopenharmony_ci .bind = webcam_bind, 60062306a36Sopenharmony_ci .unbind = webcam_unbind, 60162306a36Sopenharmony_ci}; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cimodule_usb_composite_driver(webcam_driver); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ciMODULE_AUTHOR("Laurent Pinchart"); 60662306a36Sopenharmony_ciMODULE_DESCRIPTION("Webcam Video Gadget"); 60762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 60862306a36Sopenharmony_ci 609