diff options
| author | Patrick Mochel <mochel@geena.pdx.osdl.net> | 2002-05-27 21:41:15 -0700 |
|---|---|---|
| committer | Patrick Mochel <mochel@geena.pdx.osdl.net> | 2002-05-27 21:41:15 -0700 |
| commit | 53e61445c225e893b693c84ab85fc5f864c50ad2 (patch) | |
| tree | 44f8e8b3985cd386a6252283a7995aabbab8a447 | |
| parent | c43626f4822b7c6183fa864b53d3b39c2180cdae (diff) | |
| parent | 593e7b23881806eaafd6b8819b3b1fc0048eb1a8 (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-- | Makefile | 2 | ||||
| -rw-r--r-- | drivers/base/Makefile | 4 | ||||
| -rw-r--r-- | drivers/base/base.h | 5 | ||||
| -rw-r--r-- | drivers/base/bus.c | 125 | ||||
| -rw-r--r-- | drivers/base/core.c | 13 | ||||
| -rw-r--r-- | drivers/base/fs.c | 99 | ||||
| -rw-r--r-- | drivers/pci/pci-driver.c | 11 | ||||
| -rw-r--r-- | drivers/pci/probe.c | 1 | ||||
| -rw-r--r-- | fs/driverfs/inode.c | 115 | ||||
| -rw-r--r-- | include/linux/device.h | 32 | ||||
| -rw-r--r-- | include/linux/driverfs_fs.h | 5 | ||||
| -rw-r--r-- | include/linux/pci.h | 1 |
12 files changed, 400 insertions, 13 deletions
@@ -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. |
