2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2010-05-02 20:57:42 +02:00
/*
* webcam . c - - USB webcam gadget driver
*
* Copyright ( C ) 2009 - 2010
* Laurent Pinchart ( laurent . pinchart @ ideasonboard . com )
*/
2011-09-09 16:10:44 +02:00
2010-05-02 20:57:42 +02:00
# include <linux/kernel.h>
# include <linux/device.h>
2014-09-09 02:02:09 +03:00
# include <linux/module.h>
2010-05-02 20:57:42 +02:00
# include <linux/usb/video.h>
2014-09-09 02:02:11 +03:00
# include "u_uvc.h"
2010-05-02 20:57:42 +02:00
2012-09-10 15:01:53 +02:00
USB_GADGET_COMPOSITE_OPTIONS ( ) ;
2014-09-08 11:18:16 +03:00
/*-------------------------------------------------------------------------*/
/* module parameters specific to the Video streaming endpoint */
static unsigned int streaming_interval = 1 ;
module_param ( streaming_interval , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( streaming_interval , " 1 - 16 " ) ;
static unsigned int streaming_maxpacket = 1024 ;
module_param ( streaming_maxpacket , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( streaming_maxpacket , " 1 - 1023 (FS), 1 - 3072 (hs/ss) " ) ;
static unsigned int streaming_maxburst ;
module_param ( streaming_maxburst , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( streaming_maxburst , " 0 - 15 (ss only) " ) ;
2010-05-02 20:57:42 +02:00
/* --------------------------------------------------------------------------
* Device descriptor
*/
# define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */
# define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */
# define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */
static char webcam_vendor_label [ ] = " Linux Foundation " ;
static char webcam_product_label [ ] = " Webcam gadget " ;
static char webcam_config_label [ ] = " Video " ;
/* string IDs are assigned dynamically */
2012-09-06 20:11:21 +02:00
# define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
2010-05-02 20:57:42 +02:00
static struct usb_string webcam_strings [ ] = {
2012-09-06 20:11:21 +02:00
[ USB_GADGET_MANUFACTURER_IDX ] . s = webcam_vendor_label ,
[ USB_GADGET_PRODUCT_IDX ] . s = webcam_product_label ,
[ USB_GADGET_SERIAL_IDX ] . s = " " ,
2010-05-02 20:57:42 +02:00
[ STRING_DESCRIPTION_IDX ] . s = webcam_config_label ,
{ }
} ;
static struct usb_gadget_strings webcam_stringtab = {
. language = 0x0409 , /* en-us */
. strings = webcam_strings ,
} ;
static struct usb_gadget_strings * webcam_device_strings [ ] = {
& webcam_stringtab ,
NULL ,
} ;
2014-09-09 02:02:11 +03:00
static struct usb_function_instance * fi_uvc ;
static struct usb_function * f_uvc ;
2010-05-02 20:57:42 +02:00
static struct usb_device_descriptor webcam_device_descriptor = {
. bLength = USB_DT_DEVICE_SIZE ,
. bDescriptorType = USB_DT_DEVICE ,
2015-10-20 18:33:13 +02:00
/* .bcdUSB = DYNAMIC */
2010-05-02 20:57:42 +02:00
. bDeviceClass = USB_CLASS_MISC ,
. bDeviceSubClass = 0x02 ,
. bDeviceProtocol = 0x01 ,
. bMaxPacketSize0 = 0 , /* dynamic */
. idVendor = cpu_to_le16 ( WEBCAM_VENDOR_ID ) ,
. idProduct = cpu_to_le16 ( WEBCAM_PRODUCT_ID ) ,
. bcdDevice = cpu_to_le16 ( WEBCAM_DEVICE_BCD ) ,
. iManufacturer = 0 , /* dynamic */
. iProduct = 0 , /* dynamic */
. iSerialNumber = 0 , /* dynamic */
. bNumConfigurations = 0 , /* dynamic */
} ;
DECLARE_UVC_HEADER_DESCRIPTOR ( 1 ) ;
static const struct UVC_HEADER_DESCRIPTOR ( 1 ) uvc_control_header = {
. bLength = UVC_DT_HEADER_SIZE ( 1 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VC_HEADER ,
2021-03-15 07:59:25 +01:00
. bcdUVC = cpu_to_le16 ( 0x0110 ) ,
2010-05-02 20:57:42 +02:00
. wTotalLength = 0 , /* dynamic */
. dwClockFrequency = cpu_to_le32 ( 48000000 ) ,
. bInCollection = 0 , /* dynamic */
. baInterfaceNr [ 0 ] = 0 , /* dynamic */
} ;
static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
. bLength = UVC_DT_CAMERA_TERMINAL_SIZE ( 3 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VC_INPUT_TERMINAL ,
2010-05-02 20:57:42 +02:00
. bTerminalID = 1 ,
. wTerminalType = cpu_to_le16 ( 0x0201 ) ,
. bAssocTerminal = 0 ,
. iTerminal = 0 ,
. wObjectiveFocalLengthMin = cpu_to_le16 ( 0 ) ,
. wObjectiveFocalLengthMax = cpu_to_le16 ( 0 ) ,
. wOcularFocalLength = cpu_to_le16 ( 0 ) ,
. bControlSize = 3 ,
. bmControls [ 0 ] = 2 ,
. bmControls [ 1 ] = 0 ,
. bmControls [ 2 ] = 0 ,
} ;
static const struct uvc_processing_unit_descriptor uvc_processing = {
. bLength = UVC_DT_PROCESSING_UNIT_SIZE ( 2 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VC_PROCESSING_UNIT ,
2010-05-02 20:57:42 +02:00
. bUnitID = 2 ,
. bSourceID = 1 ,
. wMaxMultiplier = cpu_to_le16 ( 16 * 1024 ) ,
. bControlSize = 2 ,
. bmControls [ 0 ] = 1 ,
. bmControls [ 1 ] = 0 ,
. iProcessing = 0 ,
2021-03-15 08:17:48 +01:00
. bmVideoStandards = 0 ,
2010-05-02 20:57:42 +02:00
} ;
static const struct uvc_output_terminal_descriptor uvc_output_terminal = {
. bLength = UVC_DT_OUTPUT_TERMINAL_SIZE ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL ,
2010-05-02 20:57:42 +02:00
. bTerminalID = 3 ,
. wTerminalType = cpu_to_le16 ( 0x0101 ) ,
. bAssocTerminal = 0 ,
. bSourceID = 2 ,
. iTerminal = 0 ,
} ;
DECLARE_UVC_INPUT_HEADER_DESCRIPTOR ( 1 , 2 ) ;
static const struct UVC_INPUT_HEADER_DESCRIPTOR ( 1 , 2 ) uvc_input_header = {
. bLength = UVC_DT_INPUT_HEADER_SIZE ( 1 , 2 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_INPUT_HEADER ,
2010-05-02 20:57:42 +02:00
. bNumFormats = 2 ,
. wTotalLength = 0 , /* dynamic */
. bEndpointAddress = 0 , /* dynamic */
. bmInfo = 0 ,
. bTerminalLink = 3 ,
. bStillCaptureMethod = 0 ,
. bTriggerSupport = 0 ,
. bTriggerUsage = 0 ,
. bControlSize = 1 ,
. bmaControls [ 0 ] [ 0 ] = 0 ,
. bmaControls [ 1 ] [ 0 ] = 4 ,
} ;
static const struct uvc_format_uncompressed uvc_format_yuv = {
. bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED ,
2010-05-02 20:57:42 +02:00
. bFormatIndex = 1 ,
. bNumFrameDescriptors = 2 ,
. guidFormat =
{ ' Y ' , ' U ' , ' Y ' , ' 2 ' , 0x00 , 0x00 , 0x10 , 0x00 ,
0x80 , 0x00 , 0x00 , 0xaa , 0x00 , 0x38 , 0x9b , 0x71 } ,
. bBitsPerPixel = 16 ,
. bDefaultFrameIndex = 1 ,
. bAspectRatioX = 0 ,
. bAspectRatioY = 0 ,
. bmInterfaceFlags = 0 ,
. bCopyProtect = 0 ,
} ;
DECLARE_UVC_FRAME_UNCOMPRESSED ( 1 ) ;
DECLARE_UVC_FRAME_UNCOMPRESSED ( 3 ) ;
static const struct UVC_FRAME_UNCOMPRESSED ( 3 ) uvc_frame_yuv_360p = {
. bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE ( 3 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED ,
2010-05-02 20:57:42 +02:00
. bFrameIndex = 1 ,
. bmCapabilities = 0 ,
. wWidth = cpu_to_le16 ( 640 ) ,
. wHeight = cpu_to_le16 ( 360 ) ,
. dwMinBitRate = cpu_to_le32 ( 18432000 ) ,
. dwMaxBitRate = cpu_to_le32 ( 55296000 ) ,
. dwMaxVideoFrameBufferSize = cpu_to_le32 ( 460800 ) ,
. dwDefaultFrameInterval = cpu_to_le32 ( 666666 ) ,
. bFrameIntervalType = 3 ,
. dwFrameInterval [ 0 ] = cpu_to_le32 ( 666666 ) ,
. dwFrameInterval [ 1 ] = cpu_to_le32 ( 1000000 ) ,
. dwFrameInterval [ 2 ] = cpu_to_le32 ( 5000000 ) ,
} ;
static const struct UVC_FRAME_UNCOMPRESSED ( 1 ) uvc_frame_yuv_720p = {
. bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE ( 1 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED ,
2010-05-02 20:57:42 +02:00
. bFrameIndex = 2 ,
. bmCapabilities = 0 ,
. wWidth = cpu_to_le16 ( 1280 ) ,
. wHeight = cpu_to_le16 ( 720 ) ,
. dwMinBitRate = cpu_to_le32 ( 29491200 ) ,
. dwMaxBitRate = cpu_to_le32 ( 29491200 ) ,
. dwMaxVideoFrameBufferSize = cpu_to_le32 ( 1843200 ) ,
. dwDefaultFrameInterval = cpu_to_le32 ( 5000000 ) ,
. bFrameIntervalType = 1 ,
. dwFrameInterval [ 0 ] = cpu_to_le32 ( 5000000 ) ,
} ;
static const struct uvc_format_mjpeg uvc_format_mjpg = {
. bLength = UVC_DT_FORMAT_MJPEG_SIZE ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_FORMAT_MJPEG ,
2010-05-02 20:57:42 +02:00
. bFormatIndex = 2 ,
. bNumFrameDescriptors = 2 ,
. bmFlags = 0 ,
. bDefaultFrameIndex = 1 ,
. bAspectRatioX = 0 ,
. bAspectRatioY = 0 ,
. bmInterfaceFlags = 0 ,
. bCopyProtect = 0 ,
} ;
DECLARE_UVC_FRAME_MJPEG ( 1 ) ;
DECLARE_UVC_FRAME_MJPEG ( 3 ) ;
static const struct UVC_FRAME_MJPEG ( 3 ) uvc_frame_mjpg_360p = {
. bLength = UVC_DT_FRAME_MJPEG_SIZE ( 3 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_FRAME_MJPEG ,
2010-05-02 20:57:42 +02:00
. bFrameIndex = 1 ,
. bmCapabilities = 0 ,
. wWidth = cpu_to_le16 ( 640 ) ,
. wHeight = cpu_to_le16 ( 360 ) ,
. dwMinBitRate = cpu_to_le32 ( 18432000 ) ,
. dwMaxBitRate = cpu_to_le32 ( 55296000 ) ,
. dwMaxVideoFrameBufferSize = cpu_to_le32 ( 460800 ) ,
. dwDefaultFrameInterval = cpu_to_le32 ( 666666 ) ,
. bFrameIntervalType = 3 ,
. dwFrameInterval [ 0 ] = cpu_to_le32 ( 666666 ) ,
. dwFrameInterval [ 1 ] = cpu_to_le32 ( 1000000 ) ,
. dwFrameInterval [ 2 ] = cpu_to_le32 ( 5000000 ) ,
} ;
static const struct UVC_FRAME_MJPEG ( 1 ) uvc_frame_mjpg_720p = {
. bLength = UVC_DT_FRAME_MJPEG_SIZE ( 1 ) ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_FRAME_MJPEG ,
2010-05-02 20:57:42 +02:00
. bFrameIndex = 2 ,
. bmCapabilities = 0 ,
. wWidth = cpu_to_le16 ( 1280 ) ,
. wHeight = cpu_to_le16 ( 720 ) ,
. dwMinBitRate = cpu_to_le32 ( 29491200 ) ,
. dwMaxBitRate = cpu_to_le32 ( 29491200 ) ,
. dwMaxVideoFrameBufferSize = cpu_to_le32 ( 1843200 ) ,
. dwDefaultFrameInterval = cpu_to_le32 ( 5000000 ) ,
. bFrameIntervalType = 1 ,
. dwFrameInterval [ 0 ] = cpu_to_le32 ( 5000000 ) ,
} ;
static const struct uvc_color_matching_descriptor uvc_color_matching = {
. bLength = UVC_DT_COLOR_MATCHING_SIZE ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
2010-07-10 15:03:20 -03:00
. bDescriptorSubType = UVC_VS_COLORFORMAT ,
2010-05-02 20:57:42 +02:00
. bColorPrimaries = 1 ,
. bTransferCharacteristics = 1 ,
. bMatrixCoefficients = 4 ,
} ;
2012-06-01 15:08:56 +05:30
static const struct uvc_descriptor_header * const uvc_fs_control_cls [ ] = {
( const struct uvc_descriptor_header * ) & uvc_control_header ,
( const struct uvc_descriptor_header * ) & uvc_camera_terminal ,
( const struct uvc_descriptor_header * ) & uvc_processing ,
( const struct uvc_descriptor_header * ) & uvc_output_terminal ,
NULL ,
} ;
static const struct uvc_descriptor_header * const uvc_ss_control_cls [ ] = {
2010-05-02 20:57:42 +02:00
( const struct uvc_descriptor_header * ) & uvc_control_header ,
( const struct uvc_descriptor_header * ) & uvc_camera_terminal ,
( const struct uvc_descriptor_header * ) & uvc_processing ,
( const struct uvc_descriptor_header * ) & uvc_output_terminal ,
NULL ,
} ;
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls [ ] = {
( const struct uvc_descriptor_header * ) & uvc_input_header ,
( const struct uvc_descriptor_header * ) & uvc_format_yuv ,
( const struct uvc_descriptor_header * ) & uvc_frame_yuv_360p ,
( const struct uvc_descriptor_header * ) & uvc_frame_yuv_720p ,
( const struct uvc_descriptor_header * ) & uvc_format_mjpg ,
( const struct uvc_descriptor_header * ) & uvc_frame_mjpg_360p ,
( const struct uvc_descriptor_header * ) & uvc_frame_mjpg_720p ,
( const struct uvc_descriptor_header * ) & uvc_color_matching ,
NULL ,
} ;
static const struct uvc_descriptor_header * const uvc_hs_streaming_cls [ ] = {
( const struct uvc_descriptor_header * ) & uvc_input_header ,
( const struct uvc_descriptor_header * ) & uvc_format_yuv ,
( const struct uvc_descriptor_header * ) & uvc_frame_yuv_360p ,
( const struct uvc_descriptor_header * ) & uvc_frame_yuv_720p ,
( const struct uvc_descriptor_header * ) & uvc_format_mjpg ,
( const struct uvc_descriptor_header * ) & uvc_frame_mjpg_360p ,
( const struct uvc_descriptor_header * ) & uvc_frame_mjpg_720p ,
( const struct uvc_descriptor_header * ) & uvc_color_matching ,
NULL ,
} ;
2012-06-01 15:08:56 +05:30
static const struct uvc_descriptor_header * const uvc_ss_streaming_cls [ ] = {
( const struct uvc_descriptor_header * ) & uvc_input_header ,
( const struct uvc_descriptor_header * ) & uvc_format_yuv ,
( const struct uvc_descriptor_header * ) & uvc_frame_yuv_360p ,
( const struct uvc_descriptor_header * ) & uvc_frame_yuv_720p ,
( const struct uvc_descriptor_header * ) & uvc_format_mjpg ,
( const struct uvc_descriptor_header * ) & uvc_frame_mjpg_360p ,
( const struct uvc_descriptor_header * ) & uvc_frame_mjpg_720p ,
( const struct uvc_descriptor_header * ) & uvc_color_matching ,
NULL ,
} ;
2010-05-02 20:57:42 +02:00
/* --------------------------------------------------------------------------
* USB configuration
*/
2015-04-11 00:14:21 +02:00
static int
2010-05-02 20:57:42 +02:00
webcam_config_bind ( struct usb_configuration * c )
{
2014-09-09 02:02:11 +03:00
int status = 0 ;
f_uvc = usb_get_function ( fi_uvc ) ;
if ( IS_ERR ( f_uvc ) )
return PTR_ERR ( f_uvc ) ;
status = usb_add_function ( c , f_uvc ) ;
if ( status < 0 )
usb_put_function ( f_uvc ) ;
return status ;
2010-05-02 20:57:42 +02:00
}
static struct usb_configuration webcam_config_driver = {
. label = webcam_config_label ,
. bConfigurationValue = 1 ,
. iConfiguration = 0 , /* dynamic */
. bmAttributes = USB_CONFIG_ATT_SELFPOWER ,
2012-12-03 20:07:05 +01:00
. MaxPower = CONFIG_USB_GADGET_VBUS_DRAW ,
2010-05-02 20:57:42 +02:00
} ;
2015-04-11 00:14:21 +02:00
static int
2010-05-02 20:57:42 +02:00
webcam_unbind ( struct usb_composite_dev * cdev )
{
2014-09-09 02:02:11 +03:00
if ( ! IS_ERR_OR_NULL ( f_uvc ) )
usb_put_function ( f_uvc ) ;
if ( ! IS_ERR_OR_NULL ( fi_uvc ) )
usb_put_function_instance ( fi_uvc ) ;
2010-05-02 20:57:42 +02:00
return 0 ;
}
2015-04-11 00:14:21 +02:00
static int
2010-05-02 20:57:42 +02:00
webcam_bind ( struct usb_composite_dev * cdev )
{
2014-09-09 02:02:11 +03:00
struct f_uvc_opts * uvc_opts ;
2010-05-02 20:57:42 +02:00
int ret ;
2014-09-09 02:02:11 +03:00
fi_uvc = usb_get_function_instance ( " uvc " ) ;
if ( IS_ERR ( fi_uvc ) )
return PTR_ERR ( fi_uvc ) ;
uvc_opts = container_of ( fi_uvc , struct f_uvc_opts , func_inst ) ;
uvc_opts - > streaming_interval = streaming_interval ;
uvc_opts - > streaming_maxpacket = streaming_maxpacket ;
uvc_opts - > streaming_maxburst = streaming_maxburst ;
uvc_opts - > fs_control = uvc_fs_control_cls ;
uvc_opts - > ss_control = uvc_ss_control_cls ;
uvc_opts - > fs_streaming = uvc_fs_streaming_cls ;
uvc_opts - > hs_streaming = uvc_hs_streaming_cls ;
uvc_opts - > ss_streaming = uvc_ss_streaming_cls ;
2010-05-02 20:57:42 +02:00
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue .
*/
2012-09-06 20:11:16 +02:00
ret = usb_string_ids_tab ( cdev , webcam_strings ) ;
if ( ret < 0 )
2010-05-02 20:57:42 +02:00
goto error ;
2012-09-06 20:11:16 +02:00
webcam_device_descriptor . iManufacturer =
2012-09-06 20:11:21 +02:00
webcam_strings [ USB_GADGET_MANUFACTURER_IDX ] . id ;
2012-09-06 20:11:16 +02:00
webcam_device_descriptor . iProduct =
2012-09-06 20:11:21 +02:00
webcam_strings [ USB_GADGET_PRODUCT_IDX ] . id ;
2012-09-06 20:11:16 +02:00
webcam_config_driver . iConfiguration =
webcam_strings [ STRING_DESCRIPTION_IDX ] . id ;
2010-05-02 20:57:42 +02:00
/* Register our configuration. */
2010-08-12 17:43:55 +02:00
if ( ( ret = usb_add_config ( cdev , & webcam_config_driver ,
webcam_config_bind ) ) < 0 )
2010-05-02 20:57:42 +02:00
goto error ;
2012-09-10 15:01:53 +02:00
usb_composite_overwrite_options ( cdev , & coverwrite ) ;
2010-05-02 20:57:42 +02:00
INFO ( cdev , " Webcam Video Gadget \n " ) ;
return 0 ;
error :
2014-09-09 02:02:11 +03:00
usb_put_function_instance ( fi_uvc ) ;
2010-05-02 20:57:42 +02:00
return ret ;
}
/* --------------------------------------------------------------------------
* Driver
*/
2015-04-11 00:14:21 +02:00
static struct usb_composite_driver webcam_driver = {
2010-05-02 20:57:42 +02:00
. name = " g_webcam " ,
. dev = & webcam_device_descriptor ,
. strings = webcam_device_strings ,
2012-06-01 15:08:56 +05:30
. max_speed = USB_SPEED_SUPER ,
2012-09-06 20:11:04 +02:00
. bind = webcam_bind ,
2010-05-02 20:57:42 +02:00
. unbind = webcam_unbind ,
} ;
2014-07-09 18:09:56 +02:00
module_usb_composite_driver ( webcam_driver ) ;
2010-05-02 20:57:42 +02:00
MODULE_AUTHOR ( " Laurent Pinchart " ) ;
MODULE_DESCRIPTION ( " Webcam Video Gadget " ) ;
MODULE_LICENSE ( " GPL " ) ;