Commit ca22e56d authored by Kay Sievers's avatar Kay Sievers Committed by Greg Kroah-Hartman

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: default avatarKay Sievers <kay.sievers@vrfy.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6261ddee
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure. * 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 * @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 * @drivers_kset - the list of drivers associated
* @klist_devices - the klist to iterate over the @devices_kset * @klist_devices - the klist to iterate over the @devices_kset
...@@ -14,10 +16,8 @@ ...@@ -14,10 +16,8 @@
* @bus - pointer back to the struct bus_type that this structure is associated * @bus - pointer back to the struct bus_type that this structure is associated
* with. * with.
* *
* @class_interfaces - list of class_interfaces associated
* @glue_dirs - "glue" directory to put in-between the parent device to * @glue_dirs - "glue" directory to put in-between the parent device to
* avoid namespace conflicts * 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 * @class - pointer back to the struct class that this structure is associated
* with. * with.
* *
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
struct subsys_private { struct subsys_private {
struct kset subsys; struct kset subsys;
struct kset *devices_kset; struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset; struct kset *drivers_kset;
struct klist klist_devices; struct klist klist_devices;
...@@ -36,9 +38,7 @@ struct subsys_private { ...@@ -36,9 +38,7 @@ struct subsys_private {
unsigned int drivers_autoprobe:1; unsigned int drivers_autoprobe:1;
struct bus_type *bus; struct bus_type *bus;
struct list_head class_interfaces;
struct kset glue_dirs; struct kset glue_dirs;
struct mutex class_mutex;
struct class *class; struct class *class;
}; };
#define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj) #define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj)
...@@ -94,7 +94,6 @@ extern int hypervisor_init(void); ...@@ -94,7 +94,6 @@ extern int hypervisor_init(void);
static inline int hypervisor_init(void) { return 0; } static inline int hypervisor_init(void) { return 0; }
#endif #endif
extern int platform_bus_init(void); extern int platform_bus_init(void);
extern int system_bus_init(void);
extern int cpu_dev_init(void); extern int cpu_dev_init(void);
extern int bus_add_device(struct device *dev); extern int bus_add_device(struct device *dev);
...@@ -116,6 +115,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj); ...@@ -116,6 +115,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj);
extern int devres_release_all(struct device *dev); extern int devres_release_all(struct device *dev);
/* /sys/devices directory */
extern struct kset *devices_kset; extern struct kset *devices_kset;
#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS) #if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
......
This diff is collapsed.
...@@ -184,9 +184,9 @@ int __class_register(struct class *cls, struct lock_class_key *key) ...@@ -184,9 +184,9 @@ int __class_register(struct class *cls, struct lock_class_key *key)
if (!cp) if (!cp)
return -ENOMEM; return -ENOMEM;
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put); 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); 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); error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
if (error) { if (error) {
kfree(cp); kfree(cp);
...@@ -460,15 +460,15 @@ int class_interface_register(struct class_interface *class_intf) ...@@ -460,15 +460,15 @@ int class_interface_register(struct class_interface *class_intf)
if (!parent) if (!parent)
return -EINVAL; return -EINVAL;
mutex_lock(&parent->p->class_mutex); mutex_lock(&parent->p->mutex);
list_add_tail(&class_intf->node, &parent->p->class_interfaces); list_add_tail(&class_intf->node, &parent->p->interfaces);
if (class_intf->add_dev) { if (class_intf->add_dev) {
class_dev_iter_init(&iter, parent, NULL, NULL); class_dev_iter_init(&iter, parent, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) while ((dev = class_dev_iter_next(&iter)))
class_intf->add_dev(dev, class_intf); class_intf->add_dev(dev, class_intf);
class_dev_iter_exit(&iter); class_dev_iter_exit(&iter);
} }
mutex_unlock(&parent->p->class_mutex); mutex_unlock(&parent->p->mutex);
return 0; return 0;
} }
...@@ -482,7 +482,7 @@ void class_interface_unregister(struct class_interface *class_intf) ...@@ -482,7 +482,7 @@ void class_interface_unregister(struct class_interface *class_intf)
if (!parent) if (!parent)
return; return;
mutex_lock(&parent->p->class_mutex); mutex_lock(&parent->p->mutex);
list_del_init(&class_intf->node); list_del_init(&class_intf->node);
if (class_intf->remove_dev) { if (class_intf->remove_dev) {
class_dev_iter_init(&iter, parent, NULL, NULL); class_dev_iter_init(&iter, parent, NULL, NULL);
...@@ -490,7 +490,7 @@ void class_interface_unregister(struct class_interface *class_intf) ...@@ -490,7 +490,7 @@ void class_interface_unregister(struct class_interface *class_intf)
class_intf->remove_dev(dev, class_intf); class_intf->remove_dev(dev, class_intf);
class_dev_iter_exit(&iter); class_dev_iter_exit(&iter);
} }
mutex_unlock(&parent->p->class_mutex); mutex_unlock(&parent->p->mutex);
class_put(parent); class_put(parent);
} }
......
...@@ -117,6 +117,56 @@ static const struct sysfs_ops dev_sysfs_ops = { ...@@ -117,6 +117,56 @@ static const struct sysfs_ops dev_sysfs_ops = {
.store = dev_attr_store, .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. * device_release - free device structure.
...@@ -463,7 +513,7 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr, ...@@ -463,7 +513,7 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
static struct device_attribute devt_attr = static struct device_attribute devt_attr =
__ATTR(dev, S_IRUGO, show_dev, NULL); __ATTR(dev, S_IRUGO, show_dev, NULL);
/* kset to create /sys/devices/ */ /* /sys/devices/ */
struct kset *devices_kset; struct kset *devices_kset;
/** /**
...@@ -710,6 +760,10 @@ static struct kobject *get_device_parent(struct device *dev, ...@@ -710,6 +760,10 @@ static struct kobject *get_device_parent(struct device *dev,
return k; 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) if (parent)
return &parent->kobj; return &parent->kobj;
return NULL; return NULL;
...@@ -730,14 +784,6 @@ static void cleanup_device_parent(struct device *dev) ...@@ -730,14 +784,6 @@ static void cleanup_device_parent(struct device *dev)
cleanup_glue_dir(dev, dev->kobj.parent); 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) static int device_add_class_symlinks(struct device *dev)
{ {
int error; int error;
...@@ -890,6 +936,7 @@ int device_private_init(struct device *dev) ...@@ -890,6 +936,7 @@ int device_private_init(struct device *dev)
int device_add(struct device *dev) int device_add(struct device *dev)
{ {
struct device *parent = NULL; struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf; struct class_interface *class_intf;
int error = -EINVAL; int error = -EINVAL;
...@@ -913,6 +960,10 @@ int device_add(struct device *dev) ...@@ -913,6 +960,10 @@ int device_add(struct device *dev)
dev->init_name = NULL; 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)) { if (!dev_name(dev)) {
error = -EINVAL; error = -EINVAL;
goto name_error; goto name_error;
...@@ -921,7 +972,9 @@ int device_add(struct device *dev) ...@@ -921,7 +972,9 @@ int device_add(struct device *dev)
pr_debug("device: '%s': %s\n", dev_name(dev), __func__); pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent); 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 */ /* use parent numa_node */
if (parent) if (parent)
...@@ -981,17 +1034,17 @@ int device_add(struct device *dev) ...@@ -981,17 +1034,17 @@ int device_add(struct device *dev)
&parent->p->klist_children); &parent->p->klist_children);
if (dev->class) { if (dev->class) {
mutex_lock(&dev->class->p->class_mutex); mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */ /* tie the class to the device */
klist_add_tail(&dev->knode_class, klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices); &dev->class->p->klist_devices);
/* notify any interfaces that the device is here */ /* notify any interfaces that the device is here */
list_for_each_entry(class_intf, list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node) &dev->class->p->interfaces, node)
if (class_intf->add_dev) if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf); class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex); mutex_unlock(&dev->class->p->mutex);
} }
done: done:
put_device(dev); put_device(dev);
...@@ -1106,15 +1159,15 @@ void device_del(struct device *dev) ...@@ -1106,15 +1159,15 @@ void device_del(struct device *dev)
if (dev->class) { if (dev->class) {
device_remove_class_symlinks(dev); 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 */ /* notify any interfaces that the device is now gone */
list_for_each_entry(class_intf, list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node) &dev->class->p->interfaces, node)
if (class_intf->remove_dev) if (class_intf->remove_dev)
class_intf->remove_dev(dev, class_intf); class_intf->remove_dev(dev, class_intf);
/* remove the device from the class list */ /* remove the device from the class list */
klist_del(&dev->knode_class); 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_file(dev, &uevent_attr);
device_remove_attrs(dev); device_remove_attrs(dev);
......
...@@ -31,7 +31,6 @@ void __init driver_init(void) ...@@ -31,7 +31,6 @@ void __init driver_init(void)
* core core pieces. * core core pieces.
*/ */
platform_bus_init(); platform_bus_init();
system_bus_init();
cpu_dev_init(); cpu_dev_init();
memory_dev_init(); memory_dev_init();
} }
...@@ -126,7 +126,7 @@ void sysdev_class_remove_file(struct sysdev_class *c, ...@@ -126,7 +126,7 @@ void sysdev_class_remove_file(struct sysdev_class *c,
} }
EXPORT_SYMBOL_GPL(sysdev_class_remove_file); 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) int sysdev_class_register(struct sysdev_class *cls)
{ {
...@@ -331,14 +331,6 @@ void sysdev_unregister(struct sys_device *sysdev) ...@@ -331,14 +331,6 @@ void sysdev_unregister(struct sys_device *sysdev)
EXPORT_SYMBOL_GPL(sysdev_register); EXPORT_SYMBOL_GPL(sysdev_register);
EXPORT_SYMBOL_GPL(sysdev_unregister); 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) #define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
ssize_t sysdev_store_ulong(struct sys_device *sysdev, ssize_t sysdev_store_ulong(struct sys_device *sysdev,
......
...@@ -53,6 +53,8 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); ...@@ -53,6 +53,8 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
* struct bus_type - The bus type of the device * struct bus_type - The bus type of the device
* *
* @name: The name of the bus. * @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. * @bus_attrs: Default attributes of the bus.
* @dev_attrs: Default attributes of the devices on the bus. * @dev_attrs: Default attributes of the devices on the bus.
* @drv_attrs: Default attributes of the device drivers 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 *); ...@@ -86,6 +88,8 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
*/ */
struct bus_type { struct bus_type {
const char *name; const char *name;
const char *dev_name;
struct device *dev_root;
struct bus_attribute *bus_attrs; struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs; struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs; struct driver_attribute *drv_attrs;
...@@ -106,12 +110,30 @@ struct bus_type { ...@@ -106,12 +110,30 @@ struct bus_type {
struct subsys_private *p; 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 void bus_unregister(struct bus_type *bus);
extern int __must_check bus_rescan_devices(struct bus_type *bus); extern int __must_check bus_rescan_devices(struct bus_type *bus);
/* iterator helpers for buses */ /* 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 bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
int (*fn)(struct device *dev, 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, ...@@ -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 *bus_find_device_by_name(struct bus_type *bus,
struct device *start, struct device *start,
const char *name); 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, int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *)); void *data, int (*fn)(struct device_driver *, void *));
void bus_sort_breadthfirst(struct bus_type *bus, void bus_sort_breadthfirst(struct bus_type *bus,
int (*compare)(const struct device *a, int (*compare)(const struct device *a,
const struct device *b)); const struct device *b));
...@@ -255,6 +277,33 @@ struct device *driver_find_device(struct device_driver *drv, ...@@ -255,6 +277,33 @@ struct device *driver_find_device(struct device_driver *drv,
struct device *start, void *data, struct device *start, void *data,
int (*match)(struct device *dev, 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 * struct class - device classes
* @name: Name of the class. * @name: Name of the class.
...@@ -438,8 +487,28 @@ struct device_attribute { ...@@ -438,8 +487,28 @@ struct device_attribute {
const char *buf, size_t count); 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) \ #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, extern int __must_check device_create_file(struct device *device,
const struct device_attribute *entry); const struct device_attribute *entry);
...@@ -603,6 +672,7 @@ struct device { ...@@ -603,6 +672,7 @@ struct device {
struct device_node *of_node; /* associated device tree node */ struct device_node *of_node; /* associated device tree node */
dev_t devt; /* dev_t, creates the sysfs "dev" */ dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock; spinlock_t devres_lock;
struct list_head devres_head; struct list_head devres_head;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment