From 53877d06d53a412d901bb323f080296c363d8b51 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 4 Apr 2006 20:42:26 +0200 Subject: [PATCH] Driver core: bus device event delay split bus_add_device() and send device uevents after sysfs population Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 6b355bd7816..d5e15a03584 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -274,11 +274,12 @@ int device_add(struct device *dev) dev->uevent_attr.store = store_uevent; device_create_file(dev, &dev->uevent_attr); - kobject_uevent(&dev->kobj, KOBJ_ADD); if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) goto BusError; + kobject_uevent(&dev->kobj, KOBJ_ADD); + bus_attach_device(dev); if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); -- cgit v1.2.3-70-g09d2 From 23681e479129854305da1da32f7f1eaf635ef22c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jun 2006 12:14:34 -0700 Subject: [PATCH] Driver core: allow struct device to have a dev_t This is the first step in moving class_device to being replaced by struct device. It allows struct device to export a dev_t and makes it easy to dynamically create and destroy struct device as long as they are associated with a specific class. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 1 + drivers/base/core.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/device.h | 14 +++++ 3 files changed, 176 insertions(+), 1 deletion(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/class.c b/drivers/base/class.c index 41a8e0934e3..50e841a33af 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -142,6 +142,7 @@ int class_register(struct class * cls) pr_debug("device class '%s': registering\n", cls->name); INIT_LIST_HEAD(&cls->children); + INIT_LIST_HEAD(&cls->devices); INIT_LIST_HEAD(&cls->interfaces); init_MUTEX(&cls->sem); error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); diff --git a/drivers/base/core.c b/drivers/base/core.c index d5e15a03584..252cf403f89 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -98,6 +99,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) struct device *dev = to_dev(kobj); if (dev->bus) return 1; + if (dev->class) + return 1; } return 0; } @@ -106,7 +109,11 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) { struct device *dev = to_dev(kobj); - return dev->bus->name; + if (dev->bus) + return dev->bus->name; + if (dev->class) + return dev->class->name; + return NULL; } static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, @@ -117,6 +124,16 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, int length = 0; int retval = 0; + /* add the major/minor if present */ + if (MAJOR(dev->devt)) { + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MAJOR=%u", MAJOR(dev->devt)); + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MINOR=%u", MINOR(dev->devt)); + } + /* add bus name of physical device */ if (dev->bus) add_uevent_var(envp, num_envp, &i, @@ -161,6 +178,12 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t show_dev(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return print_dev_t(buf, dev->devt); +} + /* * devices_subsys - structure to be registered with kobject core. */ @@ -231,6 +254,7 @@ void device_initialize(struct device *dev) klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); + INIT_LIST_HEAD(&dev->node); init_MUTEX(&dev->sem); device_init_wakeup(dev, 0); } @@ -274,6 +298,31 @@ int device_add(struct device *dev) dev->uevent_attr.store = store_uevent; device_create_file(dev, &dev->uevent_attr); + if (MAJOR(dev->devt)) { + struct device_attribute *attr; + attr = kzalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) { + error = -ENOMEM; + goto PMError; + } + attr->attr.name = "dev"; + attr->attr.mode = S_IRUGO; + if (dev->driver) + attr->attr.owner = dev->driver->owner; + attr->show = show_dev; + error = device_create_file(dev, attr); + if (error) { + kfree(attr); + goto attrError; + } + + dev->devt_attr = attr; + } + + if (dev->class) + sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, + dev->bus_id); + if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) @@ -292,6 +341,11 @@ int device_add(struct device *dev) BusError: device_pm_remove(dev); PMError: + if (dev->devt_attr) { + device_remove_file(dev, dev->devt_attr); + kfree(dev->devt_attr); + } + attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: @@ -366,6 +420,10 @@ void device_del(struct device * dev) if (parent) klist_del(&dev->knode_parent); + if (dev->devt_attr) + device_remove_file(dev, dev->devt_attr); + if (dev->class) + sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); device_remove_file(dev, &dev->uevent_attr); /* Notify the platform of the removal, in case they @@ -450,3 +508,105 @@ EXPORT_SYMBOL_GPL(put_device); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); + + +static void device_create_release(struct device *dev) +{ + pr_debug("%s called for %s\n", __FUNCTION__, dev->bus_id); + kfree(dev); +} + +/** + * device_create - creates a device and registers it with sysfs + * @cs: pointer to the struct class that this device should be registered to. + * @parent: pointer to the parent struct device of this new device, if any. + * @dev: the dev_t for the char device to be added. + * @fmt: string for the class device's name + * + * This function can be used by char device classes. A struct + * device will be created in sysfs, registered to the specified + * class. + * A "dev" file will be created, showing the dev_t for the device, if + * the dev_t is not 0,0. + * If a pointer to a parent struct device is passed in, the newly + * created struct device will be a child of that device in sysfs. The + * pointer to the struct device will be returned from the call. Any + * further sysfs files that might be required can be created using this + * pointer. + * + * Note: the struct class passed to this function must have previously + * been created with a call to class_create(). + */ +struct device *device_create(struct class *class, struct device *parent, + dev_t devt, char *fmt, ...) +{ + va_list args; + struct device *dev = NULL; + int retval = -ENODEV; + + if (class == NULL || IS_ERR(class)) + goto error; + if (parent == NULL) { + printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__); + goto error; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto error; + } + + dev->devt = devt; + dev->class = class; + dev->parent = parent; + dev->release = device_create_release; + + va_start(args, fmt); + vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args); + va_end(args); + retval = device_register(dev); + if (retval) + goto error; + + /* tie the class to the device */ + down(&class->sem); + list_add_tail(&dev->node, &class->devices); + up(&class->sem); + + return dev; + +error: + kfree(dev); + return ERR_PTR(retval); +} +EXPORT_SYMBOL_GPL(device_create); + +/** + * device_destroy - removes a device that was created with device_create() + * @class: the pointer to the struct class that this device was registered * with. + * @dev: the dev_t of the device that was previously registered. + * + * This call unregisters and cleans up a class device that was created with a + * call to class_device_create() + */ +void device_destroy(struct class *class, dev_t devt) +{ + struct device *dev = NULL; + struct device *dev_tmp; + + down(&class->sem); + list_for_each_entry(dev_tmp, &class->devices, node) { + if (dev_tmp->devt == devt) { + dev = dev_tmp; + break; + } + } + up(&class->sem); + + if (dev) { + list_del_init(&dev->node); + device_unregister(dev); + } +} +EXPORT_SYMBOL_GPL(device_destroy); diff --git a/include/linux/device.h b/include/linux/device.h index ade10dd6b77..b473f427891 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -142,6 +142,7 @@ struct class { struct subsystem subsys; struct list_head children; + struct list_head devices; struct list_head interfaces; struct semaphore sem; /* locks both the children and interfaces lists */ @@ -305,6 +306,7 @@ struct device { struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ struct device_attribute uevent_attr; + struct device_attribute *devt_attr; struct semaphore sem; /* semaphore to synchronize calls to * its driver. @@ -332,6 +334,11 @@ struct device { struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ + /* class_device migration path */ + struct list_head node; + struct class *class; /* optional*/ + dev_t devt; /* dev_t, creates the sysfs "dev" */ + void (*release)(struct device * dev); }; @@ -373,6 +380,13 @@ extern int device_attach(struct device * dev); extern void driver_attach(struct device_driver * drv); extern void device_reprobe(struct device *dev); +/* + * Easy functions for dynamically creating devices on the fly + */ +extern struct device *device_create(struct class *cls, struct device *parent, + dev_t devt, char *fmt, ...) + __attribute__((format(printf,4,5))); +extern void device_destroy(struct class *cls, dev_t devt); /* * Platform "fixup" functions - allow the platform to have their say -- cgit v1.2.3-70-g09d2 From b9d9c82b4d081feb464f62dfc786c8621d09ecd2 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 15 Jun 2006 15:31:56 +0200 Subject: [PATCH] Driver core: add generic "subsystem" link to all devices Like the SUBSYTEM= key we find in the environment of the uevent, this creates a generic "subsystem" link in sysfs for every device. Userspace usually doesn't care at all if its a "class" or a "bus" device. This provides an unified way to determine the subsytem of a device, regardless of the way the driver core has created it. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- block/genhd.c | 7 ++----- drivers/base/bus.c | 2 ++ drivers/base/class.c | 2 ++ drivers/base/core.c | 9 +++++++-- fs/partitions/check.c | 4 ++++ 5 files changed, 17 insertions(+), 7 deletions(-) (limited to 'drivers/base/core.c') diff --git a/block/genhd.c b/block/genhd.c index 5a8d3bf02f1..8d7339511e5 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -17,8 +17,7 @@ #include #include -static struct subsystem block_subsys; - +struct subsystem block_subsys; static DEFINE_MUTEX(block_subsys_lock); /* @@ -511,9 +510,7 @@ static struct kset_uevent_ops block_uevent_ops = { .uevent = block_uevent, }; -/* declare block_subsys. */ -static decl_subsys(block, &ktype_block, &block_uevent_ops); - +decl_subsys(block, &ktype_block, &block_uevent_ops); /* * aggregate disk stat collector. Uses the same stats that the sysfs diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 64ba9011d1a..050d86d0b87 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -374,6 +374,7 @@ int bus_add_device(struct device * dev) error = device_add_attrs(bus, dev); if (!error) { sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); + sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem"); sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); } } @@ -408,6 +409,7 @@ void bus_attach_device(struct device * dev) void bus_remove_device(struct device * dev) { if (dev->bus) { + sysfs_remove_link(&dev->kobj, "subsystem"); sysfs_remove_link(&dev->kobj, "bus"); sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); device_remove_attrs(dev->bus, dev); diff --git a/drivers/base/class.c b/drivers/base/class.c index 50e841a33af..9aa12746026 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -561,6 +561,7 @@ int class_device_add(struct class_device *class_dev) goto out2; /* add the needed attributes to this device */ + sysfs_create_link(&class_dev->kobj, &parent_class->subsys.kset.kobj, "subsystem"); class_dev->uevent_attr.attr.name = "uevent"; class_dev->uevent_attr.attr.mode = S_IWUSR; class_dev->uevent_attr.attr.owner = parent_class->owner; @@ -737,6 +738,7 @@ void class_device_del(struct class_device *class_dev) sysfs_remove_link(&class_dev->kobj, "device"); sysfs_remove_link(&class_dev->dev->kobj, class_name); } + sysfs_remove_link(&class_dev->kobj, "subsystem"); class_device_remove_file(class_dev, &class_dev->uevent_attr); if (class_dev->devt_attr) class_device_remove_file(class_dev, class_dev->devt_attr); diff --git a/drivers/base/core.c b/drivers/base/core.c index 252cf403f89..cc8bb97427d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -319,9 +319,12 @@ int device_add(struct device *dev) dev->devt_attr = attr; } - if (dev->class) + if (dev->class) { + sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj, + "subsystem"); sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, dev->bus_id); + } if ((error = device_pm_add(dev))) goto PMError; @@ -422,8 +425,10 @@ void device_del(struct device * dev) klist_del(&dev->knode_parent); if (dev->devt_attr) device_remove_file(dev, dev->devt_attr); - if (dev->class) + if (dev->class) { + sysfs_remove_link(&dev->kobj, "subsystem"); sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); + } device_remove_file(dev, &dev->uevent_attr); /* Notify the platform of the removal, in case they diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 7ef1f094de9..8851b81e7c5 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -329,6 +329,7 @@ void delete_partition(struct gendisk *disk, int part) p->ios[0] = p->ios[1] = 0; p->sectors[0] = p->sectors[1] = 0; devfs_remove("%s/part%d", disk->devfs_name, part); + sysfs_remove_link(&p->kobj, "subsystem"); if (p->holder_dir) kobject_unregister(p->holder_dir); kobject_uevent(&p->kobj, KOBJ_REMOVE); @@ -363,6 +364,7 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) kobject_add(&p->kobj); if (!disk->part_uevent_suppress) kobject_uevent(&p->kobj, KOBJ_ADD); + sysfs_create_link(&p->kobj, &block_subsys.kset.kobj, "subsystem"); partition_sysfs_add_subdir(p); disk->part[part-1] = p; } @@ -398,6 +400,7 @@ static void disk_sysfs_symlinks(struct gendisk *disk) kfree(disk_name); } } + sysfs_create_link(&disk->kobj, &block_subsys.kset.kobj, "subsystem"); } /* Not exported, helper to add_disk(). */ @@ -548,5 +551,6 @@ void del_gendisk(struct gendisk *disk) put_device(disk->driverfs_dev); disk->driverfs_dev = NULL; } + sysfs_remove_link(&disk->kobj, "subsystem"); kobject_del(&disk->kobj); } -- cgit v1.2.3-70-g09d2 From e9a7d305faec364ba973d6c22c9b1e802ef79204 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 20 Jun 2006 13:59:20 -0700 Subject: [PATCH] Driver core: add proper symlinks for devices We need to create the "compatible" symlinks that class_devices used to create when they were in the class directories so that userspace does not know anything changed at all. Yeah, we have a lot of symlinks now, but we should be able to get rid of them in a year or two... (wishful thinking...) Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index cc8bb97427d..a979bc3f49a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -273,6 +273,7 @@ void device_initialize(struct device *dev) int device_add(struct device *dev) { struct device *parent = NULL; + char *class_name = NULL; int error = -EINVAL; dev = get_device(dev); @@ -324,6 +325,10 @@ int device_add(struct device *dev) "subsystem"); sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, dev->bus_id); + + sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); + class_name = make_class_name(dev->class->name, &dev->kobj); + sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); } if ((error = device_pm_add(dev))) @@ -339,6 +344,7 @@ int device_add(struct device *dev) if (platform_notify) platform_notify(dev); Done: + kfree(class_name); put_device(dev); return error; BusError: @@ -420,6 +426,7 @@ void put_device(struct device * dev) void device_del(struct device * dev) { struct device * parent = dev->parent; + char *class_name = NULL; if (parent) klist_del(&dev->knode_parent); @@ -428,6 +435,10 @@ void device_del(struct device * dev) if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); + class_name = make_class_name(dev->class->name, &dev->kobj); + sysfs_remove_link(&dev->kobj, "device"); + sysfs_remove_link(&dev->parent->kobj, class_name); + kfree(class_name); } device_remove_file(dev, &dev->uevent_attr); -- cgit v1.2.3-70-g09d2 From 3e95637a48820ff8bedb33e6439def96ccff1de5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 16 Jun 2006 17:10:48 -0400 Subject: [PATCH] Driver Core: Make dev_info and friends print the bus name if there is no driver This patch (as721) makes dev_info and related macros print the device's bus name if the device doesn't have a driver, instead of printing just a blank. If the device isn't on a bus either... well, then it does leave a blank space. But it will be easier for someone else to change if they want. Cc: Matthew Wilcox Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 16 ++++++++++++++++ include/linux/device.h | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index a979bc3f49a..d0f84ff7877 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -29,6 +29,22 @@ int (*platform_notify_remove)(struct device * dev) = NULL; * sysfs bindings for devices. */ +/** + * dev_driver_string - Return a device's driver name, if at all possible + * @dev: struct device to get the name of + * + * Will return the device's driver's name if it is bound to a device. If + * the device is not bound to a device, it will return the name of the bus + * it is attached to. If it is not attached to a bus either, an empty + * string will be returned. + */ +const char *dev_driver_string(struct device *dev) +{ + return dev->driver ? dev->driver->name : + (dev->bus ? dev->bus->name : ""); +} +EXPORT_SYMBOL_GPL(dev_driver_string); + #define to_dev(obj) container_of(obj, struct device, kobj) #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) diff --git a/include/linux/device.h b/include/linux/device.h index b473f427891..1e5f30da98b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -416,8 +416,9 @@ extern int firmware_register(struct subsystem *); extern void firmware_unregister(struct subsystem *); /* debugging and troubleshooting/diagnostic helpers. */ +extern const char *dev_driver_string(struct device *dev); #define dev_printk(level, dev, format, arg...) \ - printk(level "%s %s: " format , (dev)->driver ? (dev)->driver->name : "" , (dev)->bus_id , ## arg) + printk(level "%s %s: " format , dev_driver_string(dev) , (dev)->bus_id , ## arg) #ifdef DEBUG #define dev_dbg(dev, format, arg...) \ -- cgit v1.2.3-70-g09d2 From 5d9fd169c9fbdaecdc430431e59bf94ff40b93d3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 Jun 2006 17:17:32 -0700 Subject: [PATCH] Driver core: fix locking issues with the devices that are attached to classes Doh, that was foolish... Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index d0f84ff7877..27c2176895d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -356,6 +356,13 @@ int device_add(struct device *dev) if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); + if (dev->class) { + /* tie the class to the device */ + down(&dev->class->sem); + list_add_tail(&dev->node, &dev->class->devices); + up(&dev->class->sem); + } + /* notify platform of device entry */ if (platform_notify) platform_notify(dev); @@ -455,6 +462,9 @@ void device_del(struct device * dev) sysfs_remove_link(&dev->kobj, "device"); sysfs_remove_link(&dev->parent->kobj, class_name); kfree(class_name); + down(&dev->class->sem); + list_del_init(&dev->node); + up(&dev->class->sem); } device_remove_file(dev, &dev->uevent_attr); @@ -601,11 +611,6 @@ struct device *device_create(struct class *class, struct device *parent, if (retval) goto error; - /* tie the class to the device */ - down(&class->sem); - list_add_tail(&dev->node, &class->devices); - up(&class->sem); - return dev; error: @@ -636,9 +641,7 @@ void device_destroy(struct class *class, dev_t devt) } up(&class->sem); - if (dev) { - list_del_init(&dev->node); + if (dev) device_unregister(dev); - } } EXPORT_SYMBOL_GPL(device_destroy); -- cgit v1.2.3-70-g09d2