diff --git a/drivers/base/core.c b/drivers/base/core.c index ee555d7d5c3f6d73018491f1ad6802b49548bc46..61df508fa62bfef83754e143c4f20cdd6fdbe2e3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1217,6 +1217,97 @@ EXPORT_SYMBOL_GPL(put_device); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); +struct root_device +{ + struct device dev; + struct module *owner; +}; + +#define to_root_device(dev) container_of(dev, struct root_device, dev) + +static void root_device_release(struct device *dev) +{ + kfree(to_root_device(dev)); +} + +/** + * __root_device_register - allocate and register a root device + * @name: root device name + * @owner: owner module of the root device, usually THIS_MODULE + * + * This function allocates a root device and registers it + * using device_register(). In order to free the returned + * device, use root_device_unregister(). + * + * Root devices are dummy devices which allow other devices + * to be grouped under /sys/devices. Use this function to + * allocate a root device and then use it as the parent of + * any device which should appear under /sys/devices/{name} + * + * The /sys/devices/{name} directory will also contain a + * 'module' symlink which points to the @owner directory + * in sysfs. + * + * Note: You probably want to use root_device_register(). + */ +struct device *__root_device_register(const char *name, struct module *owner) +{ + struct root_device *root; + int err = -ENOMEM; + + root = kzalloc(sizeof(struct root_device), GFP_KERNEL); + if (!root) + return ERR_PTR(err); + + err = dev_set_name(&root->dev, name); + if (err) { + kfree(root); + return ERR_PTR(err); + } + + root->dev.release = root_device_release; + + err = device_register(&root->dev); + if (err) { + put_device(&root->dev); + return ERR_PTR(err); + } + +#ifdef CONFIG_MODULE /* gotta find a "cleaner" way to do this */ + if (owner) { + struct module_kobject *mk = &owner->mkobj; + + err = sysfs_create_link(&root->dev.kobj, &mk->kobj, "module"); + if (err) { + device_unregister(&root->dev); + return ERR_PTR(err); + } + root->owner = owner; + } +#endif + + return &root->dev; +} +EXPORT_SYMBOL_GPL(__root_device_register); + +/** + * root_device_unregister - unregister and free a root device + * @root: device going away. + * + * This function unregisters and cleans up a device that was created by + * root_device_register(). + */ +void root_device_unregister(struct device *dev) +{ + struct root_device *root = to_root_device(dev); + + if (root->owner) + sysfs_remove_link(&root->dev.kobj, "module"); + + device_unregister(dev); +} +EXPORT_SYMBOL_GPL(root_device_unregister); + static void device_create_release(struct device *dev) { diff --git a/include/linux/device.h b/include/linux/device.h index b97a0cf1eb0585c88ed4f3db800beda65ef0fc5a..7d9da4b4993feae592388717bff9ffedb0e60531 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -482,6 +482,17 @@ 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); +/* + * Root device objects for grouping under /sys/devices + */ +extern struct device *__root_device_register(const char *name, + struct module *owner); +static inline struct device *root_device_register(const char *name) +{ + return __root_device_register(name, THIS_MODULE); +} +extern void root_device_unregister(struct device *root); + /* * Manual binding of a device to driver. See drivers/base/bus.c * for information on use.