Commit 27d2d64c authored by Patrick Mochel's avatar Patrick Mochel

Merge osdl.org:/home/mochel/src/kernel/devel/linux-2.5-virgin

into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-core
parents 5d8a45ef 2e646ed0
#undef DEBUG #undef DEBUG
extern struct semaphore device_sem; extern struct semaphore device_sem;
extern struct semaphore devclass_sem;
extern int bus_add_device(struct device * dev); extern int bus_add_device(struct device * dev);
extern void bus_remove_device(struct device * dev); extern void bus_remove_device(struct device * dev);
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr) #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) #define to_class(obj) container_of(obj,struct device_class,subsys.kset.kobj)
DECLARE_MUTEX(devclass_sem);
static ssize_t static ssize_t
devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{ {
...@@ -163,29 +165,34 @@ int devclass_add_device(struct device * dev) ...@@ -163,29 +165,34 @@ int devclass_add_device(struct device * dev)
struct device_class * cls; struct device_class * cls;
int error = 0; int error = 0;
down(&devclass_sem);
if (dev->driver) { if (dev->driver) {
cls = get_devclass(dev->driver->devclass); cls = get_devclass(dev->driver->devclass);
if (cls) {
down_write(&cls->subsys.rwsem); if (!cls)
goto Done;
pr_debug("device class %s: adding device %s\n", pr_debug("device class %s: adding device %s\n",
cls->name,dev->name); cls->name,dev->name);
if (cls->add_device) if (cls->add_device)
error = cls->add_device(dev); error = cls->add_device(dev);
if (!error) { if (error) {
enum_device(cls,dev); put_devclass(cls);
interface_add_dev(dev); goto Done;
} }
down_write(&cls->subsys.rwsem);
enum_device(cls,dev);
list_add_tail(&dev->class_list,&cls->devices.list); list_add_tail(&dev->class_list,&cls->devices.list);
/* notify userspace (call /sbin/hotplug) */ /* notify userspace (call /sbin/hotplug) */
class_hotplug (dev, "add"); class_hotplug (dev, "add");
up_write(&cls->subsys.rwsem); up_write(&cls->subsys.rwsem);
if (error)
put_devclass(cls); interface_add_dev(dev);
}
} }
Done:
up(&devclass_sem);
return error; return error;
} }
...@@ -193,13 +200,18 @@ void devclass_remove_device(struct device * dev) ...@@ -193,13 +200,18 @@ void devclass_remove_device(struct device * dev)
{ {
struct device_class * cls; struct device_class * cls;
down(&devclass_sem);
if (dev->driver) { if (dev->driver) {
cls = dev->driver->devclass; cls = dev->driver->devclass;
if (cls) { if (!cls)
goto Done;
interface_remove_dev(dev);
down_write(&cls->subsys.rwsem); down_write(&cls->subsys.rwsem);
pr_debug("device class %s: removing device %s\n", pr_debug("device class %s: removing device %s\n",
cls->name,dev->name); cls->name,dev->name);
interface_remove_dev(dev);
unenum_device(cls,dev); unenum_device(cls,dev);
list_del(&dev->class_list); list_del(&dev->class_list);
...@@ -207,12 +219,14 @@ void devclass_remove_device(struct device * dev) ...@@ -207,12 +219,14 @@ void devclass_remove_device(struct device * dev)
/* notify userspace (call /sbin/hotplug) */ /* notify userspace (call /sbin/hotplug) */
class_hotplug (dev, "remove"); class_hotplug (dev, "remove");
up_write(&cls->subsys.rwsem);
if (cls->remove_device) if (cls->remove_device)
cls->remove_device(dev); cls->remove_device(dev);
up_write(&cls->subsys.rwsem);
put_devclass(cls); put_devclass(cls);
} }
} Done:
up(&devclass_sem);
} }
struct device_class * get_devclass(struct device_class * cls) struct device_class * get_devclass(struct device_class * cls)
......
...@@ -143,7 +143,6 @@ void device_initialize(struct device *dev) ...@@ -143,7 +143,6 @@ void device_initialize(struct device *dev)
INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->driver_list);
INIT_LIST_HEAD(&dev->bus_list); INIT_LIST_HEAD(&dev->bus_list);
INIT_LIST_HEAD(&dev->class_list); INIT_LIST_HEAD(&dev->class_list);
INIT_LIST_HEAD(&dev->intf_list);
} }
/** /**
......
...@@ -12,80 +12,31 @@ ...@@ -12,80 +12,31 @@
#define to_intf(node) container_of(node,struct device_interface,kset.kobj.entry) #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. * 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 * 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]; return sysfs_create_link(&intf->kset.kobj,&dev->kobj,dev->bus_id);
snprintf(name,16,"%d",data->intf_num);
return sysfs_create_link(&data->intf->kset.kobj,&data->dev->kobj,name);
} }
/** /**
* intf_dev_unlink - remove symlink for interface. * intf_dev_unlink - remove symlink for interface.
* @intf: interface data descriptor. * @intf: interface.
* * @dev: device.
*/
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.
* *
* 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); sysfs_remove_link(&intf->kset.kobj,dev->bus_id);
list_del_init(&data->dev_entry);
kobject_unregister(&data->kobj);
} }
...@@ -103,33 +54,28 @@ static int add(struct device_interface * intf, struct device * dev) ...@@ -103,33 +54,28 @@ static int add(struct device_interface * intf, struct device * dev)
{ {
int error = 0; int error = 0;
if (intf->add_device) if (intf->add_device) {
error = intf->add_device(dev); if (!(error = intf->add_device(dev)))
intf_dev_link(intf,dev);
}
pr_debug(" -> %s (%d)\n",dev->bus_id,error); pr_debug(" -> %s (%d)\n",dev->bus_id,error);
return error; return error;
} }
/** /**
* del - detach device from interface. * del - detach device from interface.
* @data: interface data descriptor. * @intf: interface.
* * @dev: device.
* 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) static void del(struct device_interface * intf, struct device * dev)
{ {
struct device_interface * intf = data->intf;
pr_debug(" -> %s ",intf->name); pr_debug(" -> %s ",intf->name);
interface_remove_data(data);
if (intf->remove_device) 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. * add_intf - add class's devices to interface.
...@@ -145,10 +91,8 @@ static void add_intf(struct device_interface * intf) ...@@ -145,10 +91,8 @@ static void add_intf(struct device_interface * intf)
struct device_class * cls = intf->devclass; struct device_class * cls = intf->devclass;
struct list_head * entry; struct list_head * entry;
down_write(&cls->subsys.rwsem);
list_for_each(entry,&cls->devices.list) list_for_each(entry,&cls->devices.list)
add(intf,to_dev(entry)); add(intf,to_dev(entry));
up_write(&cls->subsys.rwsem);
} }
/** /**
...@@ -164,6 +108,7 @@ int interface_register(struct device_interface * intf) ...@@ -164,6 +108,7 @@ int interface_register(struct device_interface * intf)
{ {
struct device_class * cls = get_devclass(intf->devclass); struct device_class * cls = get_devclass(intf->devclass);
down(&devclass_sem);
if (cls) { if (cls) {
pr_debug("register interface '%s' with class '%s'\n", pr_debug("register interface '%s' with class '%s'\n",
intf->name,cls->name); intf->name,cls->name);
...@@ -173,6 +118,7 @@ int interface_register(struct device_interface * intf) ...@@ -173,6 +118,7 @@ int interface_register(struct device_interface * intf)
kset_register(&intf->kset); kset_register(&intf->kset);
add_intf(intf); add_intf(intf);
} }
up(&devclass_sem);
return 0; return 0;
} }
...@@ -188,14 +134,13 @@ int interface_register(struct device_interface * intf) ...@@ -188,14 +134,13 @@ int interface_register(struct device_interface * intf)
static void del_intf(struct device_interface * intf) static void del_intf(struct device_interface * intf)
{ {
struct device_class * cls = intf->devclass;
struct list_head * entry; struct list_head * entry;
down_write(&intf->devclass->subsys.rwsem); list_for_each(entry,&cls->devices.list) {
list_for_each(entry,&intf->kset.list) { struct device * dev = to_dev(entry);
struct intf_data * data = to_data(entry); del(intf,dev);
del(data);
} }
up_write(&intf->devclass->subsys.rwsem);
} }
/** /**
...@@ -210,6 +155,8 @@ static void del_intf(struct device_interface * intf) ...@@ -210,6 +155,8 @@ static void del_intf(struct device_interface * intf)
void interface_unregister(struct device_interface * intf) void interface_unregister(struct device_interface * intf)
{ {
struct device_class * cls = intf->devclass; struct device_class * cls = intf->devclass;
down(&devclass_sem);
if (cls) { if (cls) {
pr_debug("unregistering interface '%s' from class '%s'\n", pr_debug("unregistering interface '%s' from class '%s'\n",
intf->name,cls->name); intf->name,cls->name);
...@@ -217,6 +164,7 @@ void interface_unregister(struct device_interface * intf) ...@@ -217,6 +164,7 @@ void interface_unregister(struct device_interface * intf)
kset_unregister(&intf->kset); kset_unregister(&intf->kset);
put_devclass(cls); put_devclass(cls);
} }
up(&devclass_sem);
} }
...@@ -255,20 +203,21 @@ int interface_add_dev(struct device * dev) ...@@ -255,20 +203,21 @@ int interface_add_dev(struct device * dev)
* This is another helper for the class driver core, and called * This is another helper for the class driver core, and called
* when the device is being removed from the class. * when the device is being removed from the class.
* *
* We iterate over the list of interface data descriptors attached * We iterate over the list of the class's devices and call del()
* to the device, and call del() [above] for each. Again, the * [above] for each. Again, the class's rwsem is _not_ held, but
* class's rwsem is assumed to be held during this. * the devclass_sem is (see class.c).
*/ */
void interface_remove_dev(struct device * dev) void interface_remove_dev(struct device * dev)
{ {
struct list_head * entry, * next; struct list_head * entry, * next;
struct device_class * cls = dev->driver->devclass;
pr_debug("interfaces: removing device %s\n",dev->name); pr_debug("interfaces: removing device %s\n",dev->name);
list_for_each_safe(entry,next,&dev->intf_list) { list_for_each_safe(entry,next,&cls->subsys.kset.list) {
struct intf_data * intf_data = to_data(entry); struct device_interface * intf = to_intf(entry);
del(intf_data); del(intf,dev);
} }
} }
......
...@@ -98,10 +98,9 @@ static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t ...@@ -98,10 +98,9 @@ static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
if (!dentry->d_inode) { if (!dentry->d_inode) {
inode = sysfs_get_inode(dir->i_sb, mode, dev); inode = sysfs_get_inode(dir->i_sb, mode, dev);
if (inode) { if (inode)
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
dget(dentry); else
} else
error = -ENOSPC; error = -ENOSPC;
} else } else
error = -EEXIST; error = -EEXIST;
...@@ -703,10 +702,6 @@ static void hash_and_remove(struct dentry * dir, const char * name) ...@@ -703,10 +702,6 @@ static void hash_and_remove(struct dentry * dir, const char * name)
pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name, pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name,
atomic_read(&victim->d_count)); atomic_read(&victim->d_count));
/**
* Drop reference from initial get_dentry().
*/
dput(victim);
} }
/** /**
...@@ -795,7 +790,7 @@ void sysfs_remove_link(struct kobject * kobj, char * name) ...@@ -795,7 +790,7 @@ void sysfs_remove_link(struct kobject * kobj, char * name)
void sysfs_remove_dir(struct kobject * kobj) void sysfs_remove_dir(struct kobject * kobj)
{ {
struct list_head * node, * next; struct list_head * node;
struct dentry * dentry = dget(kobj->dentry); struct dentry * dentry = dget(kobj->dentry);
struct dentry * parent; struct dentry * parent;
...@@ -807,32 +802,31 @@ void sysfs_remove_dir(struct kobject * kobj) ...@@ -807,32 +802,31 @@ void sysfs_remove_dir(struct kobject * kobj)
down(&parent->d_inode->i_sem); down(&parent->d_inode->i_sem);
down(&dentry->d_inode->i_sem); down(&dentry->d_inode->i_sem);
list_for_each_safe(node,next,&dentry->d_subdirs) { spin_lock(&dcache_lock);
struct dentry * d = dget(list_entry(node,struct dentry,d_child)); node = dentry->d_subdirs.next;
/** while (node != &dentry->d_subdirs) {
* Make sure dentry is still there struct dentry * d = list_entry(node,struct dentry,d_child);
*/ list_del_init(node);
pr_debug(" o %s: ",d->d_name.name);
if (d->d_inode) {
pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count));
if (d->d_inode) {
d = dget_locked(d);
pr_debug("removing"); pr_debug("removing");
/** /**
* Unlink and unhash. * Unlink and unhash.
*/ */
simple_unlink(dentry->d_inode,d); spin_unlock(&dcache_lock);
d_delete(d); d_delete(d);
simple_unlink(dentry->d_inode,d);
/**
* Drop reference from initial get_dentry().
*/
dput(d); dput(d);
spin_lock(&dcache_lock);
} }
pr_debug(" done (%d)\n",atomic_read(&d->d_count)); pr_debug(" done\n");
/**
* drop reference from dget() above. node = dentry->d_subdirs.next;
*/
dput(d);
} }
spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem); up(&dentry->d_inode->i_sem);
d_invalidate(dentry); d_invalidate(dentry);
......
...@@ -27,9 +27,9 @@ ...@@ -27,9 +27,9 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/sched.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/types.h> #include <linux/types.h>
#include <asm/semaphore.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#define DEVICE_NAME_SIZE 50 #define DEVICE_NAME_SIZE 50
...@@ -207,8 +207,6 @@ extern void devclass_remove_file(struct device_class *, struct devclass_attribut ...@@ -207,8 +207,6 @@ extern void devclass_remove_file(struct device_class *, struct devclass_attribut
* it supports the device. * it supports the device.
*/ */
struct intf_data;
struct device_interface { struct device_interface {
char * name; char * name;
struct device_class * devclass; struct device_class * devclass;
...@@ -217,41 +215,19 @@ struct device_interface { ...@@ -217,41 +215,19 @@ struct device_interface {
u32 devnum; u32 devnum;
int (*add_device) (struct device *); int (*add_device) (struct device *);
int (*remove_device) (struct intf_data *); int (*remove_device) (struct device *);
}; };
extern int interface_register(struct device_interface *); extern int interface_register(struct device_interface *);
extern void interface_unregister(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 device {
struct list_head node; /* node in sibling list */ struct list_head node; /* node in sibling list */
struct list_head bus_list; /* node in bus's list */ struct list_head bus_list; /* node in bus's list */
struct list_head class_list; struct list_head class_list;
struct list_head driver_list; struct list_head driver_list;
struct list_head children; struct list_head children;
struct list_head intf_list;
struct device * parent; struct device * parent;
struct kobject kobj; struct kobject kobj;
......
...@@ -73,7 +73,7 @@ extern void kset_unregister(struct kset * k); ...@@ -73,7 +73,7 @@ extern void kset_unregister(struct kset * k);
static inline struct kset * to_kset(struct kobject * kobj) static inline struct kset * to_kset(struct kobject * kobj)
{ {
return container_of(kobj,struct kset,kobj); return kobj ? container_of(kobj,struct kset,kobj) : NULL;
} }
static inline struct kset * kset_get(struct kset * k) static inline struct kset * kset_get(struct kset * k)
......
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