Commit 487680ca authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://ldm.bkbits.net/linux-2.5-core

into home.transmeta.com:/home/torvalds/v2.5/linux
parents d103bdbf 879a1092
...@@ -1114,7 +1114,7 @@ int ecard_register_driver(struct ecard_driver *drv) ...@@ -1114,7 +1114,7 @@ int ecard_register_driver(struct ecard_driver *drv)
void ecard_remove_driver(struct ecard_driver *drv) void ecard_remove_driver(struct ecard_driver *drv)
{ {
remove_driver(&drv->drv); driver_unregister(&drv->drv);
} }
static int ecard_match(struct device *_dev, struct device_driver *_drv) static int ecard_match(struct device *_dev, struct device_driver *_drv)
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
extern struct list_head global_device_list; extern struct list_head global_device_list;
extern spinlock_t device_lock; extern spinlock_t device_lock;
extern struct semaphore device_sem;
extern struct device * get_device_locked(struct device *); extern struct device * get_device_locked(struct device *);
...@@ -20,6 +21,9 @@ extern void device_remove_dir(struct device * dev); ...@@ -20,6 +21,9 @@ extern void device_remove_dir(struct device * dev);
extern int bus_make_dir(struct bus_type * bus); extern int bus_make_dir(struct bus_type * bus);
extern void bus_remove_dir(struct bus_type * bus); extern void bus_remove_dir(struct bus_type * bus);
extern int bus_add_driver(struct device_driver *);
extern void bus_remove_driver(struct device_driver *);
extern int driver_make_dir(struct device_driver * drv); extern int driver_make_dir(struct device_driver * drv);
extern void driver_remove_dir(struct device_driver * drv); extern void driver_remove_dir(struct device_driver * drv);
...@@ -48,9 +52,6 @@ extern int interface_add(struct device_class *, struct device *); ...@@ -48,9 +52,6 @@ extern int interface_add(struct device_class *, struct device *);
extern void interface_remove(struct device_class *, struct device *); extern void interface_remove(struct device_class *, struct device *);
extern int driver_attach(struct device_driver * drv);
extern void driver_detach(struct device_driver * drv);
#ifdef CONFIG_HOTPLUG #ifdef CONFIG_HOTPLUG
extern int dev_hotplug(struct device *dev, const char *action); extern int dev_hotplug(struct device *dev, const char *action);
#else #else
......
...@@ -43,28 +43,23 @@ int bus_for_each_dev(struct bus_type * bus, void * data, ...@@ -43,28 +43,23 @@ int bus_for_each_dev(struct bus_type * bus, void * data,
int (*callback)(struct device * dev, void * data)) int (*callback)(struct device * dev, void * data))
{ {
struct list_head * node; struct list_head * node;
struct device * prev = NULL;
int error = 0; int error = 0;
get_bus(bus); bus = get_bus(bus);
spin_lock(&device_lock); if (bus) {
list_for_each(node,&bus->devices) { down_read(&bus->rwsem);
struct device * dev = get_device_locked(to_dev(node)); list_for_each(node,&bus->devices) {
if (dev) { struct device * dev = get_device(to_dev(node));
spin_unlock(&device_lock); if (dev) {
error = callback(dev,data); error = callback(dev,data);
if (prev) put_device(dev);
put_device(prev); if (error)
prev = dev; break;
spin_lock(&device_lock); }
if (error)
break;
} }
up_read(&bus->rwsem);
put_bus(bus);
} }
spin_unlock(&device_lock);
if (prev)
put_device(prev);
put_bus(bus);
return error; return error;
} }
...@@ -72,33 +67,126 @@ int bus_for_each_drv(struct bus_type * bus, void * data, ...@@ -72,33 +67,126 @@ int bus_for_each_drv(struct bus_type * bus, void * data,
int (*callback)(struct device_driver * drv, void * data)) int (*callback)(struct device_driver * drv, void * data))
{ {
struct list_head * node; struct list_head * node;
struct device_driver * prev = NULL;
int error = 0; int error = 0;
/* pin bus in memory */ bus = get_bus(bus);
get_bus(bus); if (bus) {
down_read(&bus->rwsem);
list_for_each(node,&bus->drivers) {
struct device_driver * drv = get_driver(to_drv(node));
if (drv) {
error = callback(drv,data);
put_driver(drv);
if (error)
break;
}
}
up_read(&bus->rwsem);
put_bus(bus);
}
return error;
}
spin_lock(&device_lock); static void attach(struct device * dev)
list_for_each(node,&bus->drivers) { {
struct device_driver * drv = get_driver(to_drv(node)); pr_debug("bound device '%s' to driver '%s'\n",
if (drv) { dev->bus_id,dev->driver->name);
spin_unlock(&device_lock); list_add_tail(&dev->driver_list,&dev->driver->devices);
error = callback(drv,data); }
if (prev)
put_driver(prev); static int bus_match(struct device * dev, struct device_driver * drv)
prev = drv; {
spin_lock(&device_lock); int error = 0;
if (error) if (dev->bus->match(dev,drv)) {
break; dev->driver = drv;
if (drv->probe) {
if (!(error = drv->probe(dev)))
attach(dev);
else
dev->driver = NULL;
} }
} }
spin_unlock(&device_lock);
if (prev)
put_driver(prev);
put_bus(bus);
return error; return error;
} }
static int device_attach(struct device * dev)
{
struct bus_type * bus = dev->bus;
struct list_head * entry;
int error = 0;
if (dev->driver) {
attach(dev);
return 0;
}
if (!bus->match)
return 0;
list_for_each(entry,&bus->drivers) {
struct device_driver * drv =
get_driver(container_of(entry,struct device_driver,bus_list));
if (!drv)
continue;
error = bus_match(dev,drv);
put_driver(drv);
if (!error)
break;
}
return error;
}
static int driver_attach(struct device_driver * drv)
{
struct bus_type * bus = drv->bus;
struct list_head * entry;
int error = 0;
if (!bus->match)
return 0;
list_for_each(entry,&bus->devices) {
struct device * dev = container_of(entry,struct device,bus_list);
if (get_device(dev)) {
if (!dev->driver) {
if (!bus_match(dev,drv) && dev->driver)
devclass_add_device(dev);
}
put_device(dev);
}
}
return error;
}
static void detach(struct device * dev, struct device_driver * drv)
{
if (drv) {
list_del_init(&dev->driver_list);
devclass_remove_device(dev);
if (drv->remove)
drv->remove(dev);
dev->driver = NULL;
}
}
static void device_detach(struct device * dev)
{
detach(dev,dev->driver);
}
static void driver_detach(struct device_driver * drv)
{
struct list_head * entry, * next;
list_for_each_safe(entry,next,&drv->devices) {
struct device * dev = container_of(entry,struct device,driver_list);
if (get_device(dev)) {
detach(dev,drv);
put_device(dev);
}
}
}
/** /**
* bus_add_device - add device to bus * bus_add_device - add device to bus
* @dev: device being added * @dev: device being added
...@@ -110,12 +198,13 @@ int bus_for_each_drv(struct bus_type * bus, void * data, ...@@ -110,12 +198,13 @@ int bus_for_each_drv(struct bus_type * bus, void * data,
*/ */
int bus_add_device(struct device * dev) int bus_add_device(struct device * dev)
{ {
if (dev->bus) { struct bus_type * bus = get_bus(dev->bus);
pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name); if (bus) {
get_bus(dev->bus); down_write(&dev->bus->rwsem);
spin_lock(&device_lock); pr_debug("bus %s: add device %s\n",bus->name,dev->bus_id);
list_add_tail(&dev->bus_list,&dev->bus->devices); list_add_tail(&dev->bus_list,&dev->bus->devices);
spin_unlock(&device_lock); device_attach(dev);
up_write(&dev->bus->rwsem);
device_bus_link(dev); device_bus_link(dev);
} }
return 0; return 0;
...@@ -131,17 +220,70 @@ int bus_add_device(struct device * dev) ...@@ -131,17 +220,70 @@ int bus_add_device(struct device * dev)
void bus_remove_device(struct device * dev) void bus_remove_device(struct device * dev)
{ {
if (dev->bus) { if (dev->bus) {
down_write(&dev->bus->rwsem);
pr_debug("bus %s: remove device %s\n",dev->bus->name,dev->bus_id);
device_remove_symlink(&dev->bus->device_dir,dev->bus_id); device_remove_symlink(&dev->bus->device_dir,dev->bus_id);
device_detach(dev);
list_del_init(&dev->bus_list);
up_write(&dev->bus->rwsem);
put_bus(dev->bus); put_bus(dev->bus);
} }
} }
int bus_add_driver(struct device_driver * drv)
{
struct bus_type * bus = get_bus(drv->bus);
if (bus) {
down_write(&bus->rwsem);
pr_debug("bus %s: add driver %s\n",bus->name,drv->name);
list_add_tail(&drv->bus_list,&bus->drivers);
driver_attach(drv);
up_write(&bus->rwsem);
driver_make_dir(drv);
}
return 0;
}
void bus_remove_driver(struct device_driver * drv)
{
if (drv->bus) {
down_write(&drv->bus->rwsem);
pr_debug("bus %s: remove driver %s\n",drv->bus->name,drv->name);
driver_detach(drv);
list_del_init(&drv->bus_list);
up_write(&drv->bus->rwsem);
}
}
struct bus_type * get_bus(struct bus_type * bus)
{
struct bus_type * ret = bus;
spin_lock(&device_lock);
if (bus && bus->present && atomic_read(&bus->refcount))
atomic_inc(&bus->refcount);
else
ret = NULL;
spin_unlock(&device_lock);
return ret;
}
void put_bus(struct bus_type * bus)
{
if (!atomic_dec_and_lock(&bus->refcount,&device_lock))
return;
list_del_init(&bus->node);
spin_unlock(&device_lock);
BUG_ON(bus->present);
bus_remove_dir(bus);
}
int bus_register(struct bus_type * bus) int bus_register(struct bus_type * bus)
{ {
rwlock_init(&bus->lock); init_rwsem(&bus->rwsem);
INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->devices);
INIT_LIST_HEAD(&bus->drivers); INIT_LIST_HEAD(&bus->drivers);
atomic_set(&bus->refcount,2); atomic_set(&bus->refcount,2);
bus->present = 1;
spin_lock(&device_lock); spin_lock(&device_lock);
list_add_tail(&bus->node,&bus_driver_list); list_add_tail(&bus->node,&bus_driver_list);
...@@ -156,13 +298,14 @@ int bus_register(struct bus_type * bus) ...@@ -156,13 +298,14 @@ int bus_register(struct bus_type * bus)
return 0; return 0;
} }
void put_bus(struct bus_type * bus) void bus_unregister(struct bus_type * bus)
{ {
if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) spin_lock(&device_lock);
return; bus->present = 0;
list_del_init(&bus->node);
spin_unlock(&device_lock); spin_unlock(&device_lock);
bus_remove_dir(bus);
pr_debug("bus %s: unregistering\n",bus->name);
put_bus(bus);
} }
EXPORT_SYMBOL(bus_for_each_dev); EXPORT_SYMBOL(bus_for_each_dev);
...@@ -170,4 +313,6 @@ EXPORT_SYMBOL(bus_for_each_drv); ...@@ -170,4 +313,6 @@ EXPORT_SYMBOL(bus_for_each_drv);
EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_add_device);
EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_remove_device);
EXPORT_SYMBOL(bus_register); EXPORT_SYMBOL(bus_register);
EXPORT_SYMBOL(bus_unregister);
EXPORT_SYMBOL(get_bus);
EXPORT_SYMBOL(put_bus); EXPORT_SYMBOL(put_bus);
...@@ -10,27 +10,29 @@ static LIST_HEAD(class_list); ...@@ -10,27 +10,29 @@ static LIST_HEAD(class_list);
int devclass_add_driver(struct device_driver * drv) int devclass_add_driver(struct device_driver * drv)
{ {
if (drv->devclass) { struct device_class * cls = get_devclass(drv->devclass);
pr_debug("Registering driver %s:%s with class %s\n", if (cls) {
drv->bus->name,drv->name,drv->devclass->name); down_write(&cls->rwsem);
pr_debug("device class %s: adding driver %s:%s\n",
spin_lock(&device_lock); cls->name,drv->bus->name,drv->name);
list_add_tail(&drv->class_list,&drv->devclass->drivers); list_add_tail(&drv->class_list,&cls->drivers);
spin_unlock(&device_lock);
devclass_drv_link(drv); devclass_drv_link(drv);
up_write(&cls->rwsem);
} }
return 0; return 0;
} }
void devclass_remove_driver(struct device_driver * drv) void devclass_remove_driver(struct device_driver * drv)
{ {
if (drv->devclass) { struct device_class * cls = drv->devclass;
pr_debug("Removing driver %s:%s:%s\n", if (cls) {
drv->devclass->name,drv->bus->name,drv->name); down_write(&cls->rwsem);
spin_lock(&device_lock); pr_debug("device class %s: removing driver %s:%s\n",
cls->name,drv->bus->name,drv->name);
list_del_init(&drv->class_list); list_del_init(&drv->class_list);
spin_unlock(&device_lock);
devclass_drv_unlink(drv); devclass_drv_unlink(drv);
up_write(&cls->rwsem);
put_devclass(cls);
} }
} }
...@@ -38,9 +40,7 @@ void devclass_remove_driver(struct device_driver * drv) ...@@ -38,9 +40,7 @@ void devclass_remove_driver(struct device_driver * drv)
static void enum_device(struct device_class * cls, struct device * dev) static void enum_device(struct device_class * cls, struct device * dev)
{ {
u32 val; u32 val;
spin_lock(&device_lock);
val = cls->devnum++; val = cls->devnum++;
spin_unlock(&device_lock);
dev->class_num = val; dev->class_num = val;
devclass_dev_link(cls,dev); devclass_dev_link(cls,dev);
} }
...@@ -51,18 +51,44 @@ static void unenum_device(struct device_class * cls, struct device * dev) ...@@ -51,18 +51,44 @@ static void unenum_device(struct device_class * cls, struct device * dev)
dev->class_num = 0; dev->class_num = 0;
} }
/**
* devclass_add_device - register device with device class
* @dev: device to be registered
*
* This is called when a device is either registered with the
* core, or after the a driver module is loaded and bound to
* the device.
* The class is determined by looking at @dev's driver, so one
* way or another, it must be bound to something. Once the
* class is determined, it's set to prevent against concurrent
* calls for the same device stomping on each other.
*
* /sbin/hotplug should be called once the device is added to
* class and all the interfaces.
*/
int devclass_add_device(struct device * dev) int devclass_add_device(struct device * dev)
{ {
struct device_class * cls = dev->driver->devclass; struct device_class * cls;
int error = 0; int error = 0;
if (cls) {
pr_debug("adding device '%s' to class '%s'\n", if (dev->driver) {
dev->name,cls->name); cls = get_devclass(dev->driver->devclass);
if (cls->add_device) if (cls) {
error = cls->add_device(dev); down_write(&cls->rwsem);
if (!error) { pr_debug("device class %s: adding device %s\n",
enum_device(cls,dev); cls->name,dev->name);
interface_add(cls,dev); if (cls->add_device)
error = cls->add_device(dev);
if (!error) {
enum_device(cls,dev);
interface_add(cls,dev);
}
/* notify userspace (call /sbin/hotplug) here */
up_write(&cls->rwsem);
if (error)
put_devclass(cls);
} }
} }
return error; return error;
...@@ -70,40 +96,74 @@ int devclass_add_device(struct device * dev) ...@@ -70,40 +96,74 @@ int devclass_add_device(struct device * dev)
void devclass_remove_device(struct device * dev) void devclass_remove_device(struct device * dev)
{ {
struct device_class * cls = dev->driver->devclass; struct device_class * cls;
if (cls) {
pr_debug("removing device '%s' from class '%s'\n", if (dev->driver) {
dev->name,cls->name); cls = dev->driver->devclass;
interface_remove(cls,dev); if (cls) {
unenum_device(cls,dev); down_write(&cls->rwsem);
if (cls->remove_device) pr_debug("device class %s: removing device %s\n",
cls->remove_device(dev); cls->name,dev->name);
interface_remove(cls,dev);
unenum_device(cls,dev);
if (cls->remove_device)
cls->remove_device(dev);
up_write(&cls->rwsem);
put_devclass(cls);
}
} }
} }
struct device_class * get_devclass(struct device_class * cls)
{
struct device_class * ret = cls;
spin_lock(&device_lock);
if (cls && cls->present && atomic_read(&cls->refcount) > 0)
atomic_inc(&cls->refcount);
else
ret = NULL;
spin_unlock(&device_lock);
return ret;
}
void put_devclass(struct device_class * cls)
{
if (atomic_dec_and_lock(&cls->refcount,&device_lock)) {
list_del_init(&cls->node);
spin_unlock(&device_lock);
devclass_remove_dir(cls);
}
}
int devclass_register(struct device_class * cls) int devclass_register(struct device_class * cls)
{ {
INIT_LIST_HEAD(&cls->drivers); INIT_LIST_HEAD(&cls->drivers);
INIT_LIST_HEAD(&cls->intf_list); INIT_LIST_HEAD(&cls->intf_list);
init_rwsem(&cls->rwsem);
pr_debug("registering device class '%s'\n",cls->name); atomic_set(&cls->refcount,2);
cls->present = 1;
pr_debug("device class '%s': registering\n",cls->name);
spin_lock(&device_lock); spin_lock(&device_lock);
list_add_tail(&cls->node,&class_list); list_add_tail(&cls->node,&class_list);
spin_unlock(&device_lock); spin_unlock(&device_lock);
devclass_make_dir(cls); devclass_make_dir(cls);
put_devclass(cls);
return 0; return 0;
} }
void devclass_unregister(struct device_class * cls) void devclass_unregister(struct device_class * cls)
{ {
pr_debug("unregistering device class '%s'\n",cls->name);
devclass_remove_dir(cls);
spin_lock(&device_lock); spin_lock(&device_lock);
list_del_init(&class_list); cls->present = 0;
spin_unlock(&device_lock); spin_unlock(&device_lock);
pr_debug("device class '%s': unregistering\n",cls->name);
put_devclass(cls);
} }
EXPORT_SYMBOL(devclass_register); EXPORT_SYMBOL(devclass_register);
EXPORT_SYMBOL(devclass_unregister); EXPORT_SYMBOL(devclass_unregister);
EXPORT_SYMBOL(get_devclass);
EXPORT_SYMBOL(put_devclass);
...@@ -19,136 +19,12 @@ LIST_HEAD(global_device_list); ...@@ -19,136 +19,12 @@ LIST_HEAD(global_device_list);
int (*platform_notify)(struct device * dev) = NULL; int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL; int (*platform_notify_remove)(struct device * dev) = NULL;
DECLARE_MUTEX(device_sem);
spinlock_t device_lock = SPIN_LOCK_UNLOCKED; spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
#define to_dev(node) container_of(node,struct device,driver_list) #define to_dev(node) container_of(node,struct device,driver_list)
static int probe(struct device * dev, struct device_driver * drv)
{
dev->driver = drv;
return drv->probe ? drv->probe(dev) : 0;
}
static void attach(struct device * dev)
{
spin_lock(&device_lock);
list_add_tail(&dev->driver_list,&dev->driver->devices);
spin_unlock(&device_lock);
devclass_add_device(dev);
}
/**
* found_match - do actual binding of device to driver
* @dev: device
* @drv: driver
*
* We're here because the bus's match callback returned success for this
* pair. We call the driver's probe callback to verify they're really a
* match made in heaven.
*
* In the future, we may want to notify userspace of the binding. (But,
* we might not want to do it here).
*
* We may also want to create a symlink in the driver's directory to the
* device's physical directory.
*/
static int found_match(struct device * dev, struct device_driver * drv)
{
int error = 0;
if (!(error = probe(dev,get_driver(drv)))) {
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id,drv->name);
attach(dev);
} else {
put_driver(drv);
dev->driver = NULL;
}
return error;
}
/**
* device_attach - try to associated device with a driver
* @drv: current driver to try
* @data: device in disguise
*
* This function is used as a callback to bus_for_each_drv.
* It calls the bus's match callback to check if the driver supports
* the device. If so, it calls the found_match() function above to
* take care of all the details.
*/
static int do_device_attach(struct device_driver * drv, void * data)
{
struct device * dev = (struct device *)data;
int error = 0;
if (drv->bus->match && drv->bus->match(dev,drv))
error = found_match(dev,drv);
return error;
}
static int device_attach(struct device * dev)
{
int error = 0;
if (!dev->driver) {
if (dev->bus)
error = bus_for_each_drv(dev->bus,dev,do_device_attach);
} else
attach(dev);
return error;
}
static void device_detach(struct device * dev)
{
struct device_driver * drv = dev->driver;
if (drv) {
devclass_remove_device(dev);
if (drv && drv->remove)
drv->remove(dev);
dev->driver = NULL;
}
}
static int do_driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = (struct device_driver *)data;
int error = 0;
if (!dev->driver) {
if (dev->bus->match && dev->bus->match(dev,drv))
error = found_match(dev,drv);
}
return error;
}
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus,drv,do_driver_attach);
}
void driver_detach(struct device_driver * drv)
{
struct list_head * node;
struct device * prev = NULL;
spin_lock(&device_lock);
list_for_each(node,&drv->devices) {
struct device * dev = get_device_locked(to_dev(node));
if (dev) {
if (prev)
list_del_init(&prev->driver_list);
spin_unlock(&device_lock);
device_detach(dev);
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
}
}
spin_unlock(&device_lock);
}
int device_add(struct device *dev) int device_add(struct device *dev)
{ {
int error; int error;
...@@ -156,14 +32,14 @@ int device_add(struct device *dev) ...@@ -156,14 +32,14 @@ int device_add(struct device *dev)
if (!dev || !strlen(dev->bus_id)) if (!dev || !strlen(dev->bus_id))
return -EINVAL; return -EINVAL;
spin_lock(&device_lock); down(&device_sem);
dev->present = 1; dev->state = DEVICE_REGISTERED;
if (dev->parent) { if (dev->parent) {
list_add_tail(&dev->g_list,&dev->parent->g_list); list_add_tail(&dev->g_list,&dev->parent->g_list);
list_add_tail(&dev->node,&dev->parent->children); list_add_tail(&dev->node,&dev->parent->children);
} else } else
list_add_tail(&dev->g_list,&global_device_list); list_add_tail(&dev->g_list,&global_device_list);
spin_unlock(&device_lock); up(&device_sem);
pr_debug("DEV: registering device: ID = '%s', name = %s\n", pr_debug("DEV: registering device: ID = '%s', name = %s\n",
dev->bus_id, dev->name); dev->bus_id, dev->name);
...@@ -173,9 +49,6 @@ int device_add(struct device *dev) ...@@ -173,9 +49,6 @@ int device_add(struct device *dev)
bus_add_device(dev); bus_add_device(dev);
/* bind to driver */
device_attach(dev);
/* notify platform of device entry */ /* notify platform of device entry */
if (platform_notify) if (platform_notify)
platform_notify(dev); platform_notify(dev);
...@@ -183,12 +56,13 @@ int device_add(struct device *dev) ...@@ -183,12 +56,13 @@ int device_add(struct device *dev)
/* notify userspace of device entry */ /* notify userspace of device entry */
dev_hotplug(dev, "add"); dev_hotplug(dev, "add");
devclass_add_device(dev);
register_done: register_done:
if (error) { if (error) {
spin_lock(&device_lock); up(&device_sem);
list_del_init(&dev->g_list); list_del_init(&dev->g_list);
list_del_init(&dev->node); list_del_init(&dev->node);
spin_unlock(&device_lock); up(&device_sem);
} }
return error; return error;
} }
...@@ -203,6 +77,7 @@ void device_initialize(struct device *dev) ...@@ -203,6 +77,7 @@ void device_initialize(struct device *dev)
INIT_LIST_HEAD(&dev->intf_list); INIT_LIST_HEAD(&dev->intf_list);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
atomic_set(&dev->refcount,1); atomic_set(&dev->refcount,1);
dev->state = DEVICE_INITIALIZED;
if (dev->parent) if (dev->parent)
get_device(dev->parent); get_device(dev->parent);
} }
...@@ -234,22 +109,15 @@ int device_register(struct device *dev) ...@@ -234,22 +109,15 @@ int device_register(struct device *dev)
return error; return error;
} }
struct device * get_device_locked(struct device * dev) struct device * get_device(struct device * dev)
{ {
struct device * ret = dev; struct device * ret = dev;
if (dev && dev->present && atomic_read(&dev->refcount) > 0) down(&device_sem);
if (device_present(dev) && atomic_read(&dev->refcount) > 0)
atomic_inc(&dev->refcount); atomic_inc(&dev->refcount);
else else
ret = NULL; ret = NULL;
return ret; up(&device_sem);
}
struct device * get_device(struct device * dev)
{
struct device * ret;
spin_lock(&device_lock);
ret = get_device_locked(dev);
spin_unlock(&device_lock);
return ret; return ret;
} }
...@@ -259,34 +127,23 @@ struct device * get_device(struct device * dev) ...@@ -259,34 +127,23 @@ struct device * get_device(struct device * dev)
*/ */
void put_device(struct device * dev) void put_device(struct device * dev)
{ {
struct device * parent; down(&device_sem);
if (!atomic_dec_and_lock(&dev->refcount,&device_lock)) if (!atomic_dec_and_test(&dev->refcount)) {
up(&device_sem);
return; return;
parent = dev->parent; }
dev->parent = NULL; list_del_init(&dev->node);
spin_unlock(&device_lock); list_del_init(&dev->g_list);
up(&device_sem);
BUG_ON(dev->present); BUG_ON((dev->state != DEVICE_GONE));
if (dev->release) device_del(dev);
dev->release(dev);
if (parent)
put_device(parent);
} }
void device_del(struct device * dev) void device_del(struct device * dev)
{ {
spin_lock(&device_lock); struct device * parent = dev->parent;
dev->present = 0;
list_del_init(&dev->node);
list_del_init(&dev->g_list);
list_del_init(&dev->bus_list);
list_del_init(&dev->driver_list);
spin_unlock(&device_lock);
pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name);
/* Notify the platform of the removal, in case they /* Notify the platform of the removal, in case they
* need to do anything... * need to do anything...
...@@ -297,11 +154,16 @@ void device_del(struct device * dev) ...@@ -297,11 +154,16 @@ void device_del(struct device * dev)
/* notify userspace that this device is about to disappear */ /* notify userspace that this device is about to disappear */
dev_hotplug (dev, "remove"); dev_hotplug (dev, "remove");
device_detach(dev);
bus_remove_device(dev); bus_remove_device(dev);
/* remove the driverfs directory */ /* remove the driverfs directory */
device_remove_dir(dev); device_remove_dir(dev);
if (dev->release)
dev->release(dev);
if (parent)
put_device(parent);
} }
/** /**
...@@ -315,22 +177,15 @@ void device_del(struct device * dev) ...@@ -315,22 +177,15 @@ void device_del(struct device * dev)
*/ */
void device_unregister(struct device * dev) void device_unregister(struct device * dev)
{ {
device_del(dev); down(&device_sem);
put_device(dev); dev->state = DEVICE_GONE;
} up(&device_sem);
static int __init device_init(void)
{
int error;
error = init_driverfs_fs(); pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
if (error) dev->bus_id,dev->name);
panic("DEV: could not initialize driverfs"); put_device(dev);
return 0;
} }
core_initcall(device_init);
EXPORT_SYMBOL(device_register); EXPORT_SYMBOL(device_register);
EXPORT_SYMBOL(device_unregister); EXPORT_SYMBOL(device_unregister);
EXPORT_SYMBOL(get_device); EXPORT_SYMBOL(get_device);
......
...@@ -16,29 +16,61 @@ int driver_for_each_dev(struct device_driver * drv, void * data, ...@@ -16,29 +16,61 @@ int driver_for_each_dev(struct device_driver * drv, void * data,
int (*callback)(struct device *, void * )) int (*callback)(struct device *, void * ))
{ {
struct list_head * node; struct list_head * node;
struct device * prev = NULL;
int error = 0; int error = 0;
get_driver(drv); drv = get_driver(drv);
spin_lock(&device_lock); if (drv) {
list_for_each(node,&drv->devices) { down_read(&drv->bus->rwsem);
struct device * dev = get_device_locked(to_dev(node)); list_for_each(node,&drv->devices) {
if (dev) { struct device * dev = get_device(to_dev(node));
spin_unlock(&device_lock); if (dev) {
error = callback(dev,data); error = callback(dev,data);
if (prev) put_device(dev);
put_device(prev); if (error)
prev = dev; break;
spin_lock(&device_lock); }
if (error)
break;
} }
up_read(&drv->bus->rwsem);
put_driver(drv);
} }
spin_unlock(&device_lock);
put_driver(drv);
return error; return error;
} }
struct device_driver * get_driver(struct device_driver * drv)
{
struct device_driver * ret = drv;
spin_lock(&device_lock);
if (drv && drv->present && atomic_read(&drv->refcount) > 0)
atomic_inc(&drv->refcount);
else
ret = NULL;
spin_unlock(&device_lock);
return ret;
}
void remove_driver(struct device_driver * drv)
{
BUG();
}
/**
* put_driver - decrement driver's refcount and clean up if necessary
* @drv: driver in question
*/
void put_driver(struct device_driver * drv)
{
struct bus_type * bus = drv->bus;
if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return;
spin_unlock(&device_lock);
BUG_ON(drv->present);
bus_remove_driver(drv);
if (drv->release)
drv->release(drv);
put_bus(bus);
}
/** /**
* driver_register - register driver with bus * driver_register - register driver with bus
* @drv: driver to register * @drv: driver to register
...@@ -50,54 +82,29 @@ int driver_register(struct device_driver * drv) ...@@ -50,54 +82,29 @@ int driver_register(struct device_driver * drv)
if (!drv->bus) if (!drv->bus)
return -EINVAL; return -EINVAL;
pr_debug("Registering driver '%s' with bus '%s'\n",drv->name,drv->bus->name); pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name);
get_bus(drv->bus); get_bus(drv->bus);
atomic_set(&drv->refcount,2); atomic_set(&drv->refcount,2);
rwlock_init(&drv->lock); rwlock_init(&drv->lock);
INIT_LIST_HEAD(&drv->devices); INIT_LIST_HEAD(&drv->devices);
spin_lock(&device_lock); drv->present = 1;
list_add(&drv->bus_list,&drv->bus->drivers); bus_add_driver(drv);
spin_unlock(&device_lock);
driver_make_dir(drv);
driver_attach(drv);
put_driver(drv); put_driver(drv);
return 0; return 0;
} }
static void __remove_driver(struct device_driver * drv) void driver_unregister(struct device_driver * drv)
{
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
driver_detach(drv);
driver_remove_dir(drv);
if (drv->release)
drv->release(drv);
put_bus(drv->bus);
}
void remove_driver(struct device_driver * drv)
{ {
spin_lock(&device_lock); spin_lock(&device_lock);
atomic_set(&drv->refcount,0); drv->present = 0;
list_del_init(&drv->bus_list);
spin_unlock(&device_lock); spin_unlock(&device_lock);
__remove_driver(drv); pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name);
} put_driver(drv);
/**
* put_driver - decrement driver's refcount and clean up if necessary
* @drv: driver in question
*/
void put_driver(struct device_driver * drv)
{
if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return;
list_del_init(&drv->bus_list);
spin_unlock(&device_lock);
__remove_driver(drv);
} }
EXPORT_SYMBOL(driver_for_each_dev); EXPORT_SYMBOL(driver_for_each_dev);
EXPORT_SYMBOL(driver_register); EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(get_driver);
EXPORT_SYMBOL(put_driver); EXPORT_SYMBOL(put_driver);
EXPORT_SYMBOL(remove_driver);
...@@ -19,7 +19,7 @@ static DEVICE_ATTR(name,S_IRUGO,device_read_name,NULL); ...@@ -19,7 +19,7 @@ static DEVICE_ATTR(name,S_IRUGO,device_read_name,NULL);
static ssize_t static ssize_t
device_read_power(struct device * dev, char * page, size_t count, loff_t off) device_read_power(struct device * dev, char * page, size_t count, loff_t off)
{ {
return off ? 0 : sprintf(page,"%d\n",dev->current_state); return off ? 0 : sprintf(page,"%d\n",dev->power_state);
} }
static ssize_t static ssize_t
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
* *
*/ */
#define DEBUG 0
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include "base.h" #include "base.h"
...@@ -28,34 +30,21 @@ ...@@ -28,34 +30,21 @@
int device_suspend(u32 state, u32 level) int device_suspend(u32 state, u32 level)
{ {
struct list_head * node; struct list_head * node;
struct device * prev = NULL;
int error = 0; int error = 0;
if(level == SUSPEND_POWER_DOWN) printk(KERN_EMERG "Suspending devices\n");
printk(KERN_EMERG "Shutting down devices\n");
else
printk(KERN_EMERG "Suspending devices\n");
spin_lock(&device_lock); down(&device_sem);
list_for_each(node,&global_device_list) { list_for_each(node,&global_device_list) {
struct device * dev = get_device_locked(to_dev(node)); struct device * dev = to_dev(node);
if (dev) { if (device_present(dev) && dev->driver && dev->driver->suspend) {
spin_unlock(&device_lock); pr_debug("suspending device %s\n",dev->name);
if(dev->driver) { error = dev->driver->suspend(dev,state,level);
if(level == SUSPEND_POWER_DOWN) { if (error)
if(dev->driver->remove) printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error);
dev->driver->remove(dev);
} else if(dev->driver->suspend)
error = dev->driver->suspend(dev,state,level);
}
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
} }
} }
spin_unlock(&device_lock); up(&device_sem);
return error; return error;
} }
...@@ -70,33 +59,38 @@ int device_suspend(u32 state, u32 level) ...@@ -70,33 +59,38 @@ int device_suspend(u32 state, u32 level)
void device_resume(u32 level) void device_resume(u32 level)
{ {
struct list_head * node; struct list_head * node;
struct device * prev = NULL;
spin_lock(&device_lock); down(&device_sem);
list_for_each_prev(node,&global_device_list) { list_for_each_prev(node,&global_device_list) {
struct device * dev = get_device_locked(to_dev(node)); struct device * dev = to_dev(node);
if (dev) { if (device_present(dev) && dev->driver && dev->driver->resume) {
spin_unlock(&device_lock); pr_debug("resuming device %s\n",dev->name);
if (dev->driver && dev->driver->resume) dev->driver->resume(dev,level);
dev->driver->resume(dev,level);
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
} }
} }
spin_unlock(&device_lock); up(&device_sem);
printk(KERN_EMERG "Devices Resumed\n"); printk(KERN_EMERG "Devices Resumed\n");
} }
/** /**
* device_shutdown - call device_suspend with status set to shutdown, to * device_shutdown - call ->remove() on each device to shutdown.
* cause all devices to remove themselves cleanly
*/ */
void device_shutdown(void) void device_shutdown(void)
{ {
device_suspend(4, SUSPEND_POWER_DOWN); struct list_head * entry;
printk(KERN_EMERG "Shutting down devices\n");
down(&device_sem);
list_for_each(entry,&global_device_list) {
struct device * dev = to_dev(entry);
if (device_present(dev) && dev->driver && dev->driver->shutdown) {
pr_debug("shutting down %s\n",dev->name);
dev->driver->shutdown(dev);
}
}
up(&device_sem);
} }
EXPORT_SYMBOL(device_suspend); EXPORT_SYMBOL(device_suspend);
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
* add themselves as children of the system bus. * add themselves as children of the system bus.
*/ */
#define DEBUG 1
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -76,8 +78,8 @@ int sys_register_root(struct sys_root * root) ...@@ -76,8 +78,8 @@ int sys_register_root(struct sys_root * root)
*/ */
void sys_unegister_root(struct sys_root * root) void sys_unegister_root(struct sys_root * root)
{ {
put_device(&root->sysdev); device_unregister(&root->sysdev);
put_device(&root->dev); device_unregister(&root->dev);
} }
/** /**
......
...@@ -354,7 +354,7 @@ static int __init ps2_init(void) ...@@ -354,7 +354,7 @@ static int __init ps2_init(void)
static void __exit ps2_exit(void) static void __exit ps2_exit(void)
{ {
remove_driver(&ps2_driver.drv); driver_unregister(&ps2_driver.drv);
} }
module_init(ps2_init); module_init(ps2_init);
......
...@@ -141,7 +141,7 @@ pci_register_driver(struct pci_driver *drv) ...@@ -141,7 +141,7 @@ pci_register_driver(struct pci_driver *drv)
void void
pci_unregister_driver(struct pci_driver *drv) pci_unregister_driver(struct pci_driver *drv)
{ {
remove_driver(&drv->driver); driver_unregister(&drv->driver);
} }
static struct pci_driver pci_compat_driver = { static struct pci_driver pci_compat_driver = {
......
...@@ -297,7 +297,7 @@ static int __init sa1111_drv_pcmcia_init(void) ...@@ -297,7 +297,7 @@ static int __init sa1111_drv_pcmcia_init(void)
static void __exit sa1111_drv_pcmcia_exit(void) static void __exit sa1111_drv_pcmcia_exit(void)
{ {
remove_driver(&pcmcia_driver.drv); driver_unregister(&pcmcia_driver.drv);
} }
module_init(sa1111_drv_pcmcia_init); module_init(sa1111_drv_pcmcia_init);
......
...@@ -861,8 +861,7 @@ static int scsi_debug_release(struct Scsi_Host * hpnt) ...@@ -861,8 +861,7 @@ static int scsi_debug_release(struct Scsi_Host * hpnt)
if (++num_releases == num_present) { if (++num_releases == num_present) {
#ifdef DRIVERFS_SUPPORT #ifdef DRIVERFS_SUPPORT
do_remove_driverfs_files(); do_remove_driverfs_files();
remove_driver(&sdebug_driverfs_driver); driver_unregister(&sdebug_driverfs_driver);
// driver_unregister(&sdebug_driverfs_driver);
#endif #endif
vfree(fake_storep); vfree(fake_storep);
vfree(devInfop); vfree(devInfop);
......
...@@ -1525,7 +1525,7 @@ static void __exit exit_sd(void) ...@@ -1525,7 +1525,7 @@ static void __exit exit_sd(void)
vfree(sd_dsk_arr); vfree(sd_dsk_arr);
} }
sd_template.dev_max = 0; sd_template.dev_max = 0;
remove_driver(&sd_template.scsi_driverfs_driver); driver_unregister(&sd_template.scsi_driverfs_driver);
unregister_reboot_notifier(&sd_notifier_block); unregister_reboot_notifier(&sd_notifier_block);
} }
......
...@@ -1655,7 +1655,7 @@ exit_sg(void) ...@@ -1655,7 +1655,7 @@ exit_sg(void)
sg_dev_arr = NULL; sg_dev_arr = NULL;
} }
sg_template.dev_max = 0; sg_template.dev_max = 0;
remove_driver(&sg_template.scsi_driverfs_driver); driver_unregister(&sg_template.scsi_driverfs_driver);
} }
static int static int
......
...@@ -856,7 +856,7 @@ static void __exit exit_sr(void) ...@@ -856,7 +856,7 @@ static void __exit exit_sr(void)
kfree(scsi_CDs); kfree(scsi_CDs);
sr_template.dev_max = 0; sr_template.dev_max = 0;
remove_driver(&sr_template.scsi_driverfs_driver); driver_unregister(&sr_template.scsi_driverfs_driver);
} }
module_init(init_sr); module_init(init_sr);
......
...@@ -3992,7 +3992,7 @@ static void __exit exit_st(void) ...@@ -3992,7 +3992,7 @@ static void __exit exit_st(void)
kfree(scsi_tapes); kfree(scsi_tapes);
} }
st_template.dev_max = 0; st_template.dev_max = 0;
remove_driver(&st_template.scsi_driverfs_driver); driver_unregister(&st_template.scsi_driverfs_driver);
printk(KERN_INFO "st: Unloaded.\n"); printk(KERN_INFO "st: Unloaded.\n");
} }
......
...@@ -208,7 +208,7 @@ void usb_deregister(struct usb_driver *driver) ...@@ -208,7 +208,7 @@ void usb_deregister(struct usb_driver *driver)
{ {
info("deregistering driver %s", driver->name); info("deregistering driver %s", driver->name);
remove_driver (&driver->driver); driver_unregister (&driver->driver);
usbfs_update_special(); usbfs_update_special();
} }
...@@ -1394,7 +1394,7 @@ static void __exit usb_exit(void) ...@@ -1394,7 +1394,7 @@ static void __exit usb_exit(void)
if (nousb) if (nousb)
return; return;
remove_driver(&usb_generic_driver); driver_unregister(&usb_generic_driver);
usb_major_cleanup(); usb_major_cleanup();
usbfs_cleanup(); usbfs_cleanup();
usb_hub_cleanup(); usb_hub_cleanup();
......
...@@ -413,7 +413,7 @@ static int __init ohci_hcd_sa1111_init (void) ...@@ -413,7 +413,7 @@ static int __init ohci_hcd_sa1111_init (void)
static void __exit ohci_hcd_sa1111_cleanup (void) static void __exit ohci_hcd_sa1111_cleanup (void)
{ {
remove_driver(&ohci_hcd_sa1111_driver.drv); driver_unregister(&ohci_hcd_sa1111_driver.drv);
} }
module_init (ohci_hcd_sa1111_init); module_init (ohci_hcd_sa1111_init);
......
...@@ -509,11 +509,13 @@ static void put_mount(void) ...@@ -509,11 +509,13 @@ static void put_mount(void)
DBG("driverfs: mount_count = %d\n",mount_count); DBG("driverfs: mount_count = %d\n",mount_count);
} }
int __init init_driverfs_fs(void) static int __init driverfs_init(void)
{ {
return register_filesystem(&driverfs_fs_type); return register_filesystem(&driverfs_fs_type);
} }
core_initcall(driverfs_init);
static struct dentry * get_dentry(struct dentry * parent, const char * name) static struct dentry * get_dentry(struct dentry * parent, const char * name)
{ {
struct qstr qstr; struct qstr qstr;
......
...@@ -48,14 +48,22 @@ enum { ...@@ -48,14 +48,22 @@ enum {
RESUME_ENABLE, RESUME_ENABLE,
}; };
enum device_state {
DEVICE_UNINITIALIZED = 0,
DEVICE_INITIALIZED = 1,
DEVICE_REGISTERED = 2,
DEVICE_GONE = 3,
};
struct device; struct device;
struct device_driver; struct device_driver;
struct device_class; struct device_class;
struct bus_type { struct bus_type {
char * name; char * name;
rwlock_t lock; struct rw_semaphore rwsem;
atomic_t refcount; atomic_t refcount;
u32 present;
struct list_head node; struct list_head node;
struct list_head devices; struct list_head devices;
...@@ -73,14 +81,9 @@ struct bus_type { ...@@ -73,14 +81,9 @@ struct bus_type {
extern int bus_register(struct bus_type * bus); extern int bus_register(struct bus_type * bus);
extern void bus_unregister(struct bus_type * bus);
static inline struct bus_type * get_bus(struct bus_type * bus) extern struct bus_type * get_bus(struct bus_type * bus);
{
BUG_ON(!atomic_read(&bus->refcount));
atomic_inc(&bus->refcount);
return bus;
}
extern void put_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus);
extern int bus_for_each_dev(struct bus_type * bus, void * data, extern int bus_for_each_dev(struct bus_type * bus, void * data,
...@@ -114,6 +117,7 @@ struct device_driver { ...@@ -114,6 +117,7 @@ struct device_driver {
rwlock_t lock; rwlock_t lock;
atomic_t refcount; atomic_t refcount;
u32 present;
struct list_head bus_list; struct list_head bus_list;
struct list_head class_list; struct list_head class_list;
...@@ -123,7 +127,7 @@ struct device_driver { ...@@ -123,7 +127,7 @@ struct device_driver {
int (*probe) (struct device * dev); int (*probe) (struct device * dev);
int (*remove) (struct device * dev); int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level); int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level); int (*resume) (struct device * dev, u32 level);
...@@ -131,16 +135,10 @@ struct device_driver { ...@@ -131,16 +135,10 @@ struct device_driver {
}; };
extern int driver_register(struct device_driver * drv); extern int driver_register(struct device_driver * drv);
extern void driver_unregister(struct device_driver * drv);
static inline struct device_driver * get_driver(struct device_driver * drv) extern struct device_driver * get_driver(struct device_driver * drv);
{
BUG_ON(!atomic_read(&drv->refcount));
atomic_inc(&drv->refcount);
return drv;
}
extern void put_driver(struct device_driver * drv); extern void put_driver(struct device_driver * drv);
extern void remove_driver(struct device_driver * drv); extern void remove_driver(struct device_driver * drv);
...@@ -172,6 +170,11 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * ...@@ -172,6 +170,11 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute *
*/ */
struct device_class { struct device_class {
char * name; char * name;
struct rw_semaphore rwsem;
atomic_t refcount;
u32 present;
u32 devnum; u32 devnum;
struct list_head node; struct list_head node;
...@@ -189,6 +192,9 @@ struct device_class { ...@@ -189,6 +192,9 @@ struct device_class {
extern int devclass_register(struct device_class *); extern int devclass_register(struct device_class *);
extern void devclass_unregister(struct device_class *); extern void devclass_unregister(struct device_class *);
extern struct device_class * get_devclass(struct device_class *);
extern void put_devclass(struct device_class *);
struct devclass_attribute { struct devclass_attribute {
struct attribute attr; struct attribute attr;
...@@ -289,8 +295,8 @@ struct device { ...@@ -289,8 +295,8 @@ struct device {
void *platform_data; /* Platform specific data (e.g. ACPI, void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */ BIOS data relevant to device) */
u32 present; enum device_state state;
u32 current_state; /* Current operating state. In u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0 ACPI-speak, this is D0-D3, D0
being fully functional, and D3 being fully functional, and D3
being off. */ being off. */
...@@ -362,6 +368,11 @@ extern int (*platform_notify)(struct device * dev); ...@@ -362,6 +368,11 @@ extern int (*platform_notify)(struct device * dev);
extern int (*platform_notify_remove)(struct device * dev); extern int (*platform_notify_remove)(struct device * dev);
static inline int device_present(struct device * dev)
{
return (dev && (dev->state == DEVICE_INITIALIZED || dev->state == DEVICE_REGISTERED));
}
/* device and bus locking helpers. /* device and bus locking helpers.
* *
* FIXME: Is there anything else we need to do? * FIXME: Is there anything else we need to do?
......
...@@ -65,6 +65,4 @@ driverfs_create_symlink(struct driver_dir_entry * parent, ...@@ -65,6 +65,4 @@ driverfs_create_symlink(struct driver_dir_entry * parent,
extern void extern void
driverfs_remove_file(struct driver_dir_entry *, const char * name); driverfs_remove_file(struct driver_dir_entry *, const char * name);
extern int init_driverfs_fs(void);
#endif /* _DDFS_H_ */ #endif /* _DDFS_H_ */
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