diff options
-rw-r--r-- | drivers/base/base.h | 5 | ||||
-rw-r--r-- | drivers/base/core.c | 132 | ||||
-rw-r--r-- | drivers/base/dd.c | 4 | ||||
-rw-r--r-- | drivers/input/keyboard/gpio_keys.c | 16 | ||||
-rw-r--r-- | drivers/input/misc/axp20x-pek.c | 18 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f01.c | 11 | ||||
-rw-r--r-- | include/linux/device.h | 30 | ||||
-rw-r--r-- | include/linux/kobject.h | 2 | ||||
-rw-r--r-- | lib/kobject_uevent.c | 2 |
9 files changed, 176 insertions, 44 deletions
diff --git a/drivers/base/base.h b/drivers/base/base.h index e19b1008e5fb..539432a14b5c 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -126,11 +126,6 @@ extern int driver_add_groups(struct device_driver *drv, extern void driver_remove_groups(struct device_driver *drv, const struct attribute_group **groups); -extern int device_add_groups(struct device *dev, - const struct attribute_group **groups); -extern void device_remove_groups(struct device *dev, - const struct attribute_group **groups); - extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index 755451f684bc..cb4b5b1f1b09 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1023,12 +1023,144 @@ int device_add_groups(struct device *dev, const struct attribute_group **groups) { return sysfs_create_groups(&dev->kobj, groups); } +EXPORT_SYMBOL_GPL(device_add_groups); void device_remove_groups(struct device *dev, const struct attribute_group **groups) { sysfs_remove_groups(&dev->kobj, groups); } +EXPORT_SYMBOL_GPL(device_remove_groups); + +union device_attr_group_devres { + const struct attribute_group *group; + const struct attribute_group **groups; +}; + +static int devm_attr_group_match(struct device *dev, void *res, void *data) +{ + return ((union device_attr_group_devres *)res)->group == data; +} + +static void devm_attr_group_remove(struct device *dev, void *res) +{ + union device_attr_group_devres *devres = res; + const struct attribute_group *group = devres->group; + + dev_dbg(dev, "%s: removing group %p\n", __func__, group); + sysfs_remove_group(&dev->kobj, group); +} + +static void devm_attr_groups_remove(struct device *dev, void *res) +{ + union device_attr_group_devres *devres = res; + const struct attribute_group **groups = devres->groups; + + dev_dbg(dev, "%s: removing groups %p\n", __func__, groups); + sysfs_remove_groups(&dev->kobj, groups); +} + +/** + * devm_device_add_group - given a device, create a managed attribute group + * @dev: The device to create the group for + * @grp: The attribute group to create + * + * This function creates a group for the first time. It will explicitly + * warn and error if any of the attribute files being created already exist. + * + * Returns 0 on success or error code on failure. + */ +int devm_device_add_group(struct device *dev, const struct attribute_group *grp) +{ + union device_attr_group_devres *devres; + int error; + + devres = devres_alloc(devm_attr_group_remove, + sizeof(*devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + error = sysfs_create_group(&dev->kobj, grp); + if (error) { + devres_free(devres); + return error; + } + + devres->group = grp; + devres_add(dev, devres); + return 0; +} +EXPORT_SYMBOL_GPL(devm_device_add_group); + +/** + * devm_device_remove_group: remove a managed group from a device + * @dev: device to remove the group from + * @grp: group to remove + * + * This function removes a group of attributes from a device. The attributes + * previously have to have been created for this group, otherwise it will fail. + */ +void devm_device_remove_group(struct device *dev, + const struct attribute_group *grp) +{ + WARN_ON(devres_release(dev, devm_attr_group_remove, + devm_attr_group_match, + /* cast away const */ (void *)grp)); +} +EXPORT_SYMBOL_GPL(devm_device_remove_group); + +/** + * devm_device_add_groups - create a bunch of managed attribute groups + * @dev: The device to create the group for + * @groups: The attribute groups to create, NULL terminated + * + * This function creates a bunch of managed attribute groups. If an error + * occurs when creating a group, all previously created groups will be + * removed, unwinding everything back to the original state when this + * function was called. It will explicitly warn and error if any of the + * attribute files being created already exist. + * + * Returns 0 on success or error code from sysfs_create_group on failure. + */ +int devm_device_add_groups(struct device *dev, + const struct attribute_group **groups) +{ + union device_attr_group_devres *devres; + int error; + + devres = devres_alloc(devm_attr_groups_remove, + sizeof(*devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + error = sysfs_create_groups(&dev->kobj, groups); + if (error) { + devres_free(devres); + return error; + } + + devres->groups = groups; + devres_add(dev, devres); + return 0; +} +EXPORT_SYMBOL_GPL(devm_device_add_groups); + +/** + * devm_device_remove_groups - remove a list of managed groups + * + * @dev: The device for the groups to be removed from + * @groups: NULL terminated list of groups to be removed + * + * If groups is not NULL, remove the specified groups from the device. + */ +void devm_device_remove_groups(struct device *dev, + const struct attribute_group **groups) +{ + WARN_ON(devres_release(dev, devm_attr_groups_remove, + devm_attr_group_match, + /* cast away const */ (void *)groups)); +} +EXPORT_SYMBOL_GPL(devm_device_remove_groups); static int device_add_attrs(struct device *dev) { diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 4882f06d12df..c17fefc77345 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -259,6 +259,8 @@ static void driver_bound(struct device *dev) if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); + + kobject_uevent(&dev->kobj, KOBJ_BIND); } static int driver_sysfs_add(struct device *dev) @@ -848,6 +850,8 @@ static void __device_release_driver(struct device *dev, struct device *parent) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_UNBOUND_DRIVER, dev); + + kobject_uevent(&dev->kobj, KOBJ_UNBIND); } } diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index a047b9af8369..0b10d4b356db 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -827,7 +827,7 @@ static int gpio_keys_probe(struct platform_device *pdev) fwnode_handle_put(child); - error = sysfs_create_group(&dev->kobj, &gpio_keys_attr_group); + error = devm_device_add_group(dev, &gpio_keys_attr_group); if (error) { dev_err(dev, "Unable to export keys/switches, error: %d\n", error); @@ -838,23 +838,12 @@ static int gpio_keys_probe(struct platform_device *pdev) if (error) { dev_err(dev, "Unable to register input device, error: %d\n", error); - goto err_remove_group; + return error; } device_init_wakeup(dev, wakeup); return 0; - -err_remove_group: - sysfs_remove_group(&dev->kobj, &gpio_keys_attr_group); - return error; -} - -static int gpio_keys_remove(struct platform_device *pdev) -{ - sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); - - return 0; } static int __maybe_unused gpio_keys_suspend(struct device *dev) @@ -912,7 +901,6 @@ static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, - .remove = gpio_keys_remove, .driver = { .name = "gpio-keys", .pm = &gpio_keys_pm_ops, diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index 38c79ebff033..cfeb0e943de6 100644 --- a/drivers/input/misc/axp20x-pek.c +++ b/drivers/input/misc/axp20x-pek.c @@ -182,13 +182,6 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr) return IRQ_HANDLED; } -static void axp20x_remove_sysfs_group(void *_data) -{ - struct device *dev = _data; - - sysfs_remove_group(&dev->kobj, &axp20x_attribute_group); -} - static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, struct platform_device *pdev) { @@ -313,22 +306,13 @@ static int axp20x_pek_probe(struct platform_device *pdev) return error; } - error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group); + error = devm_device_add_group(&pdev->dev, &axp20x_attribute_group); if (error) { dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n", error); return error; } - error = devm_add_action(&pdev->dev, - axp20x_remove_sysfs_group, &pdev->dev); - if (error) { - axp20x_remove_sysfs_group(&pdev->dev); - dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n", - error); - return error; - } - platform_set_drvdata(pdev, axp20x_pek); return 0; diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index 7f7e9176f7ea..6dca3c0fbb4a 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -570,18 +570,14 @@ static int rmi_f01_probe(struct rmi_function *fn) dev_set_drvdata(&fn->dev, f01); - error = sysfs_create_group(&fn->rmi_dev->dev.kobj, &rmi_f01_attr_group); + error = devm_device_add_group(&fn->rmi_dev->dev, &rmi_f01_attr_group); if (error) - dev_warn(&fn->dev, "Failed to create sysfs group: %d\n", error); + dev_warn(&fn->dev, + "Failed to create attribute group: %d\n", error); return 0; } -static void rmi_f01_remove(struct rmi_function *fn) -{ - sysfs_remove_group(&fn->rmi_dev->dev.kobj, &rmi_f01_attr_group); -} - static int rmi_f01_config(struct rmi_function *fn) { struct f01_data *f01 = dev_get_drvdata(&fn->dev); @@ -721,7 +717,6 @@ struct rmi_function_handler rmi_f01_handler = { }, .func = 0x01, .probe = rmi_f01_probe, - .remove = rmi_f01_remove, .config = rmi_f01_config, .attention = rmi_f01_attention, .suspend = rmi_f01_suspend, diff --git a/include/linux/device.h b/include/linux/device.h index 723cd54b94da..c29dd5ec7f6a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1200,6 +1200,36 @@ struct device *device_create_with_groups(struct class *cls, const char *fmt, ...); extern void device_destroy(struct class *cls, dev_t devt); +extern int __must_check device_add_groups(struct device *dev, + const struct attribute_group **groups); +extern void device_remove_groups(struct device *dev, + const struct attribute_group **groups); + +static inline int __must_check device_add_group(struct device *dev, + const struct attribute_group *grp) +{ + const struct attribute_group *groups[] = { grp, NULL }; + + return device_add_groups(dev, groups); +} + +static inline void device_remove_group(struct device *dev, + const struct attribute_group *grp) +{ + const struct attribute_group *groups[] = { grp, NULL }; + + return device_remove_groups(dev, groups); +} + +extern int __must_check devm_device_add_groups(struct device *dev, + const struct attribute_group **groups); +extern void devm_device_remove_groups(struct device *dev, + const struct attribute_group **groups); +extern int __must_check devm_device_add_group(struct device *dev, + const struct attribute_group *grp); +extern void devm_device_remove_group(struct device *dev, + const struct attribute_group *grp); + /* * Platform "fixup" functions - allow the platform to have their say * about devices and actions that the general device layer doesn't diff --git a/include/linux/kobject.h b/include/linux/kobject.h index eeab34b0f589..9e05c8b2c287 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -57,6 +57,8 @@ enum kobject_action { KOBJ_MOVE, KOBJ_ONLINE, KOBJ_OFFLINE, + KOBJ_BIND, + KOBJ_UNBIND, KOBJ_MAX }; diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 719c155fce20..e590523ea476 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -52,6 +52,8 @@ static const char *kobject_actions[] = { [KOBJ_MOVE] = "move", [KOBJ_ONLINE] = "online", [KOBJ_OFFLINE] = "offline", + [KOBJ_BIND] = "bind", + [KOBJ_UNBIND] = "unbind", }; static int kobject_action_type(const char *buf, size_t count, |