diff options
author | Kay Sievers <kay.sievers@vrfy.org> | 2009-04-30 15:23:42 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-15 21:30:25 -0700 |
commit | 6fcf53acccf85b4b0d0260e66c692a341760f464 (patch) | |
tree | 445f725558bde27492095f8d05cc645315f8c1c1 | |
parent | acc0e90fbccbc6e4d48184cba0983ea044e131af (diff) |
Driver Core: add nodename callbacks
This adds the nodename callback for struct class, struct device_type and
struct device, to allow drivers to send userspace hints on the device
name and subdirectory that should be used for it.
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Jan Blunck <jblunck@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/base/core.c | 51 | ||||
-rw-r--r-- | include/linux/device.h | 3 |
2 files changed, 53 insertions, 1 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 4d59975c24a..7ecb1938e59 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -162,10 +162,18 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, struct device *dev = to_dev(kobj); int retval = 0; - /* add the major/minor if present */ + /* add device node properties if present */ if (MAJOR(dev->devt)) { + const char *tmp; + const char *name; + add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt)); add_uevent_var(env, "MINOR=%u", MINOR(dev->devt)); + name = device_get_nodename(dev, &tmp); + if (name) { + add_uevent_var(env, "DEVNAME=%s", name); + kfree(tmp); + } } if (dev->type && dev->type->name) @@ -1129,6 +1137,47 @@ static struct device *next_device(struct klist_iter *i) } /** + * device_get_nodename - path of device node file + * @dev: device + * @tmp: possibly allocated string + * + * Return the relative path of a possible device node. + * Non-default names may need to allocate a memory to compose + * a name. This memory is returned in tmp and needs to be + * freed by the caller. + */ +const char *device_get_nodename(struct device *dev, const char **tmp) +{ + char *s; + + *tmp = NULL; + + /* the device type may provide a specific name */ + if (dev->type && dev->type->nodename) + *tmp = dev->type->nodename(dev); + if (*tmp) + return *tmp; + + /* the class may provide a specific name */ + if (dev->class && dev->class->nodename) + *tmp = dev->class->nodename(dev); + if (*tmp) + return *tmp; + + /* return name without allocation, tmp == NULL */ + if (strchr(dev_name(dev), '!') == NULL) + return dev_name(dev); + + /* replace '!' in the name with '/' */ + *tmp = kstrdup(dev_name(dev), GFP_KERNEL); + if (!*tmp) + return NULL; + while ((s = strchr(*tmp, '!'))) + s[0] = '/'; + return *tmp; +} + +/** * device_for_each_child - device child iterator. * @parent: parent struct device. * @data: data for the callback. diff --git a/include/linux/device.h b/include/linux/device.h index 4410464b134..ed4e39f2c42 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -194,6 +194,7 @@ struct class { struct kobject *dev_kobj; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); + char *(*nodename)(struct device *dev); void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); @@ -289,6 +290,7 @@ struct device_type { const char *name; struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); + char *(*nodename)(struct device *dev); void (*release)(struct device *dev); struct dev_pm_ops *pm; @@ -488,6 +490,7 @@ extern struct device *device_find_child(struct device *dev, void *data, extern int device_rename(struct device *dev, char *new_name); extern int device_move(struct device *dev, struct device *new_parent, enum dpm_order dpm_order); +extern const char *device_get_nodename(struct device *dev, const char **tmp); /* * Root device objects for grouping under /sys/devices |