2010-05-02 20:57:42 +02:00
/*
* webcam . c - - USB webcam 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 .
*/
2011-09-09 16:10:44 +02:00
2010-05-02 20:57:42 +02:00
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/usb/video.h>
# include "f_uvc.h"
/*
* Kbuild is not very cooperative with respect to linking separately
* compiled library objects into one module . So for now we won ' t use
* separate compilation . . . ensuring init / exit sections work to shrink
* the runtime footprint , and giving us at least some parts of what
* a " gcc --combine ... part1.c part2.c part3.c ... " build would .
*/
# include "composite.c"
# include "usbstring.c"
# include "config.c"
# include "epautoconf.c"
# include "uvc_queue.c"
# include "uvc_video.c"
2010-07-10 16:13:05 -03:00
# include "uvc_v4l2.c"
# include "f_uvc.c"
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 */
# define STRING_MANUFACTURER_IDX 0
# define STRING_PRODUCT_IDX 1
# define STRING_DESCRIPTION_IDX 2
static struct usb_string webcam_strings [ ] = {
[ STRING_MANUFACTURER_IDX ] . s = webcam_vendor_label ,
[ STRING_PRODUCT_IDX ] . s = webcam_product_label ,
[ STRING_DESCRIPTION_IDX ] . s = webcam_config_label ,
{ }
} ;
static struct usb_gadget_strings webcam_stringtab = {
. language = 0x0409 , /* en-us */
. strings = webcam_strings ,
} ;
static struct usb_gadget_strings * webcam_device_strings [ ] = {
& webcam_stringtab ,
NULL ,
} ;
static struct usb_device_descriptor webcam_device_descriptor = {
. bLength = USB_DT_DEVICE_SIZE ,
. bDescriptorType = USB_DT_DEVICE ,
. bcdUSB = cpu_to_le16 ( 0x0200 ) ,
. 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 ,
2010-05-02 20:57:42 +02:00
. bcdUVC = cpu_to_le16 ( 0x0100 ) ,
. 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 ,
} ;
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 ,
} ;
static const struct uvc_descriptor_header * const uvc_control_cls [ ] = {
( const struct uvc_descriptor_header * ) & uvc_control_header ,
( const struct uvc_descriptor_header * ) & uvc_camera_terminal ,
( const struct uvc_descriptor_header * ) & uvc_processing ,
( const struct uvc_descriptor_header * ) & uvc_output_terminal ,
NULL ,
} ;
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls [ ] = {
( const struct uvc_descriptor_header * ) & uvc_input_header ,
( const struct uvc_descriptor_header * ) & 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 ,
} ;
/* --------------------------------------------------------------------------
* USB configuration
*/
2010-08-12 17:43:52 +02:00
static int __init
2010-05-02 20:57:42 +02:00
webcam_config_bind ( struct usb_configuration * c )
{
return uvc_bind_config ( c , uvc_control_cls , uvc_fs_streaming_cls ,
uvc_hs_streaming_cls ) ;
}
static struct usb_configuration webcam_config_driver = {
. label = webcam_config_label ,
. bConfigurationValue = 1 ,
. iConfiguration = 0 , /* dynamic */
. bmAttributes = USB_CONFIG_ATT_SELFPOWER ,
. bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2 ,
} ;
static int /* __init_or_exit */
webcam_unbind ( struct usb_composite_dev * cdev )
{
return 0 ;
}
2010-08-12 17:43:52 +02:00
static int __init
2010-05-02 20:57:42 +02:00
webcam_bind ( struct usb_composite_dev * cdev )
{
int ret ;
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue .
*/
if ( ( ret = usb_string_id ( cdev ) ) < 0 )
goto error ;
webcam_strings [ STRING_MANUFACTURER_IDX ] . id = ret ;
webcam_device_descriptor . iManufacturer = ret ;
if ( ( ret = usb_string_id ( cdev ) ) < 0 )
goto error ;
webcam_strings [ STRING_PRODUCT_IDX ] . id = ret ;
webcam_device_descriptor . iProduct = ret ;
if ( ( ret = usb_string_id ( cdev ) ) < 0 )
goto error ;
webcam_strings [ STRING_DESCRIPTION_IDX ] . id = ret ;
webcam_config_driver . iConfiguration = ret ;
/* 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 ;
INFO ( cdev , " Webcam Video Gadget \n " ) ;
return 0 ;
error :
webcam_unbind ( cdev ) ;
return ret ;
}
/* --------------------------------------------------------------------------
* Driver
*/
static struct usb_composite_driver webcam_driver = {
. name = " g_webcam " ,
. dev = & webcam_device_descriptor ,
. strings = webcam_device_strings ,
2011-06-29 16:41:49 +03:00
. max_speed = USB_SPEED_HIGH ,
2010-05-02 20:57:42 +02:00
. unbind = webcam_unbind ,
} ;
static int __init
webcam_init ( void )
{
2010-08-12 17:43:54 +02:00
return usb_composite_probe ( & webcam_driver , webcam_bind ) ;
2010-05-02 20:57:42 +02:00
}
static void __exit
webcam_cleanup ( void )
{
usb_composite_unregister ( & webcam_driver ) ;
}
module_init ( webcam_init ) ;
module_exit ( webcam_cleanup ) ;
MODULE_AUTHOR ( " Laurent Pinchart " ) ;
MODULE_DESCRIPTION ( " Webcam Video Gadget " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( " 0.1.0 " ) ;