summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/base.h31
-rw-r--r--drivers/base/bus.c50
-rw-r--r--drivers/base/core.c172
-rw-r--r--drivers/base/cpu.c4
-rw-r--r--drivers/base/dd.c47
-rw-r--r--drivers/base/driver.c19
-rw-r--r--drivers/base/firmware_class.c160
-rw-r--r--drivers/base/iommu.c11
-rw-r--r--drivers/base/isa.c2
-rw-r--r--drivers/base/node.c2
-rw-r--r--drivers/base/platform.c326
-rw-r--r--drivers/base/power/main.c154
-rw-r--r--drivers/base/power/power.h8
-rw-r--r--drivers/base/sys.c88
-rw-r--r--drivers/base/topology.c33
15 files changed, 832 insertions, 275 deletions
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 9f50f1b545dc..b528145a078f 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -63,6 +63,32 @@ struct class_private {
#define to_class(obj) \
container_of(obj, struct class_private, class_subsys.kobj)
+/**
+ * struct device_private - structure to hold the private to the driver core portions of the device structure.
+ *
+ * @klist_children - klist containing all children of this device
+ * @knode_parent - node in sibling list
+ * @knode_driver - node in driver list
+ * @knode_bus - node in bus list
+ * @device - pointer back to the struct class that this structure is
+ * associated with.
+ *
+ * Nothing outside of the driver core should ever touch these fields.
+ */
+struct device_private {
+ struct klist klist_children;
+ struct klist_node knode_parent;
+ struct klist_node knode_driver;
+ struct klist_node knode_bus;
+ struct device *device;
+};
+#define to_device_private_parent(obj) \
+ container_of(obj, struct device_private, knode_parent)
+#define to_device_private_driver(obj) \
+ container_of(obj, struct device_private, knode_driver)
+#define to_device_private_bus(obj) \
+ container_of(obj, struct device_private, knode_bus)
+
/* initialisation functions */
extern int devices_init(void);
extern int buses_init(void);
@@ -86,6 +112,11 @@ extern void bus_remove_driver(struct device_driver *drv);
extern void driver_detach(struct device_driver *drv);
extern int driver_probe_device(struct device_driver *drv, struct device *dev);
+static inline int driver_match_device(struct device_driver *drv,
+ struct device *dev)
+{
+ return drv->bus->match ? drv->bus->match(dev, drv) : 1;
+}
extern void sysdev_shutdown(void);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 83f32b891fa9..4b04a15146d7 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -198,7 +198,7 @@ static ssize_t driver_bind(struct device_driver *drv,
int err = -ENODEV;
dev = bus_find_device_by_name(bus, NULL, buf);
- if (dev && dev->driver == NULL) {
+ if (dev && dev->driver == NULL && driver_match_device(drv, dev)) {
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
@@ -253,7 +253,14 @@ static ssize_t store_drivers_probe(struct bus_type *bus,
static struct device *next_device(struct klist_iter *i)
{
struct klist_node *n = klist_next(i);
- return n ? container_of(n, struct device, knode_bus) : NULL;
+ struct device *dev = NULL;
+ struct device_private *dev_prv;
+
+ if (n) {
+ dev_prv = to_device_private_bus(n);
+ dev = dev_prv->device;
+ }
+ return dev;
}
/**
@@ -272,7 +279,7 @@ static struct device *next_device(struct klist_iter *i)
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
- * to retain this data, it should do, and increment the reference
+ * to retain this data, it should do so, and increment the reference
* count in the supplied callback.
*/
int bus_for_each_dev(struct bus_type *bus, struct device *start,
@@ -286,7 +293,7 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start,
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->knode_bus : NULL));
+ (start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
@@ -320,7 +327,7 @@ struct device *bus_find_device(struct bus_type *bus,
return NULL;
klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->knode_bus : NULL));
+ (start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)))
if (match(dev, data) && get_device(dev))
break;
@@ -507,7 +514,8 @@ void bus_attach_device(struct device *dev)
ret = device_attach(dev);
WARN_ON(ret < 0);
if (ret >= 0)
- klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
+ klist_add_tail(&dev->p->knode_bus,
+ &bus->p->klist_devices);
}
}
@@ -528,8 +536,8 @@ void bus_remove_device(struct device *dev)
sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
dev_name(dev));
device_remove_attrs(dev->bus, dev);
- if (klist_node_attached(&dev->knode_bus))
- klist_del(&dev->knode_bus);
+ if (klist_node_attached(&dev->p->knode_bus))
+ klist_del(&dev->p->knode_bus);
pr_debug("bus: '%s': remove device %s\n",
dev->bus->name, dev_name(dev));
@@ -692,8 +700,10 @@ int bus_add_driver(struct device_driver *drv)
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
- return error;
+ return 0;
out_unregister:
+ kfree(drv->p);
+ drv->p = NULL;
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
@@ -831,14 +841,16 @@ 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);
+ struct device_private *dev_prv = to_device_private_bus(n);
+ struct device *dev = dev_prv->device;
get_device(dev);
}
static void klist_devices_put(struct klist_node *n)
{
- struct device *dev = container_of(n, struct device, knode_bus);
+ struct device_private *dev_prv = to_device_private_bus(n);
+ struct device *dev = dev_prv->device;
put_device(dev);
}
@@ -932,6 +944,7 @@ bus_uevent_fail:
kset_unregister(&bus->p->subsys);
kfree(bus->p);
out:
+ bus->p = NULL;
return retval;
}
EXPORT_SYMBOL_GPL(bus_register);
@@ -953,6 +966,7 @@ void bus_unregister(struct bus_type *bus)
bus_remove_file(bus, &bus_attr_uevent);
kset_unregister(&bus->p->subsys);
kfree(bus->p);
+ bus->p = NULL;
}
EXPORT_SYMBOL_GPL(bus_unregister);
@@ -993,18 +1007,20 @@ static void device_insertion_sort_klist(struct device *a, struct list_head *list
{
struct list_head *pos;
struct klist_node *n;
+ struct device_private *dev_prv;
struct device *b;
list_for_each(pos, list) {
n = container_of(pos, struct klist_node, n_node);
- b = container_of(n, struct device, knode_bus);
+ dev_prv = to_device_private_bus(n);
+ b = dev_prv->device;
if (compare(a, b) <= 0) {
- list_move_tail(&a->knode_bus.n_node,
- &b->knode_bus.n_node);
+ list_move_tail(&a->p->knode_bus.n_node,
+ &b->p->knode_bus.n_node);
return;
}
}
- list_move_tail(&a->knode_bus.n_node, list);
+ list_move_tail(&a->p->knode_bus.n_node, list);
}
void bus_sort_breadthfirst(struct bus_type *bus,
@@ -1014,6 +1030,7 @@ void bus_sort_breadthfirst(struct bus_type *bus,
LIST_HEAD(sorted_devices);
struct list_head *pos, *tmp;
struct klist_node *n;
+ struct device_private *dev_prv;
struct device *dev;
struct klist *device_klist;
@@ -1022,7 +1039,8 @@ void bus_sort_breadthfirst(struct bus_type *bus,
spin_lock(&device_klist->k_lock);
list_for_each_safe(pos, tmp, &device_klist->k_list) {
n = container_of(pos, struct klist_node, n_node);
- dev = container_of(n, struct device, knode_bus);
+ dev_prv = to_device_private_bus(n);
+ dev = dev_prv->device;
device_insertion_sort_klist(dev, &sorted_devices, compare);
}
list_splice(&sorted_devices, &device_klist->k_list);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index f3eae630e589..7ecb1938e590 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -22,6 +22,7 @@
#include <linux/kallsyms.h>
#include <linux/semaphore.h>
#include <linux/mutex.h>
+#include <linux/async.h>
#include "base.h"
#include "power/power.h"
@@ -109,6 +110,7 @@ static struct sysfs_ops dev_sysfs_ops = {
static void device_release(struct kobject *kobj)
{
struct device *dev = to_dev(kobj);
+ struct device_private *p = dev->p;
if (dev->release)
dev->release(dev);
@@ -120,6 +122,7 @@ static void device_release(struct kobject *kobj)
WARN(1, KERN_ERR "Device '%s' does not have a release() "
"function, it is broken and must be fixed.\n",
dev_name(dev));
+ kfree(p);
}
static struct kobj_type device_ktype = {
@@ -134,8 +137,6 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
if (ktype == &device_ktype) {
struct device *dev = to_dev(kobj);
- if (dev->uevent_suppress)
- return 0;
if (dev->bus)
return 1;
if (dev->class)
@@ -161,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)
@@ -507,14 +516,16 @@ EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
static void klist_children_get(struct klist_node *n)
{
- struct device *dev = container_of(n, struct device, knode_parent);
+ struct device_private *p = to_device_private_parent(n);
+ struct device *dev = p->device;
get_device(dev);
}
static void klist_children_put(struct klist_node *n)
{
- struct device *dev = container_of(n, struct device, knode_parent);
+ struct device_private *p = to_device_private_parent(n);
+ struct device *dev = p->device;
put_device(dev);
}
@@ -538,8 +549,6 @@ void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
- klist_init(&dev->klist_children, klist_children_get,
- klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
@@ -777,17 +786,12 @@ static void device_remove_class_symlinks(struct device *dev)
int dev_set_name(struct device *dev, const char *fmt, ...)
{
va_list vargs;
- char *s;
+ int err;
va_start(vargs, fmt);
- vsnprintf(dev->bus_id, sizeof(dev->bus_id), fmt, vargs);
+ err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
va_end(vargs);
-
- /* ewww... some of these buggers have / in the name... */
- while ((s = strchr(dev->bus_id, '/')))
- *s = '!';
-
- return 0;
+ return err;
}
EXPORT_SYMBOL_GPL(dev_set_name);
@@ -864,13 +868,27 @@ int device_add(struct device *dev)
if (!dev)
goto done;
- /* Temporarily support init_name if it is set.
- * It will override bus_id for now */
- if (dev->init_name)
+ dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
+ if (!dev->p) {
+ error = -ENOMEM;
+ goto done;
+ }
+ dev->p->device = dev;
+ klist_init(&dev->p->klist_children, klist_children_get,
+ klist_children_put);
+
+ /*
+ * for statically allocated devices, which should all be converted
+ * some day, we need to initialize the name. We prevent reading back
+ * the name, and force the use of dev_name()
+ */
+ if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
+ dev->init_name = NULL;
+ }
- if (!strlen(dev->bus_id))
- goto done;
+ if (!dev_name(dev))
+ goto name_error;
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
@@ -882,7 +900,8 @@ int device_add(struct device *dev)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
- error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev));
+ /* we require the name to be set before, and pass NULL */
+ error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
@@ -928,7 +947,8 @@ int device_add(struct device *dev)
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_attach_device(dev);
if (parent)
- klist_add_tail(&dev->knode_parent, &parent->klist_children);
+ klist_add_tail(&dev->p->knode_parent,
+ &parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
@@ -967,6 +987,9 @@ done:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
+name_error:
+ kfree(dev->p);
+ dev->p = NULL;
goto done;
}
@@ -1042,7 +1065,7 @@ void device_del(struct device *dev)
device_pm_remove(dev);
dpm_sysfs_remove(dev);
if (parent)
- klist_del(&dev->knode_parent);
+ klist_del(&dev->p->knode_parent);
if (MAJOR(dev->devt)) {
device_remove_sys_dev_entry(dev);
device_remove_file(dev, &devt_attr);
@@ -1103,7 +1126,55 @@ void device_unregister(struct device *dev)
static struct device *next_device(struct klist_iter *i)
{
struct klist_node *n = klist_next(i);
- return n ? container_of(n, struct device, knode_parent) : NULL;
+ struct device *dev = NULL;
+ struct device_private *p;
+
+ if (n) {
+ p = to_device_private_parent(n);
+ dev = p->device;
+ }
+ return dev;
+}
+
+/**
+ * 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;
}
/**
@@ -1125,7 +1196,10 @@ int device_for_each_child(struct device *parent, void *data,
struct device *child;
int error = 0;
- klist_iter_init(&parent->klist_children, &i);
+ if (!parent->p)
+ return 0;
+
+ klist_iter_init(&parent->p->klist_children, &i);
while ((child = next_device(&i)) && !error)
error = fn(child, data);
klist_iter_exit(&i);
@@ -1156,7 +1230,7 @@ struct device *device_find_child(struct device *parent, void *data,
if (!parent)
return NULL;
- klist_iter_init(&parent->klist_children, &i);
+ klist_iter_init(&parent->p->klist_children, &i);
while ((child = next_device(&i)))
if (match(child, data) && get_device(child))
break;
@@ -1247,7 +1321,7 @@ struct device *__root_device_register(const char *name, struct module *owner)
if (!root)
return ERR_PTR(err);
- err = dev_set_name(&root->dev, name);
+ err = dev_set_name(&root->dev, "%s", name);
if (err) {
kfree(root);
return ERR_PTR(err);
@@ -1348,7 +1422,10 @@ struct device *device_create_vargs(struct class *class, struct device *parent,
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
- vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);
+ retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
+ if (retval)
+ goto error;
+
retval = device_register(dev);
if (retval)
goto error;
@@ -1452,19 +1529,15 @@ int device_rename(struct device *dev, char *new_name)
old_class_name = make_class_name(dev->class->name, &dev->kobj);
#endif
- old_device_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
+ old_device_name = kstrdup(dev_name(dev), GFP_KERNEL);
if (!old_device_name) {
error = -ENOMEM;
goto out;
}
- strlcpy(old_device_name, dev->bus_id, BUS_ID_SIZE);
- strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
error = kobject_rename(&dev->kobj, new_name);
- if (error) {
- strlcpy(dev->bus_id, old_device_name, BUS_ID_SIZE);
+ if (error)
goto out;
- }
#ifdef CONFIG_SYSFS_DEPRECATED
if (old_class_name) {
@@ -1545,8 +1618,10 @@ out:
* 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 (can by NULL)
+ * @dpm_order: how to reorder the dpm_list
*/
-int device_move(struct device *dev, struct device *new_parent)
+int device_move(struct device *dev, struct device *new_parent,
+ enum dpm_order dpm_order)
{
int error;
struct device *old_parent;
@@ -1556,6 +1631,7 @@ int device_move(struct device *dev, struct device *new_parent)
if (!dev)
return -EINVAL;
+ device_pm_lock();
new_parent = get_device(new_parent);
new_parent_kobj = get_device_parent(dev, new_parent);
@@ -1570,9 +1646,10 @@ int device_move(struct device *dev, struct device *new_parent)
old_parent = dev->parent;
dev->parent = new_parent;
if (old_parent)
- klist_remove(&dev->knode_parent);
+ klist_remove(&dev->p->knode_parent);
if (new_parent) {
- klist_add_tail(&dev->knode_parent, &new_parent->klist_children);
+ klist_add_tail(&dev->p->knode_parent,
+ &new_parent->p->klist_children);
set_dev_node(dev, dev_to_node(new_parent));
}
@@ -1584,11 +1661,11 @@ int device_move(struct device *dev, struct device *new_parent)
device_move_class_links(dev, new_parent, old_parent);
if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
if (new_parent)
- klist_remove(&dev->knode_parent);
+ klist_remove(&dev->p->knode_parent);
dev->parent = old_parent;
if (old_parent) {
- klist_add_tail(&dev->knode_parent,
- &old_parent->klist_children);
+ klist_add_tail(&dev->p->knode_parent,
+ &old_parent->p->klist_children);
set_dev_node(dev, dev_to_node(old_parent));
}
}
@@ -1596,9 +1673,23 @@ int device_move(struct device *dev, struct device *new_parent)
put_device(new_parent);
goto out;
}
+ switch (dpm_order) {
+ case DPM_ORDER_NONE:
+ break;
+ case DPM_ORDER_DEV_AFTER_PARENT:
+ device_pm_move_after(dev, new_parent);
+ break;
+ case DPM_ORDER_PARENT_BEFORE_DEV:
+ device_pm_move_before(new_parent, dev);
+ break;
+ case DPM_ORDER_DEV_LAST:
+ device_pm_move_last(dev);
+ break;
+ }
out_put:
put_device(old_parent);
out:
+ device_pm_unlock();
put_device(dev);
return error;
}
@@ -1624,4 +1715,5 @@ void device_shutdown(void)
kobject_put(sysfs_dev_char_kobj);
kobject_put(sysfs_dev_block_kobj);
kobject_put(dev_kobj);
+ async_synchronize_full();
}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 719ee5c1c8d9..e62a4ccea54d 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -107,7 +107,7 @@ static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
/*
* Print cpu online, possible, present, and system maps
*/
-static ssize_t print_cpus_map(char *buf, cpumask_t *map)
+static ssize_t print_cpus_map(char *buf, const struct cpumask *map)
{
int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map);
@@ -119,7 +119,7 @@ static ssize_t print_cpus_map(char *buf, cpumask_t *map)
#define print_cpus_func(type) \
static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \
{ \
- return print_cpus_map(buf, &cpu_##type##_map); \
+ return print_cpus_map(buf, cpu_##type##_mask); \
} \
static struct sysdev_class_attribute attr_##type##_map = \
_SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 135231239103..f0106875f01d 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -30,7 +30,7 @@
static void driver_bound(struct device *dev)
{
- if (klist_node_attached(&dev->knode_driver)) {
+ if (klist_node_attached(&dev->p->knode_driver)) {
printk(KERN_WARNING "%s: device %s already bound\n",
__func__, kobject_name(&dev->kobj));
return;
@@ -43,7 +43,7 @@ static void driver_bound(struct device *dev)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
- klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
+ klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
}
static int driver_sysfs_add(struct device *dev)
@@ -172,31 +172,22 @@ int driver_probe_done(void)
/**
* wait_for_device_probe
* Wait for device probing to be completed.
- *
- * Note: this function polls at 100 msec intervals.
*/
-int wait_for_device_probe(void)
+void wait_for_device_probe(void)
{
/* wait for the known devices to complete their probing */
- while (driver_probe_done() != 0)
- msleep(100);
+ wait_event(probe_waitqueue, atomic_read(&probe_count) == 0);
async_synchronize_full();
- return 0;
}
+EXPORT_SYMBOL_GPL(wait_for_device_probe);
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
- * First, we call the bus's match function, if one present, which should
- * compare the device IDs the driver supports with the device IDs of the
- * device. Note we don't do this ourselves because we don't know the
- * format of the ID structures, nor what is to be considered a match and
- * what is not.
- *
- * This function returns 1 if a match is found, -ENODEV if the device is
- * not registered, and 0 otherwise.
+ * This function returns -ENODEV if the device is not registered,
+ * 1 if the device is bound sucessfully and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
@@ -207,21 +198,22 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
if (!device_is_registered(dev))
return -ENODEV;
- if (drv->bus->match && !drv->bus->match(dev, drv))
- goto done;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
ret = really_probe(dev, drv);
-done:
return ret;
}
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
+
+ if (!driver_match_device(drv, dev))
+ return 0;
+
return driver_probe_device(drv, dev);
}
@@ -234,7 +226,7 @@ static int __device_attach(struct device_driver *drv, void *data)
* pair is found, break out and return.
*
* Returns 1 if the device was bound to a driver;
- * 0 if no matching device was found;
+ * 0 if no matching driver was found;
* -ENODEV if the device is not registered.
*
* When called for a USB interface, @dev->parent->sem must be held.
@@ -274,7 +266,7 @@ static int __driver_attach(struct device *dev, void *data)
* is an error.
*/
- if (drv->bus->match && !drv->bus->match(dev, drv))
+ if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
@@ -327,7 +319,11 @@ static void __device_release_driver(struct device *dev)
drv->remove(dev);
devres_release_all(dev);
dev->driver = NULL;
- klist_remove(&dev->knode_driver);
+ klist_remove(&dev->p->knode_driver);
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_UNBOUND_DRIVER,
+ dev);
}
}
@@ -357,6 +353,7 @@ EXPORT_SYMBOL_GPL(device_release_driver);
*/
void driver_detach(struct device_driver *drv)
{
+ struct device_private *dev_prv;
struct device *dev;
for (;;) {
@@ -365,8 +362,10 @@ void driver_detach(struct device_driver *drv)
spin_unlock(&drv->p->klist_devices.k_lock);
break;
}
- dev = list_entry(drv->p->klist_devices.k_list.prev,
- struct device, knode_driver.n_node);
+ dev_prv = list_entry(drv->p->klist_devices.k_list.prev,
+ struct device_private,
+ knode_driver.n_node);
+ dev = dev_prv->device;
get_device(dev);
spin_unlock(&drv->p->klist_devices.k_lock);
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 1e2bda780e48..8ae0f63602e0 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -19,7 +19,14 @@
static struct device *next_device(struct klist_iter *i)
{
struct klist_node *n = klist_next(i);
- return n ? container_of(n, struct device, knode_driver) : NULL;
+ struct device *dev = NULL;
+ struct device_private *dev_prv;
+
+ if (n) {
+ dev_prv = to_device_private_driver(n);
+ dev = dev_prv->device;
+ }
+ return dev;
}
/**
@@ -42,7 +49,7 @@ int driver_for_each_device(struct device_driver *drv, struct device *start,
return -EINVAL;
klist_iter_init_node(&drv->p->klist_devices, &i,
- start ? &start->knode_driver : NULL);
+ start ? &start->p->knode_driver : NULL);
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
@@ -76,7 +83,7 @@ struct device *driver_find_device(struct device_driver *drv,
return NULL;
klist_iter_init_node(&drv->p->klist_devices, &i,
- (start ? &start->knode_driver : NULL));
+ (start ? &start->p->knode_driver : NULL));
while ((dev = next_device(&i)))
if (match(dev, data) && get_device(dev))
break;
@@ -216,6 +223,8 @@ int driver_register(struct device_driver *drv)
int ret;
struct device_driver *other;
+ BUG_ON(!drv->bus->p);
+
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
@@ -248,6 +257,10 @@ EXPORT_SYMBOL_GPL(driver_register);
*/
void driver_unregister(struct device_driver *drv)
{
+ if (!drv || !drv->p) {
+ WARN(1, "Unexpected driver unregister!\n");
+ return;
+ }
driver_remove_groups(drv, drv->groups);
bus_remove_driver(drv);
}
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 44699d9dd85c..ddeb819c8f87 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -17,7 +17,7 @@
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
-
+#include <linux/highmem.h>
#include <linux/firmware.h>
#include "base.h"
@@ -40,12 +40,15 @@ static int loading_timeout = 60; /* In seconds */
static DEFINE_MUTEX(fw_lock);
struct firmware_priv {
- char fw_id[FIRMWARE_NAME_MAX];
+ char *fw_id;
struct completion completion;
struct bin_attribute attr_data;
struct firmware *fw;
unsigned long status;
- int alloc_size;
+ struct page **pages;
+ int nr_pages;
+ int page_array_size;
+ const char *vdata;
struct timer_list timeout;
};
@@ -122,6 +125,10 @@ static ssize_t firmware_loading_show(struct device *dev,
return sprintf(buf, "%d\n", loading);
}
+/* Some architectures don't have PAGE_KERNEL_RO */
+#ifndef PAGE_KERNEL_RO
+#define PAGE_KERNEL_RO PAGE_KERNEL
+#endif
/**
* firmware_loading_store - set value in the 'loading' control file
* @dev: device pointer
@@ -141,6 +148,7 @@ static ssize_t firmware_loading_store(struct device *dev,
{
struct firmware_priv *fw_priv = dev_get_drvdata(dev);
int loading = simple_strtol(buf, NULL, 10);
+ int i;
switch (loading) {
case 1:
@@ -151,13 +159,30 @@ static ssize_t firmware_loading_store(struct device *dev,
}
vfree(fw_priv->fw->data);
fw_priv->fw->data = NULL;
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ kfree(fw_priv->pages);
+ fw_priv->pages = NULL;
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
fw_priv->fw->size = 0;
- fw_priv->alloc_size = 0;
set_bit(FW_STATUS_LOADING, &fw_priv->status);
mutex_unlock(&fw_lock);
break;
case 0:
if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+ vfree(fw_priv->fw->data);
+ fw_priv->fw->data = vmap(fw_priv->pages,
+ fw_priv->nr_pages,
+ 0, PAGE_KERNEL_RO);
+ if (!fw_priv->fw->data) {
+ dev_err(dev, "%s: vmap() failed\n", __func__);
+ goto err;
+ }
+ /* Pages will be freed by vfree() */
+ fw_priv->pages = NULL;
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
complete(&fw_priv->completion);
clear_bit(FW_STATUS_LOADING, &fw_priv->status);
break;
@@ -167,6 +192,7 @@ static ssize_t firmware_loading_store(struct device *dev,
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
+ err:
fw_load_abort(fw_priv);
break;
}
@@ -191,8 +217,28 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
ret_count = -ENODEV;
goto out;
}
- ret_count = memory_read_from_buffer(buffer, count, &offset,
- fw->data, fw->size);
+ if (offset > fw->size)
+ return 0;
+ if (count > fw->size - offset)
+ count = fw->size - offset;
+
+ ret_count = count;
+
+ while (count) {
+ void *page_data;
+ int page_nr = offset >> PAGE_SHIFT;
+ int page_ofs = offset & (PAGE_SIZE-1);
+ int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+ page_data = kmap(fw_priv->pages[page_nr]);
+
+ memcpy(buffer, page_data + page_ofs, page_cnt);
+
+ kunmap(fw_priv->pages[page_nr]);
+ buffer += page_cnt;
+ offset += page_cnt;
+ count -= page_cnt;
+ }
out:
mutex_unlock(&fw_lock);
return ret_count;
@@ -201,27 +247,39 @@ out:
static int
fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{
- u8 *new_data;
- int new_size = fw_priv->alloc_size;
+ int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
+
+ /* If the array of pages is too small, grow it... */
+ if (fw_priv->page_array_size < pages_needed) {
+ int new_array_size = max(pages_needed,
+ fw_priv->page_array_size * 2);
+ struct page **new_pages;
+
+ new_pages = kmalloc(new_array_size * sizeof(void *),
+ GFP_KERNEL);
+ if (!new_pages) {
+ fw_load_abort(fw_priv);
+ return -ENOMEM;
+ }
+ memcpy(new_pages, fw_priv->pages,
+ fw_priv->page_array_size * sizeof(void *));
+ memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
+ (new_array_size - fw_priv->page_array_size));
+ kfree(fw_priv->pages);
+ fw_priv->pages = new_pages;
+ fw_priv->page_array_size = new_array_size;
+ }
- if (min_size <= fw_priv->alloc_size)
- return 0;
+ while (fw_priv->nr_pages < pages_needed) {
+ fw_priv->pages[fw_priv->nr_pages] =
+ alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
- new_size = ALIGN(min_size, PAGE_SIZE);
- new_data = vmalloc(new_size);
- if (!new_data) {
- printk(KERN_ERR "%s: unable to alloc buffer\n", __func__);