From: Lars Poeschel <poesc...@lemonage.de> This adds a class to exported pwm devices. Exporting a pwm through sysfs did not yield udev events. The dev_uevent_filter function does filter-out devices without a bus or class. This was already addressed in commit commit 7e5d1fd75c3d ("pwm: Set class for exported channels in sysfs") but this did cause problems and the commit got reverted with commit c289d6625237 ("Revert "pwm: Set class for exported channels in sysfs"") Problem with the previous approach was, that there is a clash if we have multiple pwmchips: echo 0 > pwmchip0/export echo 0 > pwmchip1/export would both export /sys/class/pwm/pwm0 .
Now this patch changes the sysfs interface. We do include the pwmchip number into the pwm directory that gets exported. With the example above we get: /sys/class/pwm/pwm-0-0 /sys/class/pwm/pwm-1-0 We maintain ABI backward compatibility through symlinks. /sys/class/pwm/pwmchip0/pwm0 /sys/class/pwm/pwmchip1/pwm0 are now symbolic links to the new names. Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org> Signed-off-by: Lars Poeschel <poesc...@lemonage.de> --- drivers/pwm/sysfs.c | 57 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index 449dbc0f49ed..c708da17a857 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -240,8 +240,10 @@ static void pwm_export_release(struct device *child) static int pwm_export_child(struct device *parent, struct pwm_device *pwm) { + struct pwm_chip *chip = dev_get_drvdata(parent); struct pwm_export *export; char *pwm_prop[2]; + char *link_name; int ret; if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags)) @@ -256,25 +258,39 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm) export->pwm = pwm; mutex_init(&export->lock); + export->child.class = parent->class; export->child.release = pwm_export_release; export->child.parent = parent; export->child.devt = MKDEV(0, 0); export->child.groups = pwm_groups; - dev_set_name(&export->child, "pwm%u", pwm->hwpwm); + dev_set_name(&export->child, "pwm-%u-%u", chip->base, pwm->hwpwm); ret = device_register(&export->child); - if (ret) { - clear_bit(PWMF_EXPORTED, &pwm->flags); - put_device(&export->child); - export = NULL; - return ret; + if (ret) + goto error; + + link_name = kasprintf(GFP_KERNEL, "pwm%u", pwm->hwpwm); + if (link_name == NULL) { + ret = -ENOMEM; + goto dev_unregister; } - pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm); + + pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=%s", + export->child.kobj.name); pwm_prop[1] = NULL; kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); kfree(pwm_prop[0]); - return 0; + ret = sysfs_create_link(&parent->kobj, &export->child.kobj, link_name); + return ret; + +dev_unregister: + device_unregister(&export->child); +error: + clear_bit(PWMF_EXPORTED, &pwm->flags); + put_device(&export->child); + export = NULL; + return ret; } static int pwm_unexport_match(struct device *child, void *data) @@ -286,6 +302,7 @@ static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) { struct device *child; char *pwm_prop[2]; + char *link_name; if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags)) return -ENODEV; @@ -294,7 +311,11 @@ static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) if (!child) return -ENODEV; - pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm); + link_name = kasprintf(GFP_KERNEL, "pwm%u", pwm->hwpwm); + if (link_name) + sysfs_delete_link(&parent->kobj, &child->kobj, link_name); + + pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=%s", child->kobj.name); pwm_prop[1] = NULL; kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); kfree(pwm_prop[0]); @@ -365,13 +386,29 @@ static ssize_t npwm_show(struct device *parent, struct device_attribute *attr, } static DEVICE_ATTR_RO(npwm); +static umode_t pwm_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (dev->groups == pwm_groups) + return 0; + + return attr->mode; +} + static struct attribute *pwm_chip_attrs[] = { &dev_attr_export.attr, &dev_attr_unexport.attr, &dev_attr_npwm.attr, NULL, }; -ATTRIBUTE_GROUPS(pwm_chip); + +static const struct attribute_group pwm_chip_group = { + .attrs = pwm_chip_attrs, + .is_visible = pwm_is_visible, +}; +__ATTRIBUTE_GROUPS(pwm_chip); /* takes export->lock on success */ static struct pwm_export *pwm_class_get_state(struct device *parent, -- 2.28.0