1263 lines · 31.5 KB · C
RawDownload
/* * bus.c - bus driver management * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> * Copyright (c) 2007 Novell Inc. * * This file is released under the GPLv2 * */ #include <linux/device.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/string.h>#include <linux/mutex.h>#include <linux/sysfs.h>#include "base.h"#include "power/power.h" /* /sys/devices/system */static struct kset *system_kset; #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) /* * sysfs bindings for drivers */ #define to_drv_attr(_attr) container_of(_attr, struct driver_attribute, attr)  static int __must_check bus_rescan_devices_helper(struct device *dev,						void *data); static struct bus_type *bus_get(struct bus_type *bus){	if (bus) {		kset_get(&bus->p->subsys);		return bus;	}	return NULL;} static void bus_put(struct bus_type *bus){	if (bus)		kset_put(&bus->p->subsys);} static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,			     char *buf){	struct driver_attribute *drv_attr = to_drv_attr(attr);	struct driver_private *drv_priv = to_driver(kobj);	ssize_t ret = -EIO; 	if (drv_attr->show)		ret = drv_attr->show(drv_priv->driver, buf);	return ret;} static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,			      const char *buf, size_t count){	struct driver_attribute *drv_attr = to_drv_attr(attr);	struct driver_private *drv_priv = to_driver(kobj);	ssize_t ret = -EIO; 	if (drv_attr->store)		ret = drv_attr->store(drv_priv->driver, buf, count);	return ret;} static const struct sysfs_ops driver_sysfs_ops = {	.show	= drv_attr_show,	.store	= drv_attr_store,}; static void driver_release(struct kobject *kobj){	struct driver_private *drv_priv = to_driver(kobj); 	pr_debug("driver: '%s': %s\n", kobject_name(kobj), __func__);	kfree(drv_priv);} static struct kobj_type driver_ktype = {	.sysfs_ops	= &driver_sysfs_ops,	.release	= driver_release,}; /* * sysfs bindings for buses */static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,			     char *buf){	struct bus_attribute *bus_attr = to_bus_attr(attr);	struct subsys_private *subsys_priv = to_subsys_private(kobj);	ssize_t ret = 0; 	if (bus_attr->show)		ret = bus_attr->show(subsys_priv->bus, buf);	return ret;} static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,			      const char *buf, size_t count){	struct bus_attribute *bus_attr = to_bus_attr(attr);	struct subsys_private *subsys_priv = to_subsys_private(kobj);	ssize_t ret = 0; 	if (bus_attr->store)		ret = bus_attr->store(subsys_priv->bus, buf, count);	return ret;} static const struct sysfs_ops bus_sysfs_ops = {	.show	= bus_attr_show,	.store	= bus_attr_store,}; int bus_create_file(struct bus_type *bus, struct bus_attribute *attr){	int error;	if (bus_get(bus)) {		error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);		bus_put(bus);	} else		error = -EINVAL;	return error;}EXPORT_SYMBOL_GPL(bus_create_file); void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr){	if (bus_get(bus)) {		sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);		bus_put(bus);	}}EXPORT_SYMBOL_GPL(bus_remove_file); static struct kobj_type bus_ktype = {	.sysfs_ops	= &bus_sysfs_ops,}; static int bus_uevent_filter(struct kset *kset, struct kobject *kobj){	struct kobj_type *ktype = get_ktype(kobj); 	if (ktype == &bus_ktype)		return 1;	return 0;} static const struct kset_uevent_ops bus_uevent_ops = {	.filter = bus_uevent_filter,}; static struct kset *bus_kset; /* Manually detach a device from its associated driver. */static ssize_t unbind_store(struct device_driver *drv, const char *buf,			    size_t count){	struct bus_type *bus = bus_get(drv->bus);	struct device *dev;	int err = -ENODEV; 	dev = bus_find_device_by_name(bus, NULL, buf);	if (dev && dev->driver == drv) {		if (dev->parent)	/* Needed for USB */			device_lock(dev->parent);		device_release_driver(dev);		if (dev->parent)			device_unlock(dev->parent);		err = count;	}	put_device(dev);	bus_put(bus);	return err;}static DRIVER_ATTR_WO(unbind); /* * Manually attach a device to a driver. * Note: the driver must want to bind to the device, * it is not possible to override the driver's id table. */static ssize_t bind_store(struct device_driver *drv, const char *buf,			  size_t count){	struct bus_type *bus = bus_get(drv->bus);	struct device *dev;	int err = -ENODEV; 	dev = bus_find_device_by_name(bus, NULL, buf);	if (dev && dev->driver == NULL && driver_match_device(drv, dev)) {		if (dev->parent)	/* Needed for USB */			device_lock(dev->parent);		device_lock(dev);		err = driver_probe_device(drv, dev);		device_unlock(dev);		if (dev->parent)			device_unlock(dev->parent); 		if (err > 0) {			/* success */			err = count;		} else if (err == 0) {			/* driver didn't accept device */			err = -ENODEV;		}	}	put_device(dev);	bus_put(bus);	return err;}static DRIVER_ATTR_WO(bind); static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf){	return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);} static ssize_t store_drivers_autoprobe(struct bus_type *bus,				       const char *buf, size_t count){	if (buf[0] == '0')		bus->p->drivers_autoprobe = 0;	else		bus->p->drivers_autoprobe = 1;	return count;} static ssize_t store_drivers_probe(struct bus_type *bus,				   const char *buf, size_t count){	struct device *dev; 	dev = bus_find_device_by_name(bus, NULL, buf);	if (!dev)		return -ENODEV;	if (bus_rescan_devices_helper(dev, NULL) != 0)		return -EINVAL;	return count;} static struct device *next_device(struct klist_iter *i){	struct klist_node *n = klist_next(i);	struct device *dev = NULL;	struct device_private *dev_prv; 	if (n) {		dev_prv = to_device_private_bus(n);		dev = dev_prv->device;	}	return dev;} /** * bus_for_each_dev - device iterator. * @bus: bus type. * @start: device to start iterating from. * @data: data for the callback. * @fn: function to be called for each device. * * Iterate over @bus's list of devices, and call @fn for each, * passing it @data. If @start is not NULL, we use that device to * begin iterating from. * * We check the return of @fn each time. If it returns anything * other than 0, we break out and return that value. * * 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 so, and increment the reference * count in the supplied callback. */int bus_for_each_dev(struct bus_type *bus, struct device *start,		     void *data, int (*fn)(struct device *, void *)){	struct klist_iter i;	struct device *dev;	int error = 0; 	if (!bus || !bus->p)		return -EINVAL; 	klist_iter_init_node(&bus->p->klist_devices, &i,			     (start ? &start->p->knode_bus : NULL));	while ((dev = next_device(&i)) && !error)		error = fn(dev, data);	klist_iter_exit(&i);	return error;}EXPORT_SYMBOL_GPL(bus_for_each_dev); /** * bus_find_device - device iterator for locating a particular device. * @bus: bus type * @start: Device to begin with * @data: Data to pass to match function * @match: Callback function to check device * * This is similar to the bus_for_each_dev() 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, this function will * return to the caller and not iterate over any more devices. */struct device *bus_find_device(struct bus_type *bus,			       struct device *start, void *data,			       int (*match)(struct device *dev, void *data)){	struct klist_iter i;	struct device *dev; 	if (!bus || !bus->p)		return NULL; 	klist_iter_init_node(&bus->p->klist_devices, &i,			     (start ? &start->p->knode_bus : NULL));	while ((dev = next_device(&i)))		if (match(dev, data) && get_device(dev))			break;	klist_iter_exit(&i);	return dev;}EXPORT_SYMBOL_GPL(bus_find_device); static int match_name(struct device *dev, void *data){	const char *name = data; 	return sysfs_streq(name, dev_name(dev));} /** * bus_find_device_by_name - device iterator for locating a particular device of a specific name * @bus: bus type * @start: Device to begin with * @name: name of the device to match * * This is similar to the bus_find_device() function above, but it handles * searching by a name automatically, no need to write another strcmp matching * function. */struct device *bus_find_device_by_name(struct bus_type *bus,				       struct device *start, const char *name){	return bus_find_device(bus, start, (void *)name, match_name);}EXPORT_SYMBOL_GPL(bus_find_device_by_name); /** * subsys_find_device_by_id - find a device with a specific enumeration number * @subsys: subsystem * @id: index 'id' in struct device * @hint: device to check first * * Check the hint's next object and if it is a match return it directly, * otherwise, fall back to a full list search. Either way a reference for * the returned object is taken. */struct device *subsys_find_device_by_id(struct bus_type *subsys, unsigned int id,					struct device *hint){	struct klist_iter i;	struct device *dev; 	if (!subsys)		return NULL; 	if (hint) {		klist_iter_init_node(&subsys->p->klist_devices, &i, &hint->p->knode_bus);		dev = next_device(&i);		if (dev && dev->id == id && get_device(dev)) {			klist_iter_exit(&i);			return dev;		}		klist_iter_exit(&i);	} 	klist_iter_init_node(&subsys->p->klist_devices, &i, NULL);	while ((dev = next_device(&i))) {		if (dev->id == id && get_device(dev)) {			klist_iter_exit(&i);			return dev;		}	}	klist_iter_exit(&i);	return NULL;}EXPORT_SYMBOL_GPL(subsys_find_device_by_id); static struct device_driver *next_driver(struct klist_iter *i){	struct klist_node *n = klist_next(i);	struct driver_private *drv_priv; 	if (n) {		drv_priv = container_of(n, struct driver_private, knode_bus);		return drv_priv->driver;	}	return NULL;} /** * bus_for_each_drv - driver iterator * @bus: bus we're dealing with. * @start: driver to start iterating on. * @data: data to pass to the callback. * @fn: function to call for each driver. * * This is nearly identical to the device iterator above. * We iterate over each driver that belongs to @bus, and call * @fn for each. If @fn returns anything but 0, we break out * and return it. If @start is not NULL, we use it as the head * of the list. * * NOTE: we don't return the driver that returns a non-zero * value, nor do we leave the reference count incremented for that * driver. If the caller needs to know that info, it must set it * in the callback. It must also be sure to increment the refcount * so it doesn't disappear before returning to the caller. */int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,		     void *data, int (*fn)(struct device_driver *, void *)){	struct klist_iter i;	struct device_driver *drv;	int error = 0; 	if (!bus)		return -EINVAL; 	klist_iter_init_node(&bus->p->klist_drivers, &i,			     start ? &start->p->knode_bus : NULL);	while ((drv = next_driver(&i)) && !error)		error = fn(drv, data);	klist_iter_exit(&i);	return error;}EXPORT_SYMBOL_GPL(bus_for_each_drv); static int device_add_attrs(struct bus_type *bus, struct device *dev){	int error = 0;	int i; 	if (!bus->dev_attrs)		return 0; 	for (i = 0; bus->dev_attrs[i].attr.name; i++) {		error = device_create_file(dev, &bus->dev_attrs[i]);		if (error) {			while (--i >= 0)				device_remove_file(dev, &bus->dev_attrs[i]);			break;		}	}	return error;} static void device_remove_attrs(struct bus_type *bus, struct device *dev){	int i; 	if (bus->dev_attrs) {		for (i = 0; bus->dev_attrs[i].attr.name; i++)			device_remove_file(dev, &bus->dev_attrs[i]);	}} /** * bus_add_device - add device to bus * @dev: device being added * * - Add device's bus attributes. * - Create links to device's bus. * - Add the device to its bus's list of devices. */int bus_add_device(struct device *dev){	struct bus_type *bus = bus_get(dev->bus);	int error = 0; 	if (bus) {		pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));		error = device_add_attrs(bus, dev);

Lines 1–500 of 1263.