summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Grzeschik <m.grzeschik@pengutronix.de>2013-01-25 18:13:38 +0100
committerMichael Grzeschik <m.grzeschik@pengutronix.de>2014-08-13 16:23:52 +0200
commit1a62757613aa03bf94ed98a667d2d160bf357b44 (patch)
treec090d0ca5d4d06e42ecbf90c188f16365a219a19
parent017c45c52fcddedc13e6429d3df975b3c334cffe (diff)
downloadlinux-v3.17/topic/uvc.tar.gz
linux-v3.17/topic/uvc.tar.xz
gadget: add ffs + webcam compositev3.17/topic/uvc
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> Notes: This needs to be changed when the patches from Andrzej Pietras got mainline. In particular: "usb/gadget: f_uvc: convert f_uvc to new function interface" Message-ID: 1393579950-1496-5-git-send-email-andrzej.p@samsung.com This way it will be possible to use usb_get_function_instance instead of hardcoding the bind function here.
-rw-r--r--drivers/usb/gadget/legacy/Kconfig11
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c301
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
}
/*