mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-19 18:53:52 +08:00
driver-core: implement 'sysdev' functionality for regular devices and buses
All sysdev classes and sysdev devices will converted to regular devices and buses to properly hook userspace into the event processing. There is no interesting difference between a 'sysdev' and 'device' which would justify to roll an entire own subsystem with different userspace export semantics. Userspace relies on events and generic sysfs subsystem infrastructure from sysdev devices, which are currently not properly available. Every converted sysdev class will create a regular device with the class name in /sys/devices/system and all registered devices will becom a children of theses devices. For compatibility reasons, the sysdev class-wide attributes are created at this parent device. (Do not copy that logic for anything new, subsystem- wide properties belong to the subsystem, not to some fake parent device created in /sys/devices.) Every sysdev driver is implemented as a simple subsystem interface now, and no longer called a driver. After all sysdev classes are ported to regular driver core entities, the sysdev implementation will be entirely removed from the kernel. Signed-off-by: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
6261ddee70
commit
ca22e56deb
@ -4,7 +4,9 @@
|
||||
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
|
||||
*
|
||||
* @subsys - the struct kset that defines this subsystem
|
||||
* @devices_kset - the list of devices associated
|
||||
* @devices_kset - the subsystem's 'devices' directory
|
||||
* @interfaces - list of subsystem interfaces associated
|
||||
* @mutex - protect the devices, and interfaces lists.
|
||||
*
|
||||
* @drivers_kset - the list of drivers associated
|
||||
* @klist_devices - the klist to iterate over the @devices_kset
|
||||
@ -14,10 +16,8 @@
|
||||
* @bus - pointer back to the struct bus_type that this structure is associated
|
||||
* with.
|
||||
*
|
||||
* @class_interfaces - list of class_interfaces associated
|
||||
* @glue_dirs - "glue" directory to put in-between the parent device to
|
||||
* avoid namespace conflicts
|
||||
* @class_mutex - mutex to protect the children, devices, and interfaces lists.
|
||||
* @class - pointer back to the struct class that this structure is associated
|
||||
* with.
|
||||
*
|
||||
@ -28,6 +28,8 @@
|
||||
struct subsys_private {
|
||||
struct kset subsys;
|
||||
struct kset *devices_kset;
|
||||
struct list_head interfaces;
|
||||
struct mutex mutex;
|
||||
|
||||
struct kset *drivers_kset;
|
||||
struct klist klist_devices;
|
||||
@ -36,9 +38,7 @@ struct subsys_private {
|
||||
unsigned int drivers_autoprobe:1;
|
||||
struct bus_type *bus;
|
||||
|
||||
struct list_head class_interfaces;
|
||||
struct kset glue_dirs;
|
||||
struct mutex class_mutex;
|
||||
struct class *class;
|
||||
};
|
||||
#define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj)
|
||||
@ -94,7 +94,6 @@ extern int hypervisor_init(void);
|
||||
static inline int hypervisor_init(void) { return 0; }
|
||||
#endif
|
||||
extern int platform_bus_init(void);
|
||||
extern int system_bus_init(void);
|
||||
extern int cpu_dev_init(void);
|
||||
|
||||
extern int bus_add_device(struct device *dev);
|
||||
@ -116,6 +115,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj);
|
||||
|
||||
extern int devres_release_all(struct device *dev);
|
||||
|
||||
/* /sys/devices directory */
|
||||
extern struct kset *devices_kset;
|
||||
|
||||
#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
|
||||
|
@ -16,9 +16,14 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "base.h"
|
||||
#include "power/power.h"
|
||||
|
||||
/* /sys/devices/system */
|
||||
/* FIXME: make static after drivers/base/sys.c is deleted */
|
||||
struct kset *system_kset;
|
||||
|
||||
#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
|
||||
|
||||
/*
|
||||
@ -360,6 +365,47 @@ struct device *bus_find_device_by_name(struct bus_type *bus,
|
||||
}
|
||||
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);
|
||||
@ -487,38 +533,59 @@ out_put:
|
||||
void bus_probe_device(struct device *dev)
|
||||
{
|
||||
struct bus_type *bus = dev->bus;
|
||||
struct subsys_interface *sif;
|
||||
int ret;
|
||||
|
||||
if (bus && bus->p->drivers_autoprobe) {
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
if (bus->p->drivers_autoprobe) {
|
||||
ret = device_attach(dev);
|
||||
WARN_ON(ret < 0);
|
||||
}
|
||||
|
||||
mutex_lock(&bus->p->mutex);
|
||||
list_for_each_entry(sif, &bus->p->interfaces, node)
|
||||
if (sif->add_dev)
|
||||
sif->add_dev(dev, sif);
|
||||
mutex_unlock(&bus->p->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* bus_remove_device - remove device from bus
|
||||
* @dev: device to be removed
|
||||
*
|
||||
* - Remove symlink from bus's directory.
|
||||
* - Remove device from all interfaces.
|
||||
* - Remove symlink from bus' directory.
|
||||
* - Delete device from bus's list.
|
||||
* - Detach from its driver.
|
||||
* - Drop reference taken in bus_add_device().
|
||||
*/
|
||||
void bus_remove_device(struct device *dev)
|
||||
{
|
||||
if (dev->bus) {
|
||||
sysfs_remove_link(&dev->kobj, "subsystem");
|
||||
sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
|
||||
dev_name(dev));
|
||||
device_remove_attrs(dev->bus, dev);
|
||||
if (klist_node_attached(&dev->p->knode_bus))
|
||||
klist_del(&dev->p->knode_bus);
|
||||
struct bus_type *bus = dev->bus;
|
||||
struct subsys_interface *sif;
|
||||
|
||||
pr_debug("bus: '%s': remove device %s\n",
|
||||
dev->bus->name, dev_name(dev));
|
||||
device_release_driver(dev);
|
||||
bus_put(dev->bus);
|
||||
}
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
mutex_lock(&bus->p->mutex);
|
||||
list_for_each_entry(sif, &bus->p->interfaces, node)
|
||||
if (sif->remove_dev)
|
||||
sif->remove_dev(dev, sif);
|
||||
mutex_unlock(&bus->p->mutex);
|
||||
|
||||
sysfs_remove_link(&dev->kobj, "subsystem");
|
||||
sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
|
||||
dev_name(dev));
|
||||
device_remove_attrs(dev->bus, dev);
|
||||
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));
|
||||
device_release_driver(dev);
|
||||
bus_put(dev->bus);
|
||||
}
|
||||
|
||||
static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv)
|
||||
@ -847,14 +914,14 @@ static ssize_t bus_uevent_store(struct bus_type *bus,
|
||||
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
|
||||
|
||||
/**
|
||||
* bus_register - register a bus with the system.
|
||||
* __bus_register - register a driver-core subsystem
|
||||
* @bus: bus.
|
||||
*
|
||||
* Once we have that, we registered the bus with the kobject
|
||||
* infrastructure, then register the children subsystems it has:
|
||||
* the devices and drivers that belong to the bus.
|
||||
* the devices and drivers that belong to the subsystem.
|
||||
*/
|
||||
int bus_register(struct bus_type *bus)
|
||||
int __bus_register(struct bus_type *bus, struct lock_class_key *key)
|
||||
{
|
||||
int retval;
|
||||
struct subsys_private *priv;
|
||||
@ -898,6 +965,8 @@ int bus_register(struct bus_type *bus)
|
||||
goto bus_drivers_fail;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&priv->interfaces);
|
||||
__mutex_init(&priv->mutex, "subsys mutex", key);
|
||||
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
|
||||
klist_init(&priv->klist_drivers, NULL, NULL);
|
||||
|
||||
@ -927,7 +996,7 @@ out:
|
||||
bus->p = NULL;
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bus_register);
|
||||
EXPORT_SYMBOL_GPL(__bus_register);
|
||||
|
||||
/**
|
||||
* bus_unregister - remove a bus from the system
|
||||
@ -939,6 +1008,8 @@ EXPORT_SYMBOL_GPL(bus_register);
|
||||
void bus_unregister(struct bus_type *bus)
|
||||
{
|
||||
pr_debug("bus: '%s': unregistering\n", bus->name);
|
||||
if (bus->dev_root)
|
||||
device_unregister(bus->dev_root);
|
||||
bus_remove_attrs(bus);
|
||||
remove_probe_files(bus);
|
||||
kset_unregister(bus->p->drivers_kset);
|
||||
@ -1028,10 +1099,194 @@ void bus_sort_breadthfirst(struct bus_type *bus,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bus_sort_breadthfirst);
|
||||
|
||||
/**
|
||||
* subsys_dev_iter_init - initialize subsys device iterator
|
||||
* @iter: subsys iterator to initialize
|
||||
* @subsys: the subsys we wanna iterate over
|
||||
* @start: the device to start iterating from, if any
|
||||
* @type: device_type of the devices to iterate over, NULL for all
|
||||
*
|
||||
* Initialize subsys iterator @iter such that it iterates over devices
|
||||
* of @subsys. If @start is set, the list iteration will start there,
|
||||
* otherwise if it is NULL, the iteration starts at the beginning of
|
||||
* the list.
|
||||
*/
|
||||
void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys,
|
||||
struct device *start, const struct device_type *type)
|
||||
{
|
||||
struct klist_node *start_knode = NULL;
|
||||
|
||||
if (start)
|
||||
start_knode = &start->p->knode_bus;
|
||||
klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode);
|
||||
iter->type = type;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subsys_dev_iter_init);
|
||||
|
||||
/**
|
||||
* subsys_dev_iter_next - iterate to the next device
|
||||
* @iter: subsys iterator to proceed
|
||||
*
|
||||
* Proceed @iter to the next device and return it. Returns NULL if
|
||||
* iteration is complete.
|
||||
*
|
||||
* The returned device is referenced and won't be released till
|
||||
* iterator is proceed to the next device or exited. The caller is
|
||||
* free to do whatever it wants to do with the device including
|
||||
* calling back into subsys code.
|
||||
*/
|
||||
struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter)
|
||||
{
|
||||
struct klist_node *knode;
|
||||
struct device *dev;
|
||||
|
||||
for (;;) {
|
||||
knode = klist_next(&iter->ki);
|
||||
if (!knode)
|
||||
return NULL;
|
||||
dev = container_of(knode, struct device_private, knode_bus)->device;
|
||||
if (!iter->type || iter->type == dev->type)
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subsys_dev_iter_next);
|
||||
|
||||
/**
|
||||
* subsys_dev_iter_exit - finish iteration
|
||||
* @iter: subsys iterator to finish
|
||||
*
|
||||
* Finish an iteration. Always call this function after iteration is
|
||||
* complete whether the iteration ran till the end or not.
|
||||
*/
|
||||
void subsys_dev_iter_exit(struct subsys_dev_iter *iter)
|
||||
{
|
||||
klist_iter_exit(&iter->ki);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subsys_dev_iter_exit);
|
||||
|
||||
int subsys_interface_register(struct subsys_interface *sif)
|
||||
{
|
||||
struct bus_type *subsys;
|
||||
struct subsys_dev_iter iter;
|
||||
struct device *dev;
|
||||
|
||||
if (!sif || !sif->subsys)
|
||||
return -ENODEV;
|
||||
|
||||
subsys = bus_get(sif->subsys);
|
||||
if (!subsys)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&subsys->p->mutex);
|
||||
list_add_tail(&sif->node, &subsys->p->interfaces);
|
||||
if (sif->add_dev) {
|
||||
subsys_dev_iter_init(&iter, subsys, NULL, NULL);
|
||||
while ((dev = subsys_dev_iter_next(&iter)))
|
||||
sif->add_dev(dev, sif);
|
||||
subsys_dev_iter_exit(&iter);
|
||||
}
|
||||
mutex_unlock(&subsys->p->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subsys_interface_register);
|
||||
|
||||
void subsys_interface_unregister(struct subsys_interface *sif)
|
||||
{
|
||||
struct bus_type *subsys = sif->subsys;
|
||||
struct subsys_dev_iter iter;
|
||||
struct device *dev;
|
||||
|
||||
if (!sif)
|
||||
return;
|
||||
|
||||
mutex_lock(&subsys->p->mutex);
|
||||
list_del_init(&sif->node);
|
||||
if (sif->remove_dev) {
|
||||
subsys_dev_iter_init(&iter, subsys, NULL, NULL);
|
||||
while ((dev = subsys_dev_iter_next(&iter)))
|
||||
sif->remove_dev(dev, sif);
|
||||
subsys_dev_iter_exit(&iter);
|
||||
}
|
||||
mutex_unlock(&subsys->p->mutex);
|
||||
|
||||
bus_put(subsys);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subsys_interface_unregister);
|
||||
|
||||
static void system_root_device_release(struct device *dev)
|
||||
{
|
||||
kfree(dev);
|
||||
}
|
||||
/**
|
||||
* subsys_system_register - register a subsystem at /sys/devices/system/
|
||||
* @subsys - system subsystem
|
||||
* @groups - default attributes for the root device
|
||||
*
|
||||
* All 'system' subsystems have a /sys/devices/system/<name> root device
|
||||
* with the name of the subsystem. The root device can carry subsystem-
|
||||
* wide attributes. All registered devices are below this single root
|
||||
* device and are named after the subsystem with a simple enumeration
|
||||
* number appended. The registered devices are not explicitely named;
|
||||
* only 'id' in the device needs to be set.
|
||||
*
|
||||
* Do not use this interface for anything new, it exists for compatibility
|
||||
* with bad ideas only. New subsystems should use plain subsystems; and
|
||||
* add the subsystem-wide attributes should be added to the subsystem
|
||||
* directory itself and not some create fake root-device placed in
|
||||
* /sys/devices/system/<name>.
|
||||
*/
|
||||
int subsys_system_register(struct bus_type *subsys,
|
||||
const struct attribute_group **groups)
|
||||
{
|
||||
struct device *dev;
|
||||
int err;
|
||||
|
||||
err = bus_register(subsys);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
err = dev_set_name(dev, "%s", subsys->name);
|
||||
if (err < 0)
|
||||
goto err_name;
|
||||
|
||||
dev->kobj.parent = &system_kset->kobj;
|
||||
dev->groups = groups;
|
||||
dev->release = system_root_device_release;
|
||||
|
||||
err = device_register(dev);
|
||||
if (err < 0)
|
||||
goto err_dev_reg;
|
||||
|
||||
subsys->dev_root = dev;
|
||||
return 0;
|
||||
|
||||
err_dev_reg:
|
||||
put_device(dev);
|
||||
dev = NULL;
|
||||
err_name:
|
||||
kfree(dev);
|
||||
err_dev:
|
||||
bus_unregister(subsys);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subsys_system_register);
|
||||
|
||||
int __init buses_init(void)
|
||||
{
|
||||
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
|
||||
if (!bus_kset)
|
||||
return -ENOMEM;
|
||||
|
||||
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
|
||||
if (!system_kset)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -184,9 +184,9 @@ int __class_register(struct class *cls, struct lock_class_key *key)
|
||||
if (!cp)
|
||||
return -ENOMEM;
|
||||
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
|
||||
INIT_LIST_HEAD(&cp->class_interfaces);
|
||||
INIT_LIST_HEAD(&cp->interfaces);
|
||||
kset_init(&cp->glue_dirs);
|
||||
__mutex_init(&cp->class_mutex, "struct class mutex", key);
|
||||
__mutex_init(&cp->mutex, "subsys mutex", key);
|
||||
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
|
||||
if (error) {
|
||||
kfree(cp);
|
||||
@ -460,15 +460,15 @@ int class_interface_register(struct class_interface *class_intf)
|
||||
if (!parent)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&parent->p->class_mutex);
|
||||
list_add_tail(&class_intf->node, &parent->p->class_interfaces);
|
||||
mutex_lock(&parent->p->mutex);
|
||||
list_add_tail(&class_intf->node, &parent->p->interfaces);
|
||||
if (class_intf->add_dev) {
|
||||
class_dev_iter_init(&iter, parent, NULL, NULL);
|
||||
while ((dev = class_dev_iter_next(&iter)))
|
||||
class_intf->add_dev(dev, class_intf);
|
||||
class_dev_iter_exit(&iter);
|
||||
}
|
||||
mutex_unlock(&parent->p->class_mutex);
|
||||
mutex_unlock(&parent->p->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -482,7 +482,7 @@ void class_interface_unregister(struct class_interface *class_intf)
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
mutex_lock(&parent->p->class_mutex);
|
||||
mutex_lock(&parent->p->mutex);
|
||||
list_del_init(&class_intf->node);
|
||||
if (class_intf->remove_dev) {
|
||||
class_dev_iter_init(&iter, parent, NULL, NULL);
|
||||
@ -490,7 +490,7 @@ void class_interface_unregister(struct class_interface *class_intf)
|
||||
class_intf->remove_dev(dev, class_intf);
|
||||
class_dev_iter_exit(&iter);
|
||||
}
|
||||
mutex_unlock(&parent->p->class_mutex);
|
||||
mutex_unlock(&parent->p->mutex);
|
||||
|
||||
class_put(parent);
|
||||
}
|
||||
|
@ -117,6 +117,56 @@ static const struct sysfs_ops dev_sysfs_ops = {
|
||||
.store = dev_attr_store,
|
||||
};
|
||||
|
||||
#define to_ext_attr(x) container_of(x, struct dev_ext_attribute, attr)
|
||||
|
||||
ssize_t device_store_ulong(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct dev_ext_attribute *ea = to_ext_attr(attr);
|
||||
char *end;
|
||||
unsigned long new = simple_strtoul(buf, &end, 0);
|
||||
if (end == buf)
|
||||
return -EINVAL;
|
||||
*(unsigned long *)(ea->var) = new;
|
||||
/* Always return full write size even if we didn't consume all */
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_store_ulong);
|
||||
|
||||
ssize_t device_show_ulong(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct dev_ext_attribute *ea = to_ext_attr(attr);
|
||||
return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_show_ulong);
|
||||
|
||||
ssize_t device_store_int(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct dev_ext_attribute *ea = to_ext_attr(attr);
|
||||
char *end;
|
||||
long new = simple_strtol(buf, &end, 0);
|
||||
if (end == buf || new > INT_MAX || new < INT_MIN)
|
||||
return -EINVAL;
|
||||
*(int *)(ea->var) = new;
|
||||
/* Always return full write size even if we didn't consume all */
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_store_int);
|
||||
|
||||
ssize_t device_show_int(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct dev_ext_attribute *ea = to_ext_attr(attr);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_show_int);
|
||||
|
||||
/**
|
||||
* device_release - free device structure.
|
||||
@ -463,7 +513,7 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
|
||||
static struct device_attribute devt_attr =
|
||||
__ATTR(dev, S_IRUGO, show_dev, NULL);
|
||||
|
||||
/* kset to create /sys/devices/ */
|
||||
/* /sys/devices/ */
|
||||
struct kset *devices_kset;
|
||||
|
||||
/**
|
||||
@ -710,6 +760,10 @@ static struct kobject *get_device_parent(struct device *dev,
|
||||
return k;
|
||||
}
|
||||
|
||||
/* subsystems can specify a default root directory for their devices */
|
||||
if (!parent && dev->bus && dev->bus->dev_root)
|
||||
return &dev->bus->dev_root->kobj;
|
||||
|
||||
if (parent)
|
||||
return &parent->kobj;
|
||||
return NULL;
|
||||
@ -730,14 +784,6 @@ static void cleanup_device_parent(struct device *dev)
|
||||
cleanup_glue_dir(dev, dev->kobj.parent);
|
||||
}
|
||||
|
||||
static void setup_parent(struct device *dev, struct device *parent)
|
||||
{
|
||||
struct kobject *kobj;
|
||||
kobj = get_device_parent(dev, parent);
|
||||
if (kobj)
|
||||
dev->kobj.parent = kobj;
|
||||
}
|
||||
|
||||
static int device_add_class_symlinks(struct device *dev)
|
||||
{
|
||||
int error;
|
||||
@ -890,6 +936,7 @@ int device_private_init(struct device *dev)
|
||||
int device_add(struct device *dev)
|
||||
{
|
||||
struct device *parent = NULL;
|
||||
struct kobject *kobj;
|
||||
struct class_interface *class_intf;
|
||||
int error = -EINVAL;
|
||||
|
||||
@ -913,6 +960,10 @@ int device_add(struct device *dev)
|
||||
dev->init_name = NULL;
|
||||
}
|
||||
|
||||
/* subsystems can specify simple device enumeration */
|
||||
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
|
||||
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
|
||||
|
||||
if (!dev_name(dev)) {
|
||||
error = -EINVAL;
|
||||
goto name_error;
|
||||
@ -921,7 +972,9 @@ int device_add(struct device *dev)
|
||||
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
|
||||
|
||||
parent = get_device(dev->parent);
|
||||
setup_parent(dev, parent);
|
||||
kobj = get_device_parent(dev, parent);
|
||||
if (kobj)
|
||||
dev->kobj.parent = kobj;
|
||||
|
||||
/* use parent numa_node */
|
||||
if (parent)
|
||||
@ -981,17 +1034,17 @@ int device_add(struct device *dev)
|
||||
&parent->p->klist_children);
|
||||
|
||||
if (dev->class) {
|
||||
mutex_lock(&dev->class->p->class_mutex);
|
||||
mutex_lock(&dev->class->p->mutex);
|
||||
/* tie the class to the device */
|
||||
klist_add_tail(&dev->knode_class,
|
||||
&dev->class->p->klist_devices);
|
||||
|
||||
/* notify any interfaces that the device is here */
|
||||
list_for_each_entry(class_intf,
|
||||
&dev->class->p->class_interfaces, node)
|
||||
&dev->class->p->interfaces, node)
|
||||
if (class_intf->add_dev)
|
||||
class_intf->add_dev(dev, class_intf);
|
||||
mutex_unlock(&dev->class->p->class_mutex);
|
||||
mutex_unlock(&dev->class->p->mutex);
|
||||
}
|
||||
done:
|
||||
put_device(dev);
|
||||
@ -1106,15 +1159,15 @@ void device_del(struct device *dev)
|
||||
if (dev->class) {
|
||||
device_remove_class_symlinks(dev);
|
||||
|
||||
mutex_lock(&dev->class->p->class_mutex);
|
||||
mutex_lock(&dev->class->p->mutex);
|
||||
/* notify any interfaces that the device is now gone */
|
||||
list_for_each_entry(class_intf,
|
||||
&dev->class->p->class_interfaces, node)
|
||||
&dev->class->p->interfaces, node)
|
||||
if (class_intf->remove_dev)
|
||||
class_intf->remove_dev(dev, class_intf);
|
||||
/* remove the device from the class list */
|
||||
klist_del(&dev->knode_class);
|
||||
mutex_unlock(&dev->class->p->class_mutex);
|
||||
mutex_unlock(&dev->class->p->mutex);
|
||||
}
|
||||
device_remove_file(dev, &uevent_attr);
|
||||
device_remove_attrs(dev);
|
||||
|
@ -31,7 +31,6 @@ void __init driver_init(void)
|
||||
* core core pieces.
|
||||
*/
|
||||
platform_bus_init();
|
||||
system_bus_init();
|
||||
cpu_dev_init();
|
||||
memory_dev_init();
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ void sysdev_class_remove_file(struct sysdev_class *c,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
|
||||
|
||||
static struct kset *system_kset;
|
||||
extern struct kset *system_kset;
|
||||
|
||||
int sysdev_class_register(struct sysdev_class *cls)
|
||||
{
|
||||
@ -331,14 +331,6 @@ void sysdev_unregister(struct sys_device *sysdev)
|
||||
EXPORT_SYMBOL_GPL(sysdev_register);
|
||||
EXPORT_SYMBOL_GPL(sysdev_unregister);
|
||||
|
||||
int __init system_bus_init(void)
|
||||
{
|
||||
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
|
||||
if (!system_kset)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
|
||||
|
||||
ssize_t sysdev_store_ulong(struct sys_device *sysdev,
|
||||
|
@ -53,6 +53,8 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
|
||||
* struct bus_type - The bus type of the device
|
||||
*
|
||||
* @name: The name of the bus.
|
||||
* @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id).
|
||||
* @dev_root: Default device to use as the parent.
|
||||
* @bus_attrs: Default attributes of the bus.
|
||||
* @dev_attrs: Default attributes of the devices on the bus.
|
||||
* @drv_attrs: Default attributes of the device drivers on the bus.
|
||||
@ -86,6 +88,8 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
|
||||
*/
|
||||
struct bus_type {
|
||||
const char *name;
|
||||
const char *dev_name;
|
||||
struct device *dev_root;
|
||||
struct bus_attribute *bus_attrs;
|
||||
struct device_attribute *dev_attrs;
|
||||
struct driver_attribute *drv_attrs;
|
||||
@ -106,12 +110,30 @@ struct bus_type {
|
||||
struct subsys_private *p;
|
||||
};
|
||||
|
||||
extern int __must_check bus_register(struct bus_type *bus);
|
||||
/* This is a #define to keep the compiler from merging different
|
||||
* instances of the __key variable */
|
||||
#define bus_register(subsys) \
|
||||
({ \
|
||||
static struct lock_class_key __key; \
|
||||
__bus_register(subsys, &__key); \
|
||||
})
|
||||
extern int __must_check __bus_register(struct bus_type *bus,
|
||||
struct lock_class_key *key);
|
||||
extern void bus_unregister(struct bus_type *bus);
|
||||
|
||||
extern int __must_check bus_rescan_devices(struct bus_type *bus);
|
||||
|
||||
/* iterator helpers for buses */
|
||||
struct subsys_dev_iter {
|
||||
struct klist_iter ki;
|
||||
const struct device_type *type;
|
||||
};
|
||||
void subsys_dev_iter_init(struct subsys_dev_iter *iter,
|
||||
struct bus_type *subsys,
|
||||
struct device *start,
|
||||
const struct device_type *type);
|
||||
struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter);
|
||||
void subsys_dev_iter_exit(struct subsys_dev_iter *iter);
|
||||
|
||||
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
|
||||
int (*fn)(struct device *dev, void *data));
|
||||
@ -121,10 +143,10 @@ struct device *bus_find_device(struct bus_type *bus, struct device *start,
|
||||
struct device *bus_find_device_by_name(struct bus_type *bus,
|
||||
struct device *start,
|
||||
const char *name);
|
||||
|
||||
struct device *subsys_find_device_by_id(struct bus_type *bus, unsigned int id,
|
||||
struct device *hint);
|
||||
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
|
||||
void *data, int (*fn)(struct device_driver *, void *));
|
||||
|
||||
void bus_sort_breadthfirst(struct bus_type *bus,
|
||||
int (*compare)(const struct device *a,
|
||||
const struct device *b));
|
||||
@ -255,6 +277,33 @@ struct device *driver_find_device(struct device_driver *drv,
|
||||
struct device *start, void *data,
|
||||
int (*match)(struct device *dev, void *data));
|
||||
|
||||
/**
|
||||
* struct subsys_interface - interfaces to device functions
|
||||
* @name name of the device function
|
||||
* @subsystem subsytem of the devices to attach to
|
||||
* @node the list of functions registered at the subsystem
|
||||
* @add device hookup to device function handler
|
||||
* @remove device hookup to device function handler
|
||||
*
|
||||
* Simple interfaces attached to a subsystem. Multiple interfaces can
|
||||
* attach to a subsystem and its devices. Unlike drivers, they do not
|
||||
* exclusively claim or control devices. Interfaces usually represent
|
||||
* a specific functionality of a subsystem/class of devices.
|
||||
*/
|
||||
struct subsys_interface {
|
||||
const char *name;
|
||||
struct bus_type *subsys;
|
||||
struct list_head node;
|
||||
int (*add_dev)(struct device *dev, struct subsys_interface *sif);
|
||||
int (*remove_dev)(struct device *dev, struct subsys_interface *sif);
|
||||
};
|
||||
|
||||
int subsys_interface_register(struct subsys_interface *sif);
|
||||
void subsys_interface_unregister(struct subsys_interface *sif);
|
||||
|
||||
int subsys_system_register(struct bus_type *subsys,
|
||||
const struct attribute_group **groups);
|
||||
|
||||
/**
|
||||
* struct class - device classes
|
||||
* @name: Name of the class.
|
||||
@ -438,8 +487,28 @@ struct device_attribute {
|
||||
const char *buf, size_t count);
|
||||
};
|
||||
|
||||
struct dev_ext_attribute {
|
||||
struct device_attribute attr;
|
||||
void *var;
|
||||
};
|
||||
|
||||
ssize_t device_show_ulong(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t device_store_ulong(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
ssize_t device_show_int(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t device_store_int(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
#define DEVICE_ATTR(_name, _mode, _show, _store) \
|
||||
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
||||
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
||||
#define DEVICE_ULONG_ATTR(_name, _mode, _var) \
|
||||
struct dev_ext_attribute dev_attr_##_name = \
|
||||
{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
|
||||
#define DEVICE_INT_ATTR(_name, _mode, _var) \
|
||||
struct dev_ext_attribute dev_attr_##_name = \
|
||||
{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
|
||||
|
||||
extern int __must_check device_create_file(struct device *device,
|
||||
const struct device_attribute *entry);
|
||||
@ -603,6 +672,7 @@ struct device {
|
||||
struct device_node *of_node; /* associated device tree node */
|
||||
|
||||
dev_t devt; /* dev_t, creates the sysfs "dev" */
|
||||
u32 id; /* device instance */
|
||||
|
||||
spinlock_t devres_lock;
|
||||
struct list_head devres_head;
|
||||
|
Loading…
Reference in New Issue
Block a user