diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/attribute_container.c | 86 | ||||
-rw-r--r-- | drivers/base/bus.c | 34 | ||||
-rw-r--r-- | drivers/base/core.c | 17 | ||||
-rw-r--r-- | drivers/base/driver.c | 15 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 79 | ||||
-rw-r--r-- | drivers/base/transport_class.c | 19 |
6 files changed, 200 insertions, 50 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 17e96698410..03204bfd17a 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -568,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. @@ -602,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/core.c b/drivers/base/core.c index c8a33df0076..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); } 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/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) |