diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/bus.c | 34 | ||||
-rw-r--r-- | drivers/base/class.c | 166 | ||||
-rw-r--r-- | drivers/base/core.c | 237 | ||||
-rw-r--r-- | drivers/base/dd.c | 92 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 119 | ||||
-rw-r--r-- | drivers/base/platform.c | 48 | ||||
-rw-r--r-- | drivers/base/topology.c | 55 |
7 files changed, 546 insertions, 205 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7d8a7ce73fb..472810f8e6e 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -355,6 +355,21 @@ static void device_remove_attrs(struct bus_type * bus, struct device * dev) } } +#ifdef CONFIG_SYSFS_DEPRECATED +static int make_deprecated_bus_links(struct device *dev) +{ + return sysfs_create_link(&dev->kobj, + &dev->bus->subsys.kset.kobj, "bus"); +} + +static void remove_deprecated_bus_links(struct device *dev) +{ + sysfs_remove_link(&dev->kobj, "bus"); +} +#else +static inline int make_deprecated_bus_links(struct device *dev) { return 0; } +static inline void remove_deprecated_bus_links(struct device *dev) { } +#endif /** * bus_add_device - add device to bus @@ -381,8 +396,7 @@ int bus_add_device(struct device * dev) &dev->bus->subsys.kset.kobj, "subsystem"); if (error) goto out_subsys; - error = sysfs_create_link(&dev->kobj, - &dev->bus->subsys.kset.kobj, "bus"); + error = make_deprecated_bus_links(dev); if (error) goto out_deprecated; } @@ -436,7 +450,7 @@ void bus_remove_device(struct device * dev) { if (dev->bus) { sysfs_remove_link(&dev->kobj, "subsystem"); - sysfs_remove_link(&dev->kobj, "bus"); + remove_deprecated_bus_links(dev); sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); device_remove_attrs(dev->bus, dev); if (dev->is_registered) { @@ -724,6 +738,8 @@ int bus_register(struct bus_type * bus) { int retval; + BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier); + retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name); if (retval) goto out; @@ -782,6 +798,18 @@ void bus_unregister(struct bus_type * bus) subsystem_unregister(&bus->subsys); } +int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&bus->bus_notifier, nb); +} +EXPORT_SYMBOL_GPL(bus_register_notifier); + +int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&bus->bus_notifier, nb); +} +EXPORT_SYMBOL_GPL(bus_unregister_notifier); + int __init buses_init(void) { return subsystem_register(&bus_subsys); diff --git a/drivers/base/class.c b/drivers/base/class.c index 0ff267a248d..f098881f45b 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -352,6 +352,92 @@ static const char *class_uevent_name(struct kset *kset, struct kobject *kobj) return class_dev->class->name; } +#ifdef CONFIG_SYSFS_DEPRECATED +char *make_class_name(const char *name, struct kobject *kobj) +{ + char *class_name; + int size; + + size = strlen(name) + strlen(kobject_name(kobj)) + 2; + + class_name = kmalloc(size, GFP_KERNEL); + if (!class_name) + return ERR_PTR(-ENOMEM); + + strcpy(class_name, name); + strcat(class_name, ":"); + strcat(class_name, kobject_name(kobj)); + return class_name; +} + +static int deprecated_class_uevent(char **envp, int num_envp, int *cur_index, + char *buffer, int buffer_size, + int *cur_len, + struct class_device *class_dev) +{ + struct device *dev = class_dev->dev; + char *path; + + if (!dev) + return 0; + + /* add device, backing this class device (deprecated) */ + path = kobject_get_path(&dev->kobj, GFP_KERNEL); + + add_uevent_var(envp, num_envp, cur_index, buffer, buffer_size, + cur_len, "PHYSDEVPATH=%s", path); + kfree(path); + + if (dev->bus) + add_uevent_var(envp, num_envp, cur_index, + buffer, buffer_size, cur_len, + "PHYSDEVBUS=%s", dev->bus->name); + + if (dev->driver) + add_uevent_var(envp, num_envp, cur_index, + buffer, buffer_size, cur_len, + "PHYSDEVDRIVER=%s", dev->driver->name); + return 0; +} + +static int make_deprecated_class_device_links(struct class_device *class_dev) +{ + char *class_name; + int error; + + if (!class_dev->dev) + return 0; + + class_name = make_class_name(class_dev->class->name, &class_dev->kobj); + error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj, + class_name); + kfree(class_name); + return error; +} + +static void remove_deprecated_class_device_links(struct class_device *class_dev) +{ + char *class_name; + + if (!class_dev->dev) + return; + + class_name = make_class_name(class_dev->class->name, &class_dev->kobj); + sysfs_remove_link(&class_dev->dev->kobj, class_name); + kfree(class_name); +} +#else +static inline int deprecated_class_uevent(char **envp, int num_envp, + int *cur_index, char *buffer, + int buffer_size, int *cur_len, + struct class_device *class_dev) +{ return 0; } +static inline int make_deprecated_class_device_links(struct class_device *cd) +{ return 0; } +static void remove_deprecated_class_device_links(struct class_device *cd) +{ } +#endif + static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size) { @@ -362,25 +448,8 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp, pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id); - if (class_dev->dev) { - /* add device, backing this class device (deprecated) */ - struct device *dev = class_dev->dev; - char *path = kobject_get_path(&dev->kobj, GFP_KERNEL); - - add_uevent_var(envp, num_envp, &i, buffer, buffer_size, - &length, "PHYSDEVPATH=%s", path); - kfree(path); - - if (dev->bus) - add_uevent_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "PHYSDEVBUS=%s", dev->bus->name); - - if (dev->driver) - add_uevent_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "PHYSDEVDRIVER=%s", dev->driver->name); - } + deprecated_class_uevent(envp, num_envp, &i, buffer, buffer_size, + &length, class_dev); if (MAJOR(class_dev->devt)) { add_uevent_var(envp, num_envp, &i, @@ -506,29 +575,11 @@ void class_device_initialize(struct class_device *class_dev) INIT_LIST_HEAD(&class_dev->node); } -char *make_class_name(const char *name, struct kobject *kobj) -{ - char *class_name; - int size; - - size = strlen(name) + strlen(kobject_name(kobj)) + 2; - - class_name = kmalloc(size, GFP_KERNEL); - if (!class_name) - return ERR_PTR(-ENOMEM); - - strcpy(class_name, name); - strcat(class_name, ":"); - strcat(class_name, kobject_name(kobj)); - return class_name; -} - int class_device_add(struct class_device *class_dev) { struct class *parent_class = NULL; struct class_device *parent_class_dev = NULL; struct class_interface *class_intf; - char *class_name = NULL; int error = -EINVAL; class_dev = class_device_get(class_dev); @@ -599,20 +650,18 @@ int class_device_add(struct class_device *class_dev) goto out5; if (class_dev->dev) { - class_name = make_class_name(class_dev->class->name, - &class_dev->kobj); error = sysfs_create_link(&class_dev->kobj, &class_dev->dev->kobj, "device"); if (error) goto out6; - error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj, - class_name); - if (error) - goto out7; } error = class_device_add_groups(class_dev); if (error) + goto out7; + + error = make_deprecated_class_device_links(class_dev); + if (error) goto out8; kobject_uevent(&class_dev->kobj, KOBJ_ADD); @@ -629,8 +678,7 @@ int class_device_add(struct class_device *class_dev) goto out1; out8: - if (class_dev->dev) - sysfs_remove_link(&class_dev->kobj, class_name); + class_device_remove_groups(class_dev); out7: if (class_dev->dev) sysfs_remove_link(&class_dev->kobj, "device"); @@ -649,7 +697,6 @@ int class_device_add(struct class_device *class_dev) class_put(parent_class); out1: class_device_put(class_dev); - kfree(class_name); return error; } @@ -726,7 +773,6 @@ void class_device_del(struct class_device *class_dev) struct class *parent_class = class_dev->class; struct class_device *parent_device = class_dev->parent; struct class_interface *class_intf; - char *class_name = NULL; if (parent_class) { down(&parent_class->sem); @@ -738,10 +784,8 @@ void class_device_del(struct class_device *class_dev) } if (class_dev->dev) { - class_name = make_class_name(class_dev->class->name, - &class_dev->kobj); + remove_deprecated_class_device_links(class_dev); sysfs_remove_link(&class_dev->kobj, "device"); - sysfs_remove_link(&class_dev->dev->kobj, class_name); } sysfs_remove_link(&class_dev->kobj, "subsystem"); class_device_remove_file(class_dev, &class_dev->uevent_attr); @@ -755,7 +799,6 @@ void class_device_del(struct class_device *class_dev) class_device_put(parent_device); class_put(parent_class); - kfree(class_name); } void class_device_unregister(struct class_device *class_dev) @@ -804,14 +847,17 @@ int class_device_rename(struct class_device *class_dev, char *new_name) pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id, new_name); +#ifdef CONFIG_SYSFS_DEPRECATED if (class_dev->dev) old_class_name = make_class_name(class_dev->class->name, &class_dev->kobj); +#endif strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN); error = kobject_rename(&class_dev->kobj, new_name); +#ifdef CONFIG_SYSFS_DEPRECATED if (class_dev->dev) { new_class_name = make_class_name(class_dev->class->name, &class_dev->kobj); @@ -819,6 +865,7 @@ int class_device_rename(struct class_device *class_dev, char *new_name) new_class_name); sysfs_remove_link(&class_dev->dev->kobj, old_class_name); } +#endif class_device_put(class_dev); kfree(old_class_name); @@ -893,23 +940,6 @@ void class_interface_unregister(struct class_interface *class_intf) class_put(parent); } -int virtual_device_parent(struct device *dev) -{ - if (!dev->class) - return -ENODEV; - - if (!dev->class->virtual_dir) { - static struct kobject *virtual_dir = NULL; - - if (!virtual_dir) - virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); - dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); - } - - dev->kobj.parent = dev->class->virtual_dir; - return 0; -} - int __init classes_init(void) { int retval; diff --git a/drivers/base/core.c b/drivers/base/core.c index 002fde46d38..e4b530ef757 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/kdev_t.h> +#include <linux/notifier.h> #include <asm/semaphore.h> @@ -153,20 +154,24 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, "MINOR=%u", MINOR(dev->devt)); } +#ifdef CONFIG_SYSFS_DEPRECATED /* add bus name (same as SUBSYSTEM, deprecated) */ if (dev->bus) add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, "PHYSDEVBUS=%s", dev->bus->name); +#endif /* add driver name (PHYSDEV* values are deprecated)*/ if (dev->driver) { add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, "DRIVER=%s", dev->driver->name); +#ifdef CONFIG_SYSFS_DEPRECATED add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, "PHYSDEVDRIVER=%s", dev->driver->name); +#endif } /* terminate, set to next free slot, shrink available space */ @@ -383,6 +388,52 @@ void device_initialize(struct device *dev) device_init_wakeup(dev, 0); } +#ifdef CONFIG_SYSFS_DEPRECATED +static int setup_parent(struct device *dev, struct device *parent) +{ + /* Set the parent to the class, not the parent device */ + /* this keeps sysfs from having a symlink to make old udevs happy */ + if (dev->class) + dev->kobj.parent = &dev->class->subsys.kset.kobj; + else if (parent) + dev->kobj.parent = &parent->kobj; + + return 0; +} +#else +static int virtual_device_parent(struct device *dev) +{ + if (!dev->class) + return -ENODEV; + + if (!dev->class->virtual_dir) { + static struct kobject *virtual_dir = NULL; + + if (!virtual_dir) + virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); + dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); + } + + dev->kobj.parent = dev->class->virtual_dir; + return 0; +} + +static int setup_parent(struct device *dev, struct device *parent) +{ + int error; + + /* if this is a class device, and has no parent, create one */ + if ((dev->class) && (parent == NULL)) { + error = virtual_device_parent(dev); + if (error) + return error; + } else if (parent) + dev->kobj.parent = &parent->kobj; + + return 0; +} +#endif + /** * device_add - add device to device hierarchy. * @dev: device. @@ -405,29 +456,29 @@ int device_add(struct device *dev) if (!dev || !strlen(dev->bus_id)) goto Error; - /* if this is a class device, and has no parent, create one */ - if ((dev->class) && (dev->parent == NULL)) { - error = virtual_device_parent(dev); - if (error) - goto Error; - } + pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); parent = get_device(dev->parent); - pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); + error = setup_parent(dev, parent); + if (error) + goto Error; /* first, register with generic layer. */ kobject_set_name(&dev->kobj, "%s", dev->bus_id); - if (parent) - dev->kobj.parent = &parent->kobj; - - if ((error = kobject_add(&dev->kobj))) + error = kobject_add(&dev->kobj); + if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); + /* notify clients of device entry (new way) */ + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_ADD_DEVICE, dev); + dev->uevent_attr.attr.name = "uevent"; dev->uevent_attr.attr.mode = S_IWUSR; if (dev->driver) @@ -461,13 +512,18 @@ int device_add(struct device *dev) if (dev->class) { sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj, "subsystem"); - sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, - dev->bus_id); + /* If this is not a "fake" compatible device, then create the + * symlink from the class to the device. */ + if (dev->kobj.parent != &dev->class->subsys.kset.kobj) + sysfs_create_link(&dev->class->subsys.kset.kobj, + &dev->kobj, dev->bus_id); +#ifdef CONFIG_SYSFS_DEPRECATED if (parent) { sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); class_name = make_class_name(dev->class->name, &dev->kobj); sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); } +#endif } if ((error = device_add_attrs(dev))) @@ -504,6 +560,9 @@ int device_add(struct device *dev) BusError: device_pm_remove(dev); PMError: + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_DEL_DEVICE, dev); device_remove_groups(dev); GroupError: device_remove_attrs(dev); @@ -586,7 +645,6 @@ void put_device(struct device * dev) void device_del(struct device * dev) { struct device * parent = dev->parent; - char *class_name = NULL; struct class_interface *class_intf; if (parent) @@ -597,13 +655,21 @@ void device_del(struct device * dev) } if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); - sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); - class_name = make_class_name(dev->class->name, &dev->kobj); + /* If this is not a "fake" compatible device, remove the + * symlink from the class to the device. */ + if (dev->kobj.parent != &dev->class->subsys.kset.kobj) + sysfs_remove_link(&dev->class->subsys.kset.kobj, + dev->bus_id); +#ifdef CONFIG_SYSFS_DEPRECATED if (parent) { - sysfs_remove_link(&dev->kobj, "device"); + char *class_name = make_class_name(dev->class->name, + &dev->kobj); sysfs_remove_link(&dev->parent->kobj, class_name); + kfree(class_name); + sysfs_remove_link(&dev->kobj, "device"); } - kfree(class_name); +#endif + down(&dev->class->sem); /* notify any interfaces that the device is now gone */ list_for_each_entry(class_intf, &dev->class->interfaces, node) @@ -616,13 +682,16 @@ void device_del(struct device * dev) device_remove_file(dev, &dev->uevent_attr); device_remove_groups(dev); device_remove_attrs(dev); + bus_remove_device(dev); /* Notify the platform of the removal, in case they * need to do anything... */ if (platform_notify_remove) platform_notify_remove(dev); - bus_remove_device(dev); + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_DEL_DEVICE, dev); device_pm_remove(dev); kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); @@ -681,12 +750,45 @@ int device_for_each_child(struct device * parent, void * data, return error; } +/** + * device_find_child - device iterator for locating a particular device. + * @parent: parent struct device + * @data: Data to pass to match function + * @match: Callback function to check device + * + * This is similar to the device_for_each_child() function above, but it + * returns a reference to a device that is 'found' for later use, as + * determined by the @match callback. + * + * The callback should return 0 if the device doesn't match and non-zero + * if it does. If the callback returns non-zero and a reference to the + * current device can be obtained, this function will return to the caller + * and not iterate over any more devices. + */ +struct device * device_find_child(struct device *parent, void *data, + int (*match)(struct device *, void *)) +{ + struct klist_iter i; + struct device *child; + + if (!parent) + return NULL; + + klist_iter_init(&parent->klist_children, &i); + while ((child = next_device(&i))) + if (match(child, data) && get_device(child)) + break; + klist_iter_exit(&i); + return child; +} + int __init devices_init(void) { return subsystem_register(&devices_subsys); } EXPORT_SYMBOL_GPL(device_for_each_child); +EXPORT_SYMBOL_GPL(device_find_child); EXPORT_SYMBOL_GPL(device_initialize); EXPORT_SYMBOL_GPL(device_add); @@ -809,8 +911,10 @@ int device_rename(struct device *dev, char *new_name) pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name); +#ifdef CONFIG_SYSFS_DEPRECATED if ((dev->class) && (dev->parent)) old_class_name = make_class_name(dev->class->name, &dev->kobj); +#endif if (dev->class) { old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); @@ -825,6 +929,7 @@ int device_rename(struct device *dev, char *new_name) error = kobject_rename(&dev->kobj, new_name); +#ifdef CONFIG_SYSFS_DEPRECATED if (old_class_name) { new_class_name = make_class_name(dev->class->name, &dev->kobj); if (new_class_name) { @@ -833,6 +938,8 @@ int device_rename(struct device *dev, char *new_name) sysfs_remove_link(&dev->parent->kobj, old_class_name); } } +#endif + if (dev->class) { sysfs_remove_link(&dev->class->subsys.kset.kobj, old_symlink_name); @@ -848,3 +955,95 @@ int device_rename(struct device *dev, char *new_name) return error; } + + +static int device_move_class_links(struct device *dev, + struct device *old_parent, + struct device *new_parent) +{ +#ifdef CONFIG_SYSFS_DEPRECATED + int error; + char *class_name; + + class_name = make_class_name(dev->class->name, &dev->kobj); + if (!class_name) { + error = PTR_ERR(class_name); + class_name = NULL; + goto out; + } + if (old_parent) { + sysfs_remove_link(&dev->kobj, "device"); + sysfs_remove_link(&old_parent->kobj, class_name); + } + error = sysfs_create_link(&dev->kobj, &new_parent->kobj, "device"); + if (error) + goto out; + error = sysfs_create_link(&new_parent->kobj, &dev->kobj, class_name); + if (error) + sysfs_remove_link(&dev->kobj, "device"); +out: + kfree(class_name); + return error; +#else + return 0; +#endif +} + +/** + * device_move - moves a device to a new parent + * @dev: the pointer to the struct device to be moved + * @new_parent: the new parent of the device + */ +int device_move(struct device *dev, struct device *new_parent) +{ + int error; + struct device *old_parent; + + dev = get_device(dev); + if (!dev) + return -EINVAL; + + if (!device_is_registered(dev)) { + error = -EINVAL; + goto out; + } + new_parent = get_device(new_parent); + if (!new_parent) { + error = -EINVAL; + goto out; + } + pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id, + new_parent->bus_id); + error = kobject_move(&dev->kobj, &new_parent->kobj); + if (error) { + put_device(new_parent); + goto out; + } + old_parent = dev->parent; + dev->parent = new_parent; + if (old_parent) + klist_remove(&dev->knode_parent); + klist_add_tail(&dev->knode_parent, &new_parent->klist_children); + if (!dev->class) + goto out_put; + error = device_move_class_links(dev, old_parent, new_parent); + if (error) { + /* We ignore errors on cleanup since we're hosed anyway... */ + device_move_class_links(dev, new_parent, old_parent); + if (!kobject_move(&dev->kobj, &old_parent->kobj)) { + klist_remove(&dev->knode_parent); + if (old_parent) + klist_add_tail(&dev->knode_parent, + &old_parent->klist_children); + } + put_device(new_parent); + goto out; + } +out_put: + put_device(old_parent); +out: + put_device(dev); + return error; +} + +EXPORT_SYMBOL_GPL(device_move); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index c5d6bb4290a..510e7884975 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -26,33 +26,28 @@ #define to_drv(node) container_of(node, struct device_driver, kobj.entry) -/** - * device_bind_driver - bind a driver to one device. - * @dev: device. - * - * Allow manual attachment of a driver to a device. - * Caller must have already set @dev->driver. - * - * Note that this does not modify the bus reference count - * nor take the bus's rwsem. Please verify those are accounted - * for before calling this. (It is ok to call with no other effort - * from a driver's probe() method.) - * - * This function must be called with @dev->sem held. - */ -int device_bind_driver(struct device *dev) +static void driver_bound(struct device *dev) { - int ret; - if (klist_node_attached(&dev->knode_driver)) { printk(KERN_WARNING "%s: device %s already bound\n", __FUNCTION__, kobject_name(&dev->kobj)); - return 0; + return; } pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); + + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_BOUND_DRIVER, dev); + klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); +} + +static int driver_sysfs_add(struct device *dev) +{ + int ret; + ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj)); if (ret == 0) { @@ -65,6 +60,36 @@ int device_bind_driver(struct device *dev) return ret; } +static void driver_sysfs_remove(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv) { + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + } +} + +/** + * device_bind_driver - bind a driver to one device. + * @dev: device. + * + * Allow manual attachment of a driver to a device. + * Caller must have already set @dev->driver. + * + * Note that this does not modify the bus reference count + * nor take the bus's rwsem. Please verify those are accounted + * for before calling this. (It is ok to call with no other effort + * from a driver's probe() method.) + * + * This function must be called with @dev->sem held. + */ +int device_bind_driver(struct device *dev) +{ + driver_bound(dev); + return driver_sysfs_add(dev); +} + struct stupid_thread_structure { struct device_driver *drv; struct device *dev; @@ -85,30 +110,32 @@ static int really_probe(void *void_data) drv->bus->name, drv->name, dev->bus_id); dev->driver = drv; + if (driver_sysfs_add(dev)) { + printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", + __FUNCTION__, dev->bus_id); + goto probe_failed; + } + if (dev->bus->probe) { ret = dev->bus->probe(dev); - if (ret) { - dev->driver = NULL; + if (ret) goto probe_failed; - } } else if (drv->probe) { ret = drv->probe(dev); - if (ret) { - dev->driver = NULL; + if (ret) goto probe_failed; - } - } - if (device_bind_driver(dev)) { - printk(KERN_ERR "%s: device_bind_driver(%s) failed\n", - __FUNCTION__, dev->bus_id); - /* How does undo a ->probe? We're screwed. */ } + + driver_bound(dev); ret = 1; pr_debug("%s: Bound Device %s to Driver %s\n", drv->bus->name, dev->bus_id, drv->name); goto done; probe_failed: + driver_sysfs_remove(dev); + dev->driver = NULL; + if (ret == -ENODEV || ret == -ENXIO) { /* Driver matched, but didn't support device * or device not found. @@ -284,10 +311,15 @@ static void __device_release_driver(struct device * dev) drv = dev->driver; if (drv) { get_driver(drv); - sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + driver_sysfs_remove(dev); sysfs_remove_link(&dev->kobj, "driver"); klist_remove(&dev->knode_driver); + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->bus_notifier, + BUS_NOTIFY_UNBIND_DRIVER, + dev); + if (dev->bus && dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 14615694ae9..4bad2870c48 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -21,6 +21,8 @@ #include <linux/firmware.h> #include "base.h" +#define to_dev(obj) container_of(obj, struct device, kobj) + MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); MODULE_LICENSE("GPL"); @@ -86,12 +88,12 @@ firmware_timeout_store(struct class *class, const char *buf, size_t count) static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store); -static void fw_class_dev_release(struct class_device *class_dev); +static void fw_dev_release(struct device *dev); -static int firmware_class_uevent(struct class_device *class_dev, char **envp, - int num_envp, char *buffer, int buffer_size) +static int firmware_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) { - struct firmware_priv *fw_priv = class_get_devdata(class_dev); + struct firmware_priv *fw_priv = dev_get_drvdata(dev); int i = 0, len = 0; if (!test_bit(FW_STATUS_READY, &fw_priv->status)) @@ -110,21 +112,21 @@ static int firmware_class_uevent(struct class_device *class_dev, char **envp, static struct class firmware_class = { .name = "firmware", - .uevent = firmware_class_uevent, - .release = fw_class_dev_release, + .dev_uevent = firmware_uevent, + .dev_release = fw_dev_release, }; -static ssize_t -firmware_loading_show(struct class_device *class_dev, char *buf) +static ssize_t firmware_loading_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct firmware_priv *fw_priv = class_get_devdata(class_dev); + struct firmware_priv *fw_priv = dev_get_drvdata(dev); int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status); return sprintf(buf, "%d\n", loading); } /** * firmware_loading_store - set value in the 'loading' control file - * @class_dev: class_device pointer + * @dev: device pointer * @buf: buffer to scan for loading control value * @count: number of bytes in @buf * @@ -134,11 +136,11 @@ firmware_loading_show(struct class_device *class_dev, char *buf) * 0: Conclude the load and hand the data to the driver code. * -1: Conclude the load with an error and discard any written data. **/ -static ssize_t -firmware_loading_store(struct class_device *class_dev, - const char *buf, size_t count) +static ssize_t firmware_loading_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct firmware_priv *fw_priv = class_get_devdata(class_dev); + struct firmware_priv *fw_priv = dev_get_drvdata(dev); int loading = simple_strtol(buf, NULL, 10); switch (loading) { @@ -174,15 +176,14 @@ firmware_loading_store(struct class_device *class_dev, return count; } -static CLASS_DEVICE_ATTR(loading, 0644, - firmware_loading_show, firmware_loading_store); +static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); static ssize_t firmware_data_read(struct kobject *kobj, char *buffer, loff_t offset, size_t count) { - struct class_device *class_dev = to_class_dev(kobj); - struct firmware_priv *fw_priv = class_get_devdata(class_dev); + struct device *dev = to_dev(kobj); + struct firmware_priv *fw_priv = dev_get_drvdata(dev); struct firmware *fw; ssize_t ret_count = count; @@ -234,7 +235,7 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) /** * firmware_data_write - write method for firmware - * @kobj: kobject for the class_device + * @kobj: kobject for the device * @buffer: buffer being written * @offset: buffer offset for write in total data store area * @count: buffer size @@ -246,8 +247,8 @@ static ssize_t firmware_data_write(struct kobject *kobj, char *buffer, loff_t offset, size_t count) { - struct class_device *class_dev = to_class_dev(kobj); - struct firmware_priv *fw_priv = class_get_devdata(class_dev); + struct device *dev = to_dev(kobj); + struct firmware_priv *fw_priv = dev_get_drvdata(dev); struct firmware *fw; ssize_t retval; @@ -280,13 +281,12 @@ static struct bin_attribute firmware_attr_data_tmpl = { .write = firmware_data_write, }; -static void -fw_class_dev_release(struct class_device *class_dev) +static void fw_dev_release(struct device *dev) { - struct firmware_priv *fw_priv = class_get_devdata(class_dev); + struct firmware_priv *fw_priv = dev_get_drvdata(dev); kfree(fw_priv); - kfree(class_dev); + kfree(dev); module_put(THIS_MODULE); } @@ -298,26 +298,23 @@ firmware_class_timeout(u_long data) fw_load_abort(fw_priv); } -static inline void -fw_setup_class_device_id(struct class_device *class_dev, struct device *dev) +static inline void fw_setup_device_id(struct device *f_dev, struct device *dev) { /* XXX warning we should watch out for name collisions */ - strlcpy(class_dev->class_id, dev->bus_id, BUS_ID_SIZE); + strlcpy(f_dev->bus_id, dev->bus_id, BUS_ID_SIZE); } -static int -fw_register_class_device(struct class_device **class_dev_p, - const char *fw_name, struct device *device) +static int fw_register_device(struct device **dev_p, const char *fw_name, + struct device *device) { int retval; struct firmware_priv *fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); - struct class_device *class_dev = kzalloc(sizeof(*class_dev), - GFP_KERNEL); + struct device *f_dev = kzalloc(sizeof(*f_dev), GFP_KERNEL); - *class_dev_p = NULL; + *dev_p = NULL; - if (!fw_priv || !class_dev) { + if (!fw_priv || !f_dev) { printk(KERN_ERR "%s: kmalloc failed\n", __FUNCTION__); retval = -ENOMEM; goto error_kfree; @@ -331,55 +328,54 @@ fw_register_class_device(struct class_device **class_dev_p, fw_priv->timeout.data = (u_long) fw_priv; init_timer(&fw_priv->timeout); - fw_setup_class_device_id(class_dev, device); - class_dev->dev = device; - class_dev->class = &firmware_class; - class_set_devdata(class_dev, fw_priv); - retval = class_device_register(class_dev); + fw_setup_device_id(f_dev, device); + f_dev->parent = device; + f_dev->class = &firmware_class; + dev_set_drvdata(f_dev, fw_priv); + retval = device_register(f_dev); if (retval) { - printk(KERN_ERR "%s: class_device_register failed\n", + printk(KERN_ERR "%s: device_register failed\n", __FUNCTION__); goto error_kfree; } - *class_dev_p = class_dev; + *dev_p = f_dev; return 0; error_kfree: kfree(fw_priv); - kfree(class_dev); + kfree(f_dev); return retval; } -static int -fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p, - const char *fw_name, struct device *device, int uevent) +static int fw_setup_device(struct firmware *fw, struct device **dev_p, + const char *fw_name, struct device *device, + int uevent) { - struct class_device *class_dev; + struct device *f_dev; struct firmware_priv *fw_priv; int retval; - *class_dev_p = NULL; - retval = fw_register_class_device(&class_dev, fw_name, device); + *dev_p = NULL; + retval = fw_register_device(&f_dev, fw_name, device); if (retval) goto out; /* Need to pin this module until class device is destroyed */ __module_get(THIS_MODULE); - fw_priv = class_get_devdata(class_dev); + fw_priv = dev_get_drvdata(f_dev); fw_priv->fw = fw; - retval = sysfs_create_bin_file(&class_dev->kobj, &fw_priv->attr_data); + retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data); if (retval) { printk(KERN_ERR "%s: sysfs_create_bin_file failed\n", __FUNCTION__); goto error_unreg; } - retval = class_device_create_file(class_dev, - &class_device_attr_loading); + retval = device_create_file(f_dev, &dev_attr_loading); if (retval) { - printk(KERN_ERR "%s: class_device_create_file failed\n", + printk(KERN_ERR "%s: device_create_file failed\n", __FUNCTION__); goto error_unreg; } @@ -388,11 +384,11 @@ fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p, set_bit(FW_STATUS_READY, &fw_priv->status); else set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status); - *class_dev_p = class_dev; + *dev_p = f_dev; goto out; error_unreg: - class_device_unregister(class_dev); + device_unregister(f_dev); out: return retval; } @@ -401,7 +397,7 @@ static int _request_firmware(const struct firmware **firmware_p, const char *name, struct device *device, int uevent) { - struct class_device *class_dev; + struct device *f_dev; struct firmware_priv *fw_priv; struct firmware *firmware; int retval; @@ -417,12 +413,11 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } - retval = fw_setup_class_device(firmware, &class_dev, name, device, - uevent); + retval = fw_setup_device(firmware, &f_dev, name, device, uevent); if (retval) goto error_kfree_fw; - fw_priv = class_get_devdata(class_dev); + fw_priv = dev_get_drvdata(f_dev); if (uevent) { if (loading_timeout > 0) { @@ -430,7 +425,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, add_timer(&fw_priv->timeout); } - kobject_uevent(&class_dev->kobj, KOBJ_ADD); + kobject_uevent(&f_dev->kobj, KOBJ_ADD); wait_for_completion(&fw_priv->completion); set_bit(FW_STATUS_DONE, &fw_priv->status); del_timer_sync(&fw_priv->timeout); @@ -445,7 +440,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, } fw_priv->fw = NULL; mutex_unlock(&fw_lock); - class_device_unregister(class_dev); + device_unregister(f_dev); goto out; error_kfree_fw: diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 940ce41f188..d1df4a08792 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -388,6 +388,11 @@ static int platform_drv_probe(struct device *_dev) return drv->probe(dev); } +static int platform_drv_probe_fail(struct device *_dev) +{ + return -ENXIO; +} + static int platform_drv_remove(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); @@ -451,6 +456,49 @@ void platform_driver_unregister(struct platform_driver *drv) } EXPORT_SYMBOL_GPL(platform_driver_unregister); +/** + * platform_driver_probe - register driver for non-hotpluggable device + * @drv: platform driver structure + * @probe: the driver probe routine, probably from an __init section + * + * Use this instead of platform_driver_register() when you know the device + * is not hotpluggable and has already been registered, and you want to + * remove its run-once probe() infrastructure from memory after the driver + * has bound to the device. + * + * One typical use for this would be with drivers for controllers integrated + * into system-on-chip processors, where the controller devices have been + * configured as part of board setup. + * + * Returns zero if the driver registered and bound to a device, else returns + * a negative error code and with the driver not registered. + */ +int platform_driver_probe(struct platform_driver *drv, + int (*probe)(struct platform_device *)) +{ + int retval, code; + + /* temporary section violation during probe() */ + drv->probe = probe; + retval = code = platform_driver_register(drv); + + /* Fixup that section violation, being paranoid about code scanning + * the list of drivers in order to probe new devices. Check to see + * if the probe was successful, and make sure any forced probes of + * new devices fail. + */ + spin_lock(&platform_bus_type.klist_drivers.k_lock); + drv->probe = NULL; + if (code == 0 && list_empty(&drv->driver.klist_devices.k_list)) + retval = -ENODEV; + drv->driver.probe = platform_drv_probe_fail; + spin_unlock(&platform_bus_type.klist_drivers.k_lock); + + if (code != retval) + platform_driver_unregister(drv); + return retval; +} +EXPORT_SYMBOL_GPL(platform_driver_probe); /* modalias support enables more hands-off userspace setup: * (a) environment variable lets new-style hotplug events work once system is diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 28dccb730af..3d12b85b096 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -94,54 +94,63 @@ static struct attribute_group topology_attr_group = { .name = "topology" }; +static cpumask_t topology_dev_map = CPU_MASK_NONE; + /* Add/Remove cpu_topology interface for CPU device */ -static int __cpuinit topology_add_dev(struct sys_device * sys_dev) +static int __cpuinit topology_add_dev(unsigned int cpu) { - return sysfs_create_group(&sys_dev->kobj, &topology_attr_group); + int rc; + struct sys_device *sys_dev = get_cpu_sysdev(cpu); + + rc = sysfs_create_group(&sys_dev->kobj, &topology_attr_group); + if (!rc) + cpu_set(cpu, topology_dev_map); + return rc; } -static int __cpuinit topology_remove_dev(struct sys_device * sys_dev) +#ifdef CONFIG_HOTPLUG_CPU +static void __cpuinit topology_remove_dev(unsigned int cpu) { + struct sys_device *sys_dev = get_cpu_sysdev(cpu); + + if (!cpu_isset(cpu, topology_dev_map)) + return; + cpu_clear(cpu, topology_dev_map); sysfs_remove_group(&sys_dev->kobj, &topology_attr_group); - return 0; } static int __cpuinit topology_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) + unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; - struct sys_device *sys_dev; + int rc = 0; - sys_dev = get_cpu_sysdev(cpu); switch (action) { - case CPU_ONLINE: - topology_add_dev(sys_dev); + case CPU_UP_PREPARE: + rc = topology_add_dev(cpu); break; + case CPU_UP_CANCELED: case CPU_DEAD: - topology_remove_dev(sys_dev); + topology_remove_dev(cpu); break; } - return NOTIFY_OK; + return rc ? NOTIFY_BAD : NOTIFY_OK; } - -static struct notifier_block __cpuinitdata topology_cpu_notifier = -{ - .notifier_call = topology_cpu_callback, -}; +#endif static int __cpuinit topology_sysfs_init(void) { - int i; + int cpu; + int rc; - for_each_online_cpu(i) { - topology_cpu_callback(&topology_cpu_notifier, CPU_ONLINE, - (void *)(long)i); + for_each_online_cpu(cpu) { + rc = topology_add_dev(cpu); + if (rc) + return rc; } - - register_hotcpu_notifier(&topology_cpu_notifier); + hotcpu_notifier(topology_cpu_callback, 0); return 0; } device_initcall(topology_sysfs_init); - |