summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@geena.pdx.osdl.net>2002-05-27 21:41:15 -0700
committerPatrick Mochel <mochel@geena.pdx.osdl.net>2002-05-27 21:41:15 -0700
commit53e61445c225e893b693c84ab85fc5f864c50ad2 (patch)
tree44f8e8b3985cd386a6252283a7995aabbab8a447
parentc43626f4822b7c6183fa864b53d3b39c2180cdae (diff)
parent593e7b23881806eaafd6b8819b3b1fc0048eb1a8 (diff)
Merge geena.pdx.osdl.net:/home/mochel/src/kernel/devel/linux-2.5-virgin
into geena.pdx.osdl.net:/home/mochel/src/kernel/devel/linux-2.5-sync
-rw-r--r--Makefile2
-rw-r--r--drivers/base/Makefile4
-rw-r--r--drivers/base/base.h5
-rw-r--r--drivers/base/bus.c125
-rw-r--r--drivers/base/core.c13
-rw-r--r--drivers/base/fs.c99
-rw-r--r--drivers/pci/pci-driver.c11
-rw-r--r--drivers/pci/probe.c1
-rw-r--r--fs/driverfs/inode.c115
-rw-r--r--include/linux/device.h32
-rw-r--r--include/linux/driverfs_fs.h5
-rw-r--r--include/linux/pci.h1
12 files changed, 400 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 10a3ce1a2d12..c985f9c2f17f 100644
--- a/Makefile
+++ b/Makefile
@@ -115,8 +115,8 @@ DRIVERS-y :=
DRIVERS-m :=
DRIVERS- :=
-DRIVERS-$(CONFIG_ACPI) += drivers/acpi/acpi.o
DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o
+DRIVERS-$(CONFIG_ACPI) += drivers/acpi/acpi.o
DRIVERS-$(CONFIG_PARPORT) += drivers/parport/driver.o
DRIVERS-y += drivers/base/base.o \
drivers/char/char.o \
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 3ff40bf56fc6..eae7bc965fcb 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -1,7 +1,7 @@
O_TARGET := base.o
-obj-y := core.o sys.o interface.o fs.o power.o
+obj-y := core.o sys.o interface.o fs.o power.o bus.o
-export-objs := core.o fs.o power.o sys.o
+export-objs := core.o fs.o power.o sys.o bus.o
include $(TOPDIR)/Rules.make
diff --git a/drivers/base/base.h b/drivers/base/base.h
index a8b3a708a207..d00e85db3924 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -9,6 +9,11 @@
extern struct device device_root;
extern spinlock_t device_lock;
+extern int bus_add_device(struct device * dev);
+extern void bus_remove_device(struct device * dev);
+
+extern int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent);
extern int device_make_dir(struct device * dev);
extern void device_remove_dir(struct device * dev);
+extern int device_bus_link(struct device * dev);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
new file mode 100644
index 000000000000..0159a32a59db
--- /dev/null
+++ b/drivers/base/bus.c
@@ -0,0 +1,125 @@
+/*
+ * bus.c - bus driver management
+ *
+ * Copyright (c) 2002 Patrick Mochel
+ * 2002 Open Source Development Lab
+ *
+ *
+ */
+
+#define DEBUG 0
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include "base.h"
+
+static LIST_HEAD(bus_driver_list);
+
+static struct driver_dir_entry bus_dir = {
+ name: "bus",
+ mode: (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
+};
+
+/**
+ * bus_add_device - add device to bus
+ * @dev: device being added
+ *
+ * Add the device to its bus's list of devices.
+ * Create a symlink in the bus's 'devices' directory to the
+ * device's physical location.
+ * Try and bind the device to a driver.
+ */
+int bus_add_device(struct device * dev)
+{
+ if (dev->bus) {
+ pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name);
+ get_bus(dev->bus);
+ write_lock(&dev->bus->lock);
+ list_add_tail(&dev->bus_list,&dev->bus->devices);
+ write_unlock(&dev->bus->lock);
+ device_bus_link(dev);
+ }
+ return 0;
+}
+
+/**
+ * bus_remove_device - remove device from bus
+ * @dev: device to be removed
+ *
+ * Remove symlink from bus's directory.
+ * Delete device from bus's list.
+ */
+void bus_remove_device(struct device * dev)
+{
+ if (dev->bus) {
+ driverfs_remove_file(&dev->bus->device_dir,dev->bus_id);
+ write_lock(&dev->bus->lock);
+ list_del_init(&dev->bus_list);
+ write_unlock(&dev->bus->lock);
+ put_bus(dev->bus);
+ }
+}
+
+static int bus_make_dir(struct bus_type * bus)
+{
+ int error;
+ bus->dir.name = bus->name;
+
+ error = device_create_dir(&bus->dir,&bus_dir);
+ if (!error) {
+ bus->device_dir.name = "devices";
+ device_create_dir(&bus->device_dir,&bus->dir);
+
+ bus->driver_dir.name = "drivers";
+ device_create_dir(&bus->driver_dir,&bus->dir);
+ }
+ return error;
+}
+
+
+int bus_register(struct bus_type * bus)
+{
+ spin_lock(&device_lock);
+ rwlock_init(&bus->lock);
+ INIT_LIST_HEAD(&bus->devices);
+ INIT_LIST_HEAD(&bus->drivers);
+ list_add_tail(&bus->node,&bus_driver_list);
+ atomic_set(&bus->refcount,2);
+ spin_unlock(&device_lock);
+
+ pr_debug("bus type '%s' registered\n",bus->name);
+
+ /* give it some driverfs entities */
+ bus_make_dir(bus);
+ put_bus(bus);
+
+ return 0;
+}
+
+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);
+
+ /* remove driverfs entries */
+ driverfs_remove_dir(&bus->driver_dir);
+ driverfs_remove_dir(&bus->device_dir);
+ driverfs_remove_dir(&bus->dir);
+}
+
+static int __init bus_init(void)
+{
+ /* make 'bus' driverfs directory */
+ return driverfs_create_dir(&bus_dir,NULL);
+}
+
+core_initcall(bus_init);
+
+EXPORT_SYMBOL(bus_add_device);
+EXPORT_SYMBOL(bus_remove_device);
+EXPORT_SYMBOL(bus_register);
+EXPORT_SYMBOL(put_bus);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 93b8a0bb046a..b163f48aaa5f 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -70,6 +70,8 @@ int device_register(struct device *dev)
if ((error = device_make_dir(dev)))
goto register_done;
+ bus_add_device(dev);
+
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
@@ -96,15 +98,14 @@ void put_device(struct device * dev)
DBG("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name);
- /* remove the driverfs directory */
- device_remove_dir(dev);
-
/* Notify the platform of the removal, in case they
* need to do anything...
*/
if (platform_notify_remove)
platform_notify_remove(dev);
+ bus_remove_device(dev);
+
/* Tell the driver to clean up after itself.
* Note that we likely didn't allocate the device,
* so this is the driver's chance to free that up...
@@ -112,6 +113,12 @@ void put_device(struct device * dev)
if (dev->driver && dev->driver->remove)
dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
+ /* remove the driverfs directory */
+ device_remove_dir(dev);
+
+ if (dev->release)
+ dev->release(dev);
+
put_device(dev->parent);
}
diff --git a/drivers/base/fs.c b/drivers/base/fs.c
index 39cf1fb509f7..cb5047cbd5da 100644
--- a/drivers/base/fs.c
+++ b/drivers/base/fs.c
@@ -5,12 +5,15 @@
* 2002 Open Source Development Lab
*/
+#define DEBUG 0
+
#include <linux/device.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/stat.h>
+#include <linux/limits.h>
extern struct driver_file_entry * device_default_files[];
@@ -68,6 +71,91 @@ void device_remove_dir(struct device * dev)
driverfs_remove_dir(&dev->dir);
}
+
+static int get_devpath_length(struct device * dev)
+{
+ int length = 1;
+ struct device * parent = dev;
+
+ /* walk up the ancestors until we hit the root.
+ * Add 1 to strlen for leading '/' of each level.
+ */
+ do {
+ length += strlen(parent->bus_id) + 1;
+ parent = parent->parent;
+ } while (parent);
+ return length;
+}
+
+static void fill_devpath(struct device * dev, char * path, int length)
+{
+ struct device * parent;
+ --length;
+ for (parent = dev; parent; parent = parent->parent) {
+ int cur = strlen(parent->bus_id);
+
+ /* back up enough to print this bus id with '/' */
+ length -= cur;
+ strncpy(path + length,parent->bus_id,cur);
+ *(path + --length) = '/';
+ }
+
+ pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
+}
+
+static int create_symlink(struct driver_dir_entry * parent, char * name, char * path)
+{
+ struct driver_file_entry * entry;
+
+ entry = kmalloc(sizeof(struct driver_file_entry),GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+ entry->name = name;
+ entry->mode = S_IRUGO;
+ return driverfs_create_symlink(parent,entry,path);
+}
+
+int device_bus_link(struct device * dev)
+{
+ char * path;
+ int length;
+ int error = 0;
+
+ if (!dev->bus)
+ return 0;
+
+ length = get_devpath_length(dev);
+
+ /* now add the path from the bus directory
+ * It should be '../..' (one to get to the 'bus' directory,
+ * and one to get to the root of the fs.
+ */
+ length += strlen("../..");
+
+ if (length > PATH_MAX)
+ return -ENAMETOOLONG;
+
+ if (!(path = kmalloc(length,GFP_KERNEL)))
+ return -ENOMEM;
+ memset(path,0,length);
+
+ /* our relative position */
+ strcpy(path,"../..");
+
+ fill_devpath(dev,path,length);
+ error = create_symlink(&dev->bus->device_dir,dev->bus_id,path);
+
+ kfree(path);
+ return error;
+}
+
+int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent)
+{
+ INIT_LIST_HEAD(&dir->files);
+ dir->mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO);
+ return driverfs_create_dir(dir,parent);
+}
+
/**
* device_make_dir - create a driverfs directory
* @name: name of directory
@@ -87,23 +175,20 @@ int device_make_dir(struct device * dev)
int error;
int i;
- INIT_LIST_HEAD(&dev->dir.files);
- dev->dir.mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO);
- dev->dir.name = dev->bus_id;
-
if (dev->parent)
parent = &dev->parent->dir;
+ dev->dir.name = dev->bus_id;
- if ((error = driverfs_create_dir(&dev->dir,parent)))
+ if ((error = device_create_dir(&dev->dir,parent)))
return error;
for (i = 0; (entry = *(device_default_files + i)); i++) {
if ((error = device_create_file(dev,entry))) {
device_remove_dir(dev);
- return error;
+ break;
}
}
- return 0;
+ return error;
}
EXPORT_SYMBOL(device_create_file);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 702542470631..8ff363949d68 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -164,6 +164,17 @@ struct device_driver pci_device_driver = {
resume: pci_device_resume,
};
+struct bus_type pci_bus_type = {
+ name: "pci",
+};
+
+static int __init pci_driver_init(void)
+{
+ return bus_register(&pci_bus_type);
+}
+
+subsys_initcall(pci_driver_init);
+
EXPORT_SYMBOL(pci_match_device);
EXPORT_SYMBOL(pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index f56278fbbbf9..a105a7b862a2 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -513,6 +513,7 @@ unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus)
dev0.sysdata = bus->sysdata;
dev0.dev.parent = bus->dev;
dev0.dev.driver = &pci_device_driver;
+ dev0.dev.bus = &pci_bus_type;
/* Go find them, Rover! */
for (devfn = 0; devfn < 0x100; devfn += 8) {
diff --git a/fs/driverfs/inode.c b/fs/driverfs/inode.c
index 5de6cd337e74..dacf2dec5cbb 100644
--- a/fs/driverfs/inode.c
+++ b/fs/driverfs/inode.c
@@ -53,11 +53,49 @@ static struct file_operations driverfs_file_operations;
static struct inode_operations driverfs_dir_inode_operations;
static struct dentry_operations driverfs_dentry_dir_ops;
static struct dentry_operations driverfs_dentry_file_ops;
+static struct address_space_operations driverfs_aops;
static struct vfsmount *driverfs_mount;
static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED;
static int mount_count = 0;
+
+static int driverfs_readpage(struct file *file, struct page * page)
+{
+ if (!PageUptodate(page)) {
+ memset(kmap(page), 0, PAGE_CACHE_SIZE);
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ }
+ unlock_page(page);
+ return 0;
+}
+
+static int driverfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ void *addr = kmap(page);
+ if (!PageUptodate(page)) {
+ memset(addr, 0, PAGE_CACHE_SIZE);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ }
+ SetPageDirty(page);
+ return 0;
+}
+
+static int driverfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+
+ kunmap(page);
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ return 0;
+}
+
+
struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev)
{
struct inode *inode = new_inode(sb);
@@ -70,6 +108,7 @@ struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev)
inode->i_blocks = 0;
inode->i_rdev = NODEV;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_mapping->a_ops = &driverfs_aops;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
@@ -81,6 +120,9 @@ struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev)
inode->i_op = &driverfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
break;
+ case S_IFLNK:
+ inode->i_op = &page_symlink_inode_operations;
+ break;
}
}
return inode;
@@ -121,6 +163,24 @@ static int driverfs_create(struct inode *dir, struct dentry *dentry, int mode)
return res;
}
+static int driverfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
+{
+ struct inode *inode;
+ int error = -ENOSPC;
+
+ inode = driverfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
+ if (inode) {
+ int l = strlen(symname)+1;
+ error = block_symlink(inode, symname, l);
+ if (!error) {
+ d_instantiate(dentry, inode);
+ dget(dentry);
+ } else
+ iput(inode);
+ }
+ return error;
+}
+
static inline int driverfs_positive(struct dentry *dentry)
{
return (dentry->d_inode && !d_unhashed(dentry));
@@ -364,10 +424,18 @@ static struct inode_operations driverfs_dir_inode_operations = {
create: driverfs_create,
lookup: simple_lookup,
unlink: driverfs_unlink,
+ symlink: driverfs_symlink,
mkdir: driverfs_mkdir,
rmdir: driverfs_rmdir,
};
+static struct address_space_operations driverfs_aops = {
+ readpage: driverfs_readpage,
+ writepage: fail_writepage,
+ prepare_write: driverfs_prepare_write,
+ commit_write: driverfs_commit_write
+};
+
static struct dentry_operations driverfs_dentry_file_ops = {
d_delete: driverfs_d_delete_file,
};
@@ -569,6 +637,52 @@ driverfs_create_file(struct driver_file_entry * entry,
}
/**
+ * driverfs_create_symlink - make a symlink
+ * @parent: directory we're creating in
+ * @entry: entry describing link
+ * @target: place we're symlinking to
+ *
+ */
+int driverfs_create_symlink(struct driver_dir_entry * parent,
+ struct driver_file_entry * entry,
+ char * target)
+{
+ struct dentry * dentry;
+ struct qstr qstr;
+ int error = 0;
+
+ if (!entry || !parent)
+ return -EINVAL;
+
+ get_mount();
+
+ if (!parent->dentry) {
+ put_mount();
+ return -EINVAL;
+ }
+ down(&parent->dentry->d_inode->i_sem);
+ qstr.name = entry->name;
+ qstr.len = strlen(entry->name);
+ qstr.hash = full_name_hash(entry->name,qstr.len);
+ dentry = lookup_hash(&qstr,parent->dentry);
+ if (!IS_ERR(dentry)) {
+ dentry->d_fsdata = (void *)entry;
+ error = vfs_symlink(parent->dentry->d_inode,dentry,target);
+ if (!error) {
+ dentry->d_inode->u.generic_ip = (void *)entry;
+ entry->dentry = dentry;
+ entry->parent = parent;
+ list_add_tail(&entry->node,&parent->files);
+ }
+ } else
+ error = PTR_ERR(dentry);
+ up(&parent->dentry->d_inode->i_sem);
+ if (error)
+ put_mount();
+ return error;
+}
+
+/**
* driverfs_remove_file - exported file removal
* @dir: directory the file supposedly resides in
* @name: name of the file
@@ -641,6 +755,7 @@ void driverfs_remove_dir(struct driver_dir_entry * dir)
}
EXPORT_SYMBOL(driverfs_create_file);
+EXPORT_SYMBOL(driverfs_create_symlink);
EXPORT_SYMBOL(driverfs_create_dir);
EXPORT_SYMBOL(driverfs_remove_file);
EXPORT_SYMBOL(driverfs_remove_dir);
diff --git a/include/linux/device.h b/include/linux/device.h
index 96d9cb108139..078c65e02370 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -55,6 +55,34 @@ enum {
struct device;
+
+struct bus_type {
+ char * name;
+ rwlock_t lock;
+ atomic_t refcount;
+
+ list_t node;
+ list_t devices;
+ list_t drivers;
+
+ struct driver_dir_entry dir;
+ struct driver_dir_entry device_dir;
+ struct driver_dir_entry driver_dir;
+};
+
+
+extern int bus_register(struct bus_type * bus);
+
+static inline 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);
+
+
struct device_driver {
int (*probe) (struct device * dev);
int (*remove) (struct device * dev, u32 flags);
@@ -66,6 +94,7 @@ struct device_driver {
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 children;
struct device * parent;
@@ -78,6 +107,7 @@ struct device {
atomic_t refcount; /* refcount to make sure the device
* persists for the right amount of time */
+ struct bus_type * bus; /* type of bus device is on */
struct driver_dir_entry dir;
struct device_driver *driver; /* which driver has allocated this
@@ -92,6 +122,8 @@ struct device {
being off. */
unsigned char *saved_state; /* saved device state */
+
+ void (*release)(struct device * dev);
};
static inline struct device *
diff --git a/include/linux/driverfs_fs.h b/include/linux/driverfs_fs.h
index 48a773dcfb9a..f9cc70450e6b 100644
--- a/include/linux/driverfs_fs.h
+++ b/include/linux/driverfs_fs.h
@@ -56,6 +56,11 @@ extern int
driverfs_create_file(struct driver_file_entry * entry,
struct driver_dir_entry * parent);
+extern int
+driverfs_create_symlink(struct driver_dir_entry * parent,
+ struct driver_file_entry * entry,
+ char * target);
+
extern void
driverfs_remove_file(struct driver_dir_entry *, const char * name);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d138ba50a678..f6e3a68a2a0d 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -439,6 +439,7 @@ struct pci_bus {
extern struct list_head pci_root_buses; /* list of all known PCI buses */
extern struct list_head pci_devices; /* list of all devices */
+extern struct bus_type pci_bus_type;
/*
* Error values that may be returned by PCI functions.