diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-07-22 06:00:15 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-07-22 06:00:15 -0400 |
commit | 1af824f085e813e06548212b18bbc788b16f60e6 (patch) | |
tree | 7e4bea76e0780330e27d660633ba514dc02e2fbf | |
parent | d8bcf4db9244e2b85597c680f4e1c3a837b067fe (diff) | |
parent | 072a7852338af900c302490474939e089f4bd4c4 (diff) |
Merge branch 'bind_unbind' into driver-core-next
This merges the bind_unbind driver core feature into the
driver-core-next branch. bind_unbind is a branch so that others can
pull and work off of it safely.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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, | |||
126 | extern void driver_remove_groups(struct device_driver *drv, | 126 | extern void driver_remove_groups(struct device_driver *drv, |
127 | const struct attribute_group **groups); | 127 | const struct attribute_group **groups); |
128 | 128 | ||
129 | extern int device_add_groups(struct device *dev, | ||
130 | const struct attribute_group **groups); | ||
131 | extern void device_remove_groups(struct device *dev, | ||
132 | const struct attribute_group **groups); | ||
133 | |||
134 | extern char *make_class_name(const char *name, struct kobject *kobj); | 129 | extern char *make_class_name(const char *name, struct kobject *kobj); |
135 | 130 | ||
136 | extern int devres_release_all(struct device *dev); | 131 | 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) | |||
1023 | { | 1023 | { |
1024 | return sysfs_create_groups(&dev->kobj, groups); | 1024 | return sysfs_create_groups(&dev->kobj, groups); |
1025 | } | 1025 | } |
1026 | EXPORT_SYMBOL_GPL(device_add_groups); | ||
1026 | 1027 | ||
1027 | void device_remove_groups(struct device *dev, | 1028 | void device_remove_groups(struct device *dev, |
1028 | const struct attribute_group **groups) | 1029 | const struct attribute_group **groups) |
1029 | { | 1030 | { |
1030 | sysfs_remove_groups(&dev->kobj, groups); | 1031 | sysfs_remove_groups(&dev->kobj, groups); |
1031 | } | 1032 | } |
1033 | EXPORT_SYMBOL_GPL(device_remove_groups); | ||
1034 | |||
1035 | union device_attr_group_devres { | ||
1036 | const struct attribute_group *group; | ||
1037 | const struct attribute_group **groups; | ||
1038 | }; | ||
1039 | |||
1040 | static int devm_attr_group_match(struct device *dev, void *res, void *data) | ||
1041 | { | ||
1042 | return ((union device_attr_group_devres *)res)->group == data; | ||
1043 | } | ||
1044 | |||
1045 | static void devm_attr_group_remove(struct device *dev, void *res) | ||
1046 | { | ||
1047 | union device_attr_group_devres *devres = res; | ||
1048 | const struct attribute_group *group = devres->group; | ||
1049 | |||
1050 | dev_dbg(dev, "%s: removing group %p\n", __func__, group); | ||
1051 | sysfs_remove_group(&dev->kobj, group); | ||
1052 | } | ||
1053 | |||
1054 | static void devm_attr_groups_remove(struct device *dev, void *res) | ||
1055 | { | ||
1056 | union device_attr_group_devres *devres = res; | ||
1057 | const struct attribute_group **groups = devres->groups; | ||
1058 | |||
1059 | dev_dbg(dev, "%s: removing groups %p\n", __func__, groups); | ||
1060 | sysfs_remove_groups(&dev->kobj, groups); | ||
1061 | } | ||
1062 | |||
1063 | /** | ||
1064 | * devm_device_add_group - given a device, create a managed attribute group | ||
1065 | * @dev: The device to create the group for | ||
1066 | * @grp: The attribute group to create | ||
1067 | * | ||
1068 | * This function creates a group for the first time. It will explicitly | ||
1069 | * warn and error if any of the attribute files being created already exist. | ||
1070 | * | ||
1071 | * Returns 0 on success or error code on failure. | ||
1072 | */ | ||
1073 | int devm_device_add_group(struct device *dev, const struct attribute_group *grp) | ||
1074 | { | ||
1075 | union device_attr_group_devres *devres; | ||
1076 | int error; | ||
1077 | |||
1078 | devres = devres_alloc(devm_attr_group_remove, | ||
1079 | sizeof(*devres), GFP_KERNEL); | ||
1080 | if (!devres) | ||
1081 | return -ENOMEM; | ||
1082 | |||
1083 | error = sysfs_create_group(&dev->kobj, grp); | ||
1084 | if (error) { | ||
1085 | devres_free(devres); | ||
1086 | return error; | ||
1087 | } | ||
1088 | |||
1089 | devres->group = grp; | ||
1090 | devres_add(dev, devres); | ||
1091 | return 0; | ||
1092 | } | ||
1093 | EXPORT_SYMBOL_GPL(devm_device_add_group); | ||
1094 | |||
1095 | /** | ||
1096 | * devm_device_remove_group: remove a managed group from a device | ||
1097 | * @dev: device to remove the group from | ||
1098 | * @grp: group to remove | ||
1099 | * | ||
1100 | * This function removes a group of attributes from a device. The attributes | ||
1101 | * previously have to have been created for this group, otherwise it will fail. | ||
1102 | */ | ||
1103 | void devm_device_remove_group(struct device *dev, | ||
1104 | const struct attribute_group *grp) | ||
1105 | { | ||
1106 | WARN_ON(devres_release(dev, devm_attr_group_remove, | ||
1107 | devm_attr_group_match, | ||
1108 | /* cast away const */ (void *)grp)); | ||
1109 | } | ||
1110 | EXPORT_SYMBOL_GPL(devm_device_remove_group); | ||
1111 | |||
1112 | /** | ||
1113 | * devm_device_add_groups - create a bunch of managed attribute groups | ||
1114 | * @dev: The device to create the group for | ||
1115 | * @groups: The attribute groups to create, NULL terminated | ||
1116 | * | ||
1117 | * This function creates a bunch of managed attribute groups. If an error | ||
1118 | * occurs when creating a group, all previously created groups will be | ||
1119 | * removed, unwinding everything back to the original state when this | ||
1120 | * function was called. It will explicitly warn and error if any of the | ||
1121 | * attribute files being created already exist. | ||
1122 | * | ||
1123 | * Returns 0 on success or error code from sysfs_create_group on failure. | ||
1124 | */ | ||
1125 | int devm_device_add_groups(struct device *dev, | ||
1126 | const struct attribute_group **groups) | ||
1127 | { | ||
1128 | union device_attr_group_devres *devres; | ||
1129 | int error; | ||
1130 | |||
1131 | devres = devres_alloc(devm_attr_groups_remove, | ||
1132 | sizeof(*devres), GFP_KERNEL); | ||
1133 | if (!devres) | ||
1134 | return -ENOMEM; | ||
1135 | |||
1136 | error = sysfs_create_groups(&dev->kobj, groups); | ||
1137 | if (error) { | ||
1138 | devres_free(devres); | ||
1139 | return error; | ||
1140 | } | ||
1141 | |||
1142 | devres->groups = groups; | ||
1143 | devres_add(dev, devres); | ||
1144 | return 0; | ||
1145 | } | ||
1146 | EXPORT_SYMBOL_GPL(devm_device_add_groups); | ||
1147 | |||
1148 | /** | ||
1149 | * devm_device_remove_groups - remove a list of managed groups | ||
1150 | * | ||
1151 | * @dev: The device for the groups to be removed from | ||
1152 | * @groups: NULL terminated list of groups to be removed | ||
1153 | * | ||
1154 | * If groups is not NULL, remove the specified groups from the device. | ||
1155 | */ | ||
1156 | void devm_device_remove_groups(struct device *dev, | ||
1157 | const struct attribute_group **groups) | ||
1158 | { | ||
1159 | WARN_ON(devres_release(dev, devm_attr_groups_remove, | ||
1160 | devm_attr_group_match, | ||
1161 | /* cast away const */ (void *)groups)); | ||
1162 | } | ||
1163 | EXPORT_SYMBOL_GPL(devm_device_remove_groups); | ||
1032 | 1164 | ||
1033 | static int device_add_attrs(struct device *dev) | 1165 | static int device_add_attrs(struct device *dev) |
1034 | { | 1166 | { |
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) | |||
259 | if (dev->bus) | 259 | if (dev->bus) |
260 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, | 260 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, |
261 | BUS_NOTIFY_BOUND_DRIVER, dev); | 261 | BUS_NOTIFY_BOUND_DRIVER, dev); |
262 | |||
263 | kobject_uevent(&dev->kobj, KOBJ_BIND); | ||
262 | } | 264 | } |
263 | 265 | ||
264 | static int driver_sysfs_add(struct device *dev) | 266 | static int driver_sysfs_add(struct device *dev) |
@@ -848,6 +850,8 @@ static void __device_release_driver(struct device *dev, struct device *parent) | |||
848 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, | 850 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, |
849 | BUS_NOTIFY_UNBOUND_DRIVER, | 851 | BUS_NOTIFY_UNBOUND_DRIVER, |
850 | dev); | 852 | dev); |
853 | |||
854 | kobject_uevent(&dev->kobj, KOBJ_UNBIND); | ||
851 | } | 855 | } |
852 | } | 856 | } |
853 | 857 | ||
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) | |||
827 | 827 | ||
828 | fwnode_handle_put(child); | 828 | fwnode_handle_put(child); |
829 | 829 | ||
830 | error = sysfs_create_group(&dev->kobj, &gpio_keys_attr_group); | 830 | error = devm_device_add_group(dev, &gpio_keys_attr_group); |
831 | if (error) { | 831 | if (error) { |
832 | dev_err(dev, "Unable to export keys/switches, error: %d\n", | 832 | dev_err(dev, "Unable to export keys/switches, error: %d\n", |
833 | error); | 833 | error); |
@@ -838,23 +838,12 @@ static int gpio_keys_probe(struct platform_device *pdev) | |||
838 | if (error) { | 838 | if (error) { |
839 | dev_err(dev, "Unable to register input device, error: %d\n", | 839 | dev_err(dev, "Unable to register input device, error: %d\n", |
840 | error); | 840 | error); |
841 | goto err_remove_group; | 841 | return error; |
842 | } | 842 | } |
843 | 843 | ||
844 | device_init_wakeup(dev, wakeup); | 844 | device_init_wakeup(dev, wakeup); |
845 | 845 | ||
846 | return 0; | 846 | return 0; |
847 | |||
848 | err_remove_group: | ||
849 | sysfs_remove_group(&dev->kobj, &gpio_keys_attr_group); | ||
850 | return error; | ||
851 | } | ||
852 | |||
853 | static int gpio_keys_remove(struct platform_device *pdev) | ||
854 | { | ||
855 | sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); | ||
856 | |||
857 | return 0; | ||
858 | } | 847 | } |
859 | 848 | ||
860 | static int __maybe_unused gpio_keys_suspend(struct device *dev) | 849 | 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); | |||
912 | 901 | ||
913 | static struct platform_driver gpio_keys_device_driver = { | 902 | static struct platform_driver gpio_keys_device_driver = { |
914 | .probe = gpio_keys_probe, | 903 | .probe = gpio_keys_probe, |
915 | .remove = gpio_keys_remove, | ||
916 | .driver = { | 904 | .driver = { |
917 | .name = "gpio-keys", | 905 | .name = "gpio-keys", |
918 | .pm = &gpio_keys_pm_ops, | 906 | .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) | |||
182 | return IRQ_HANDLED; | 182 | return IRQ_HANDLED; |
183 | } | 183 | } |
184 | 184 | ||
185 | static void axp20x_remove_sysfs_group(void *_data) | ||
186 | { | ||
187 | struct device *dev = _data; | ||
188 | |||
189 | sysfs_remove_group(&dev->kobj, &axp20x_attribute_group); | ||
190 | } | ||
191 | |||
192 | static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, | 185 | static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, |
193 | struct platform_device *pdev) | 186 | struct platform_device *pdev) |
194 | { | 187 | { |
@@ -313,22 +306,13 @@ static int axp20x_pek_probe(struct platform_device *pdev) | |||
313 | return error; | 306 | return error; |
314 | } | 307 | } |
315 | 308 | ||
316 | error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group); | 309 | error = devm_device_add_group(&pdev->dev, &axp20x_attribute_group); |
317 | if (error) { | 310 | if (error) { |
318 | dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n", | 311 | dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n", |
319 | error); | 312 | error); |
320 | return error; | 313 | return error; |
321 | } | 314 | } |
322 | 315 | ||
323 | error = devm_add_action(&pdev->dev, | ||
324 | axp20x_remove_sysfs_group, &pdev->dev); | ||
325 | if (error) { | ||
326 | axp20x_remove_sysfs_group(&pdev->dev); | ||
327 | dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n", | ||
328 | error); | ||
329 | return error; | ||
330 | } | ||
331 | |||
332 | platform_set_drvdata(pdev, axp20x_pek); | 316 | platform_set_drvdata(pdev, axp20x_pek); |
333 | 317 | ||
334 | return 0; | 318 | 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) | |||
570 | 570 | ||
571 | dev_set_drvdata(&fn->dev, f01); | 571 | dev_set_drvdata(&fn->dev, f01); |
572 | 572 | ||
573 | error = sysfs_create_group(&fn->rmi_dev->dev.kobj, &rmi_f01_attr_group); | 573 | error = devm_device_add_group(&fn->rmi_dev->dev, &rmi_f01_attr_group); |
574 | if (error) | 574 | if (error) |
575 | dev_warn(&fn->dev, "Failed to create sysfs group: %d\n", error); | 575 | dev_warn(&fn->dev, |
576 | "Failed to create attribute group: %d\n", error); | ||
576 | 577 | ||
577 | return 0; | 578 | return 0; |
578 | } | 579 | } |
579 | 580 | ||
580 | static void rmi_f01_remove(struct rmi_function *fn) | ||
581 | { | ||
582 | sysfs_remove_group(&fn->rmi_dev->dev.kobj, &rmi_f01_attr_group); | ||
583 | } | ||
584 | |||
585 | static int rmi_f01_config(struct rmi_function *fn) | 581 | static int rmi_f01_config(struct rmi_function *fn) |
586 | { | 582 | { |
587 | struct f01_data *f01 = dev_get_drvdata(&fn->dev); | 583 | struct f01_data *f01 = dev_get_drvdata(&fn->dev); |
@@ -721,7 +717,6 @@ struct rmi_function_handler rmi_f01_handler = { | |||
721 | }, | 717 | }, |
722 | .func = 0x01, | 718 | .func = 0x01, |
723 | .probe = rmi_f01_probe, | 719 | .probe = rmi_f01_probe, |
724 | .remove = rmi_f01_remove, | ||
725 | .config = rmi_f01_config, | 720 | .config = rmi_f01_config, |
726 | .attention = rmi_f01_attention, | 721 | .attention = rmi_f01_attention, |
727 | .suspend = rmi_f01_suspend, | 722 | .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, | |||
1200 | const char *fmt, ...); | 1200 | const char *fmt, ...); |
1201 | extern void device_destroy(struct class *cls, dev_t devt); | 1201 | extern void device_destroy(struct class *cls, dev_t devt); |
1202 | 1202 | ||
1203 | extern int __must_check device_add_groups(struct device *dev, | ||
1204 | const struct attribute_group **groups); | ||
1205 | extern void device_remove_groups(struct device *dev, | ||
1206 | const struct attribute_group **groups); | ||
1207 | |||
1208 | static inline int __must_check device_add_group(struct device *dev, | ||
1209 | const struct attribute_group *grp) | ||
1210 | { | ||
1211 | const struct attribute_group *groups[] = { grp, NULL }; | ||
1212 | |||
1213 | return device_add_groups(dev, groups); | ||
1214 | } | ||
1215 | |||
1216 | static inline void device_remove_group(struct device *dev, | ||
1217 | const struct attribute_group *grp) | ||
1218 | { | ||
1219 | const struct attribute_group *groups[] = { grp, NULL }; | ||
1220 | |||
1221 | return device_remove_groups(dev, groups); | ||
1222 | } | ||
1223 | |||
1224 | extern int __must_check devm_device_add_groups(struct device *dev, | ||
1225 | const struct attribute_group **groups); | ||
1226 | extern void devm_device_remove_groups(struct device *dev, | ||
1227 | const struct attribute_group **groups); | ||
1228 | extern int __must_check devm_device_add_group(struct device *dev, | ||
1229 | const struct attribute_group *grp); | ||
1230 | extern void devm_device_remove_group(struct device *dev, | ||
1231 | const struct attribute_group *grp); | ||
1232 | |||
1203 | /* | 1233 | /* |
1204 | * Platform "fixup" functions - allow the platform to have their say | 1234 | * Platform "fixup" functions - allow the platform to have their say |
1205 | * about devices and actions that the general device layer doesn't | 1235 | * 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 { | |||
57 | KOBJ_MOVE, | 57 | KOBJ_MOVE, |
58 | KOBJ_ONLINE, | 58 | KOBJ_ONLINE, |
59 | KOBJ_OFFLINE, | 59 | KOBJ_OFFLINE, |
60 | KOBJ_BIND, | ||
61 | KOBJ_UNBIND, | ||
60 | KOBJ_MAX | 62 | KOBJ_MAX |
61 | }; | 63 | }; |
62 | 64 | ||
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[] = { | |||
52 | [KOBJ_MOVE] = "move", | 52 | [KOBJ_MOVE] = "move", |
53 | [KOBJ_ONLINE] = "online", | 53 | [KOBJ_ONLINE] = "online", |
54 | [KOBJ_OFFLINE] = "offline", | 54 | [KOBJ_OFFLINE] = "offline", |
55 | [KOBJ_BIND] = "bind", | ||
56 | [KOBJ_UNBIND] = "unbind", | ||
55 | }; | 57 | }; |
56 | 58 | ||
57 | static int kobject_action_type(const char *buf, size_t count, | 59 | static int kobject_action_type(const char *buf, size_t count, |