From 3747eb8489228468dad737eb593289988ac36b62 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Thu, 23 May 2002 21:20:21 -0700 Subject: Device Model: do better cleanup on device removal - make sure driverfs directory is removed after driver is detached and platform is notified (since they might have added files to it) - Add release callback to struct device that is to be set by whoever allocated the device (e.g. the bus). This is the last thing called from put_device, so the owner of it can free the memory for the structure --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index 96d9cb108139..ccfd221d5dcd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -92,6 +92,8 @@ struct device { being off. */ unsigned char *saved_state; /* saved device state */ + + void (*release)(struct device * dev); }; static inline struct device * -- cgit v1.2.3 From cfff1d8f5243e04c7d04d36ec7e559b5c41eeb9f Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 24 May 2002 02:04:42 -0700 Subject: Introduce struct bus_type for describing types of buses Define bus_register for bus registration Define get_bus and put_bus for bus refcounting --- drivers/base/bus.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 24 ++++++++++ 2 files changed, 148 insertions(+) create mode 100644 drivers/base/bus.c (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c new file mode 100644 index 000000000000..fedeb05973c9 --- /dev/null +++ b/drivers/base/bus.c @@ -0,0 +1,124 @@ +/* + * bus.c - bus driver management + * + * Copyright (c) 2002 Patrick Mochel + * 2002 Open Source Development Lab + * + * + */ + +#include +#include +#include + + +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) { + get_bus(dev->bus); + write_lock(&dev->bus->lock); + list_add_tail(&dev->bus_list,&dev->bus->devices); + write_unlock(&dev->bus->lock); + } + 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) { + 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); + + /* tell the driver it can go away now */ + if (bus->release) + bus->release(); +} + +static int __init bus_init(void) +{ + /* make 'bus' driverfs directory */ + return driverfs_create_dir(&bus_dir,NULL); +} + +subsys_initcall(bus_init); + +EXPORT_SYMBOL(bus_for_each_dev); +EXPORT_SYMBOL(bus_add_device); +EXPORT_SYMBOL(bus_remove_device); +EXPORT_SYMBOL(bus_register); +EXPORT_SYMBOL(put_bus); diff --git a/include/linux/device.h b/include/linux/device.h index ccfd221d5dcd..970b974918d6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -55,6 +55,29 @@ enum { struct device; + +struct bus_type { + char * name; + rwlock_t lock; + atomic_t refcount; + + list_t node; + list_t devices; +}; + + +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 +89,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; -- cgit v1.2.3 From 8f1640cbd899a01f3c4be8b48d3a4cf4a910c76f Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 27 May 2002 19:58:58 -0700 Subject: deivce model: actually compile and use bus drivers --- drivers/base/Makefile | 4 ++-- drivers/base/base.h | 4 ++++ drivers/base/bus.c | 8 ++------ drivers/base/core.c | 4 ++++ drivers/base/fs.c | 18 +++++++++++------- include/linux/device.h | 6 ++++++ 6 files changed, 29 insertions(+), 15 deletions(-) (limited to 'include') 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..6602cb7c4762 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -9,6 +9,10 @@ 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); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index fedeb05973c9..2df0d1a9cf60 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -10,7 +10,8 @@ #include #include #include - +#include +#include "base.h" static LIST_HEAD(bus_driver_list); @@ -103,10 +104,6 @@ void put_bus(struct bus_type * bus) driverfs_remove_dir(&bus->driver_dir); driverfs_remove_dir(&bus->device_dir); driverfs_remove_dir(&bus->dir); - - /* tell the driver it can go away now */ - if (bus->release) - bus->release(); } static int __init bus_init(void) @@ -117,7 +114,6 @@ static int __init bus_init(void) subsys_initcall(bus_init); -EXPORT_SYMBOL(bus_for_each_dev); EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); diff --git a/drivers/base/core.c b/drivers/base/core.c index 391e0abefaa0..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); @@ -102,6 +104,8 @@ void put_device(struct device * dev) 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... diff --git a/drivers/base/fs.c b/drivers/base/fs.c index 39cf1fb509f7..4cdb9b588e1b 100644 --- a/drivers/base/fs.c +++ b/drivers/base/fs.c @@ -68,6 +68,13 @@ void device_remove_dir(struct device * dev) driverfs_remove_dir(&dev->dir); } +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 +94,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/include/linux/device.h b/include/linux/device.h index 970b974918d6..078c65e02370 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -63,6 +63,11 @@ struct bus_type { 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; }; @@ -102,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 -- cgit v1.2.3 From 1781fe6e7af9322940d3bf2383e2dba8a13a39ef Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 27 May 2002 21:21:15 -0700 Subject: PCI: define pci_bus_type and register it on startup --- Makefile | 2 +- drivers/pci/pci-driver.c | 11 +++++++++++ drivers/pci/probe.c | 1 + include/linux/pci.h | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Makefile b/Makefile index b30bf4575c4c..d067c38ba25d 100644 --- a/Makefile +++ b/Makefile @@ -104,8 +104,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/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/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. -- cgit v1.2.3 From 77f881c378bffa3748bdf8da37522349db5a73b5 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 27 May 2002 21:28:36 -0700 Subject: driverfs: add and export driverfs_create_symlink for general kernel use --- fs/driverfs/inode.c | 115 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/driverfs_fs.h | 5 ++ 2 files changed, 120 insertions(+) (limited to 'include') 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, }; @@ -568,6 +636,52 @@ driverfs_create_file(struct driver_file_entry * entry, return error; } +/** + * 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 @@ -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/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); -- cgit v1.2.3