#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr)
#define to_class(obj) container_of(obj,struct device_class,subsys.kset.kobj)
+DECLARE_MUTEX(devclass_sem);
+
static ssize_t
devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{
struct device_class * cls;
int error = 0;
+ down(&devclass_sem);
if (dev->driver) {
cls = get_devclass(dev->driver->devclass);
- if (cls) {
- down_write(&cls->subsys.rwsem);
- pr_debug("device class %s: adding device %s\n",
- cls->name,dev->name);
- if (cls->add_device)
- error = cls->add_device(dev);
- if (!error) {
- enum_device(cls,dev);
- interface_add_dev(dev);
- }
-
- list_add_tail(&dev->class_list,&cls->devices.list);
-
- /* notify userspace (call /sbin/hotplug) */
- class_hotplug (dev, "add");
-
- up_write(&cls->subsys.rwsem);
- if (error)
- put_devclass(cls);
+
+ if (!cls)
+ goto Done;
+
+ pr_debug("device class %s: adding device %s\n",
+ cls->name,dev->name);
+ if (cls->add_device)
+ error = cls->add_device(dev);
+ if (error) {
+ put_devclass(cls);
+ goto Done;
}
+
+ down_write(&cls->subsys.rwsem);
+ enum_device(cls,dev);
+ list_add_tail(&dev->class_list,&cls->devices.list);
+ /* notify userspace (call /sbin/hotplug) */
+ class_hotplug (dev, "add");
+
+ up_write(&cls->subsys.rwsem);
+
+ interface_add_dev(dev);
}
+ Done:
+ up(&devclass_sem);
return error;
}
{
struct device_class * cls;
+ down(&devclass_sem);
if (dev->driver) {
cls = dev->driver->devclass;
- if (cls) {
- down_write(&cls->subsys.rwsem);
- pr_debug("device class %s: removing device %s\n",
- cls->name,dev->name);
- interface_remove_dev(dev);
- unenum_device(cls,dev);
+ if (!cls)
+ goto Done;
- list_del(&dev->class_list);
+ interface_remove_dev(dev);
- /* notify userspace (call /sbin/hotplug) */
- class_hotplug (dev, "remove");
+ down_write(&cls->subsys.rwsem);
+ pr_debug("device class %s: removing device %s\n",
+ cls->name,dev->name);
- if (cls->remove_device)
- cls->remove_device(dev);
- up_write(&cls->subsys.rwsem);
- put_devclass(cls);
- }
+ unenum_device(cls,dev);
+
+ list_del(&dev->class_list);
+
+ /* notify userspace (call /sbin/hotplug) */
+ class_hotplug (dev, "remove");
+
+ up_write(&cls->subsys.rwsem);
+
+ if (cls->remove_device)
+ cls->remove_device(dev);
+ put_devclass(cls);
}
+ Done:
+ up(&devclass_sem);
}
struct device_class * get_devclass(struct device_class * cls)
#define to_intf(node) container_of(node,struct device_interface,kset.kobj.entry)
-#define to_data(e) container_of(e,struct intf_data,kobj.entry)
+#define to_dev(d) container_of(d,struct device,class_list)
/**
* intf_dev_link - create sysfs symlink for interface.
- * @data: interface data descriptor.
+ * @intf: interface.
+ * @dev: device.
*
* Create a symlink 'phys' in the interface's directory to
*/
-static int intf_dev_link(struct intf_data * data)
+static int intf_dev_link(struct device_interface * intf, struct device * dev)
{
- char name[16];
- snprintf(name,16,"%d",data->intf_num);
- return sysfs_create_link(&data->intf->kset.kobj,&data->dev->kobj,name);
+ return sysfs_create_link(&intf->kset.kobj,&dev->kobj,dev->bus_id);
}
/**
* intf_dev_unlink - remove symlink for interface.
- * @intf: interface data descriptor.
- *
- */
-
-static void intf_dev_unlink(struct intf_data * data)
-{
- char name[16];
- snprintf(name,16,"%d",data->intf_num);
- sysfs_remove_link(&data->intf->kset.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 = data->intf;
-
- if (intf) {
- data->intf_num = intf->devnum++;
- data->kobj.kset = &intf->kset;
- kobject_register(&data->kobj);
-
- list_add_tail(&data->dev_entry,&data->dev->intf_list);
- return intf_dev_link(data);
- }
- return -EINVAL;
-}
-
-
-/**
- * interface_remove_data - detach data descriptor.
- * @data: interface data descriptor.
+ * @intf: interface.
+ * @dev: device.
*
- * 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)
+static void intf_dev_unlink(struct device_interface * intf, struct device * dev)
{
- intf_dev_unlink(data);
- list_del_init(&data->dev_entry);
- kobject_unregister(&data->kobj);
+ sysfs_remove_link(&intf->kset.kobj,dev->bus_id);
}
{
int error = 0;
- if (intf->add_device)
- error = intf->add_device(dev);
+ if (intf->add_device) {
+ if (!(error = intf->add_device(dev)))
+ intf_dev_link(intf,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.
+ * @intf: interface.
+ * @dev: device.
*/
-static void del(struct intf_data * data)
+static void del(struct device_interface * intf, struct device * dev)
{
- struct device_interface * intf = data->intf;
-
pr_debug(" -> %s ",intf->name);
- interface_remove_data(data);
if (intf->remove_device)
- intf->remove_device(data);
+ intf->remove_device(dev);
+ intf_dev_unlink(intf,dev);
}
-#define to_dev(entry) container_of(entry,struct device,class_list)
-
/**
* add_intf - add class's devices to interface.
struct device_class * cls = intf->devclass;
struct list_head * entry;
- down_write(&cls->subsys.rwsem);
list_for_each(entry,&cls->devices.list)
add(intf,to_dev(entry));
- up_write(&cls->subsys.rwsem);
}
/**
{
struct device_class * cls = get_devclass(intf->devclass);
+ down(&devclass_sem);
if (cls) {
pr_debug("register interface '%s' with class '%s'\n",
intf->name,cls->name);
kset_register(&intf->kset);
add_intf(intf);
}
+ up(&devclass_sem);
return 0;
}
static void del_intf(struct device_interface * intf)
{
+ struct device_class * cls = intf->devclass;
struct list_head * entry;
- down_write(&intf->devclass->subsys.rwsem);
- list_for_each(entry,&intf->kset.list) {
- struct intf_data * data = to_data(entry);
- del(data);
+ list_for_each(entry,&cls->devices.list) {
+ struct device * dev = to_dev(entry);
+ del(intf,dev);
}
- up_write(&intf->devclass->subsys.rwsem);
}
/**
void interface_unregister(struct device_interface * intf)
{
struct device_class * cls = intf->devclass;
+
+ down(&devclass_sem);
if (cls) {
pr_debug("unregistering interface '%s' from class '%s'\n",
intf->name,cls->name);
kset_unregister(&intf->kset);
put_devclass(cls);
}
+ up(&devclass_sem);
}
* 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.
+ * We iterate over the list of the class's devices and call del()
+ * [above] for each. Again, the class's rwsem is _not_ held, but
+ * the devclass_sem is (see class.c).
*/
void interface_remove_dev(struct device * dev)
{
struct list_head * entry, * next;
+ struct device_class * cls = dev->driver->devclass;
pr_debug("interfaces: removing device %s\n",dev->name);
- list_for_each_safe(entry,next,&dev->intf_list) {
- struct intf_data * intf_data = to_data(entry);
- del(intf_data);
+ list_for_each_safe(entry,next,&cls->subsys.kset.list) {
+ struct device_interface * intf = to_intf(entry);
+ del(intf,dev);
}
}
* it supports the device.
*/
-struct intf_data;
-
struct device_interface {
char * name;
struct device_class * devclass;
u32 devnum;
int (*add_device) (struct device *);
- int (*remove_device) (struct intf_data *);
+ int (*remove_device) (struct device *);
};
extern int interface_register(struct device_interface *);
extern void interface_unregister(struct device_interface *);
-/*
- * intf_data - per-device data for an interface
- * Each interface typically has a per-device data structure
- * that it allocates. It should embed one of these structures
- * in that structure and call interface_add_data() to add it
- * to the device's list.
- * That will also enumerate the device within the interface
- * and create a driverfs symlink for it.
- */
-struct intf_data {
- 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 *);
-
-
-
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;
struct device * parent;
struct kobject kobj;