diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/attribute_container.c | 86 | ||||
-rw-r--r-- | drivers/base/bus.c | 42 | ||||
-rw-r--r-- | drivers/base/class.c | 39 | ||||
-rw-r--r-- | drivers/base/core.c | 19 | ||||
-rw-r--r-- | drivers/base/dd.c | 2 | ||||
-rw-r--r-- | drivers/base/driver.c | 15 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 79 | ||||
-rw-r--r-- | drivers/base/node.c | 24 | ||||
-rw-r--r-- | drivers/base/power/resume.c | 8 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 8 | ||||
-rw-r--r-- | drivers/base/power/suspend.c | 12 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 8 | ||||
-rw-r--r-- | drivers/base/sys.c | 110 | ||||
-rw-r--r-- | drivers/base/transport_class.c | 19 |
14 files changed, 365 insertions, 106 deletions
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index ec615d854be..373e7b728fa 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -22,11 +22,26 @@ /* This is a private structure used to tie the classdev and the * container .. it should never be visible outside this file */ struct internal_container { - struct list_head node; + struct klist_node node; struct attribute_container *cont; struct class_device classdev; }; +static void internal_container_klist_get(struct klist_node *n) +{ + struct internal_container *ic = + container_of(n, struct internal_container, node); + class_device_get(&ic->classdev); +} + +static void internal_container_klist_put(struct klist_node *n) +{ + struct internal_container *ic = + container_of(n, struct internal_container, node); + class_device_put(&ic->classdev); +} + + /** * attribute_container_classdev_to_container - given a classdev, return the container * @@ -57,7 +72,8 @@ int attribute_container_register(struct attribute_container *cont) { INIT_LIST_HEAD(&cont->node); - INIT_LIST_HEAD(&cont->containers); + klist_init(&cont->containers,internal_container_klist_get, + internal_container_klist_put); down(&attribute_container_mutex); list_add_tail(&cont->node, &attribute_container_list); @@ -77,11 +93,13 @@ attribute_container_unregister(struct attribute_container *cont) { int retval = -EBUSY; down(&attribute_container_mutex); - if (!list_empty(&cont->containers)) + spin_lock(&cont->containers.k_lock); + if (!list_empty(&cont->containers.k_list)) goto out; retval = 0; list_del(&cont->node); out: + spin_unlock(&cont->containers.k_lock); up(&attribute_container_mutex); return retval; @@ -140,7 +158,6 @@ attribute_container_add_device(struct device *dev, continue; } memset(ic, 0, sizeof(struct internal_container)); - INIT_LIST_HEAD(&ic->node); ic->cont = cont; class_device_initialize(&ic->classdev); ic->classdev.dev = get_device(dev); @@ -151,11 +168,22 @@ attribute_container_add_device(struct device *dev, fn(cont, dev, &ic->classdev); else attribute_container_add_class_device(&ic->classdev); - list_add_tail(&ic->node, &cont->containers); + klist_add_tail(&ic->node, &cont->containers); } up(&attribute_container_mutex); } +/* FIXME: can't break out of this unless klist_iter_exit is also + * called before doing the break + */ +#define klist_for_each_entry(pos, head, member, iter) \ + for (klist_iter_init(head, iter); (pos = ({ \ + struct klist_node *n = klist_next(iter); \ + n ? container_of(n, typeof(*pos), member) : \ + ({ klist_iter_exit(iter) ; NULL; }); \ + }) ) != NULL; ) + + /** * attribute_container_remove_device - make device eligible for removal. * @@ -182,17 +210,19 @@ attribute_container_remove_device(struct device *dev, down(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { - struct internal_container *ic, *tmp; + struct internal_container *ic; + struct klist_iter iter; if (attribute_container_no_classdevs(cont)) continue; if (!cont->match(cont, dev)) continue; - list_for_each_entry_safe(ic, tmp, &cont->containers, node) { + + klist_for_each_entry(ic, &cont->containers, node, &iter) { if (dev != ic->classdev.dev) continue; - list_del(&ic->node); + klist_del(&ic->node); if (fn) fn(cont, dev, &ic->classdev); else { @@ -225,12 +255,18 @@ attribute_container_device_trigger(struct device *dev, down(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { - struct internal_container *ic, *tmp; + struct internal_container *ic; + struct klist_iter iter; if (!cont->match(cont, dev)) continue; - list_for_each_entry_safe(ic, tmp, &cont->containers, node) { + if (attribute_container_no_classdevs(cont)) { + fn(cont, dev, NULL); + continue; + } + + klist_for_each_entry(ic, &cont->containers, node, &iter) { if (dev == ic->classdev.dev) fn(cont, dev, &ic->classdev); } @@ -368,6 +404,36 @@ attribute_container_class_device_del(struct class_device *classdev) } EXPORT_SYMBOL_GPL(attribute_container_class_device_del); +/** + * attribute_container_find_class_device - find the corresponding class_device + * + * @cont: the container + * @dev: the generic device + * + * Looks up the device in the container's list of class devices and returns + * the corresponding class_device. + */ +struct class_device * +attribute_container_find_class_device(struct attribute_container *cont, + struct device *dev) +{ + struct class_device *cdev = NULL; + struct internal_container *ic; + struct klist_iter iter; + + klist_for_each_entry(ic, &cont->containers, node, &iter) { + if (ic->classdev.dev == dev) { + cdev = &ic->classdev; + /* FIXME: must exit iterator then break */ + klist_iter_exit(&iter); + break; + } + } + + return cdev; +} +EXPORT_SYMBOL_GPL(attribute_container_find_class_device); + int __init attribute_container_init(void) { diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ab53832d57e..03204bfd17a 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -156,7 +156,9 @@ static ssize_t driver_unbind(struct device_driver *drv, device_release_driver(dev); err = count; } - return err; + if (err) + return err; + return count; } static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind); @@ -358,7 +360,7 @@ int bus_add_device(struct device * dev) if (bus) { pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); device_attach(dev); - klist_add_tail(&bus->klist_devices, &dev->knode_bus); + klist_add_tail(&dev->knode_bus, &bus->klist_devices); error = device_add_attrs(bus, dev); if (!error) { sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); @@ -446,7 +448,7 @@ int bus_add_driver(struct device_driver * drv) } driver_attach(drv); - klist_add_tail(&bus->klist_drivers, &drv->knode_bus); + klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); driver_add_attrs(bus, drv); @@ -566,6 +568,36 @@ static void bus_remove_attrs(struct bus_type * bus) } } +static void klist_devices_get(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_bus); + + get_device(dev); +} + +static void klist_devices_put(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_bus); + + put_device(dev); +} + +static void klist_drivers_get(struct klist_node *n) +{ + struct device_driver *drv = container_of(n, struct device_driver, + knode_bus); + + get_driver(drv); +} + +static void klist_drivers_put(struct klist_node *n) +{ + struct device_driver *drv = container_of(n, struct device_driver, + knode_bus); + + put_driver(drv); +} + /** * bus_register - register a bus with the system. * @bus: bus. @@ -600,8 +632,8 @@ int bus_register(struct bus_type * bus) if (retval) goto bus_drivers_fail; - klist_init(&bus->klist_devices); - klist_init(&bus->klist_drivers); + klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); + klist_init(&bus->klist_drivers, klist_drivers_get, klist_drivers_put); bus_add_attrs(bus); pr_debug("bus type '%s' registered\n", bus->name); diff --git a/drivers/base/class.c b/drivers/base/class.c index 0154a1623b2..d164c32a97a 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -299,10 +299,8 @@ static void class_dev_release(struct kobject * kobj) pr_debug("device class '%s': release.\n", cd->class_id); - if (cd->devt_attr) { - kfree(cd->devt_attr); - cd->devt_attr = NULL; - } + kfree(cd->devt_attr); + cd->devt_attr = NULL; if (cls->release) cls->release(cd); @@ -452,10 +450,29 @@ void class_device_initialize(struct class_device *class_dev) INIT_LIST_HEAD(&class_dev->node); } +static char *make_class_name(struct class_device *class_dev) +{ + char *name; + int size; + + size = strlen(class_dev->class->name) + + strlen(kobject_name(&class_dev->kobj)) + 2; + + name = kmalloc(size, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + + strcpy(name, class_dev->class->name); + strcat(name, ":"); + strcat(name, kobject_name(&class_dev->kobj)); + return name; +} + int class_device_add(struct class_device *class_dev) { struct class * parent = NULL; struct class_interface * class_intf; + char *class_name = NULL; int error; class_dev = class_device_get(class_dev); @@ -500,9 +517,13 @@ int class_device_add(struct class_device *class_dev) } class_device_add_attrs(class_dev); - if (class_dev->dev) + if (class_dev->dev) { + class_name = make_class_name(class_dev); sysfs_create_link(&class_dev->kobj, &class_dev->dev->kobj, "device"); + sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj, + class_name); + } /* notify any interfaces this device is now here */ if (parent) { @@ -519,6 +540,7 @@ int class_device_add(struct class_device *class_dev) if (error && parent) class_put(parent); class_device_put(class_dev); + kfree(class_name); return error; } @@ -584,6 +606,7 @@ void class_device_del(struct class_device *class_dev) { struct class * parent = class_dev->class; struct class_interface * class_intf; + char *class_name = NULL; if (parent) { down(&parent->sem); @@ -594,8 +617,11 @@ void class_device_del(struct class_device *class_dev) up(&parent->sem); } - if (class_dev->dev) + if (class_dev->dev) { + class_name = make_class_name(class_dev); sysfs_remove_link(&class_dev->kobj, "device"); + sysfs_remove_link(&class_dev->dev->kobj, class_name); + } if (class_dev->devt_attr) class_device_remove_file(class_dev, class_dev->devt_attr); class_device_remove_attrs(class_dev); @@ -605,6 +631,7 @@ void class_device_del(struct class_device *class_dev) if (parent) class_put(parent); + kfree(class_name); } void class_device_unregister(struct class_device *class_dev) diff --git a/drivers/base/core.c b/drivers/base/core.c index efe03a024a5..6ab73f5c799 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -191,6 +191,20 @@ void device_remove_file(struct device * dev, struct device_attribute * attr) } } +static void klist_children_get(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_parent); + + get_device(dev); +} + +static void klist_children_put(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_parent); + + put_device(dev); +} + /** * device_initialize - init device structure. @@ -207,7 +221,8 @@ void device_initialize(struct device *dev) { kobj_set_kset_s(dev, devices_subsys); kobject_init(&dev->kobj); - klist_init(&dev->klist_children); + klist_init(&dev->klist_children, klist_children_get, + klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); } @@ -249,7 +264,7 @@ int device_add(struct device *dev) if ((error = bus_add_device(dev))) goto BusError; if (parent) - klist_add_tail(&parent->klist_children, &dev->knode_parent); + klist_add_tail(&dev->knode_parent, &parent->klist_children); /* notify platform of device entry */ if (platform_notify) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 16323f9cbff..d5bbce38282 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -42,7 +42,7 @@ void device_bind_driver(struct device * dev) { pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); - klist_add_tail(&dev->driver->klist_devices, &dev->knode_driver); + klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj)); sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 291c5954a3a..ef3fe513e39 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -142,6 +142,19 @@ void put_driver(struct device_driver * drv) kobject_put(&drv->kobj); } +static void klist_devices_get(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_driver); + + get_device(dev); +} + +static void klist_devices_put(struct klist_node *n) +{ + struct device *dev = container_of(n, struct device, knode_driver); + + put_device(dev); +} /** * driver_register - register driver with bus @@ -157,7 +170,7 @@ void put_driver(struct device_driver * drv) */ int driver_register(struct device_driver * drv) { - klist_init(&drv->klist_devices); + klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put); init_completion(&drv->unloaded); return bus_add_driver(drv); } diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 652281402c9..5bfa2e9a7c2 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -28,6 +28,7 @@ enum { FW_STATUS_DONE, FW_STATUS_ABORT, FW_STATUS_READY, + FW_STATUS_READY_NOHOTPLUG, }; static int loading_timeout = 10; /* In seconds */ @@ -344,7 +345,7 @@ error_kfree: static int fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p, - const char *fw_name, struct device *device) + const char *fw_name, struct device *device, int hotplug) { struct class_device *class_dev; struct firmware_priv *fw_priv; @@ -376,7 +377,10 @@ fw_setup_class_device(struct firmware *fw, struct class_device **class_dev_p, goto error_unreg; } - set_bit(FW_STATUS_READY, &fw_priv->status); + if (hotplug) + set_bit(FW_STATUS_READY, &fw_priv->status); + else + set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status); *class_dev_p = class_dev; goto out; @@ -386,21 +390,9 @@ out: return retval; } -/** - * request_firmware: - request firmware to hotplug and wait for it - * Description: - * @firmware will be used to return a firmware image by the name - * of @name for device @device. - * - * Should be called from user context where sleeping is allowed. - * - * @name will be use as $FIRMWARE in the hotplug environment and - * should be distinctive enough not to be confused with any other - * firmware image for this or any other device. - **/ -int -request_firmware(const struct firmware **firmware_p, const char *name, - struct device *device) +static int +_request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device, int hotplug) { struct class_device *class_dev; struct firmware_priv *fw_priv; @@ -419,22 +411,25 @@ request_firmware(const struct firmware **firmware_p, const char *name, } memset(firmware, 0, sizeof (*firmware)); - retval = fw_setup_class_device(firmware, &class_dev, name, device); + retval = fw_setup_class_device(firmware, &class_dev, name, device, + hotplug); if (retval) goto error_kfree_fw; fw_priv = class_get_devdata(class_dev); - if (loading_timeout > 0) { - fw_priv->timeout.expires = jiffies + loading_timeout * HZ; - add_timer(&fw_priv->timeout); - } - - kobject_hotplug(&class_dev->kobj, KOBJ_ADD); - wait_for_completion(&fw_priv->completion); - set_bit(FW_STATUS_DONE, &fw_priv->status); + if (hotplug) { + if (loading_timeout > 0) { + fw_priv->timeout.expires = jiffies + loading_timeout * HZ; + add_timer(&fw_priv->timeout); + } - del_timer_sync(&fw_priv->timeout); + kobject_hotplug(&class_dev->kobj, KOBJ_ADD); + wait_for_completion(&fw_priv->completion); + set_bit(FW_STATUS_DONE, &fw_priv->status); + del_timer_sync(&fw_priv->timeout); + } else + wait_for_completion(&fw_priv->completion); down(&fw_lock); if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) { @@ -455,6 +450,26 @@ out: } /** + * request_firmware: - request firmware to hotplug and wait for it + * Description: + * @firmware will be used to return a firmware image by the name + * of @name for device @device. + * + * Should be called from user context where sleeping is allowed. + * + * @name will be use as $FIRMWARE in the hotplug environment and + * should be distinctive enough not to be confused with any other + * firmware image for this or any other device. + **/ +int +request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device) +{ + int hotplug = 1; + return _request_firmware(firmware_p, name, device, hotplug); +} + +/** * release_firmware: - release the resource associated with a firmware image **/ void @@ -491,6 +506,7 @@ struct firmware_work { struct device *device; void *context; void (*cont)(const struct firmware *fw, void *context); + int hotplug; }; static int @@ -503,7 +519,8 @@ request_firmware_work_func(void *arg) return 0; } daemonize("%s/%s", "firmware", fw_work->name); - request_firmware(&fw, fw_work->name, fw_work->device); + _request_firmware(&fw, fw_work->name, fw_work->device, + fw_work->hotplug); fw_work->cont(fw, fw_work->context); release_firmware(fw); module_put(fw_work->module); @@ -518,6 +535,9 @@ request_firmware_work_func(void *arg) * Asynchronous variant of request_firmware() for contexts where * it is not possible to sleep. * + * @hotplug invokes hotplug event to copy the firmware image if this flag + * is non-zero else the firmware copy must be done manually. + * * @cont will be called asynchronously when the firmware request is over. * * @context will be passed over to @cont. @@ -527,7 +547,7 @@ request_firmware_work_func(void *arg) **/ int request_firmware_nowait( - struct module *module, + struct module *module, int hotplug, const char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context)) { @@ -548,6 +568,7 @@ request_firmware_nowait( .device = device, .context = context, .cont = cont, + .hotplug = hotplug, }; ret = kernel_thread(request_firmware_work_func, fw_work, diff --git a/drivers/base/node.c b/drivers/base/node.c index 904b27caf69..16c513aa4d4 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -39,13 +39,25 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) int n; int nid = dev->id; struct sysinfo i; + struct page_state ps; unsigned long inactive; unsigned long active; unsigned long free; si_meminfo_node(&i, nid); + get_page_state_node(&ps, nid); __get_zone_counts(&active, &inactive, &free, NODE_DATA(nid)); + /* Check for negative values in these approximate counters */ + if ((long)ps.nr_dirty < 0) + ps.nr_dirty = 0; + if ((long)ps.nr_writeback < 0) + ps.nr_writeback = 0; + if ((long)ps.nr_mapped < 0) + ps.nr_mapped = 0; + if ((long)ps.nr_slab < 0) + ps.nr_slab = 0; + n = sprintf(buf, "\n" "Node %d MemTotal: %8lu kB\n" "Node %d MemFree: %8lu kB\n" @@ -55,7 +67,11 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) "Node %d HighTotal: %8lu kB\n" "Node %d HighFree: %8lu kB\n" "Node %d LowTotal: %8lu kB\n" - "Node %d LowFree: %8lu kB\n", + "Node %d LowFree: %8lu kB\n" + "Node %d Dirty: %8lu kB\n" + "Node %d Writeback: %8lu kB\n" + "Node %d Mapped: %8lu kB\n" + "Node %d Slab: %8lu kB\n", nid, K(i.totalram), nid, K(i.freeram), nid, K(i.totalram - i.freeram), @@ -64,7 +80,11 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) nid, K(i.totalhigh), nid, K(i.freehigh), nid, K(i.totalram - i.totalhigh), - nid, K(i.freeram - i.freehigh)); + nid, K(i.freeram - i.freehigh), + nid, K(ps.nr_dirty), + nid, K(ps.nr_writeback), + nid, K(ps.nr_mapped), + nid, K(ps.nr_slab)); n += hugetlb_report_node_meminfo(nid, buf + n); return n; } diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index bdd96b03b88..0a7aa07b9a2 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -26,11 +26,11 @@ int resume_device(struct device * dev) down(&dev->sem); if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state) { + && dev->power.pm_parent->power.power_state.event) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", - dev->power.power_state, + dev->power.power_state.event, dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state); + dev->power.pm_parent->power.power_state.event); } if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); @@ -54,7 +54,7 @@ void dpm_resume(void) list_add_tail(entry, &dpm_active); up(&dpm_list_sem); - if (!dev->power.prev_state) + if (!dev->power.prev_state.event) resume_device(dev); down(&dpm_list_sem); put_device(dev); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 325962d8019..e8f0519f5df 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -13,10 +13,10 @@ static void runtime_resume(struct device * dev) { dev_dbg(dev, "resuming\n"); - if (!dev->power.power_state) + if (!dev->power.power_state.event) return; if (!resume_device(dev)) - dev->power.power_state = 0; + dev->power.power_state = PMSG_ON; } @@ -49,10 +49,10 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state) int error = 0; down(&dpm_sem); - if (dev->power.power_state == state) + if (dev->power.power_state.event == state.event) goto Done; - if (dev->power.power_state) + if (dev->power.power_state.event) runtime_resume(dev); if (!(error = suspend_device(dev, state))) diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 2ccee3763ac..50501764d05 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -40,22 +40,22 @@ int suspend_device(struct device * dev, pm_message_t state) int error = 0; down(&dev->sem); - if (dev->power.power_state) { + if (dev->power.power_state.event) { dev_dbg(dev, "PM: suspend %d-->%d\n", - dev->power.power_state, state); + dev->power.power_state.event, state.event); } if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state) { + && dev->power.pm_parent->power.power_state.event) { dev_err(dev, "PM: suspend %d->%d, parent %s already %d\n", - dev->power.power_state, state, + dev->power.power_state.event, state.event, dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state); + dev->power.pm_parent->power.power_state.event); } dev->power.prev_state = dev->power.power_state; - if (dev->bus && dev->bus->suspend && !dev->power.power_state) { + if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { dev_dbg(dev, "suspending\n"); error = dev->bus->suspend(dev, state); } diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index f82b3df9545..8d04fb435c1 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -26,19 +26,19 @@ static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) { - return sprintf(buf, "%u\n", dev->power.power_state); + return sprintf(buf, "%u\n", dev->power.power_state.event); } static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n) { - u32 state; + pm_message_t state; char * rest; int error = 0; - state = simple_strtoul(buf, &rest, 10); + state.event = simple_strtoul(buf, &rest, 10); if (*rest) return -EINVAL; - if (state) + if (state.event) error = dpm_runtime_suspend(dev, state); else dpm_runtime_resume(dev); diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 214b9643540..3431eb6004c 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -288,6 +288,27 @@ void sysdev_shutdown(void) up(&sysdev_drivers_lock); } +static void __sysdev_resume(struct sys_device *dev) +{ + struct sysdev_class *cls = dev->cls; + struct sysdev_driver *drv; + + /* First, call the class-specific one */ + if (cls->resume) + cls->resume(dev); + + /* Call auxillary drivers next. */ + list_for_each_entry(drv, &cls->drivers, entry) { + if (drv->resume) + drv->resume(dev); + } + + /* Call global drivers. */ + list_for_each_entry(drv, &sysdev_drivers, entry) { + if (drv->resume) + drv->resume(dev); + } +} /** * sysdev_suspend - Suspend all system devices. @@ -305,38 +326,93 @@ void sysdev_shutdown(void) int sysdev_suspend(pm_message_t state) { struct sysdev_class * cls; + struct sys_device *sysdev, *err_dev; + struct sysdev_driver *drv, *err_drv; + int ret; pr_debug("Suspending System Devices\n"); list_for_each_entry_reverse(cls, &system_subsys.kset.list, kset.kobj.entry) { - struct sys_device * sysdev; pr_debug("Suspending type '%s':\n", kobject_name(&cls->kset.kobj)); list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { - struct sysdev_driver * drv; pr_debug(" %s\n", kobject_name(&sysdev->kobj)); /* Call global drivers first. */ list_for_each_entry(drv, &sysdev_drivers, entry) { - if (drv->suspend) - drv->suspend(sysdev, state); + if (drv->suspend) { + ret = drv->suspend(sysdev, state); + if (ret) + goto gbl_driver; + } } /* Call auxillary drivers next. */ list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->suspend) - drv->suspend(sysdev, state); + if (drv->suspend) { + ret = drv->suspend(sysdev, state); + if (ret) + goto aux_driver; + } } /* Now call the generic one */ - if (cls->suspend) - cls->suspend(sysdev, state); + if (cls->suspend) { + ret = cls->suspend(sysdev, state); + if (ret) + goto cls_driver; + } } } return 0; + /* resume current sysdev */ +cls_driver: + drv = NULL; + printk(KERN_ERR "Class suspend failed for %s\n", + kobject_name(&sysdev->kobj)); + +aux_driver: + if (drv) + printk(KERN_ERR "Class driver suspend failed for %s\n", + kobject_name(&sysdev->kobj)); + list_for_each_entry(err_drv, &cls->drivers, entry) { + if (err_drv == drv) + break; + if (err_drv->resume) + err_drv->resume(sysdev); + } + drv = NULL; + +gbl_driver: + if (drv) + printk(KERN_ERR "sysdev driver suspend failed for %s\n", + kobject_name(&sysdev->kobj)); + list_for_each_entry(err_drv, &sysdev_drivers, entry) { + if (err_drv == drv) + break; + if (err_drv->resume) + err_drv->resume(sysdev); + } + /* resume other sysdevs in current class */ + list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { + if (err_dev == sysdev) + break; + pr_debug(" %s\n", kobject_name(&err_dev->kobj)); + __sysdev_resume(err_dev); + } + + /* resume other classes */ + list_for_each_entry_continue(cls, &system_subsys.kset.list, + kset.kobj.entry) { + list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { + pr_debug(" %s\n", kobject_name(&err_dev->kobj)); + __sysdev_resume(err_dev); + } + } + return ret; } @@ -362,25 +438,9 @@ int sysdev_resume(void) kobject_name(&cls->kset.kobj)); list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { - struct sysdev_driver * drv; pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - /* First, call the class-specific one */ - if (cls->resume) - cls->resume(sysdev); - - /* Call auxillary drivers next. */ - list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->resume) - drv->resume(sysdev); - } - - /* Call global drivers. */ - list_for_each_entry(drv, &sysdev_drivers, entry) { - if (drv->resume) - drv->resume(sysdev); - } - + __sysdev_resume(sysdev); } } return 0; diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c index 6c2b447a333..f25e7c6b2d2 100644 --- a/drivers/base/transport_class.c +++ b/drivers/base/transport_class.c @@ -7,7 +7,7 @@ * This file is licensed under GPLv2 * * The basic idea here is to allow any "device controller" (which - * would most often be a Host Bus Adapter" to use the services of one + * would most often be a Host Bus Adapter to use the services of one * or more tranport classes for performing transport specific * services. Transport specific services are things that the generic * command layer doesn't want to know about (speed settings, line @@ -64,7 +64,9 @@ void transport_class_unregister(struct transport_class *tclass) } EXPORT_SYMBOL_GPL(transport_class_unregister); -static int anon_transport_dummy_function(struct device *dev) +static int anon_transport_dummy_function(struct transport_container *tc, + struct device *dev, + struct class_device *cdev) { /* do nothing */ return 0; @@ -115,9 +117,10 @@ static int transport_setup_classdev(struct attribute_container *cont, struct class_device *classdev) { struct transport_class *tclass = class_to_transport_class(cont->class); + struct transport_container *tcont = attribute_container_to_transport_container(cont); if (tclass->setup) - tclass->setup(dev); + tclass->setup(tcont, dev, classdev); return 0; } @@ -178,12 +181,14 @@ void transport_add_device(struct device *dev) EXPORT_SYMBOL_GPL(transport_add_device); static int transport_configure(struct attribute_container *cont, - struct device *dev) + struct device *dev, + struct class_device *cdev) { struct transport_class *tclass = class_to_transport_class(cont->class); + struct transport_container *tcont = attribute_container_to_transport_container(cont); if (tclass->configure) - tclass->configure(dev); + tclass->configure(tcont, dev, cdev); return 0; } @@ -202,7 +207,7 @@ static int transport_configure(struct attribute_container *cont, */ void transport_configure_device(struct device *dev) { - attribute_container_trigger(dev, transport_configure); + attribute_container_device_trigger(dev, transport_configure); } EXPORT_SYMBOL_GPL(transport_configure_device); @@ -215,7 +220,7 @@ static int transport_remove_classdev(struct attribute_container *cont, struct transport_class *tclass = class_to_transport_class(cont->class); if (tclass->remove) - tclass->remove(dev); + tclass->remove(tcont, dev, classdev); if (tclass->remove != anon_transport_dummy_function) { if (tcont->statistics) |