[PATCH 8/8] usb/gadget: f_uvc: add configfs support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add support for using uvc as a component of a composite gadget
set up with configfs.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx>
Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
---
 Documentation/ABI/testing/configfs-usb-gadget-uvc |   11 +
 drivers/usb/gadget/Kconfig                        |   12 +
 drivers/usb/gadget/f_uvc.c                        |  229 +++++++++++++++++++++
 drivers/usb/gadget/u_uvc.h                        |    9 +
 4 files changed, 261 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-uvc

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc
new file mode 100644
index 0000000..e9f9663
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc
@@ -0,0 +1,11 @@
+What:		/config/usb-gadget/gadget/functions/uvc.name
+Date:		Nov 2013
+KenelVersion:	3.13
+Description:
+		The attributes:
+
+		streaming_interval	- 1..16
+		streaming_maxpacket	- 1..1023 (fs), 1..3072 (hs/ss)
+		streaming_maxburst	- 0..15 (ss only)
+		trace			- trace level bitmask,
+					  common for all uvc instances
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c8a72e0..1e863ad 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -692,6 +692,18 @@ config USB_CONFIGFS_MASS_STORAGE
 	  device (in much the same way as the "loop" device driver),
 	  specified as a module parameter or sysfs option.
 
+config USB_CONFIGFS_F_UVC
+	boolean "USB Webcam function"
+	depends on USB_CONFIGFS
+	depends on VIDEO_DEV
+	select USB_LIBCOMPOSITE
+	select VIDEOBUF2_VMALLOC
+	select USB_F_UVC
+	help
+	  The Webcam function acts as a composite USB Audio and Video Class
+	  device. It provides a userspace API to process UVC control requests
+	  and stream video data to the host.
+
 config USB_ZERO
 	tristate "Gadget Zero (DEVELOPMENT)"
 	select USB_LIBCOMPOSITE
diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c
index 9e1deff..4df877b 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/f_uvc.c
@@ -961,6 +961,220 @@ error:
 	return ret;
 }
 
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_uvc_opts,
+			    func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uvc_opts);
+CONFIGFS_ATTR_OPS(f_uvc_opts);
+
+static void uvc_attr_release(struct config_item *item)
+{
+	struct f_uvc_opts *opts = to_f_uvc_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations uvc_item_ops = {
+	.release		= uvc_attr_release,
+	.show_attribute		= f_uvc_opts_attr_show,
+	.store_attribute	= f_uvc_opts_attr_store,
+};
+
+static ssize_t uvc_opts_trace_show(struct f_uvc_opts *opts, char *page)
+{
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d", uvc_gadget_trace_param);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t uvc_opts_trace_store(struct f_uvc_opts *opts,
+				    const char *page, size_t len)
+{
+	int ret;
+	u32 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou32(page, 0, &num);
+	if (ret)
+		goto end;
+
+	uvc_gadget_trace_param = num;
+	ret = len;
+
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+static struct f_uvc_opts_attribute f_uvc_opts_trace =
+	__CONFIGFS_ATTR(trace, S_IRUGO | S_IWUSR, uvc_opts_trace_show,
+			uvc_opts_trace_store);
+
+static ssize_t uvc_opts_streaming_interval_show(struct f_uvc_opts *opts,
+						char *page)
+{
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d", opts->streaming_interval);
+	mutex_unlock(&opts->lock);
+	result = 0;
+
+	return result;
+}
+
+static ssize_t uvc_opts_streaming_interval_store(struct f_uvc_opts *opts,
+						 const char *page, size_t len)
+{
+	int ret;
+	u8 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou8(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num > 16) {
+		ret = -EINVAL;
+		goto end;
+	}
+	opts->streaming_interval = num;
+	ret = len;
+
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+static struct f_uvc_opts_attribute f_uvc_opts_streaming_interval =
+	__CONFIGFS_ATTR(streaming_interval, S_IRUGO | S_IWUSR,
+			uvc_opts_streaming_interval_show,
+			uvc_opts_streaming_interval_store);
+
+static ssize_t uvc_opts_streaming_maxpacket_show(struct f_uvc_opts *opts,
+						char *page)
+{
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d", opts->streaming_maxpacket);
+	mutex_unlock(&opts->lock);
+	result = 0;
+
+	return result;
+}
+
+static ssize_t uvc_opts_streaming_maxpacket_store(struct f_uvc_opts *opts,
+						 const char *page, size_t len)
+{
+	int ret;
+	u16 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou16(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num > 3072) {
+		ret = -EINVAL;
+		goto end;
+	}
+	opts->streaming_maxpacket = num;
+	ret = len;
+
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+static struct f_uvc_opts_attribute f_uvc_opts_streaming_maxpacket =
+	__CONFIGFS_ATTR(streaming_maxpacket, S_IRUGO | S_IWUSR,
+			uvc_opts_streaming_maxpacket_show,
+			uvc_opts_streaming_maxpacket_store);
+
+static ssize_t uvc_opts_streaming_maxburst_show(struct f_uvc_opts *opts,
+						char *page)
+{
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d", opts->streaming_maxburst);
+	mutex_unlock(&opts->lock);
+	result = 0;
+
+	return result;
+}
+
+static ssize_t uvc_opts_streaming_maxburst_store(struct f_uvc_opts *opts,
+						 const char *page, size_t len)
+{
+	int ret;
+	u8 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou8(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num > 15) {
+		ret = -EINVAL;
+		goto end;
+	}
+	opts->streaming_maxburst = num;
+	ret = len;
+
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+static struct f_uvc_opts_attribute f_uvc_opts_streaming_maxburst =
+	__CONFIGFS_ATTR(streaming_maxburst, S_IRUGO | S_IWUSR,
+			uvc_opts_streaming_maxburst_show,
+			uvc_opts_streaming_maxburst_store);
+
+
+static struct configfs_attribute *uvc_attrs[] = {
+	&f_uvc_opts_trace.attr,
+	&f_uvc_opts_streaming_interval.attr,
+	&f_uvc_opts_streaming_maxpacket.attr,
+	&f_uvc_opts_streaming_maxburst.attr,
+	NULL,
+};
+
+static struct config_item_type uvc_func_type = {
+	.ct_item_ops	= &uvc_item_ops,
+	.ct_attrs	= uvc_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
 /* --------------------------------------------------------------------------
  * USB gadget function
  */
@@ -981,17 +1195,26 @@ static struct usb_function_instance *uvc_alloc_inst(void)
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	if (!opts)
 		return ERR_PTR(-ENOMEM);
+	mutex_init(&opts->lock);
 	opts->func_inst.free_func_inst = uvc_free_inst;
 
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &uvc_func_type);
+
 	return &opts->func_inst;
 }
 
 static void uvc_free(struct usb_function *f)
 {
 	struct uvc_device *uvc;
+	struct f_uvc_opts *opts;
 
 	uvc = to_uvc(f);
+	opts = container_of(f->fi, struct f_uvc_opts, func_inst);
 	kfree(uvc);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
 }
 
 static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -1015,11 +1238,17 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
 struct usb_function *uvc_alloc(struct usb_function_instance *fi)
 {
 	struct uvc_device *uvc;
+	struct f_uvc_opts *opts;
 
 	uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
 	if (uvc == NULL)
 		return ERR_PTR(-ENOMEM);
 
+	opts = container_of(fi, struct f_uvc_opts, func_inst);
+	mutex_lock(&opts->lock);
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
+
 	uvc->state = UVC_STATE_DISCONNECTED;
 
 	uvc->desc.fs_control = uvc_fs_control_cls;
diff --git a/drivers/usb/gadget/u_uvc.h b/drivers/usb/gadget/u_uvc.h
index 6758ba4..85b782e 100644
--- a/drivers/usb/gadget/u_uvc.h
+++ b/drivers/usb/gadget/u_uvc.h
@@ -24,6 +24,15 @@ struct f_uvc_opts {
 	unsigned int			streaming_interval;
 	unsigned int			streaming_maxpacket;
 	unsigned int			streaming_maxburst;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
 };
 
 void uvc_set_trace_param(unsigned int uvc_gadget_trace_param_webcam);
-- 
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux