aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/core.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2017-07-19 20:24:33 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-07-22 05:59:23 -0400
commit57b8ff070f9897b22e4f80fda775a489fc797008 (patch)
tree94f60efe6120ca7cb9214d5ed98a5e7ecb95f812 /drivers/base/core.c
parente323b2dddc1ce7ea7f032c986c012a04d5977dc8 (diff)
driver core: add devm_device_add_group() and friends
Many drivers create additional driver-specific device attributes when binding to the device, and providing managed version of device_create_group() will simplify unbinding and error handling in probe path for such drivers. Without managed version driver writers either have to mix manual and managed resources, which is prone to errors, or open-code this function by providing a wrapper to device_add_group() and use it with devm_add_action() or devm_add_action_or_reset(). Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r--drivers/base/core.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 14f8cf5c8b05..09723532725d 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1035,6 +1035,136 @@ void device_remove_groups(struct device *dev,
1035} 1035}
1036EXPORT_SYMBOL_GPL(device_remove_groups); 1036EXPORT_SYMBOL_GPL(device_remove_groups);
1037 1037
1038union device_attr_group_devres {
1039 const struct attribute_group *group;
1040 const struct attribute_group **groups;
1041};
1042
1043static int devm_attr_group_match(struct device *dev, void *res, void *data)
1044{
1045 return ((union device_attr_group_devres *)res)->group == data;
1046}
1047
1048static void devm_attr_group_remove(struct device *dev, void *res)
1049{
1050 union device_attr_group_devres *devres = res;
1051 const struct attribute_group *group = devres->group;
1052
1053 dev_dbg(dev, "%s: removing group %p\n", __func__, group);
1054 sysfs_remove_group(&dev->kobj, group);
1055}
1056
1057static void devm_attr_groups_remove(struct device *dev, void *res)
1058{
1059 union device_attr_group_devres *devres = res;
1060 const struct attribute_group **groups = devres->groups;
1061
1062 dev_dbg(dev, "%s: removing groups %p\n", __func__, groups);
1063 sysfs_remove_groups(&dev->kobj, groups);
1064}
1065
1066/**
1067 * devm_device_add_group - given a device, create a managed attribute group
1068 * @dev: The device to create the group for
1069 * @grp: The attribute group to create
1070 *
1071 * This function creates a group for the first time. It will explicitly
1072 * warn and error if any of the attribute files being created already exist.
1073 *
1074 * Returns 0 on success or error code on failure.
1075 */
1076int devm_device_add_group(struct device *dev, const struct attribute_group *grp)
1077{
1078 union device_attr_group_devres *devres;
1079 int error;
1080
1081 devres = devres_alloc(devm_attr_group_remove,
1082 sizeof(*devres), GFP_KERNEL);
1083 if (!devres)
1084 return -ENOMEM;
1085
1086 error = sysfs_create_group(&dev->kobj, grp);
1087 if (error) {
1088 devres_free(devres);
1089 return error;
1090 }
1091
1092 devres->group = grp;
1093 devres_add(dev, devres);
1094 return 0;
1095}
1096EXPORT_SYMBOL_GPL(devm_device_add_group);
1097
1098/**
1099 * devm_device_remove_group: remove a managed group from a device
1100 * @dev: device to remove the group from
1101 * @grp: group to remove
1102 *
1103 * This function removes a group of attributes from a device. The attributes
1104 * previously have to have been created for this group, otherwise it will fail.
1105 */
1106void devm_device_remove_group(struct device *dev,
1107 const struct attribute_group *grp)
1108{
1109 WARN_ON(devres_release(dev, devm_attr_group_remove,
1110 devm_attr_group_match,
1111 /* cast away const */ (void *)grp));
1112}
1113EXPORT_SYMBOL_GPL(devm_device_remove_group);
1114
1115/**
1116 * devm_device_add_groups - create a bunch of managed attribute groups
1117 * @dev: The device to create the group for
1118 * @groups: The attribute groups to create, NULL terminated
1119 *
1120 * This function creates a bunch of managed attribute groups. If an error
1121 * occurs when creating a group, all previously created groups will be
1122 * removed, unwinding everything back to the original state when this
1123 * function was called. It will explicitly warn and error if any of the
1124 * attribute files being created already exist.
1125 *
1126 * Returns 0 on success or error code from sysfs_create_group on failure.
1127 */
1128int devm_device_add_groups(struct device *dev,
1129 const struct attribute_group **groups)
1130{
1131 union device_attr_group_devres *devres;
1132 int error;
1133
1134 devres = devres_alloc(devm_attr_groups_remove,
1135 sizeof(*devres), GFP_KERNEL);
1136 if (!devres)
1137 return -ENOMEM;
1138
1139 error = sysfs_create_groups(&dev->kobj, groups);
1140 if (error) {
1141 devres_free(devres);
1142 return error;
1143 }
1144
1145 devres->groups = groups;
1146 devres_add(dev, devres);
1147 return 0;
1148}
1149EXPORT_SYMBOL_GPL(devm_device_add_groups);
1150
1151/**
1152 * devm_device_remove_groups - remove a list of managed groups
1153 *
1154 * @dev: The device for the groups to be removed from
1155 * @groups: NULL terminated list of groups to be removed
1156 *
1157 * If groups is not NULL, remove the specified groups from the device.
1158 */
1159void devm_device_remove_groups(struct device *dev,
1160 const struct attribute_group **groups)
1161{
1162 WARN_ON(devres_release(dev, devm_attr_groups_remove,
1163 devm_attr_group_match,
1164 /* cast away const */ (void *)groups));
1165}
1166EXPORT_SYMBOL_GPL(devm_device_remove_groups);
1167
1038static int device_add_attrs(struct device *dev) 1168static int device_add_attrs(struct device *dev)
1039{ 1169{
1040 struct class *class = dev->class; 1170 struct class *class = dev->class;