瀏覽代碼

usb: gadget: configfs: OS Extended Properties descriptors support

Add handling of OS Extended Properties descriptors from configfs interface.
One kind of "OS Descriptors" are "Extended Properties" descriptors, which
need to be specified per interface or per group of interfaces described
by an IAD. This patch adds support for creating subdirectories
in interface.<n> directory located in the function's directory.
Names of subdirectories created become names of properties.
Each property contains two attributes: "type" and "data".
The type can be a numeric value 1..7 while data is a blob interpreted
depending on the type specified.
The types are:
1 - unicode string
2 - unicode string with environment variables
3 - binary
4 - little-endian 32-bit
5 - big-endian 32-bit
6 - unicode string with a symbolic link
7 - multiple unicode strings

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Andrzej Pietrasiewicz 11 年之前
父節點
當前提交
7419485f19
共有 3 個文件被更改,包括 226 次插入0 次删除
  1. 21 0
      Documentation/ABI/testing/configfs-usb-gadget
  2. 201 0
      drivers/usb/gadget/configfs.c
  3. 4 0
      include/linux/usb/composite.h

+ 21 - 0
Documentation/ABI/testing/configfs-usb-gadget

@@ -75,6 +75,27 @@ Description:
 		compatible_id		- 8-byte string for "Compatible ID"
 		compatible_id		- 8-byte string for "Compatible ID"
 		sub_compatible_id	- 8-byte string for "Sub Compatible ID"
 		sub_compatible_id	- 8-byte string for "Sub Compatible ID"
 
 
+What:		/config/usb-gadget/gadget/functions/<func>.<inst>/interface.<n>/<property>
+Date:		May 2014
+KernelVersion:	3.16
+Description:
+		This group contains "Extended Property Descriptors" specific for one
+		gadget's USB interface or one interface group described
+		by an IAD.
+
+		The attributes:
+
+		type		- value 1..7 for interpreting the data
+				1: unicode string
+				2: unicode string with environment variable
+				3: binary
+				4: little-endian 32-bit
+				5: big-endian 32-bit
+				6: unicode string with a symbolic link
+				7: multiple unicode strings
+		data		- blob of data to be interpreted depending on
+				type
+
 What:		/config/usb-gadget/gadget/strings
 What:		/config/usb-gadget/gadget/strings
 Date:		Jun 2013
 Date:		Jun 2013
 KernelVersion:	3.11
 KernelVersion:	3.11

+ 201 - 0
drivers/usb/gadget/configfs.c

@@ -7,6 +7,7 @@
 #include <linux/usb/gadget_configfs.h>
 #include <linux/usb/gadget_configfs.h>
 #include "configfs.h"
 #include "configfs.h"
 #include "u_f.h"
 #include "u_f.h"
+#include "u_os_desc.h"
 
 
 int check_user_usb_string(const char *name,
 int check_user_usb_string(const char *name,
 		struct usb_gadget_strings *stringtab_dev)
 		struct usb_gadget_strings *stringtab_dev)
@@ -941,6 +942,204 @@ static struct config_item_type os_desc_type = {
 CONFIGFS_ATTR_STRUCT(usb_os_desc);
 CONFIGFS_ATTR_STRUCT(usb_os_desc);
 CONFIGFS_ATTR_OPS(usb_os_desc);
 CONFIGFS_ATTR_OPS(usb_os_desc);
 
 
+
+static inline struct usb_os_desc_ext_prop
+*to_usb_os_desc_ext_prop(struct config_item *item)
+{
+	return container_of(item, struct usb_os_desc_ext_prop, item);
+}
+
+CONFIGFS_ATTR_STRUCT(usb_os_desc_ext_prop);
+CONFIGFS_ATTR_OPS(usb_os_desc_ext_prop);
+
+static ssize_t ext_prop_type_show(struct usb_os_desc_ext_prop *ext_prop,
+				  char *page)
+{
+	return sprintf(page, "%d", ext_prop->type);
+}
+
+static ssize_t ext_prop_type_store(struct usb_os_desc_ext_prop *ext_prop,
+				   const char *page, size_t len)
+{
+	struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent);
+	u8 type;
+	int ret;
+
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	ret = kstrtou8(page, 0, &type);
+	if (ret)
+		goto end;
+	if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if ((ext_prop->type == USB_EXT_PROP_BINARY ||
+	    ext_prop->type == USB_EXT_PROP_LE32 ||
+	    ext_prop->type == USB_EXT_PROP_BE32) &&
+	    (type == USB_EXT_PROP_UNICODE ||
+	    type == USB_EXT_PROP_UNICODE_ENV ||
+	    type == USB_EXT_PROP_UNICODE_LINK))
+		ext_prop->data_len <<= 1;
+	else if ((ext_prop->type == USB_EXT_PROP_UNICODE ||
+		   ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
+		   ext_prop->type == USB_EXT_PROP_UNICODE_LINK) &&
+		   (type == USB_EXT_PROP_BINARY ||
+		   type == USB_EXT_PROP_LE32 ||
+		   type == USB_EXT_PROP_BE32))
+		ext_prop->data_len >>= 1;
+	ext_prop->type = type;
+	ret = len;
+
+end:
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+	return ret;
+}
+
+static ssize_t ext_prop_data_show(struct usb_os_desc_ext_prop *ext_prop,
+				  char *page)
+{
+	int len = ext_prop->data_len;
+
+	if (ext_prop->type == USB_EXT_PROP_UNICODE ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_LINK)
+		len >>= 1;
+	memcpy(page, ext_prop->data, len);
+
+	return len;
+}
+
+static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop,
+				   const char *page, size_t len)
+{
+	struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent);
+	char *new_data;
+	size_t ret_len = len;
+
+	if (page[len - 1] == '\n' || page[len - 1] == '\0')
+		--len;
+	new_data = kzalloc(len, GFP_KERNEL);
+	if (!new_data)
+		return -ENOMEM;
+
+	memcpy(new_data, page, len);
+
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	kfree(ext_prop->data);
+	ext_prop->data = new_data;
+	desc->ext_prop_len -= ext_prop->data_len;
+	ext_prop->data_len = len;
+	desc->ext_prop_len += ext_prop->data_len;
+	if (ext_prop->type == USB_EXT_PROP_UNICODE ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_LINK) {
+		desc->ext_prop_len -= ext_prop->data_len;
+		ext_prop->data_len <<= 1;
+		ext_prop->data_len += 2;
+		desc->ext_prop_len += ext_prop->data_len;
+	}
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+	return ret_len;
+}
+
+static struct usb_os_desc_ext_prop_attribute ext_prop_type =
+	__CONFIGFS_ATTR(type, S_IRUGO | S_IWUSR,
+			ext_prop_type_show, ext_prop_type_store);
+
+static struct usb_os_desc_ext_prop_attribute ext_prop_data =
+	__CONFIGFS_ATTR(data, S_IRUGO | S_IWUSR,
+			ext_prop_data_show, ext_prop_data_store);
+
+static struct configfs_attribute *ext_prop_attrs[] = {
+	&ext_prop_type.attr,
+	&ext_prop_data.attr,
+	NULL,
+};
+
+static void usb_os_desc_ext_prop_release(struct config_item *item)
+{
+	struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
+
+	kfree(ext_prop); /* frees a whole chunk */
+}
+
+static struct configfs_item_operations ext_prop_ops = {
+	.release		= usb_os_desc_ext_prop_release,
+	.show_attribute		= usb_os_desc_ext_prop_attr_show,
+	.store_attribute	= usb_os_desc_ext_prop_attr_store,
+};
+
+static struct config_item *ext_prop_make(
+		struct config_group *group,
+		const char *name)
+{
+	struct usb_os_desc_ext_prop *ext_prop;
+	struct config_item_type *ext_prop_type;
+	struct usb_os_desc *desc;
+	char *vlabuf;
+
+	vla_group(data_chunk);
+	vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1);
+	vla_item(data_chunk, struct config_item_type, ext_prop_type, 1);
+
+	vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
+	if (!vlabuf)
+		return ERR_PTR(-ENOMEM);
+
+	ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop);
+	ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type);
+
+	desc = container_of(group, struct usb_os_desc, group);
+	ext_prop_type->ct_item_ops = &ext_prop_ops;
+	ext_prop_type->ct_attrs = ext_prop_attrs;
+	ext_prop_type->ct_owner = desc->owner;
+
+	config_item_init_type_name(&ext_prop->item, name, ext_prop_type);
+
+	ext_prop->name = kstrdup(name, GFP_KERNEL);
+	if (!ext_prop->name) {
+		kfree(vlabuf);
+		return ERR_PTR(-ENOMEM);
+	}
+	desc->ext_prop_len += 14;
+	ext_prop->name_len = 2 * strlen(ext_prop->name) + 2;
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	desc->ext_prop_len += ext_prop->name_len;
+	list_add_tail(&ext_prop->entry, &desc->ext_prop);
+	++desc->ext_prop_count;
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+
+	return &ext_prop->item;
+}
+
+static void ext_prop_drop(struct config_group *group, struct config_item *item)
+{
+	struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
+	struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item);
+
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	list_del(&ext_prop->entry);
+	--desc->ext_prop_count;
+	kfree(ext_prop->name);
+	desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14);
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+	config_item_put(item);
+}
+
+static struct configfs_group_operations interf_grp_ops = {
+	.make_item	= &ext_prop_make,
+	.drop_item	= &ext_prop_drop,
+};
+
 static struct configfs_item_operations interf_item_ops = {
 static struct configfs_item_operations interf_item_ops = {
 	.show_attribute		= usb_os_desc_attr_show,
 	.show_attribute		= usb_os_desc_attr_show,
 	.store_attribute	= usb_os_desc_attr_store,
 	.store_attribute	= usb_os_desc_attr_store,
@@ -1048,6 +1247,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent,
 
 
 	os_desc_group->default_groups = interface_groups;
 	os_desc_group->default_groups = interface_groups;
 	interface_type->ct_item_ops = &interf_item_ops;
 	interface_type->ct_item_ops = &interf_item_ops;
+	interface_type->ct_group_ops = &interf_grp_ops;
 	interface_type->ct_attrs = interf_grp_attrs;
 	interface_type->ct_attrs = interf_grp_attrs;
 	interface_type->ct_owner = owner;
 	interface_type->ct_owner = owner;
 
 
@@ -1055,6 +1255,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent,
 		struct usb_os_desc *d;
 		struct usb_os_desc *d;
 
 
 		d = desc[n_interf];
 		d = desc[n_interf];
+		d->owner = owner;
 		config_group_init_type_name(&d->group, "", interface_type);
 		config_group_init_type_name(&d->group, "", interface_type);
 		config_item_set_name(&d->group.cg_item, "interface.%d",
 		config_item_set_name(&d->group.cg_item, "interface.%d",
 				     n_interf);
 				     n_interf);

+ 4 - 0
include/linux/usb/composite.h

@@ -64,6 +64,7 @@ struct usb_configuration;
  * @name: Extended Property name
  * @name: Extended Property name
  * @data_len: Length of Extended Property blob (for unicode store double len)
  * @data_len: Length of Extended Property blob (for unicode store double len)
  * @data: Extended Property blob
  * @data: Extended Property blob
+ * @item: Represents this Extended Property in configfs
  */
  */
 struct usb_os_desc_ext_prop {
 struct usb_os_desc_ext_prop {
 	struct list_head	entry;
 	struct list_head	entry;
@@ -72,6 +73,7 @@ struct usb_os_desc_ext_prop {
 	char			*name;
 	char			*name;
 	int			data_len;
 	int			data_len;
 	char			*data;
 	char			*data;
+	struct config_item	item;
 };
 };
 
 
 /**
 /**
@@ -82,6 +84,7 @@ struct usb_os_desc_ext_prop {
  * @ext_prop_count: Number of Extended Properties
  * @ext_prop_count: Number of Extended Properties
  * @opts_mutex: Optional mutex protecting config data of a usb_function_instance
  * @opts_mutex: Optional mutex protecting config data of a usb_function_instance
  * @group: Represents OS descriptors associated with an interface in configfs
  * @group: Represents OS descriptors associated with an interface in configfs
+ * @owner: Module associated with this OS descriptor
  */
  */
 struct usb_os_desc {
 struct usb_os_desc {
 	char			*ext_compat_id;
 	char			*ext_compat_id;
@@ -90,6 +93,7 @@ struct usb_os_desc {
 	int			ext_prop_count;
 	int			ext_prop_count;
 	struct mutex		*opts_mutex;
 	struct mutex		*opts_mutex;
 	struct config_group	group;
 	struct config_group	group;
+	struct module		*owner;
 };
 };
 
 
 /**
 /**