diff options
-rw-r--r-- | drivers/usb/gadget/legacy/Kconfig | 11 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/g_ffs.c | 301 |
2 files changed, 311 insertions, 1 deletions
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 8efa3d157cfb..a1685509b03c 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -187,7 +187,7 @@ config USB_FUNCTIONFS tristate "Function Filesystem" select USB_LIBCOMPOSITE select USB_F_FS - select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) + select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS || USB_FUNCTIONFS_WEBCAM) help The Function Filesystem (FunctionFS) lets one create USB composite functions in user space in the same way GadgetFS @@ -220,6 +220,15 @@ config USB_FUNCTIONFS_RNDIS help Include a configuration with RNDIS function (Ethernet) and the Filesystem. +config USB_FUNCTIONFS_WEBCAM + bool "Include configuration with Gadget Webcam" + depends on USB_FUNCTIONFS + depends on VIDEO_DEV + select VIDEOBUF2_DMA_CONTIG + select USB_F_UVC + help + Include a configuration with Gadget Webcam and the Function Filesystem. + config USB_FUNCTIONFS_GENERIC bool "Include 'pure' configuration" depends on USB_FUNCTIONFS diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index 06acfa55864a..cc55f09a73d3 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -14,6 +14,23 @@ #include <linux/module.h> +/* 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)"); + +static unsigned int trace; +module_param(trace, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); + #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS #include <linux/netdevice.h> @@ -49,6 +66,228 @@ static struct usb_function *f_rndis; #endif #include "u_fs.h" +#if defined CONFIG_USB_FUNCTIONFS_WEBCAM +#include <linux/usb/video.h> +# include "u_uvc.h" + +static struct usb_function_instance *fi_uvc; +static struct usb_function *f_uvc; + +/* -------------------------------------------------------------------------- + * 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 */ + +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, + .bDescriptorSubType = UVC_VC_HEADER, + .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(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_INPUT_TERMINAL, + .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 = 0, +}; + +static const struct uvc_processing_unit_descriptor uvc_processing = { + .bLength = UVC_DT_PROCESSING_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_PROCESSING_UNIT, + .bUnitID = 2, + .bSourceID = 1, + .wMaxMultiplier = cpu_to_le16(16*1024), + .bControlSize = 0, + .iProcessing = 0, +}; + +static const struct uvc_output_terminal_descriptor uvc_output_terminal = { + .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL, + .bTerminalID = 3, + .wTerminalType = cpu_to_le16(0x0101), + .bAssocTerminal = 0, + .bSourceID = 2, + .iTerminal = 0, +}; + +DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(0, 0); + +static const struct UVC_INPUT_HEADER_DESCRIPTOR(0, 0) uvc_input_header = { + .bLength = UVC_DT_INPUT_HEADER_SIZE(0, 0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_INPUT_HEADER, + .bNumFormats = 1, + .wTotalLength = 0, /* dynamic */ + .bEndpointAddress = 0, /* dynamic */ + .bmInfo = 0, + .bTerminalLink = 3, + .bStillCaptureMethod = 0, + .bTriggerSupport = 0, + .bTriggerUsage = 0, + .bControlSize = 0, +}; + +static const struct uvc_format_mjpeg uvc_format_mjpg = { + .bLength = UVC_DT_FORMAT_MJPEG_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, + .bFormatIndex = 1, + .bNumFrameDescriptors = 3, + .bmFlags = 0, + .bDefaultFrameIndex = 3, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterfaceFlags = 0, + .bCopyProtect = 0, +}; + +DECLARE_UVC_FRAME_MJPEG(1); + +static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_360p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .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(333333), + .bFrameIntervalType = 1, + .dwFrameInterval[0] = cpu_to_le32(333333), +}; + +static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .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(333333), + .bFrameIntervalType = 1, + .dwFrameInterval[0] = cpu_to_le32(333333), +}; + +static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_1080p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .bFrameIndex = 3, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(1920), + .wHeight = cpu_to_le16(1080), + .dwMinBitRate = cpu_to_le32(66355200), + .dwMaxBitRate = cpu_to_le32(66355200), + .dwMaxVideoFrameBufferSize = cpu_to_le32(4147200), + .dwDefaultFrameInterval = cpu_to_le32(333333), + .bFrameIntervalType = 1, + .dwFrameInterval[0] = cpu_to_le32(333333), +}; + + +static const struct uvc_color_matching_descriptor uvc_color_matching = { + .bLength = UVC_DT_COLOR_MATCHING_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_COLORFORMAT, + .bColorPrimaries = 1, + .bTransferCharacteristics = 1, + .bMatrixCoefficients = 4, +}; + +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[] = { + (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_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_frame_mjpg_1080p, + (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_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_frame_mjpg_1080p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, +}; + +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_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_frame_mjpg_1080p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, +}; + +/* -------------------------------------------------------------------------- + * USB configuration + */ + +static int +cam_config_bind(struct usb_configuration *c) +{ + 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; +} + +#endif #define DRIVER_NAME "g_ffs" #define DRIVER_DESC "USB Function Filesystem" @@ -115,6 +354,9 @@ static struct usb_string gfs_strings[] = { #ifdef CONFIG_USB_FUNCTIONFS_ETH { .s = "FunctionFS + ECM" }, #endif +#ifdef CONFIG_USB_FUNCTIONFS_WEBCAM + { .s = "FunctionFS + Webcam" }, +#endif #ifdef CONFIG_USB_FUNCTIONFS_GENERIC { .s = "FunctionFS" }, #endif @@ -133,6 +375,9 @@ struct gfs_configuration { struct usb_configuration c; int (*eth)(struct usb_configuration *c); int num; +#ifdef CONFIG_USB_FUNCTIONFS_WEBCAM + int (*webcam)(struct usb_configuration *c); +#endif } gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { @@ -146,6 +391,12 @@ struct gfs_configuration { }, #endif +#ifdef CONFIG_USB_FUNCTIONFS_WEBCAM + { + .webcam = cam_config_bind, + }, +#endif + #ifdef CONFIG_USB_FUNCTIONFS_GENERIC { }, @@ -183,9 +434,14 @@ static struct usb_function **f_ffs[] = { NULL, #endif +#ifdef CONFIG_USB_FUNCTIONFS_WEBCAM + NULL, +#endif + #ifdef CONFIG_USB_FUNCTIONFS_GENERIC NULL, #endif + }; #define N_CONF ARRAY_SIZE(f_ffs) @@ -328,6 +584,9 @@ static int gfs_bind(struct usb_composite_dev *cdev) #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS struct net_device *net; #endif +#if defined CONFIG_USB_FUNCTIONFS_WEBCAM + struct f_uvc_opts *uvc_opts; +#endif int ret, i; ENTER(); @@ -402,6 +661,25 @@ static int gfs_bind(struct usb_composite_dev *cdev) rndis_borrow_net(fi_rndis, net); #endif +#if defined CONFIG_USB_FUNCTIONFS_WEBCAM + fi_uvc = usb_get_function_instance("uvc"); + if (IS_ERR(fi_uvc)) + goto error_webcam; + + 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_set_trace_param(trace); + + 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; +#endif + /* TODO: gstrings_attach? */ ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) @@ -428,6 +706,13 @@ static int gfs_bind(struct usb_composite_dev *cdev) /* TODO */ error_unbind: +error_webcam: +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + 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); +#endif error_rndis: #ifdef CONFIG_USB_FUNCTIONFS_RNDIS usb_put_function_instance(fi_rndis); @@ -466,6 +751,14 @@ static int gfs_unbind(struct usb_composite_dev *cdev) usb_put_function_instance(fi_geth); } #endif + + +#if defined CONFIG_USB_FUNCTIONFS_WEBCAM + 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); +#endif for (i = 0; i < N_CONF * func_num; ++i) usb_put_function(*(f_ffs[0] + i)); @@ -508,6 +801,14 @@ static int gfs_do_config(struct usb_configuration *c) usb_put_function(f_ffs[gc->num][i]); goto error; } + +#if defined CONFIG_USB_FUNCTIONFS_WEBCAM + if (gc->webcam && i == 0) { + ret = gc->webcam(c); + if (unlikely(ret < 0)) + return ret; + } +#endif } /* |