diff options
Diffstat (limited to 'drivers/usb/roles/class.c')
| -rw-r--r-- | drivers/usb/roles/class.c | 101 |
1 files changed, 79 insertions, 22 deletions
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index dfaed7eee94f..30482d4cf826 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -7,19 +7,26 @@ * Hans de Goede <hdegoede@redhat.com> */ +#include <linux/component.h> #include <linux/usb/role.h> #include <linux/property.h> #include <linux/device.h> +#include <linux/lockdep.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> -static struct class *role_class; +static const struct class role_class = { + .name = "usb_role", +}; struct usb_role_switch { struct device dev; + struct lock_class_key key; struct mutex lock; /* device lock*/ + struct module *module; /* the module this device depends on */ enum usb_role role; + bool registered; /* From descriptor */ struct device *usb2_port; @@ -32,6 +39,32 @@ struct usb_role_switch { #define to_role_switch(d) container_of(d, struct usb_role_switch, dev) +static int connector_bind(struct device *dev, struct device *connector, void *data) +{ + int ret; + + ret = sysfs_create_link(&dev->kobj, &connector->kobj, "connector"); + if (ret) + return ret; + + ret = sysfs_create_link(&connector->kobj, &dev->kobj, "usb-role-switch"); + if (ret) + sysfs_remove_link(&dev->kobj, "connector"); + + return ret; +} + +static void connector_unbind(struct device *dev, struct device *connector, void *data) +{ + sysfs_remove_link(&connector->kobj, "usb-role-switch"); + sysfs_remove_link(&dev->kobj, "connector"); +} + +static const struct component_ops connector_ops = { + .bind = connector_bind, + .unbind = connector_unbind, +}; + /** * usb_role_switch_set_role - Set USB role for a switch * @sw: USB role switch @@ -46,6 +79,9 @@ int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) if (IS_ERR_OR_NULL(sw)) return 0; + if (!sw->registered) + return -EOPNOTSUPP; + mutex_lock(&sw->lock); ret = sw->set(sw, role); @@ -71,7 +107,7 @@ enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) { enum usb_role role; - if (IS_ERR_OR_NULL(sw)) + if (IS_ERR_OR_NULL(sw) || !sw->registered) return USB_ROLE_NONE; mutex_lock(&sw->lock); @@ -87,7 +123,7 @@ enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) } EXPORT_SYMBOL_GPL(usb_role_switch_get_role); -static void *usb_role_switch_match(struct fwnode_handle *fwnode, const char *id, +static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const char *id, void *data) { struct device *dev; @@ -95,7 +131,7 @@ static void *usb_role_switch_match(struct fwnode_handle *fwnode, const char *id, if (id && !fwnode_property_present(fwnode, id)) return NULL; - dev = class_find_device_by_fwnode(role_class, fwnode); + dev = class_find_device_by_fwnode(&role_class, fwnode); return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -106,10 +142,13 @@ usb_role_switch_is_parent(struct fwnode_handle *fwnode) struct fwnode_handle *parent = fwnode_get_parent(fwnode); struct device *dev; - if (!parent || !fwnode_property_present(parent, "usb-role-switch")) + if (!fwnode_property_present(parent, "usb-role-switch")) { + fwnode_handle_put(parent); return NULL; + } - dev = class_find_device_by_fwnode(role_class, parent); + dev = class_find_device_by_fwnode(&role_class, parent); + fwnode_handle_put(parent); return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -130,7 +169,7 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev) usb_role_switch_match); if (!IS_ERR_OR_NULL(sw)) - WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); + WARN_ON(!try_module_get(sw->module)); return sw; } @@ -152,7 +191,7 @@ struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode) sw = fwnode_connection_find_match(fwnode, "usb-role-switch", NULL, usb_role_switch_match); if (!IS_ERR_OR_NULL(sw)) - WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); + WARN_ON(!try_module_get(sw->module)); return sw; } @@ -167,7 +206,7 @@ EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get); void usb_role_switch_put(struct usb_role_switch *sw) { if (!IS_ERR_OR_NULL(sw)) { - module_put(sw->dev.parent->driver->owner); + module_put(sw->module); put_device(&sw->dev); } } @@ -184,15 +223,18 @@ struct usb_role_switch * usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode) { struct device *dev; + struct usb_role_switch *sw = NULL; if (!fwnode) return NULL; - dev = class_find_device_by_fwnode(role_class, fwnode); - if (dev) - WARN_ON(!try_module_get(dev->parent->driver->owner)); + dev = class_find_device_by_fwnode(&role_class, fwnode); + if (dev) { + sw = to_role_switch(dev); + WARN_ON(!try_module_get(sw->module)); + } - return dev ? to_role_switch(dev) : NULL; + return sw; } EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode); @@ -271,8 +313,7 @@ static const struct attribute_group *usb_role_switch_groups[] = { NULL, }; -static int -usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) +static int usb_role_switch_uevent(const struct device *dev, struct kobj_uevent_env *env) { int ret; @@ -287,6 +328,8 @@ static void usb_role_switch_release(struct device *dev) { struct usb_role_switch *sw = to_role_switch(dev); + mutex_destroy(&sw->lock); + lockdep_unregister_key(&sw->key); kfree(sw); } @@ -325,7 +368,8 @@ usb_role_switch_register(struct device *parent, if (!sw) return ERR_PTR(-ENOMEM); - mutex_init(&sw->lock); + lockdep_register_key(&sw->key); + mutex_init_with_key(&sw->lock, &sw->key); sw->allow_userspace_control = desc->allow_userspace_control; sw->usb2_port = desc->usb2_port; @@ -334,20 +378,30 @@ usb_role_switch_register(struct device *parent, sw->set = desc->set; sw->get = desc->get; + sw->module = parent->driver->owner; sw->dev.parent = parent; sw->dev.fwnode = desc->fwnode; - sw->dev.class = role_class; + sw->dev.class = &role_class; sw->dev.type = &usb_role_dev_type; dev_set_drvdata(&sw->dev, desc->driver_data); dev_set_name(&sw->dev, "%s-role-switch", desc->name ? desc->name : dev_name(parent)); + sw->registered = true; + ret = device_register(&sw->dev); if (ret) { + sw->registered = false; put_device(&sw->dev); return ERR_PTR(ret); } + if (dev_fwnode(&sw->dev)) { + ret = component_add(&sw->dev, &connector_ops); + if (ret) + dev_warn(&sw->dev, "failed to add component\n"); + } + /* TODO: Symlinks for the host port and the device controller. */ return sw; @@ -362,8 +416,12 @@ EXPORT_SYMBOL_GPL(usb_role_switch_register); */ void usb_role_switch_unregister(struct usb_role_switch *sw) { - if (!IS_ERR_OR_NULL(sw)) - device_unregister(&sw->dev); + if (IS_ERR_OR_NULL(sw)) + return; + sw->registered = false; + if (dev_fwnode(&sw->dev)) + component_del(&sw->dev, &connector_ops); + device_unregister(&sw->dev); } EXPORT_SYMBOL_GPL(usb_role_switch_unregister); @@ -390,14 +448,13 @@ EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata); static int __init usb_roles_init(void) { - role_class = class_create(THIS_MODULE, "usb_role"); - return PTR_ERR_OR_ZERO(role_class); + return class_register(&role_class); } subsys_initcall(usb_roles_init); static void __exit usb_roles_exit(void) { - class_destroy(role_class); + class_unregister(&role_class); } module_exit(usb_roles_exit); |
