2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2010-05-02 20:57:41 +02:00
/*
* uvc_gadget . c - - USB Video Class Gadget driver
*
* Copyright ( C ) 2009 - 2010
* Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*/
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/fs.h>
2018-05-21 11:28:52 +03:00
# include <linux/kernel.h>
2010-05-02 20:57:41 +02:00
# include <linux/list.h>
2018-05-21 11:28:52 +03:00
# include <linux/module.h>
2010-05-02 20:57:41 +02:00
# include <linux/mutex.h>
2013-02-02 15:48:54 +08:00
# include <linux/string.h>
2010-05-02 20:57:41 +02:00
# include <linux/usb/ch9.h>
# include <linux/usb/gadget.h>
2018-05-21 11:28:52 +03:00
# include <linux/usb/g_uvc.h>
2010-05-02 20:57:41 +02:00
# include <linux/usb/video.h>
# include <linux/vmalloc.h>
# include <linux/wait.h>
# include <media/v4l2-dev.h>
# include <media/v4l2-event.h>
2014-12-10 12:34:02 +01:00
# include "u_uvc.h"
2010-05-02 20:57:41 +02:00
# include "uvc.h"
2014-12-10 12:34:02 +01:00
# include "uvc_configfs.h"
2014-09-09 02:02:09 +03:00
# include "uvc_v4l2.h"
# include "uvc_video.h"
2010-05-02 20:57:41 +02:00
2010-07-10 16:13:05 -03:00
unsigned int uvc_gadget_trace_param ;
2018-05-21 11:28:53 +03:00
module_param_named ( trace , uvc_gadget_trace_param , uint , 0644 ) ;
MODULE_PARM_DESC ( trace , " Trace level bitmask " ) ;
2012-06-01 15:08:56 +05:30
2010-05-02 20:57:41 +02:00
/* --------------------------------------------------------------------------
* Function descriptors
*/
/* string IDs are assigned dynamically */
2013-03-01 20:46:29 +01:00
# define UVC_STRING_CONTROL_IDX 0
# define UVC_STRING_STREAMING_IDX 1
2010-05-02 20:57:41 +02:00
static struct usb_string uvc_en_us_strings [ ] = {
2013-03-01 20:46:29 +01:00
[ UVC_STRING_CONTROL_IDX ] . s = " UVC Camera " ,
2010-05-02 20:57:41 +02:00
[ UVC_STRING_STREAMING_IDX ] . s = " Video Streaming " ,
{ }
} ;
static struct usb_gadget_strings uvc_stringtab = {
. language = 0x0409 , /* en-us */
. strings = uvc_en_us_strings ,
} ;
static struct usb_gadget_strings * uvc_function_strings [ ] = {
& uvc_stringtab ,
NULL ,
} ;
# define UVC_INTF_VIDEO_CONTROL 0
# define UVC_INTF_VIDEO_STREAMING 1
2013-03-01 20:46:23 +01:00
# define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */
2012-06-01 15:08:55 +05:30
2014-09-09 02:02:10 +03:00
static struct usb_interface_assoc_descriptor uvc_iad = {
2010-07-10 15:03:20 -03:00
. bLength = sizeof ( uvc_iad ) ,
2010-05-02 20:57:41 +02:00
. bDescriptorType = USB_DT_INTERFACE_ASSOCIATION ,
. bFirstInterface = 0 ,
. bInterfaceCount = 2 ,
. bFunctionClass = USB_CLASS_VIDEO ,
2010-07-10 15:03:20 -03:00
. bFunctionSubClass = UVC_SC_VIDEO_INTERFACE_COLLECTION ,
2010-05-02 20:57:41 +02:00
. bFunctionProtocol = 0x00 ,
. iFunction = 0 ,
} ;
2014-09-09 02:02:10 +03:00
static struct usb_interface_descriptor uvc_control_intf = {
2010-05-02 20:57:41 +02:00
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = UVC_INTF_VIDEO_CONTROL ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_VIDEO ,
2010-07-10 15:03:20 -03:00
. bInterfaceSubClass = UVC_SC_VIDEOCONTROL ,
2010-05-02 20:57:41 +02:00
. bInterfaceProtocol = 0x00 ,
. iInterface = 0 ,
} ;
2014-09-09 02:02:10 +03:00
static struct usb_endpoint_descriptor uvc_control_ep = {
2010-05-02 20:57:41 +02:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_INT ,
2013-03-01 20:46:23 +01:00
. wMaxPacketSize = cpu_to_le16 ( UVC_STATUS_MAX_PACKET_SIZE ) ,
2010-05-02 20:57:41 +02:00
. bInterval = 8 ,
} ;
2014-09-09 02:02:10 +03:00
static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = {
2013-03-01 20:46:25 +01:00
. bLength = sizeof ( uvc_ss_control_comp ) ,
. bDescriptorType = USB_DT_SS_ENDPOINT_COMP ,
/* The following 3 values can be tweaked if necessary. */
. bMaxBurst = 0 ,
. bmAttributes = 0 ,
. wBytesPerInterval = cpu_to_le16 ( UVC_STATUS_MAX_PACKET_SIZE ) ,
} ;
2014-09-09 02:02:10 +03:00
static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = {
2010-05-02 20:57:41 +02:00
. bLength = UVC_DT_CONTROL_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_CS_ENDPOINT ,
. bDescriptorSubType = UVC_EP_INTERRUPT ,
2013-03-01 20:46:23 +01:00
. wMaxTransferSize = cpu_to_le16 ( UVC_STATUS_MAX_PACKET_SIZE ) ,
2010-05-02 20:57:41 +02:00
} ;
2014-09-09 02:02:10 +03:00
static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
2010-05-02 20:57:41 +02:00
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = UVC_INTF_VIDEO_STREAMING ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 0 ,
. bInterfaceClass = USB_CLASS_VIDEO ,
2010-07-10 15:03:20 -03:00
. bInterfaceSubClass = UVC_SC_VIDEOSTREAMING ,
2010-05-02 20:57:41 +02:00
. bInterfaceProtocol = 0x00 ,
. iInterface = 0 ,
} ;
2014-09-09 02:02:10 +03:00
static struct usb_interface_descriptor uvc_streaming_intf_alt1 = {
2010-05-02 20:57:41 +02:00
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = UVC_INTF_VIDEO_STREAMING ,
. bAlternateSetting = 1 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_VIDEO ,
2010-07-10 15:03:20 -03:00
. bInterfaceSubClass = UVC_SC_VIDEOSTREAMING ,
2010-05-02 20:57:41 +02:00
. bInterfaceProtocol = 0x00 ,
. iInterface = 0 ,
} ;
2014-09-09 02:02:10 +03:00
static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
2010-05-02 20:57:41 +02:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
2013-03-01 20:46:28 +01:00
. bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC ,
2013-03-01 20:46:26 +01:00
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters .
*/
2010-05-02 20:57:41 +02:00
} ;
2014-09-09 02:02:10 +03:00
static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
2012-06-01 15:08:56 +05:30
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
2013-03-01 20:46:28 +01:00
. bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC ,
2013-03-01 20:46:26 +01:00
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters .
*/
2012-06-01 15:08:56 +05:30
} ;
2014-09-09 02:02:10 +03:00
static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
2013-03-01 20:46:24 +01:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
2012-06-01 15:08:56 +05:30
2013-03-01 20:46:24 +01:00
. bEndpointAddress = USB_DIR_IN ,
2013-03-01 20:46:28 +01:00
. bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC ,
2013-03-01 20:46:26 +01:00
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters .
*/
2012-06-01 15:08:56 +05:30
} ;
2014-09-09 02:02:10 +03:00
static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
2013-03-01 20:46:24 +01:00
. bLength = sizeof ( uvc_ss_streaming_comp ) ,
. bDescriptorType = USB_DT_SS_ENDPOINT_COMP ,
2013-04-29 12:16:30 +02:00
/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
* initialized from module parameters .
*/
2012-06-01 15:08:56 +05:30
} ;
2010-05-02 20:57:41 +02:00
static const struct usb_descriptor_header * const uvc_fs_streaming [ ] = {
( struct usb_descriptor_header * ) & uvc_streaming_intf_alt1 ,
2012-06-01 15:08:56 +05:30
( struct usb_descriptor_header * ) & uvc_fs_streaming_ep ,
2010-05-02 20:57:41 +02:00
NULL ,
} ;
static const struct usb_descriptor_header * const uvc_hs_streaming [ ] = {
( struct usb_descriptor_header * ) & uvc_streaming_intf_alt1 ,
2012-06-01 15:08:56 +05:30
( struct usb_descriptor_header * ) & uvc_hs_streaming_ep ,
NULL ,
} ;
static const struct usb_descriptor_header * const uvc_ss_streaming [ ] = {
( struct usb_descriptor_header * ) & uvc_streaming_intf_alt1 ,
( struct usb_descriptor_header * ) & uvc_ss_streaming_ep ,
( struct usb_descriptor_header * ) & uvc_ss_streaming_comp ,
2010-05-02 20:57:41 +02:00
NULL ,
} ;
/* --------------------------------------------------------------------------
* Control requests
*/
static void
uvc_function_ep0_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct uvc_device * uvc = req - > context ;
struct v4l2_event v4l2_event ;
struct uvc_event * uvc_event = ( void * ) & v4l2_event . u . data ;
if ( uvc - > event_setup_out ) {
uvc - > event_setup_out = 0 ;
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_DATA ;
uvc_event - > data . length = req - > actual ;
memcpy ( & uvc_event - > data . data , req - > buf , req - > actual ) ;
2015-03-09 13:34:08 -03:00
v4l2_event_queue ( & uvc - > vdev , & v4l2_event ) ;
2010-05-02 20:57:41 +02:00
}
}
static int
uvc_function_setup ( struct usb_function * f , const struct usb_ctrlrequest * ctrl )
{
struct uvc_device * uvc = to_uvc ( f ) ;
struct v4l2_event v4l2_event ;
struct uvc_event * uvc_event = ( void * ) & v4l2_event . u . data ;
if ( ( ctrl - > bRequestType & USB_TYPE_MASK ) ! = USB_TYPE_CLASS ) {
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " invalid request type \n " ) ;
2010-05-02 20:57:41 +02:00
return - EINVAL ;
}
/* Stall too big requests. */
if ( le16_to_cpu ( ctrl - > wLength ) > UVC_MAX_REQUEST_SIZE )
return - EINVAL ;
2014-09-08 11:18:14 +03:00
/* Tell the complete callback to generate an event for the next request
* that will be enqueued by UVCIOC_SEND_RESPONSE .
*/
uvc - > event_setup_out = ! ( ctrl - > bRequestType & USB_DIR_IN ) ;
uvc - > event_length = le16_to_cpu ( ctrl - > wLength ) ;
2010-05-02 20:57:41 +02:00
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_SETUP ;
memcpy ( & uvc_event - > req , ctrl , sizeof ( uvc_event - > req ) ) ;
2015-03-09 13:34:08 -03:00
v4l2_event_queue ( & uvc - > vdev , & v4l2_event ) ;
2010-05-02 20:57:41 +02:00
return 0 ;
}
2013-03-01 20:46:30 +01:00
void uvc_function_setup_continue ( struct uvc_device * uvc )
{
struct usb_composite_dev * cdev = uvc - > func . config - > cdev ;
usb_composite_setup_continue ( cdev ) ;
}
2010-05-02 20:57:41 +02:00
static int
uvc_function_get_alt ( struct usb_function * f , unsigned interface )
{
struct uvc_device * uvc = to_uvc ( f ) ;
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " %s(%u) \n " , __func__ , interface ) ;
2010-05-02 20:57:41 +02:00
if ( interface = = uvc - > control_intf )
return 0 ;
else if ( interface ! = uvc - > streaming_intf )
return - EINVAL ;
else
2015-09-16 12:11:00 +02:00
return uvc - > video . ep - > enabled ? 1 : 0 ;
2010-05-02 20:57:41 +02:00
}
static int
uvc_function_set_alt ( struct usb_function * f , unsigned interface , unsigned alt )
{
struct uvc_device * uvc = to_uvc ( f ) ;
2014-09-29 09:20:35 -05:00
struct usb_composite_dev * cdev = f - > config - > cdev ;
2010-05-02 20:57:41 +02:00
struct v4l2_event v4l2_event ;
struct uvc_event * uvc_event = ( void * ) & v4l2_event . u . data ;
2012-06-01 15:08:56 +05:30
int ret ;
2010-05-02 20:57:41 +02:00
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " %s(%u, %u) \n " , __func__ , interface , alt ) ;
2010-05-02 20:57:41 +02:00
if ( interface = = uvc - > control_intf ) {
if ( alt )
return - EINVAL ;
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " reset UVC Control \n " ) ;
2015-09-16 12:11:00 +02:00
usb_ep_disable ( uvc - > control_ep ) ;
2014-09-29 13:41:04 -05:00
if ( ! uvc - > control_ep - > desc )
if ( config_ep_by_speed ( cdev - > gadget , f , uvc - > control_ep ) )
return - EINVAL ;
usb_ep_enable ( uvc - > control_ep ) ;
2010-05-02 20:57:41 +02:00
if ( uvc - > state = = UVC_STATE_DISCONNECTED ) {
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_CONNECT ;
2014-09-29 09:20:35 -05:00
uvc_event - > speed = cdev - > gadget - > speed ;
2015-03-09 13:34:08 -03:00
v4l2_event_queue ( & uvc - > vdev , & v4l2_event ) ;
2010-05-02 20:57:41 +02:00
uvc - > state = UVC_STATE_CONNECTED ;
}
return 0 ;
}
if ( interface ! = uvc - > streaming_intf )
return - EINVAL ;
/* TODO
if ( usb_endpoint_xfer_bulk ( & uvc - > desc . vs_ep ) )
return alt ? - EINVAL : 0 ;
*/
switch ( alt ) {
case 0 :
if ( uvc - > state ! = UVC_STATE_STREAMING )
return 0 ;
2015-09-16 12:11:00 +02:00
if ( uvc - > video . ep )
2010-05-02 20:57:41 +02:00
usb_ep_disable ( uvc - > video . ep ) ;
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_STREAMOFF ;
2015-03-09 13:34:08 -03:00
v4l2_event_queue ( & uvc - > vdev , & v4l2_event ) ;
2010-05-02 20:57:41 +02:00
uvc - > state = UVC_STATE_CONNECTED ;
2013-03-01 20:46:30 +01:00
return 0 ;
2010-05-02 20:57:41 +02:00
case 1 :
if ( uvc - > state ! = UVC_STATE_CONNECTED )
return 0 ;
2014-09-29 09:20:35 -05:00
if ( ! uvc - > video . ep )
return - EINVAL ;
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " reset UVC \n " ) ;
2015-09-16 12:11:00 +02:00
usb_ep_disable ( uvc - > video . ep ) ;
2010-05-02 20:57:41 +02:00
2014-09-29 09:20:35 -05:00
ret = config_ep_by_speed ( f - > config - > cdev - > gadget ,
& ( uvc - > func ) , uvc - > video . ep ) ;
if ( ret )
return ret ;
usb_ep_enable ( uvc - > video . ep ) ;
2010-05-02 20:57:41 +02:00
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_STREAMON ;
2015-03-09 13:34:08 -03:00
v4l2_event_queue ( & uvc - > vdev , & v4l2_event ) ;
2013-03-01 20:46:30 +01:00
return USB_GADGET_DELAYED_STATUS ;
2010-05-02 20:57:41 +02:00
default :
return - EINVAL ;
}
}
static void
uvc_function_disable ( struct usb_function * f )
{
struct uvc_device * uvc = to_uvc ( f ) ;
struct v4l2_event v4l2_event ;
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " %s() \n " , __func__ ) ;
2010-05-02 20:57:41 +02:00
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_DISCONNECT ;
2015-03-09 13:34:08 -03:00
v4l2_event_queue ( & uvc - > vdev , & v4l2_event ) ;
2010-05-02 20:57:41 +02:00
uvc - > state = UVC_STATE_DISCONNECTED ;
2014-09-29 13:43:20 -05:00
2015-09-16 12:11:00 +02:00
usb_ep_disable ( uvc - > video . ep ) ;
usb_ep_disable ( uvc - > control_ep ) ;
2010-05-02 20:57:41 +02:00
}
/* --------------------------------------------------------------------------
* Connection / disconnection
*/
void
uvc_function_connect ( struct uvc_device * uvc )
{
int ret ;
if ( ( ret = usb_function_activate ( & uvc - > func ) ) < 0 )
2018-08-10 15:48:02 +03:00
uvcg_info ( & uvc - > func , " UVC connect failed with %d \n " , ret ) ;
2010-05-02 20:57:41 +02:00
}
void
uvc_function_disconnect ( struct uvc_device * uvc )
{
int ret ;
if ( ( ret = usb_function_deactivate ( & uvc - > func ) ) < 0 )
2018-08-10 15:48:02 +03:00
uvcg_info ( & uvc - > func , " UVC disconnect failed with %d \n " , ret ) ;
2010-05-02 20:57:41 +02:00
}
/* --------------------------------------------------------------------------
* USB probe and disconnect
*/
2018-05-24 17:16:12 +01:00
static ssize_t function_name_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct uvc_device * uvc = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %s \n " , uvc - > func . fi - > group . cg_item . ci_name ) ;
}
static DEVICE_ATTR_RO ( function_name ) ;
2010-05-02 20:57:41 +02:00
static int
uvc_register_video ( struct uvc_device * uvc )
{
struct usb_composite_dev * cdev = uvc - > func . config - > cdev ;
2018-05-24 17:16:12 +01:00
int ret ;
2010-05-02 20:57:41 +02:00
/* TODO reference counting. */
2015-03-09 13:34:08 -03:00
uvc - > vdev . v4l2_dev = & uvc - > v4l2_dev ;
uvc - > vdev . fops = & uvc_v4l2_fops ;
uvc - > vdev . ioctl_ops = & uvc_v4l2_ioctl_ops ;
uvc - > vdev . release = video_device_release_empty ;
uvc - > vdev . vfl_dir = VFL_DIR_TX ;
uvc - > vdev . lock = & uvc - > video . mutex ;
2019-06-04 07:19:55 -04:00
uvc - > vdev . device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING ;
2015-03-09 13:34:08 -03:00
strlcpy ( uvc - > vdev . name , cdev - > gadget - > name , sizeof ( uvc - > vdev . name ) ) ;
video_set_drvdata ( & uvc - > vdev , uvc ) ;
2020-02-03 12:41:10 +01:00
ret = video_register_device ( & uvc - > vdev , VFL_TYPE_VIDEO , - 1 ) ;
2018-05-24 17:16:12 +01:00
if ( ret < 0 )
return ret ;
ret = device_create_file ( & uvc - > vdev . dev , & dev_attr_function_name ) ;
if ( ret < 0 ) {
video_unregister_device ( & uvc - > vdev ) ;
return ret ;
}
return 0 ;
2010-05-02 20:57:41 +02:00
}
# define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
do { \
memcpy ( mem , desc , ( desc ) - > bLength ) ; \
* ( dst ) + + = mem ; \
mem + = ( desc ) - > bLength ; \
} while ( 0 ) ;
# define UVC_COPY_DESCRIPTORS(mem, dst, src) \
do { \
const struct usb_descriptor_header * const * __src ; \
for ( __src = src ; * __src ; + + __src ) { \
memcpy ( mem , * __src , ( * __src ) - > bLength ) ; \
* dst + + = mem ; \
mem + = ( * __src ) - > bLength ; \
} \
} while ( 0 )
2014-09-09 02:02:10 +03:00
static struct usb_descriptor_header * *
2010-05-02 20:57:41 +02:00
uvc_copy_descriptors ( struct uvc_device * uvc , enum usb_device_speed speed )
{
struct uvc_input_header_descriptor * uvc_streaming_header ;
struct uvc_header_descriptor * uvc_control_header ;
2012-06-01 15:08:56 +05:30
const struct uvc_descriptor_header * const * uvc_control_desc ;
2010-05-02 20:57:41 +02:00
const struct uvc_descriptor_header * const * uvc_streaming_cls ;
const struct usb_descriptor_header * const * uvc_streaming_std ;
const struct usb_descriptor_header * const * src ;
struct usb_descriptor_header * * dst ;
struct usb_descriptor_header * * hdr ;
unsigned int control_size ;
unsigned int streaming_size ;
unsigned int n_desc ;
unsigned int bytes ;
void * mem ;
2012-06-01 15:08:56 +05:30
switch ( speed ) {
case USB_SPEED_SUPER :
uvc_control_desc = uvc - > desc . ss_control ;
uvc_streaming_cls = uvc - > desc . ss_streaming ;
uvc_streaming_std = uvc_ss_streaming ;
break ;
case USB_SPEED_HIGH :
uvc_control_desc = uvc - > desc . fs_control ;
uvc_streaming_cls = uvc - > desc . hs_streaming ;
uvc_streaming_std = uvc_hs_streaming ;
break ;
case USB_SPEED_FULL :
default :
uvc_control_desc = uvc - > desc . fs_control ;
uvc_streaming_cls = uvc - > desc . fs_streaming ;
uvc_streaming_std = uvc_fs_streaming ;
break ;
}
2010-05-02 20:57:41 +02:00
2014-12-10 12:34:01 +01:00
if ( ! uvc_control_desc | | ! uvc_streaming_cls )
return ERR_PTR ( - ENODEV ) ;
2010-05-02 20:57:41 +02:00
/* Descriptors layout
*
* uvc_iad
* uvc_control_intf
* Class - specific UVC control descriptors
* uvc_control_ep
* uvc_control_cs_ep
2013-03-01 20:46:25 +01:00
* uvc_ss_control_comp ( for SS only )
2010-05-02 20:57:41 +02:00
* uvc_streaming_intf_alt0
* Class - specific UVC streaming descriptors
* uvc_ { fs | hs } _streaming
*/
/* Count descriptors and compute their size. */
control_size = 0 ;
streaming_size = 0 ;
bytes = uvc_iad . bLength + uvc_control_intf . bLength
2013-03-01 20:46:25 +01:00
+ uvc_control_ep . bLength + uvc_control_cs_ep . bLength
2010-05-02 20:57:41 +02:00
+ uvc_streaming_intf_alt0 . bLength ;
2012-06-01 15:08:56 +05:30
if ( speed = = USB_SPEED_SUPER ) {
bytes + = uvc_ss_control_comp . bLength ;
n_desc = 6 ;
} else {
n_desc = 5 ;
}
for ( src = ( const struct usb_descriptor_header * * ) uvc_control_desc ;
2013-03-01 20:46:24 +01:00
* src ; + + src ) {
2010-05-02 20:57:41 +02:00
control_size + = ( * src ) - > bLength ;
bytes + = ( * src ) - > bLength ;
n_desc + + ;
}
2012-06-01 15:08:56 +05:30
for ( src = ( const struct usb_descriptor_header * * ) uvc_streaming_cls ;
2013-03-01 20:46:24 +01:00
* src ; + + src ) {
2010-05-02 20:57:41 +02:00
streaming_size + = ( * src ) - > bLength ;
bytes + = ( * src ) - > bLength ;
n_desc + + ;
}
for ( src = uvc_streaming_std ; * src ; + + src ) {
bytes + = ( * src ) - > bLength ;
n_desc + + ;
}
mem = kmalloc ( ( n_desc + 1 ) * sizeof ( * src ) + bytes , GFP_KERNEL ) ;
if ( mem = = NULL )
return NULL ;
hdr = mem ;
dst = mem ;
mem + = ( n_desc + 1 ) * sizeof ( * src ) ;
/* Copy the descriptors. */
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_iad ) ;
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_control_intf ) ;
uvc_control_header = mem ;
UVC_COPY_DESCRIPTORS ( mem , dst ,
2012-06-01 15:08:56 +05:30
( const struct usb_descriptor_header * * ) uvc_control_desc ) ;
2010-05-02 20:57:41 +02:00
uvc_control_header - > wTotalLength = cpu_to_le16 ( control_size ) ;
uvc_control_header - > bInCollection = 1 ;
uvc_control_header - > baInterfaceNr [ 0 ] = uvc - > streaming_intf ;
2013-03-01 20:46:25 +01:00
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_control_ep ) ;
2012-06-01 15:08:56 +05:30
if ( speed = = USB_SPEED_SUPER )
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_ss_control_comp ) ;
2010-05-02 20:57:41 +02:00
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_control_cs_ep ) ;
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_streaming_intf_alt0 ) ;
uvc_streaming_header = mem ;
UVC_COPY_DESCRIPTORS ( mem , dst ,
( const struct usb_descriptor_header * * ) uvc_streaming_cls ) ;
uvc_streaming_header - > wTotalLength = cpu_to_le16 ( streaming_size ) ;
2013-03-01 20:46:27 +01:00
uvc_streaming_header - > bEndpointAddress = uvc - > video . ep - > address ;
2010-05-02 20:57:41 +02:00
UVC_COPY_DESCRIPTORS ( mem , dst , uvc_streaming_std ) ;
* dst = NULL ;
return hdr ;
}
2014-09-09 02:02:10 +03:00
static int
2010-05-02 20:57:41 +02:00
uvc_function_bind ( struct usb_configuration * c , struct usb_function * f )
{
struct usb_composite_dev * cdev = c - > cdev ;
struct uvc_device * uvc = to_uvc ( f ) ;
2014-09-09 02:02:13 +03:00
struct usb_string * us ;
2013-03-01 20:46:26 +01:00
unsigned int max_packet_mult ;
unsigned int max_packet_size ;
2010-05-02 20:57:41 +02:00
struct usb_ep * ep ;
2014-09-09 02:02:10 +03:00
struct f_uvc_opts * opts ;
2010-05-02 20:57:41 +02:00
int ret = - EINVAL ;
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " %s() \n " , __func__ ) ;
2010-05-02 20:57:41 +02:00
2014-12-10 12:34:00 +01:00
opts = fi_to_f_uvc_opts ( f - > fi ) ;
2014-09-09 02:02:10 +03:00
/* Sanity check the streaming endpoint module parameters.
*/
opts - > streaming_interval = clamp ( opts - > streaming_interval , 1U , 16U ) ;
opts - > streaming_maxpacket = clamp ( opts - > streaming_maxpacket , 1U , 3072U ) ;
opts - > streaming_maxburst = min ( opts - > streaming_maxburst , 15U ) ;
2017-03-08 16:05:44 +02:00
/* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */
if ( opts - > streaming_maxburst & &
( opts - > streaming_maxpacket % 1024 ) ! = 0 ) {
opts - > streaming_maxpacket = roundup ( opts - > streaming_maxpacket , 1024 ) ;
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " overriding streaming_maxpacket to %d \n " ,
opts - > streaming_maxpacket ) ;
2017-03-08 16:05:44 +02:00
}
2014-09-09 02:02:10 +03:00
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
* module parameters .
*
* NOTE : We assume that the user knows what they are doing and won ' t
* give parameters that their UDC doesn ' t support .
*/
if ( opts - > streaming_maxpacket < = 1024 ) {
max_packet_mult = 1 ;
max_packet_size = opts - > streaming_maxpacket ;
} else if ( opts - > streaming_maxpacket < = 2048 ) {
max_packet_mult = 2 ;
max_packet_size = opts - > streaming_maxpacket / 2 ;
} else {
max_packet_mult = 3 ;
max_packet_size = opts - > streaming_maxpacket / 3 ;
}
uvc_fs_streaming_ep . wMaxPacketSize =
2014-09-16 17:26:47 +03:00
cpu_to_le16 ( min ( opts - > streaming_maxpacket , 1023U ) ) ;
2014-09-09 02:02:10 +03:00
uvc_fs_streaming_ep . bInterval = opts - > streaming_interval ;
2014-09-16 17:26:47 +03:00
uvc_hs_streaming_ep . wMaxPacketSize =
cpu_to_le16 ( max_packet_size | ( ( max_packet_mult - 1 ) < < 11 ) ) ;
2021-03-08 13:53:38 +01:00
/* A high-bandwidth endpoint must specify a bInterval value of 1 */
if ( max_packet_mult > 1 )
uvc_hs_streaming_ep . bInterval = 1 ;
else
uvc_hs_streaming_ep . bInterval = opts - > streaming_interval ;
2014-09-09 02:02:10 +03:00
2014-09-16 17:26:47 +03:00
uvc_ss_streaming_ep . wMaxPacketSize = cpu_to_le16 ( max_packet_size ) ;
2014-09-09 02:02:10 +03:00
uvc_ss_streaming_ep . bInterval = opts - > streaming_interval ;
uvc_ss_streaming_comp . bmAttributes = max_packet_mult - 1 ;
uvc_ss_streaming_comp . bMaxBurst = opts - > streaming_maxburst ;
uvc_ss_streaming_comp . wBytesPerInterval =
2014-09-16 17:26:47 +03:00
cpu_to_le16 ( max_packet_size * max_packet_mult *
2017-03-08 16:05:43 +02:00
( opts - > streaming_maxburst + 1 ) ) ;
2013-03-01 20:46:26 +01:00
2010-05-02 20:57:41 +02:00
/* Allocate endpoints. */
2013-03-01 20:46:25 +01:00
ep = usb_ep_autoconfig ( cdev - > gadget , & uvc_control_ep ) ;
2010-05-02 20:57:41 +02:00
if ( ! ep ) {
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " Unable to allocate control EP \n " ) ;
2010-05-02 20:57:41 +02:00
goto error ;
}
uvc - > control_ep = ep ;
2013-03-01 20:46:27 +01:00
if ( gadget_is_superspeed ( c - > cdev - > gadget ) )
ep = usb_ep_autoconfig_ss ( cdev - > gadget , & uvc_ss_streaming_ep ,
& uvc_ss_streaming_comp ) ;
else if ( gadget_is_dualspeed ( cdev - > gadget ) )
ep = usb_ep_autoconfig ( cdev - > gadget , & uvc_hs_streaming_ep ) ;
else
ep = usb_ep_autoconfig ( cdev - > gadget , & uvc_fs_streaming_ep ) ;
2010-05-02 20:57:41 +02:00
if ( ! ep ) {
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " Unable to allocate streaming EP \n " ) ;
2010-05-02 20:57:41 +02:00
goto error ;
}
uvc - > video . ep = ep ;
2013-03-01 20:46:27 +01:00
uvc_fs_streaming_ep . bEndpointAddress = uvc - > video . ep - > address ;
uvc_hs_streaming_ep . bEndpointAddress = uvc - > video . ep - > address ;
uvc_ss_streaming_ep . bEndpointAddress = uvc - > video . ep - > address ;
2013-03-01 20:46:26 +01:00
2014-09-09 02:02:13 +03:00
us = usb_gstrings_attach ( cdev , uvc_function_strings ,
ARRAY_SIZE ( uvc_en_us_strings ) ) ;
if ( IS_ERR ( us ) ) {
ret = PTR_ERR ( us ) ;
goto error ;
2014-09-09 02:02:10 +03:00
}
2014-09-09 02:02:13 +03:00
uvc_iad . iFunction = us [ UVC_STRING_CONTROL_IDX ] . id ;
uvc_control_intf . iInterface = us [ UVC_STRING_CONTROL_IDX ] . id ;
ret = us [ UVC_STRING_STREAMING_IDX ] . id ;
uvc_streaming_intf_alt0 . iInterface = ret ;
uvc_streaming_intf_alt1 . iInterface = ret ;
2014-09-09 02:02:10 +03:00
2010-05-02 20:57:41 +02:00
/* Allocate interface IDs. */
if ( ( ret = usb_interface_id ( c , f ) ) < 0 )
goto error ;
uvc_iad . bFirstInterface = ret ;
uvc_control_intf . bInterfaceNumber = ret ;
uvc - > control_intf = ret ;
2018-05-23 18:47:56 +03:00
opts - > control_interface = ret ;
2010-05-02 20:57:41 +02:00
if ( ( ret = usb_interface_id ( c , f ) ) < 0 )
goto error ;
uvc_streaming_intf_alt0 . bInterfaceNumber = ret ;
uvc_streaming_intf_alt1 . bInterfaceNumber = ret ;
uvc - > streaming_intf = ret ;
2018-05-23 18:47:56 +03:00
opts - > streaming_interface = ret ;
2010-05-02 20:57:41 +02:00
2012-10-22 22:15:06 +02:00
/* Copy descriptors */
f - > fs_descriptors = uvc_copy_descriptors ( uvc , USB_SPEED_FULL ) ;
2014-12-10 12:34:01 +01:00
if ( IS_ERR ( f - > fs_descriptors ) ) {
ret = PTR_ERR ( f - > fs_descriptors ) ;
f - > fs_descriptors = NULL ;
goto error ;
}
if ( gadget_is_dualspeed ( cdev - > gadget ) ) {
2012-10-22 22:15:06 +02:00
f - > hs_descriptors = uvc_copy_descriptors ( uvc , USB_SPEED_HIGH ) ;
2014-12-10 12:34:01 +01:00
if ( IS_ERR ( f - > hs_descriptors ) ) {
ret = PTR_ERR ( f - > hs_descriptors ) ;
f - > hs_descriptors = NULL ;
goto error ;
}
}
if ( gadget_is_superspeed ( c - > cdev - > gadget ) ) {
2012-06-01 15:08:56 +05:30
f - > ss_descriptors = uvc_copy_descriptors ( uvc , USB_SPEED_SUPER ) ;
2014-12-10 12:34:01 +01:00
if ( IS_ERR ( f - > ss_descriptors ) ) {
ret = PTR_ERR ( f - > ss_descriptors ) ;
f - > ss_descriptors = NULL ;
goto error ;
}
}
2010-05-02 20:57:41 +02:00
/* Preallocate control endpoint request. */
uvc - > control_req = usb_ep_alloc_request ( cdev - > gadget - > ep0 , GFP_KERNEL ) ;
uvc - > control_buf = kmalloc ( UVC_MAX_REQUEST_SIZE , GFP_KERNEL ) ;
if ( uvc - > control_req = = NULL | | uvc - > control_buf = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
uvc - > control_req - > buf = uvc - > control_buf ;
uvc - > control_req - > complete = uvc_function_ep0_complete ;
uvc - > control_req - > context = uvc ;
2013-06-12 06:02:38 -03:00
if ( v4l2_device_register ( & cdev - > gadget - > dev , & uvc - > v4l2_dev ) ) {
2018-08-10 15:48:02 +03:00
uvcg_err ( f , " failed to register V4L2 device \n " ) ;
2013-06-12 06:02:38 -03:00
goto error ;
}
2010-05-02 20:57:41 +02:00
/* Initialise video. */
2018-08-10 15:48:02 +03:00
ret = uvcg_video_init ( & uvc - > video , uvc ) ;
2010-05-02 20:57:41 +02:00
if ( ret < 0 )
2020-09-27 16:01:16 +08:00
goto v4l2_error ;
2010-05-02 20:57:41 +02:00
/* Register a V4L2 device. */
ret = uvc_register_video ( uvc ) ;
if ( ret < 0 ) {
2018-08-10 15:48:02 +03:00
uvcg_err ( f , " failed to register video device \n " ) ;
2020-09-27 16:01:16 +08:00
goto v4l2_error ;
2010-05-02 20:57:41 +02:00
}
return 0 ;
2020-09-27 16:01:16 +08:00
v4l2_error :
2013-06-12 06:02:38 -03:00
v4l2_device_unregister ( & uvc - > v4l2_dev ) ;
2020-09-27 16:01:16 +08:00
error :
2014-08-21 16:54:45 +02:00
if ( uvc - > control_req )
2012-10-22 22:15:05 +02:00
usb_ep_free_request ( cdev - > gadget - > ep0 , uvc - > control_req ) ;
2014-08-21 16:54:45 +02:00
kfree ( uvc - > control_buf ) ;
2012-10-22 22:15:05 +02:00
2012-10-22 22:15:06 +02:00
usb_free_all_descriptors ( f ) ;
2010-05-02 20:57:41 +02:00
return ret ;
}
/* --------------------------------------------------------------------------
* USB gadget function
*/
2014-09-09 02:02:10 +03:00
static void uvc_free_inst ( struct usb_function_instance * f )
{
2014-12-10 12:34:00 +01:00
struct f_uvc_opts * opts = fi_to_f_uvc_opts ( f ) ;
2014-09-09 02:02:10 +03:00
2014-12-10 12:34:02 +01:00
mutex_destroy ( & opts - > lock ) ;
2014-09-09 02:02:10 +03:00
kfree ( opts ) ;
}
static struct usb_function_instance * uvc_alloc_inst ( void )
{
struct f_uvc_opts * opts ;
2014-12-10 12:34:02 +01:00
struct uvc_camera_terminal_descriptor * cd ;
struct uvc_processing_unit_descriptor * pd ;
struct uvc_output_terminal_descriptor * od ;
struct uvc_color_matching_descriptor * md ;
struct uvc_descriptor_header * * ctl_cls ;
2018-05-24 17:49:34 +03:00
int ret ;
2014-09-09 02:02:10 +03:00
opts = kzalloc ( sizeof ( * opts ) , GFP_KERNEL ) ;
if ( ! opts )
return ERR_PTR ( - ENOMEM ) ;
opts - > func_inst . free_func_inst = uvc_free_inst ;
2014-12-10 12:34:02 +01:00
mutex_init ( & opts - > lock ) ;
cd = & opts - > uvc_camera_terminal ;
cd - > bLength = UVC_DT_CAMERA_TERMINAL_SIZE ( 3 ) ;
cd - > bDescriptorType = USB_DT_CS_INTERFACE ;
cd - > bDescriptorSubType = UVC_VC_INPUT_TERMINAL ;
cd - > bTerminalID = 1 ;
cd - > wTerminalType = cpu_to_le16 ( 0x0201 ) ;
cd - > bAssocTerminal = 0 ;
cd - > iTerminal = 0 ;
cd - > wObjectiveFocalLengthMin = cpu_to_le16 ( 0 ) ;
cd - > wObjectiveFocalLengthMax = cpu_to_le16 ( 0 ) ;
cd - > wOcularFocalLength = cpu_to_le16 ( 0 ) ;
cd - > bControlSize = 3 ;
cd - > bmControls [ 0 ] = 2 ;
cd - > bmControls [ 1 ] = 0 ;
cd - > bmControls [ 2 ] = 0 ;
pd = & opts - > uvc_processing ;
pd - > bLength = UVC_DT_PROCESSING_UNIT_SIZE ( 2 ) ;
pd - > bDescriptorType = USB_DT_CS_INTERFACE ;
pd - > bDescriptorSubType = UVC_VC_PROCESSING_UNIT ;
pd - > bUnitID = 2 ;
pd - > bSourceID = 1 ;
pd - > wMaxMultiplier = cpu_to_le16 ( 16 * 1024 ) ;
pd - > bControlSize = 2 ;
pd - > bmControls [ 0 ] = 1 ;
pd - > bmControls [ 1 ] = 0 ;
pd - > iProcessing = 0 ;
2021-03-15 08:17:48 +01:00
pd - > bmVideoStandards = 0 ;
2014-12-10 12:34:02 +01:00
od = & opts - > uvc_output_terminal ;
od - > bLength = UVC_DT_OUTPUT_TERMINAL_SIZE ;
od - > bDescriptorType = USB_DT_CS_INTERFACE ;
od - > bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL ;
od - > bTerminalID = 3 ;
od - > wTerminalType = cpu_to_le16 ( 0x0101 ) ;
od - > bAssocTerminal = 0 ;
od - > bSourceID = 2 ;
od - > iTerminal = 0 ;
md = & opts - > uvc_color_matching ;
md - > bLength = UVC_DT_COLOR_MATCHING_SIZE ;
md - > bDescriptorType = USB_DT_CS_INTERFACE ;
md - > bDescriptorSubType = UVC_VS_COLORFORMAT ;
md - > bColorPrimaries = 1 ;
md - > bTransferCharacteristics = 1 ;
md - > bMatrixCoefficients = 4 ;
/* Prepare fs control class descriptors for configfs-based gadgets */
ctl_cls = opts - > uvc_fs_control_cls ;
ctl_cls [ 0 ] = NULL ; /* assigned elsewhere by configfs */
ctl_cls [ 1 ] = ( struct uvc_descriptor_header * ) cd ;
ctl_cls [ 2 ] = ( struct uvc_descriptor_header * ) pd ;
ctl_cls [ 3 ] = ( struct uvc_descriptor_header * ) od ;
ctl_cls [ 4 ] = NULL ; /* NULL-terminate */
opts - > fs_control =
( const struct uvc_descriptor_header * const * ) ctl_cls ;
/* Prepare hs control class descriptors for configfs-based gadgets */
ctl_cls = opts - > uvc_ss_control_cls ;
ctl_cls [ 0 ] = NULL ; /* assigned elsewhere by configfs */
ctl_cls [ 1 ] = ( struct uvc_descriptor_header * ) cd ;
ctl_cls [ 2 ] = ( struct uvc_descriptor_header * ) pd ;
ctl_cls [ 3 ] = ( struct uvc_descriptor_header * ) od ;
ctl_cls [ 4 ] = NULL ; /* NULL-terminate */
opts - > ss_control =
( const struct uvc_descriptor_header * const * ) ctl_cls ;
opts - > streaming_interval = 1 ;
opts - > streaming_maxpacket = 1024 ;
2018-05-24 17:49:34 +03:00
ret = uvcg_attach_configfs ( opts ) ;
if ( ret < 0 ) {
kfree ( opts ) ;
return ERR_PTR ( ret ) ;
}
2014-09-09 02:02:10 +03:00
return & opts - > func_inst ;
}
static void uvc_free ( struct usb_function * f )
{
struct uvc_device * uvc = to_uvc ( f ) ;
2014-12-10 12:34:02 +01:00
struct f_uvc_opts * opts = container_of ( f - > fi , struct f_uvc_opts ,
func_inst ) ;
- - opts - > refcnt ;
2014-09-09 02:02:10 +03:00
kfree ( uvc ) ;
}
static void uvc_unbind ( struct usb_configuration * c , struct usb_function * f )
{
struct usb_composite_dev * cdev = c - > cdev ;
struct uvc_device * uvc = to_uvc ( f ) ;
2018-08-10 15:48:02 +03:00
uvcg_info ( f , " %s \n " , __func__ ) ;
2014-09-09 02:02:10 +03:00
2018-05-24 17:16:12 +01:00
device_remove_file ( & uvc - > vdev . dev , & dev_attr_function_name ) ;
2015-03-09 13:34:08 -03:00
video_unregister_device ( & uvc - > vdev ) ;
2014-09-09 02:02:10 +03:00
v4l2_device_unregister ( & uvc - > v4l2_dev ) ;
usb_ep_free_request ( cdev - > gadget - > ep0 , uvc - > control_req ) ;
kfree ( uvc - > control_buf ) ;
usb_free_all_descriptors ( f ) ;
}
2014-09-16 17:26:46 +03:00
static struct usb_function * uvc_alloc ( struct usb_function_instance * fi )
2014-09-09 02:02:10 +03:00
{
struct uvc_device * uvc ;
struct f_uvc_opts * opts ;
2014-12-10 12:34:02 +01:00
struct uvc_descriptor_header * * strm_cls ;
2014-09-09 02:02:10 +03:00
uvc = kzalloc ( sizeof ( * uvc ) , GFP_KERNEL ) ;
if ( uvc = = NULL )
return ERR_PTR ( - ENOMEM ) ;
2015-02-17 05:44:06 -03:00
mutex_init ( & uvc - > video . mutex ) ;
2014-09-09 02:02:10 +03:00
uvc - > state = UVC_STATE_DISCONNECTED ;
2014-12-10 12:34:00 +01:00
opts = fi_to_f_uvc_opts ( fi ) ;
2014-09-09 02:02:10 +03:00
2014-12-10 12:34:02 +01:00
mutex_lock ( & opts - > lock ) ;
if ( opts - > uvc_fs_streaming_cls ) {
strm_cls = opts - > uvc_fs_streaming_cls ;
opts - > fs_streaming =
( const struct uvc_descriptor_header * const * ) strm_cls ;
}
if ( opts - > uvc_hs_streaming_cls ) {
strm_cls = opts - > uvc_hs_streaming_cls ;
opts - > hs_streaming =
( const struct uvc_descriptor_header * const * ) strm_cls ;
}
if ( opts - > uvc_ss_streaming_cls ) {
strm_cls = opts - > uvc_ss_streaming_cls ;
opts - > ss_streaming =
( const struct uvc_descriptor_header * const * ) strm_cls ;
}
2014-09-09 02:02:10 +03:00
uvc - > desc . fs_control = opts - > fs_control ;
uvc - > desc . ss_control = opts - > ss_control ;
uvc - > desc . fs_streaming = opts - > fs_streaming ;
uvc - > desc . hs_streaming = opts - > hs_streaming ;
uvc - > desc . ss_streaming = opts - > ss_streaming ;
2014-12-10 12:34:02 +01:00
+ + opts - > refcnt ;
mutex_unlock ( & opts - > lock ) ;
2014-09-09 02:02:10 +03:00
/* Register the function. */
uvc - > func . name = " uvc " ;
uvc - > func . bind = uvc_function_bind ;
uvc - > func . unbind = uvc_unbind ;
uvc - > func . get_alt = uvc_function_get_alt ;
uvc - > func . set_alt = uvc_function_set_alt ;
uvc - > func . disable = uvc_function_disable ;
uvc - > func . setup = uvc_function_setup ;
uvc - > func . free_func = uvc_free ;
2015-05-04 14:55:14 +02:00
uvc - > func . bind_deactivated = true ;
2014-09-09 02:02:10 +03:00
return & uvc - > func ;
}
DECLARE_USB_FUNCTION_INIT ( uvc , uvc_alloc_inst , uvc_alloc ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Laurent Pinchart " ) ;