aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget13
-rw-r--r--drivers/usb/gadget/configfs.c190
-rw-r--r--drivers/usb/gadget/configfs.h12
-rw-r--r--include/linux/usb/composite.h6
4 files changed, 221 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget b/Documentation/ABI/testing/configfs-usb-gadget
index 0e7b786f24ac..5c0b3e6eb981 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget
+++ b/Documentation/ABI/testing/configfs-usb-gadget
@@ -62,6 +62,19 @@ KernelVersion: 3.11
Description:
This group contains functions available to this USB gadget.
+What: /config/usb-gadget/gadget/functions/<func>.<inst>/interface.<n>
+Date: May 2014
+KernelVersion: 3.16
+Description:
+ This group contains "Feature Descriptors" specific for one
+ gadget's USB interface or one interface group described
+ by an IAD.
+
+ The attributes:
+
+ compatible_id - 8-byte string for "Compatible ID"
+ sub_compatible_id - 8-byte string for "Sub Compatible ID"
+
What: /config/usb-gadget/gadget/strings
Date: Jun 2013
KernelVersion: 3.11
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 8b9e038ac22b..fa6cb06cca09 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -6,6 +6,7 @@
#include <linux/usb/composite.h>
#include <linux/usb/gadget_configfs.h>
#include "configfs.h"
+#include "u_f.h"
int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
@@ -872,10 +873,63 @@ static void os_desc_attr_release(struct config_item *item)
kfree(os_desc);
}
+static int os_desc_link(struct config_item *os_desc_ci,
+ struct config_item *usb_cfg_ci)
+{
+ struct gadget_info *gi = container_of(to_config_group(os_desc_ci),
+ struct gadget_info, os_desc_group);
+ struct usb_composite_dev *cdev = &gi->cdev;
+ struct config_usb_cfg *c_target =
+ container_of(to_config_group(usb_cfg_ci),
+ struct config_usb_cfg, group);
+ struct usb_configuration *c;
+ int ret;
+
+ mutex_lock(&gi->lock);
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c == &c_target->c)
+ break;
+ }
+ if (c != &c_target->c) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (cdev->os_desc_config) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ cdev->os_desc_config = &c_target->c;
+ ret = 0;
+
+out:
+ mutex_unlock(&gi->lock);
+ return ret;
+}
+
+static int os_desc_unlink(struct config_item *os_desc_ci,
+ struct config_item *usb_cfg_ci)
+{
+ struct gadget_info *gi = container_of(to_config_group(os_desc_ci),
+ struct gadget_info, os_desc_group);
+ struct usb_composite_dev *cdev = &gi->cdev;
+
+ mutex_lock(&gi->lock);
+ if (gi->udc_name)
+ unregister_gadget(gi);
+ cdev->os_desc_config = NULL;
+ WARN_ON(gi->udc_name);
+ mutex_unlock(&gi->lock);
+ return 0;
+}
+
static struct configfs_item_operations os_desc_ops = {
.release = os_desc_attr_release,
.show_attribute = os_desc_attr_show,
.store_attribute = os_desc_attr_store,
+ .allow_link = os_desc_link,
+ .drop_link = os_desc_unlink,
};
static struct config_item_type os_desc_type = {
@@ -884,6 +938,133 @@ static struct config_item_type os_desc_type = {
.ct_owner = THIS_MODULE,
};
+CONFIGFS_ATTR_STRUCT(usb_os_desc);
+CONFIGFS_ATTR_OPS(usb_os_desc);
+
+static struct configfs_item_operations interf_item_ops = {
+ .show_attribute = usb_os_desc_attr_show,
+ .store_attribute = usb_os_desc_attr_store,
+};
+
+static ssize_t rndis_grp_compatible_id_show(struct usb_os_desc *desc,
+ char *page)
+{
+ memcpy(page, desc->ext_compat_id, 8);
+ return 8;
+}
+
+static ssize_t rndis_grp_compatible_id_store(struct usb_os_desc *desc,
+ const char *page, size_t len)
+{
+ int l;
+
+ l = min_t(int, 8, len);
+ if (page[l - 1] == '\n')
+ --l;
+ if (desc->opts_mutex)
+ mutex_lock(desc->opts_mutex);
+ memcpy(desc->ext_compat_id, page, l);
+ desc->ext_compat_id[l] = '\0';
+
+ if (desc->opts_mutex)
+ mutex_unlock(desc->opts_mutex);
+
+ return len;
+}
+
+static struct usb_os_desc_attribute rndis_grp_attr_compatible_id =
+ __CONFIGFS_ATTR(compatible_id, S_IRUGO | S_IWUSR,
+ rndis_grp_compatible_id_show,
+ rndis_grp_compatible_id_store);
+
+static ssize_t rndis_grp_sub_compatible_id_show(struct usb_os_desc *desc,
+ char *page)
+{
+ memcpy(page, desc->ext_compat_id + 8, 8);
+ return 8;
+}
+
+static ssize_t rndis_grp_sub_compatible_id_store(struct usb_os_desc *desc,
+ const char *page, size_t len)
+{
+ int l;
+
+ l = min_t(int, 8, len);
+ if (page[l - 1] == '\n')
+ --l;
+ if (desc->opts_mutex)
+ mutex_lock(desc->opts_mutex);
+ memcpy(desc->ext_compat_id + 8, page, l);
+ desc->ext_compat_id[l + 8] = '\0';
+
+ if (desc->opts_mutex)
+ mutex_unlock(desc->opts_mutex);
+
+ return len;
+}
+
+static struct usb_os_desc_attribute rndis_grp_attr_sub_compatible_id =
+ __CONFIGFS_ATTR(sub_compatible_id, S_IRUGO | S_IWUSR,
+ rndis_grp_sub_compatible_id_show,
+ rndis_grp_sub_compatible_id_store);
+
+static struct configfs_attribute *interf_grp_attrs[] = {
+ &rndis_grp_attr_compatible_id.attr,
+ &rndis_grp_attr_sub_compatible_id.attr,
+ NULL
+};
+
+int usb_os_desc_prepare_interf_dir(struct config_group *parent,
+ int n_interf,
+ struct usb_os_desc **desc,
+ struct module *owner)
+{
+ struct config_group **f_default_groups, *os_desc_group,
+ **interface_groups;
+ struct config_item_type *os_desc_type, *interface_type;
+
+ vla_group(data_chunk);
+ vla_item(data_chunk, struct config_group *, f_default_groups, 2);
+ vla_item(data_chunk, struct config_group, os_desc_group, 1);
+ vla_item(data_chunk, struct config_group *, interface_groups,
+ n_interf + 1);
+ vla_item(data_chunk, struct config_item_type, os_desc_type, 1);
+ vla_item(data_chunk, struct config_item_type, interface_type, 1);
+
+ char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
+ if (!vlabuf)
+ return -ENOMEM;
+
+ f_default_groups = vla_ptr(vlabuf, data_chunk, f_default_groups);
+ os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group);
+ os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type);
+ interface_groups = vla_ptr(vlabuf, data_chunk, interface_groups);
+ interface_type = vla_ptr(vlabuf, data_chunk, interface_type);
+
+ parent->default_groups = f_default_groups;
+ os_desc_type->ct_owner = owner;
+ config_group_init_type_name(os_desc_group, "os_desc", os_desc_type);
+ f_default_groups[0] = os_desc_group;
+
+ os_desc_group->default_groups = interface_groups;
+ interface_type->ct_item_ops = &interf_item_ops;
+ interface_type->ct_attrs = interf_grp_attrs;
+ interface_type->ct_owner = owner;
+
+ while (n_interf--) {
+ struct usb_os_desc *d;
+
+ d = desc[n_interf];
+ config_group_init_type_name(&d->group, "", interface_type);
+ config_item_set_name(&d->group.cg_item, "interface.%d",
+ n_interf);
+ interface_groups[n_interf] = &d->group;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
+
static int configfs_do_nothing(struct usb_composite_dev *cdev)
{
WARN_ON(1);
@@ -893,6 +1074,9 @@ static int configfs_do_nothing(struct usb_composite_dev *cdev)
int composite_dev_prepare(struct usb_composite_driver *composite,
struct usb_composite_dev *dev);
+int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
+ struct usb_ep *ep0);
+
static void purge_configs_funcs(struct gadget_info *gi)
{
struct usb_configuration *c;
@@ -1028,6 +1212,12 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
}
usb_ep_autoconfig_reset(cdev->gadget);
}
+ if (cdev->use_os_string) {
+ ret = composite_os_desc_req_prepare(cdev, gadget->ep0);
+ if (ret)
+ goto err_purge_funcs;
+ }
+
usb_ep_autoconfig_reset(cdev->gadget);
return 0;
diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h
index a7b564a913d1..a14ac792c698 100644
--- a/drivers/usb/gadget/configfs.h
+++ b/drivers/usb/gadget/configfs.h
@@ -1,6 +1,18 @@
#ifndef USB__GADGET__CONFIGFS__H
#define USB__GADGET__CONFIGFS__H
+#include <linux/configfs.h>
+
void unregister_gadget_item(struct config_item *item);
+int usb_os_desc_prepare_interf_dir(struct config_group *parent,
+ int n_interf,
+ struct usb_os_desc **desc,
+ struct module *owner);
+
+static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct usb_os_desc, group);
+}
+
#endif /* USB__GADGET__CONFIGFS__H */
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 549f5382b01a..9c3903d76781 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -80,12 +80,16 @@ struct usb_os_desc_ext_prop {
* @ext_prop: Extended Properties list
* @ext_prop_len: Total length of Extended Properties blobs
* @ext_prop_count: Number of Extended Properties
+ * @opts_mutex: Optional mutex protecting config data of a usb_function_instance
+ * @group: Represents OS descriptors associated with an interface in configfs
*/
struct usb_os_desc {
char *ext_compat_id;
struct list_head ext_prop;
int ext_prop_len;
int ext_prop_count;
+ struct mutex *opts_mutex;
+ struct config_group group;
};
/**
@@ -381,6 +385,8 @@ extern void usb_composite_unregister(struct usb_composite_driver *driver);
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
extern int composite_dev_prepare(struct usb_composite_driver *composite,
struct usb_composite_dev *cdev);
+extern int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
+ struct usb_ep *ep0);
void composite_dev_cleanup(struct usb_composite_dev *cdev);
static inline struct usb_composite_driver *to_cdriver(