From 2ce5e0bca93f06683b40fbae3b5b4031dd02b2cc Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Thu, 21 Nov 2002 05:01:54 -0600 Subject: Allow subsystem to have attributes, too. This is really handy for subsystems that have any attributes they want to export, esp. if they have just a few, since they don't have to define all of the infrastructure for creating and tearing down the attributes for the high level type that they are. - define struct subsys_attribute for declaring attributes of subsystems themselves. - define subsys_create_file() and subsys_remove_file(). - define subsys_sysfs_ops for forwarding sysfs reads and writes to the subsystem attribute callbacks. - Set the sysfs_ops to be the subsystem ones if the kobject doesn't belong to a subsystem itself (meaning that it is a subsystem). --- include/linux/kobject.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 7b3157a78f7e..6b528c5a61cb 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -60,4 +60,13 @@ static inline void subsys_put(struct subsystem * s) kobject_put(&s->kobj); } +struct subsys_attribute { + struct attribute attr; + ssize_t (*show)(struct subsystem *, char *, size_t, loff_t); + ssize_t (*store)(struct subsystem *, const char *, size_t, loff_t); +}; + +extern int subsys_create_file(struct subsystem * , struct subsys_attribute *); +extern void subsys_remove_file(struct subsystem * , struct subsys_attribute *); + #endif /* _KOBJECT_H_ */ -- cgit v1.2.3 From 8ab1bc19e974fdebe76c065fe444979c84ba2f74 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Nov 2002 04:39:35 -0600 Subject: move dma_mask into struct device Attached is a patch which moves dma_mask into struct device and cleans up the scsi mid-layer to use it (instead of using struct pci_dev). The advantage to doing this is probably most apparent on non-pci bus architectures where currently you have to construct a fake pci_dev just so you can get the bounce buffers to work correctly. The patch tries to perturb the minimum amount of code, so dma_mask in struct device is simply a pointer to the one in pci_dev. However, it will make it easy for me now to add generic device to MCA without having to go the fake pci route. --- drivers/pci/probe.c | 1 + include/linux/device.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5dfb1af81376..cfe5444f9fac 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -448,6 +448,7 @@ struct pci_dev * __devinit pci_scan_device(struct pci_dev *temp) /* now put in global tree */ strcpy(dev->dev.bus_id,dev->slot_name); + dev->dev.dma_mask = &dev->dma_mask; device_register(&dev->dev); return dev; diff --git a/include/linux/device.h b/include/linux/device.h index 6ce7d11cc8f9..44262d7f34c3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -303,6 +303,7 @@ struct device { being off. */ unsigned char *saved_state; /* saved device state */ + u64 *dma_mask; /* dma mask (if dma'able device) */ void (*release)(struct device * dev); }; -- cgit v1.2.3 From fc8aae5360008cf232c5f5bdc65b772a73a248c2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Dec 2002 11:22:05 -0600 Subject: driver model: reinstate bus iterators. This replaces the bus iterators bus_for_each_dev() and bus_for_each_drv(). Though no one in the kernel was using these, the MCA bus updates that are about to appear depend on them. They both now take a start pointer, which is the item to begin walking the list from, if it is not NULL. --- drivers/base/bus.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/device.h | 9 +++++ 2 files changed, 98 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d18effa7f981..ca5ef19aa4a9 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -132,6 +132,91 @@ struct subsystem bus_subsys = { }; +/** + * 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, 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 list_head * head, * entry; + int error = 0; + + if (!(bus = get_bus(bus))) + return -EINVAL; + + head = start ? &start->bus_list : &bus->devices; + + down_read(&bus->subsys.rwsem); + list_for_each(entry,head) { + struct device * dev = get_device(to_dev(entry)); + error = fn(dev,data); + put_device(dev); + if (error) + break; + } + up_read(&bus->subsys.rwsem); + return error; +} + +/** + * 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 list_head * head, * entry; + int error = 0; + + if(!(bus = get_bus(bus))) + return -EINVAL; + + head = start ? &start->bus_list : &bus->drivers; + + down_read(&bus->subsys.rwsem); + list_for_each(entry,head) { + struct device_driver * drv = get_driver(to_drv(entry)); + error = fn(drv,data); + put_driver(drv); + if(error) + break; + } + up_read(&bus->subsys.rwsem); + return error; +} + /** * attach - add device to driver. * @dev: device. @@ -190,7 +275,7 @@ static int bus_match(struct device * dev, struct device_driver * drv) */ static int device_attach(struct device * dev) { - struct bus_type * bus = dev->bus; + struct bus_type * bus = dev->bus; struct list_head * entry; int error = 0; @@ -455,6 +540,9 @@ static int __init bus_subsys_init(void) core_initcall(bus_subsys_init); +EXPORT_SYMBOL(bus_for_each_dev); +EXPORT_SYMBOL(bus_for_each_drv); + EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); diff --git a/include/linux/device.h b/include/linux/device.h index 9835d4fcdb12..435159cb14b8 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -85,6 +85,15 @@ extern struct bus_type * get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); +/* iterator helpers for buses */ + +int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, + int (*fn)(struct device *, void *)); + +int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, + void * data, int (*fn)(struct device_driver *, void *)); + + /* driverfs interface for exporting bus attributes */ struct bus_attribute { -- cgit v1.2.3 From c445065596bb32adc9bf3f31b50657de41496b1c Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 2 Dec 2002 01:35:26 -0600 Subject: driver model: get rid of global device list; minor cleanups. Since the device subsystem contains a global list of registered objects, we don't need to keep our own separate list, so we axe it. This also results in a savings of sizeof(void *) x 2 in the size of each device. drivers/base/power.c is updated to take the subsystem's rwsem and iterate over that list of objects. This leaves only the children list of struct device protected by the driver model semaphore. This could be removed, but there is one user of it (EDD), so it can't happen yet. This patch also removes the driver model spinlock, which isn't used anymore. --- drivers/base/base.h | 8 -------- drivers/base/core.c | 23 ++++++++--------------- drivers/base/power.c | 22 ++++++++++++---------- include/linux/device.h | 7 ------- 4 files changed, 20 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/base/base.h b/drivers/base/base.h index 6194d88242ec..0eaf5a2f0673 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -1,13 +1,5 @@ #undef DEBUG -#ifdef DEBUG -# define DBG(x...) printk(x) -#else -# define DBG(x...) -#endif - -extern struct list_head global_device_list; -extern spinlock_t device_lock; extern struct semaphore device_sem; extern int bus_add_device(struct device * dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index 6425fc8e0634..e6504bef4b5d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -18,15 +18,11 @@ #include "base.h" -LIST_HEAD(global_device_list); - int (*platform_notify)(struct device * dev) = NULL; int (*platform_notify_remove)(struct device * dev) = NULL; DECLARE_MUTEX(device_sem); -spinlock_t device_lock = SPIN_LOCK_UNLOCKED; - #define to_dev(obj) container_of(obj,struct device,kobj) @@ -146,11 +142,9 @@ void device_initialize(struct device *dev) kobject_init(&dev->kobj); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->g_list); INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); INIT_LIST_HEAD(&dev->intf_list); -// spin_lock_init(&dev->lock); } /** @@ -188,13 +182,11 @@ int device_add(struct device *dev) goto register_done; /* now take care of our own registration */ - down(&device_sem); if (parent) { - list_add_tail(&dev->g_list,&dev->parent->g_list); + down(&device_sem); list_add_tail(&dev->node,&parent->children); - } else - list_add_tail(&dev->g_list,&global_device_list); - up(&device_sem); + up(&device_sem); + } bus_add_device(dev); @@ -276,10 +268,11 @@ void device_del(struct device * dev) { struct device * parent = dev->parent; - down(&device_sem); - list_del_init(&dev->node); - list_del_init(&dev->g_list); - up(&device_sem); + if (parent) { + down(&device_sem); + list_del_init(&dev->node); + up(&device_sem); + } /* Notify the platform of the removal, in case they * need to do anything... diff --git a/drivers/base/power.c b/drivers/base/power.c index 594a793e24f9..9e38aa766ef1 100644 --- a/drivers/base/power.c +++ b/drivers/base/power.c @@ -15,7 +15,9 @@ #include #include "base.h" -#define to_dev(node) container_of(node,struct device,g_list) +#define to_dev(node) container_of(node,struct device,kobj.entry) + +extern struct subsystem device_subsys; /** * device_suspend - suspend/remove all devices on the device ree @@ -35,8 +37,8 @@ int device_suspend(u32 state, u32 level) printk(KERN_EMERG "Suspending devices\n"); - down(&device_sem); - list_for_each(node,&global_device_list) { + down_write(&device_subsys.rwsem); + list_for_each(node,&device_subsys.list) { struct device * dev = to_dev(node); if (dev->driver && dev->driver->suspend) { pr_debug("suspending device %s\n",dev->name); @@ -45,7 +47,7 @@ int device_suspend(u32 state, u32 level) printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error); } } - up(&device_sem); + up_write(&device_subsys.rwsem); return error; } @@ -61,15 +63,15 @@ void device_resume(u32 level) { struct list_head * node; - down(&device_sem); - list_for_each_prev(node,&global_device_list) { + down_write(&device_subsys.rwsem); + list_for_each_prev(node,&device_subsys.list) { struct device * dev = to_dev(node); if (dev->driver && dev->driver->resume) { pr_debug("resuming device %s\n",dev->name); dev->driver->resume(dev,level); } } - up(&device_sem); + up_write(&device_subsys.rwsem); printk(KERN_EMERG "Devices Resumed\n"); } @@ -83,15 +85,15 @@ void device_shutdown(void) printk(KERN_EMERG "Shutting down devices\n"); - down(&device_sem); - list_for_each(entry,&global_device_list) { + down_write(&device_subsys.rwsem); + list_for_each(entry,&device_subsys.list) { struct device * dev = to_dev(entry); if (dev->driver && dev->driver->shutdown) { pr_debug("shutting down %s\n",dev->name); dev->driver->shutdown(dev); } } - up(&device_sem); + up_write(&device_subsys.rwsem); } EXPORT_SYMBOL(device_suspend); diff --git a/include/linux/device.h b/include/linux/device.h index 435159cb14b8..c1918942c4cd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -250,7 +250,6 @@ extern int interface_add_data(struct intf_data *); struct device { - struct list_head g_list; /* node in depth-first order list */ struct list_head node; /* node in sibling list */ struct list_head bus_list; /* node in bus's list */ struct list_head driver_list; @@ -290,12 +289,6 @@ list_to_dev(struct list_head *node) return list_entry(node, struct device, node); } -static inline struct device * -g_list_to_dev(struct list_head *g_list) -{ - return list_entry(g_list, struct device, g_list); -} - static inline void * dev_get_drvdata (struct device *dev) { -- cgit v1.2.3 From 6f614c99601d8d5f2b67a186806bbd21e5108fc5 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 3 Dec 2002 00:40:21 -0600 Subject: driver model: clean up interface handling. - Make sure devices are added to an interface if the interface is registered e.g. as a module. - Make sure interface data descriptors are inserted into the interface's list by adding a kobject to them and calling kobject_register. - Create interface_remove_data() for explicit removal of data descriptor. - Break common pieces into separate functions. - Add lots of comments. - Add class_list to struct device, so we can access the devices registered with the class. --- drivers/base/base.h | 4 +- drivers/base/class.c | 12 ++- drivers/base/core.c | 1 + drivers/base/intf.c | 275 +++++++++++++++++++++++++++++++++++++++---------- include/linux/device.h | 9 +- 5 files changed, 238 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/drivers/base/base.h b/drivers/base/base.h index 0eaf5a2f0673..ab31ff74207c 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -14,8 +14,8 @@ extern void devclass_remove_device(struct device *); extern int devclass_add_driver(struct device_driver *); extern void devclass_remove_driver(struct device_driver *); -extern int interface_add(struct device_class *, struct device *); -extern void interface_remove(struct device_class *, struct device *); +extern int interface_add_dev(struct device *); +extern void interface_remove_dev(struct device *); #ifdef CONFIG_HOTPLUG diff --git a/drivers/base/class.c b/drivers/base/class.c index fbc6326312d8..86c7775c42bd 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -2,7 +2,7 @@ * class.c - basic device class management */ -#undef DEBUG +#define DEBUG #include #include @@ -98,7 +98,6 @@ void devclass_remove_file(struct device_class * cls, struct devclass_attribute * } - int devclass_add_driver(struct device_driver * drv) { struct device_class * cls = get_devclass(drv->devclass); @@ -172,9 +171,11 @@ int devclass_add_device(struct device * dev) error = cls->add_device(dev); if (!error) { enum_device(cls,dev); - interface_add(cls,dev); + interface_add_dev(dev); } + list_add_tail(&dev->class_list,&cls->devices); + /* notify userspace (call /sbin/hotplug) */ class_hotplug (dev, "add"); @@ -196,9 +197,11 @@ void devclass_remove_device(struct device * dev) down_write(&cls->subsys.rwsem); pr_debug("device class %s: removing device %s\n", cls->name,dev->name); - interface_remove(cls,dev); + interface_remove_dev(dev); unenum_device(cls,dev); + list_del(&dev->class_list); + /* notify userspace (call /sbin/hotplug) */ class_hotplug (dev, "remove"); @@ -224,6 +227,7 @@ void put_devclass(struct device_class * cls) int devclass_register(struct device_class * cls) { INIT_LIST_HEAD(&cls->drivers); + INIT_LIST_HEAD(&cls->devices); pr_debug("device class '%s': registering\n",cls->name); strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN); diff --git a/drivers/base/core.c b/drivers/base/core.c index e6504bef4b5d..a5ab807d9f49 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -144,6 +144,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); + INIT_LIST_HEAD(&dev->class_list); INIT_LIST_HEAD(&dev->intf_list); } diff --git a/drivers/base/intf.c b/drivers/base/intf.c index d31ad38bf3d2..63c5a4d12cd4 100644 --- a/drivers/base/intf.c +++ b/drivers/base/intf.c @@ -2,7 +2,7 @@ * intf.c - class-specific interface management */ -#undef DEBUG +#define DEBUG #include #include @@ -10,98 +10,267 @@ #include "base.h" -#define to_intf(node) container_of(node,struct device_interface,kobj.entry) +#define to_intf(node) container_of(node,struct device_interface,subsys.kobj.entry) + +#define to_data(e) container_of(e,struct intf_data,kobj.entry) + +#define intf_from_data(d) container_of(d->kobj.subsys,struct device_interface, subsys); + /** - * intf_dev_link - symlink from interface's directory to device's directory + * intf_dev_link - create sysfs symlink for interface. + * @data: interface data descriptor. * + * Create a symlink 'phys' in the interface's directory to */ + static int intf_dev_link(struct intf_data * data) { - char linkname[16]; - - snprintf(linkname,16,"%u",data->intf_num); - return sysfs_create_link(&data->intf->kobj,&data->dev->kobj,linkname); + char name[16]; + snprintf(name,16,"%d",data->intf_num); + return sysfs_create_link(&data->intf->subsys.kobj,&data->dev->kobj,name); } +/** + * intf_dev_unlink - remove symlink for interface. + * @intf: interface data descriptor. + * + */ + static void intf_dev_unlink(struct intf_data * data) { - char linkname[16]; - snprintf(linkname,16,"%u",data->intf_num); - sysfs_remove_link(&data->intf->kobj,linkname); + char name[16]; + snprintf(name,16,"%d",data->intf_num); + sysfs_remove_link(&data->intf->subsys.kobj,name); +} + + +/** + * interface_add_data - attach data descriptor + * @data: interface data descriptor. + * + * This attaches the per-instance interface object to the + * interface (by registering its kobject) and the device + * itself (by inserting it into the device's list). + * + * Note that there is no explicit protection done in this + * function. This should be called from the interface's + * add_device() method, which is called under the protection + * of the class's rwsem. + */ + +int interface_add_data(struct intf_data * data) +{ + struct device_interface * intf = intf_from_data(data); + + data->intf_num = data->intf->devnum++; + data->kobj.subsys = &intf->subsys; + kobject_register(&data->kobj); + + list_add_tail(&data->dev_entry,&data->dev->intf_list); + intf_dev_link(data); + return 0; +} + + +/** + * interface_remove_data - detach data descriptor. + * @data: interface data descriptor. + * + * This detaches the per-instance data descriptor by removing + * it from the device's list and unregistering the kobject from + * the subsystem. + */ + +void interface_remove_data(struct intf_data * data) +{ + intf_dev_unlink(data); + list_del_init(&data->dev_entry); + kobject_unregister(&data->kobj); } +/** + * add - attach device to interface + * @intf: interface. + * @dev: device. + * + * This is just a simple helper. Check the interface's interface + * helper and call it. This is called when adding an interface + * the class's devices, or a device to the class's interfaces. + */ + +static int add(struct device_interface * intf, struct device * dev) +{ + int error = 0; + + if (intf->add_device) + error = intf->add_device(dev); + pr_debug(" -> %s (%d)\n",dev->bus_id,error); + return error; +} + +/** + * del - detach device from interface. + * @data: interface data descriptor. + * + * Another simple helper. Remove the data descriptor from + * the device and the interface, then call the interface's + * remove_device() method. + */ + +static void del(struct intf_data * data) +{ + struct device_interface * intf = intf_from_data(data); + + pr_debug(" -> %s ",data->intf->name); + interface_remove_data(data); + if (intf->remove_device) + intf->remove_device(data); +} + +#define to_dev(entry) container_of(entry,struct device,class_list) + + +/** + * add_intf - add class's devices to interface. + * @intf: interface. + * + * Loop over the devices registered with the class, and call + * the interface's add_device() method for each. + * + * On an error, we won't break, but we will print debugging info. + */ +static void add_intf(struct device_interface * intf) +{ + struct device_class * cls = intf->devclass; + struct list_head * entry; + + down_write(&cls->subsys.rwsem); + list_for_each(entry,&cls->devices) + add(intf,to_dev(entry)); + up_write(&cls->subsys.rwsem); +} + +/** + * interface_register - register an interface with a device class. + * @intf: interface. + * + * An interface may be loaded after drivers and devices have been + * added to the class. So, we must add each device already known to + * the class to the interface as its registered. + */ + int interface_register(struct device_interface * intf) { struct device_class * cls = get_devclass(intf->devclass); - int error = 0; if (cls) { pr_debug("register interface '%s' with class '%s'\n", intf->name,cls->name); - strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN); - intf->kobj.subsys = &cls->subsys; - kobject_register(&intf->kobj); - } else - error = -EINVAL; - return error; + + strncpy(intf->subsys.kobj.name,intf->name,KOBJ_NAME_LEN); + intf->subsys.kobj.subsys = &cls->subsys; + subsystem_register(&intf->subsys); + add_intf(intf); + } + return 0; } + +/** + * del_intf - remove devices from interface. + * @intf: interface being unloaded. + * + * This loops over the devices registered with a class and + * calls the interface's remove_device() method for each. + * This is called when an interface is being unregistered. + */ + +static void del_intf(struct device_interface * intf) +{ + struct list_head * entry; + + down_write(&intf->devclass->subsys.rwsem); + list_for_each(entry,&intf->subsys.list) { + struct intf_data * data = to_data(entry); + del(data); + } + up_write(&intf->devclass->subsys.rwsem); +} + +/** + * interface_unregister - remove interface from class. + * @intf: interface. + * + * This is called when an interface in unloaded, giving it a + * chance to remove itself from devicse that have been added to + * it. + */ + void interface_unregister(struct device_interface * intf) { - pr_debug("unregistering interface '%s' from class '%s'\n", - intf->name,intf->devclass->name); - kobject_unregister(&intf->kobj); + struct device_class * cls = intf->devclass; + if (cls) { + pr_debug("unregistering interface '%s' from class '%s'\n", + intf->name,cls->name); + del_intf(intf); + subsystem_unregister(&intf->subsys); + put_devclass(cls); + } } -int interface_add(struct device_class * cls, struct device * dev) + +/** + * interface_add_dev - add device to interfaces. + * @dev: device. + * + * This is a helper for the class driver core. When a + * device is being added to a class, this is called to add + * the device to all the interfaces in the class. + * + * The operation is simple enough: loop over the interfaces + * and call add() [above] for each. The class rwsem is assumed + * to be held. + */ + +int interface_add_dev(struct device * dev) { + struct device_class * cls = dev->driver->devclass; struct list_head * node; - int error = 0; - pr_debug("adding '%s' to %s class interfaces\n",dev->name,cls->name); + pr_debug("interfaces: adding device %s\n",dev->name); list_for_each(node,&cls->subsys.list) { struct device_interface * intf = to_intf(node); - if (intf->add_device) { - error = intf->add_device(dev); - if (error) - pr_debug("%s:%s: adding '%s' failed: %d\n", - cls->name,intf->name,dev->name,error); - } - + add(intf,dev); } return 0; } -void interface_remove(struct device_class * cls, struct device * dev) -{ - struct list_head * node; - struct list_head * next; - pr_debug("remove '%s' from %s class interfaces: ",dev->name,cls->name); +/** + * interface_remove_dev - remove device from interfaces. + * @dev: device. + * + * This is another helper for the class driver core, and called + * when the device is being removed from the class. + * + * We iterate over the list of interface data descriptors attached + * to the device, and call del() [above] for each. Again, the + * class's rwsem is assumed to be held during this. + */ - list_for_each_safe(node,next,&dev->intf_list) { - struct intf_data * intf_data = container_of(node,struct intf_data,node); - list_del_init(&intf_data->node); +void interface_remove_dev(struct device * dev) +{ + struct list_head * entry, * next; - intf_dev_unlink(intf_data); - pr_debug("%s ",intf_data->intf->name); - if (intf_data->intf->remove_device) - intf_data->intf->remove_device(intf_data); - } - pr_debug("\n"); -} + pr_debug("interfaces: removing device %s\n",dev->name); -int interface_add_data(struct intf_data * data) -{ - down_write(&data->intf->devclass->subsys.rwsem); - list_add_tail(&data->node,&data->dev->intf_list); - data->intf_num = data->intf->devnum++; - intf_dev_link(data); - up_write(&data->intf->devclass->subsys.rwsem); - return 0; + list_for_each_safe(entry,next,&dev->intf_list) { + struct intf_data * intf_data = to_data(entry); + del(intf_data); + } } EXPORT_SYMBOL(interface_register); diff --git a/include/linux/device.h b/include/linux/device.h index c1918942c4cd..48612267bd65 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -168,6 +168,7 @@ struct device_class { struct subsystem devsubsys; struct subsystem drvsubsys; struct list_head drivers; + struct list_head devices; int (*add_device)(struct device *); void (*remove_device)(struct device *); @@ -216,9 +217,7 @@ struct device_interface { char * name; struct device_class * devclass; - struct kobject kobj; - struct list_head devices; - + struct subsystem subsys; u32 devnum; int (*add_device) (struct device *); @@ -239,10 +238,11 @@ extern void interface_unregister(struct device_interface *); * and create a driverfs symlink for it. */ struct intf_data { - struct list_head node; struct device_interface * intf; struct device * dev; u32 intf_num; + struct list_head dev_entry; + struct kobject kobj; }; extern int interface_add_data(struct intf_data *); @@ -252,6 +252,7 @@ extern int interface_add_data(struct intf_data *); struct device { struct list_head node; /* node in sibling list */ struct list_head bus_list; /* node in bus's list */ + struct list_head class_list; struct list_head driver_list; struct list_head children; struct list_head intf_list; -- cgit v1.2.3