2010-05-02 22:57:41 +04:00
/*
* uvc_gadget . c - - USB Video Class Gadget driver
*
* Copyright ( C ) 2009 - 2010
* Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/list.h>
# include <linux/mutex.h>
2013-02-02 11:48:54 +04:00
# include <linux/string.h>
2010-05-02 22:57:41 +04:00
# include <linux/usb/ch9.h>
# include <linux/usb/gadget.h>
# include <linux/usb/video.h>
# include <linux/vmalloc.h>
# include <linux/wait.h>
# include <media/v4l2-dev.h>
# include <media/v4l2-event.h>
# include "uvc.h"
2010-07-10 23:13:05 +04:00
unsigned int uvc_gadget_trace_param ;
2010-05-02 22:57:41 +04:00
2012-06-01 13:38:56 +04:00
/*-------------------------------------------------------------------------*/
/* module parameters specific to the Video streaming endpoint */
2013-03-01 23:46:26 +04:00
static unsigned int streaming_interval = 1 ;
2012-06-01 13:38:56 +04:00
module_param ( streaming_interval , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( streaming_interval , " 1 - 16 " ) ;
2013-03-01 23:46:26 +04:00
static unsigned int streaming_maxpacket = 1024 ;
2012-06-01 13:38:56 +04:00
module_param ( streaming_maxpacket , uint , S_IRUGO | S_IWUSR ) ;
2013-03-01 23:46:26 +04:00
MODULE_PARM_DESC ( streaming_maxpacket , " 1 - 1023 (FS), 1 - 3072 (hs/ss) " ) ;
2012-06-01 13:38:56 +04:00
2013-03-01 23:46:26 +04:00
static unsigned int streaming_maxburst ;
2012-06-01 13:38:56 +04:00
module_param ( streaming_maxburst , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( streaming_maxburst , " 0 - 15 (ss only) " ) ;
2010-05-02 22:57:41 +04:00
/* --------------------------------------------------------------------------
* Function descriptors
*/
/* string IDs are assigned dynamically */
2013-03-01 23:46:29 +04:00
# define UVC_STRING_CONTROL_IDX 0
# define UVC_STRING_STREAMING_IDX 1
2010-05-02 22:57:41 +04:00
static struct usb_string uvc_en_us_strings [ ] = {
2013-03-01 23:46:29 +04:00
[ UVC_STRING_CONTROL_IDX ] . s = " UVC Camera " ,
2010-05-02 22:57:41 +04: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 23:46:23 +04:00
# define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */
2012-06-01 13:38:55 +04:00
2010-05-02 22:57:41 +04:00
static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
2010-07-10 22:03:20 +04:00
. bLength = sizeof ( uvc_iad ) ,
2010-05-02 22:57:41 +04:00
. bDescriptorType = USB_DT_INTERFACE_ASSOCIATION ,
. bFirstInterface = 0 ,
. bInterfaceCount = 2 ,
. bFunctionClass = USB_CLASS_VIDEO ,
2010-07-10 22:03:20 +04:00
. bFunctionSubClass = UVC_SC_VIDEO_INTERFACE_COLLECTION ,
2010-05-02 22:57:41 +04:00
. bFunctionProtocol = 0x00 ,
. iFunction = 0 ,
} ;
static struct usb_interface_descriptor uvc_control_intf __initdata = {
. 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 22:03:20 +04:00
. bInterfaceSubClass = UVC_SC_VIDEOCONTROL ,
2010-05-02 22:57:41 +04:00
. bInterfaceProtocol = 0x00 ,
. iInterface = 0 ,
} ;
2013-03-01 23:46:25 +04:00
static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
2010-05-02 22:57:41 +04:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_INT ,
2013-03-01 23:46:23 +04:00
. wMaxPacketSize = cpu_to_le16 ( UVC_STATUS_MAX_PACKET_SIZE ) ,
2010-05-02 22:57:41 +04:00
. bInterval = 8 ,
} ;
2013-03-01 23:46:25 +04:00
static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
. 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 ) ,
} ;
2010-05-02 22:57:41 +04:00
static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
. bLength = UVC_DT_CONTROL_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_CS_ENDPOINT ,
. bDescriptorSubType = UVC_EP_INTERRUPT ,
2013-03-01 23:46:23 +04:00
. wMaxTransferSize = cpu_to_le16 ( UVC_STATUS_MAX_PACKET_SIZE ) ,
2010-05-02 22:57:41 +04:00
} ;
static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
. 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 22:03:20 +04:00
. bInterfaceSubClass = UVC_SC_VIDEOSTREAMING ,
2010-05-02 22:57:41 +04:00
. bInterfaceProtocol = 0x00 ,
. iInterface = 0 ,
} ;
static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
. 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 22:03:20 +04:00
. bInterfaceSubClass = UVC_SC_VIDEOSTREAMING ,
2010-05-02 22:57:41 +04:00
. bInterfaceProtocol = 0x00 ,
. iInterface = 0 ,
} ;
2013-03-01 23:46:25 +04:00
static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
2010-05-02 22:57:41 +04:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
2013-03-01 23:46:28 +04:00
. bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC ,
2013-03-01 23:46:26 +04:00
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters .
*/
2010-05-02 22:57:41 +04:00
} ;
2013-03-01 23:46:25 +04:00
static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
2012-06-01 13:38:56 +04:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
2013-03-01 23:46:28 +04:00
. bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC ,
2013-03-01 23:46:26 +04:00
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters .
*/
2012-06-01 13:38:56 +04:00
} ;
static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
2013-03-01 23:46:24 +04:00
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
2012-06-01 13:38:56 +04:00
2013-03-01 23:46:24 +04:00
. bEndpointAddress = USB_DIR_IN ,
2013-03-01 23:46:28 +04:00
. bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC ,
2013-03-01 23:46:26 +04:00
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters .
*/
2012-06-01 13:38:56 +04:00
} ;
2013-03-01 23:46:25 +04:00
static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
2013-03-01 23:46:24 +04:00
. bLength = sizeof ( uvc_ss_streaming_comp ) ,
. bDescriptorType = USB_DT_SS_ENDPOINT_COMP ,
2013-04-29 14:16:30 +04:00
/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
* initialized from module parameters .
*/
2012-06-01 13:38:56 +04:00
} ;
2010-05-02 22:57:41 +04:00
static const struct usb_descriptor_header * const uvc_fs_streaming [ ] = {
( struct usb_descriptor_header * ) & uvc_streaming_intf_alt1 ,
2012-06-01 13:38:56 +04:00
( struct usb_descriptor_header * ) & uvc_fs_streaming_ep ,
2010-05-02 22:57:41 +04:00
NULL ,
} ;
static const struct usb_descriptor_header * const uvc_hs_streaming [ ] = {
( struct usb_descriptor_header * ) & uvc_streaming_intf_alt1 ,
2012-06-01 13:38:56 +04:00
( 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 22:57:41 +04: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 ) ;
v4l2_event_queue ( uvc - > vdev , & v4l2_event ) ;
}
}
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 ;
/* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
* ctrl - > bRequestType , ctrl - > bRequest , le16_to_cpu ( ctrl - > wValue ) ,
* le16_to_cpu ( ctrl - > wIndex ) , le16_to_cpu ( ctrl - > wLength ) ) ;
*/
if ( ( ctrl - > bRequestType & USB_TYPE_MASK ) ! = USB_TYPE_CLASS ) {
INFO ( f - > config - > cdev , " invalid request type \n " ) ;
return - EINVAL ;
}
/* Stall too big requests. */
if ( le16_to_cpu ( ctrl - > wLength ) > UVC_MAX_REQUEST_SIZE )
return - EINVAL ;
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_SETUP ;
memcpy ( & uvc_event - > req , ctrl , sizeof ( uvc_event - > req ) ) ;
v4l2_event_queue ( uvc - > vdev , & v4l2_event ) ;
return 0 ;
}
2013-03-01 23:46:30 +04: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 22:57:41 +04:00
static int
uvc_function_get_alt ( struct usb_function * f , unsigned interface )
{
struct uvc_device * uvc = to_uvc ( f ) ;
INFO ( f - > config - > cdev , " uvc_function_get_alt(%u) \n " , interface ) ;
if ( interface = = uvc - > control_intf )
return 0 ;
else if ( interface ! = uvc - > streaming_intf )
return - EINVAL ;
else
return uvc - > state = = UVC_STATE_STREAMING ? 1 : 0 ;
}
static int
uvc_function_set_alt ( struct usb_function * f , unsigned interface , unsigned alt )
{
struct uvc_device * uvc = to_uvc ( f ) ;
struct v4l2_event v4l2_event ;
struct uvc_event * uvc_event = ( void * ) & v4l2_event . u . data ;
2012-06-01 13:38:56 +04:00
int ret ;
2010-05-02 22:57:41 +04:00
INFO ( f - > config - > cdev , " uvc_function_set_alt(%u, %u) \n " , interface , alt ) ;
if ( interface = = uvc - > control_intf ) {
if ( alt )
return - EINVAL ;
if ( uvc - > state = = UVC_STATE_DISCONNECTED ) {
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_CONNECT ;
uvc_event - > speed = f - > config - > cdev - > gadget - > speed ;
v4l2_event_queue ( uvc - > vdev , & v4l2_event ) ;
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 ;
if ( uvc - > video . ep )
usb_ep_disable ( uvc - > video . ep ) ;
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_STREAMOFF ;
v4l2_event_queue ( uvc - > vdev , & v4l2_event ) ;
uvc - > state = UVC_STATE_CONNECTED ;
2013-03-01 23:46:30 +04:00
return 0 ;
2010-05-02 22:57:41 +04:00
case 1 :
if ( uvc - > state ! = UVC_STATE_CONNECTED )
return 0 ;
2011-06-28 17:33:48 +04:00
if ( uvc - > video . ep ) {
2012-06-01 13:38:56 +04:00
ret = config_ep_by_speed ( f - > config - > cdev - > gadget ,
& ( uvc - > func ) , uvc - > video . ep ) ;
if ( ret )
return ret ;
2011-06-28 17:33:48 +04:00
usb_ep_enable ( uvc - > video . ep ) ;
}
2010-05-02 22:57:41 +04:00
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_STREAMON ;
v4l2_event_queue ( uvc - > vdev , & v4l2_event ) ;
2013-03-01 23:46:30 +04:00
return USB_GADGET_DELAYED_STATUS ;
2010-05-02 22:57:41 +04: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 ;
INFO ( f - > config - > cdev , " uvc_function_disable \n " ) ;
memset ( & v4l2_event , 0 , sizeof ( v4l2_event ) ) ;
v4l2_event . type = UVC_EVENT_DISCONNECT ;
v4l2_event_queue ( uvc - > vdev , & v4l2_event ) ;
uvc - > state = UVC_STATE_DISCONNECTED ;
}
/* --------------------------------------------------------------------------
* Connection / disconnection
*/
void
uvc_function_connect ( struct uvc_device * uvc )
{
struct usb_composite_dev * cdev = uvc - > func . config - > cdev ;
int ret ;
if ( ( ret = usb_function_activate ( & uvc - > func ) ) < 0 )
INFO ( cdev , " UVC connect failed with %d \n " , ret ) ;
}
void
uvc_function_disconnect ( struct uvc_device * uvc )
{
struct usb_composite_dev * cdev = uvc - > func . config - > cdev ;
int ret ;
if ( ( ret = usb_function_deactivate ( & uvc - > func ) ) < 0 )
INFO ( cdev , " UVC disconnect failed with %d \n " , ret ) ;
}
/* --------------------------------------------------------------------------
* USB probe and disconnect
*/
static int
uvc_register_video ( struct uvc_device * uvc )
{
struct usb_composite_dev * cdev = uvc - > func . config - > cdev ;
struct video_device * video ;
/* TODO reference counting. */
video = video_device_alloc ( ) ;
if ( video = = NULL )
return - ENOMEM ;
video - > parent = & cdev - > gadget - > dev ;
video - > fops = & uvc_v4l2_fops ;
video - > release = video_device_release ;
2013-02-02 11:48:54 +04:00
strlcpy ( video - > name , cdev - > gadget - > name , sizeof ( video - > name ) ) ;
2010-05-02 22:57:41 +04:00
uvc - > vdev = video ;
video_set_drvdata ( video , uvc ) ;
return video_register_device ( video , VFL_TYPE_GRABBER , - 1 ) ;
}
# 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 )
static struct usb_descriptor_header * * __init
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 13:38:56 +04:00
const struct uvc_descriptor_header * const * uvc_control_desc ;
2010-05-02 22:57:41 +04: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 13:38:56 +04:00
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 22:57:41 +04:00
/* Descriptors layout
*
* uvc_iad
* uvc_control_intf
* Class - specific UVC control descriptors
* uvc_control_ep
* uvc_control_cs_ep
2013-03-01 23:46:25 +04:00
* uvc_ss_control_comp ( for SS only )
2010-05-02 22:57:41 +04: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 23:46:25 +04:00
+ uvc_control_ep . bLength + uvc_control_cs_ep . bLength
2010-05-02 22:57:41 +04:00
+ uvc_streaming_intf_alt0 . bLength ;
2012-06-01 13:38:56 +04:00
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 23:46:24 +04:00
* src ; + + src ) {
2010-05-02 22:57:41 +04:00
control_size + = ( * src ) - > bLength ;
bytes + = ( * src ) - > bLength ;
n_desc + + ;
}
2012-06-01 13:38:56 +04:00
for ( src = ( const struct usb_descriptor_header * * ) uvc_streaming_cls ;
2013-03-01 23:46:24 +04:00
* src ; + + src ) {
2010-05-02 22:57:41 +04: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 13:38:56 +04:00
( const struct usb_descriptor_header * * ) uvc_control_desc ) ;
2010-05-02 22:57:41 +04: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 23:46:25 +04:00
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_control_ep ) ;
2012-06-01 13:38:56 +04:00
if ( speed = = USB_SPEED_SUPER )
UVC_COPY_DESCRIPTOR ( mem , dst , & uvc_ss_control_comp ) ;
2010-05-02 22:57:41 +04: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 23:46:27 +04:00
uvc_streaming_header - > bEndpointAddress = uvc - > video . ep - > address ;
2010-05-02 22:57:41 +04:00
UVC_COPY_DESCRIPTORS ( mem , dst , uvc_streaming_std ) ;
* dst = NULL ;
return hdr ;
}
static void
uvc_function_unbind ( struct usb_configuration * c , struct usb_function * f )
{
struct usb_composite_dev * cdev = c - > cdev ;
struct uvc_device * uvc = to_uvc ( f ) ;
INFO ( cdev , " uvc_function_unbind \n " ) ;
2012-10-23 00:15:05 +04:00
video_unregister_device ( uvc - > vdev ) ;
uvc - > control_ep - > driver_data = NULL ;
uvc - > video . ep - > driver_data = NULL ;
2010-05-02 22:57:41 +04:00
2013-03-01 23:46:29 +04:00
uvc_en_us_strings [ UVC_STRING_CONTROL_IDX ] . id = 0 ;
2012-10-23 00:15:05 +04:00
usb_ep_free_request ( cdev - > gadget - > ep0 , uvc - > control_req ) ;
kfree ( uvc - > control_buf ) ;
2010-05-02 22:57:41 +04:00
2012-10-23 00:15:06 +04:00
usb_free_all_descriptors ( f ) ;
2010-05-02 22:57:41 +04:00
kfree ( uvc ) ;
}
static int __init
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 ) ;
2013-03-01 23:46:26 +04:00
unsigned int max_packet_mult ;
unsigned int max_packet_size ;
2010-05-02 22:57:41 +04:00
struct usb_ep * ep ;
int ret = - EINVAL ;
INFO ( cdev , " uvc_function_bind \n " ) ;
2013-03-01 23:46:26 +04:00
/* Sanity check the streaming endpoint module parameters.
*/
streaming_interval = clamp ( streaming_interval , 1U , 16U ) ;
streaming_maxpacket = clamp ( streaming_maxpacket , 1U , 3072U ) ;
streaming_maxburst = min ( streaming_maxburst , 15U ) ;
/* 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 .
2012-06-01 13:38:56 +04:00
*/
2013-03-01 23:46:26 +04:00
if ( streaming_maxpacket < = 1024 ) {
max_packet_mult = 1 ;
max_packet_size = streaming_maxpacket ;
} else if ( streaming_maxpacket < = 2048 ) {
max_packet_mult = 2 ;
max_packet_size = streaming_maxpacket / 2 ;
} else {
max_packet_mult = 3 ;
max_packet_size = streaming_maxpacket / 3 ;
}
uvc_fs_streaming_ep . wMaxPacketSize = min ( streaming_maxpacket , 1023U ) ;
2012-06-01 13:38:56 +04:00
uvc_fs_streaming_ep . bInterval = streaming_interval ;
2013-03-01 23:46:26 +04:00
uvc_hs_streaming_ep . wMaxPacketSize = max_packet_size ;
uvc_hs_streaming_ep . wMaxPacketSize | = ( ( max_packet_mult - 1 ) < < 11 ) ;
uvc_hs_streaming_ep . bInterval = streaming_interval ;
uvc_ss_streaming_ep . wMaxPacketSize = max_packet_size ;
uvc_ss_streaming_ep . bInterval = streaming_interval ;
uvc_ss_streaming_comp . bmAttributes = max_packet_mult - 1 ;
uvc_ss_streaming_comp . bMaxBurst = streaming_maxburst ;
uvc_ss_streaming_comp . wBytesPerInterval =
max_packet_size * max_packet_mult * streaming_maxburst ;
2010-05-02 22:57:41 +04:00
/* Allocate endpoints. */
2013-03-01 23:46:25 +04:00
ep = usb_ep_autoconfig ( cdev - > gadget , & uvc_control_ep ) ;
2010-05-02 22:57:41 +04:00
if ( ! ep ) {
INFO ( cdev , " Unable to allocate control EP \n " ) ;
goto error ;
}
uvc - > control_ep = ep ;
ep - > driver_data = uvc ;
2013-03-01 23:46:27 +04: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 22:57:41 +04:00
if ( ! ep ) {
INFO ( cdev , " Unable to allocate streaming EP \n " ) ;
goto error ;
}
uvc - > video . ep = ep ;
ep - > driver_data = uvc ;
2013-03-01 23:46:27 +04: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 23:46:26 +04:00
2010-05-02 22:57:41 +04: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 ;
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 ;
2012-10-23 00:15:06 +04:00
/* Copy descriptors */
f - > fs_descriptors = uvc_copy_descriptors ( uvc , USB_SPEED_FULL ) ;
if ( gadget_is_dualspeed ( cdev - > gadget ) )
f - > hs_descriptors = uvc_copy_descriptors ( uvc , USB_SPEED_HIGH ) ;
if ( gadget_is_superspeed ( c - > cdev - > gadget ) )
2012-06-01 13:38:56 +04:00
f - > ss_descriptors = uvc_copy_descriptors ( uvc , USB_SPEED_SUPER ) ;
2010-05-02 22:57:41 +04: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 ;
/* Avoid letting this gadget enumerate until the userspace server is
* active .
*/
if ( ( ret = usb_function_deactivate ( f ) ) < 0 )
goto error ;
/* Initialise video. */
ret = uvc_video_init ( & uvc - > video ) ;
if ( ret < 0 )
goto error ;
/* Register a V4L2 device. */
ret = uvc_register_video ( uvc ) ;
if ( ret < 0 ) {
printk ( KERN_INFO " Unable to register video device \n " ) ;
goto error ;
}
return 0 ;
error :
2012-10-23 00:15:05 +04:00
if ( uvc - > vdev )
video_device_release ( uvc - > vdev ) ;
if ( uvc - > control_ep )
uvc - > control_ep - > driver_data = NULL ;
if ( uvc - > video . ep )
uvc - > video . ep - > driver_data = NULL ;
if ( uvc - > control_req ) {
usb_ep_free_request ( cdev - > gadget - > ep0 , uvc - > control_req ) ;
kfree ( uvc - > control_buf ) ;
}
2012-10-23 00:15:06 +04:00
usb_free_all_descriptors ( f ) ;
2010-05-02 22:57:41 +04:00
return ret ;
}
/* --------------------------------------------------------------------------
* USB gadget function
*/
/**
* uvc_bind_config - add a UVC function to a configuration
* @ c : the configuration to support the UVC instance
* Context : single threaded during gadget setup
*
* Returns zero on success , else negative errno .
*
* Caller must have called @ uvc_setup ( ) . Caller is also responsible for
* calling @ uvc_cleanup ( ) before module unload .
*/
int __init
uvc_bind_config ( struct usb_configuration * c ,
2012-06-01 13:38:56 +04:00
const struct uvc_descriptor_header * const * fs_control ,
const struct uvc_descriptor_header * const * ss_control ,
2010-05-02 22:57:41 +04:00
const struct uvc_descriptor_header * const * fs_streaming ,
2012-06-01 13:38:56 +04:00
const struct uvc_descriptor_header * const * hs_streaming ,
const struct uvc_descriptor_header * const * ss_streaming )
2010-05-02 22:57:41 +04:00
{
struct uvc_device * uvc ;
int ret = 0 ;
/* TODO Check if the USB device controller supports the required
* features .
*/
if ( ! gadget_is_dualspeed ( c - > cdev - > gadget ) )
return - EINVAL ;
uvc = kzalloc ( sizeof ( * uvc ) , GFP_KERNEL ) ;
if ( uvc = = NULL )
return - ENOMEM ;
uvc - > state = UVC_STATE_DISCONNECTED ;
/* Validate the descriptors. */
2012-06-01 13:38:56 +04:00
if ( fs_control = = NULL | | fs_control [ 0 ] = = NULL | |
2013-03-01 23:46:24 +04:00
fs_control [ 0 ] - > bDescriptorSubType ! = UVC_VC_HEADER )
2012-06-01 13:38:56 +04:00
goto error ;
if ( ss_control = = NULL | | ss_control [ 0 ] = = NULL | |
2013-03-01 23:46:24 +04:00
ss_control [ 0 ] - > bDescriptorSubType ! = UVC_VC_HEADER )
2010-05-02 22:57:41 +04:00
goto error ;
if ( fs_streaming = = NULL | | fs_streaming [ 0 ] = = NULL | |
2013-03-01 23:46:24 +04:00
fs_streaming [ 0 ] - > bDescriptorSubType ! = UVC_VS_INPUT_HEADER )
2010-05-02 22:57:41 +04:00
goto error ;
if ( hs_streaming = = NULL | | hs_streaming [ 0 ] = = NULL | |
2013-03-01 23:46:24 +04:00
hs_streaming [ 0 ] - > bDescriptorSubType ! = UVC_VS_INPUT_HEADER )
2012-06-01 13:38:56 +04:00
goto error ;
if ( ss_streaming = = NULL | | ss_streaming [ 0 ] = = NULL | |
2013-03-01 23:46:24 +04:00
ss_streaming [ 0 ] - > bDescriptorSubType ! = UVC_VS_INPUT_HEADER )
2010-05-02 22:57:41 +04:00
goto error ;
2012-06-01 13:38:56 +04:00
uvc - > desc . fs_control = fs_control ;
uvc - > desc . ss_control = ss_control ;
2010-05-02 22:57:41 +04:00
uvc - > desc . fs_streaming = fs_streaming ;
uvc - > desc . hs_streaming = hs_streaming ;
2012-06-01 13:38:56 +04:00
uvc - > desc . ss_streaming = ss_streaming ;
2010-05-02 22:57:41 +04:00
2013-03-01 23:46:22 +04:00
/* String descriptors are global, we only need to allocate string IDs
* for the first UVC function . UVC functions beyond the first ( if any )
* will reuse the same IDs .
*/
2013-03-01 23:46:29 +04:00
if ( uvc_en_us_strings [ UVC_STRING_CONTROL_IDX ] . id = = 0 ) {
2012-10-23 00:15:10 +04:00
ret = usb_string_ids_tab ( c - > cdev , uvc_en_us_strings ) ;
if ( ret )
2012-06-01 13:38:54 +04:00
goto error ;
2012-10-23 00:15:10 +04:00
uvc_iad . iFunction =
2013-03-01 23:46:29 +04:00
uvc_en_us_strings [ UVC_STRING_CONTROL_IDX ] . id ;
2012-10-23 00:15:10 +04:00
uvc_control_intf . iInterface =
uvc_en_us_strings [ UVC_STRING_CONTROL_IDX ] . id ;
ret = uvc_en_us_strings [ UVC_STRING_STREAMING_IDX ] . id ;
2012-06-01 13:38:54 +04:00
uvc_streaming_intf_alt0 . iInterface = ret ;
uvc_streaming_intf_alt1 . iInterface = ret ;
}
2010-05-02 22:57:41 +04:00
/* Register the function. */
uvc - > func . name = " uvc " ;
uvc - > func . strings = uvc_function_strings ;
uvc - > func . bind = uvc_function_bind ;
uvc - > func . unbind = uvc_function_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 ;
ret = usb_add_function ( c , & uvc - > func ) ;
if ( ret )
kfree ( uvc ) ;
2011-06-24 22:47:26 +04:00
return ret ;
2010-05-02 22:57:41 +04:00
error :
kfree ( uvc ) ;
return ret ;
}
2010-07-10 23:13:05 +04:00
module_param_named ( trace , uvc_gadget_trace_param , uint , S_IRUGO | S_IWUSR ) ;
2010-05-02 22:57:41 +04:00
MODULE_PARM_DESC ( trace , " Trace level bitmask " ) ;