patch-2.2.18 linux/drivers/usb/inode.c

Next file: linux/drivers/usb/input.c
Previous file: linux/drivers/usb/inits.h
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/usb/inode.c linux/drivers/usb/inode.c
@@ -0,0 +1,730 @@
+/*****************************************************************************/
+
+/*
+ *	inode.c  --  Inode/Dentry functions for the USB device file system.
+ *
+ *	Copyright (C) 2000
+ *          Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  $Id: inode.c,v 1.3 2000/01/11 13:58:25 tom Exp $
+ *
+ *  History:
+ *   0.1  04.01.2000  Created
+ */
+
+/*****************************************************************************/
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/uaccess.h>
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(superlist);
+
+struct special {
+	const char *name;
+	struct inode_operations *iops;
+	struct inode *inode;
+	struct list_head inodes;
+};
+
+static struct inode_operations usbdevfs_devices_inode_operations = {
+	default_file_ops: &usbdevfs_devices_fops,
+};
+
+static struct inode_operations usbdevfs_drivers_inode_operations = {
+	default_file_ops: &usbdevfs_drivers_fops,
+};
+
+static struct inode_operations usbdevfs_device_inode_operations = {
+	default_file_ops: &usbdevfs_device_file_operations,
+};
+
+static struct special special[] = { 
+	{ "devices", &usbdevfs_devices_inode_operations,  },
+	{ "drivers", &usbdevfs_drivers_inode_operations,  }
+};
+
+#define NRSPECIAL (sizeof(special)/sizeof(special[0]))
+
+/* --------------------------------------------------------------------- */
+
+static int dnumber(struct dentry *dentry)
+{
+	const char *name;
+	unsigned int s;
+
+	if (dentry->d_name.len != 3)
+		return -1;
+	name = dentry->d_name.name;
+	if (name[0] < '0' || name[0] > '9' ||
+	    name[1] < '0' || name[1] > '9' ||
+	    name[2] < '0' || name[2] > '9')
+		return -1;
+	s = name[0] - '0';
+	s = s * 10 + name[1] - '0';
+	s = s * 10 + name[2] - '0';
+	return s;
+}
+
+/*
+ * utility functions; should be called with the kernel lock held
+ * to protect against busses/devices appearing/disappearing
+ */
+
+static void new_dev_inode(struct usb_device *dev, struct super_block *sb)
+{
+	struct inode *inode;
+	unsigned int devnum = dev->devnum;
+	unsigned int busnum = dev->bus->busnum;
+
+	if (devnum < 1 || devnum > 127 || busnum > 255)
+		return;
+	inode = iget(sb, IDEVICE | (busnum << 8) | devnum);
+	if (!inode) {
+		printk(KERN_ERR "usbdevfs: cannot create inode for bus %u device %u\n", busnum, devnum);
+		return;
+	}
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_uid = sb->u.usbdevfs_sb.devuid;
+	inode->i_gid = sb->u.usbdevfs_sb.devgid;
+	inode->i_mode = sb->u.usbdevfs_sb.devmode | S_IFREG;
+	inode->i_op = &usbdevfs_device_inode_operations;
+	inode->i_size = sizeof(struct usb_device_descriptor);
+	inode->u.usbdev_i.p.dev = dev;
+	list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist);
+	list_add_tail(&inode->u.usbdev_i.dlist, &dev->inodes);
+}
+
+static void recurse_new_dev_inode(struct usb_device *dev, struct super_block *sb)
+{
+	unsigned int i;
+
+	if (!dev)
+		return;
+	new_dev_inode(dev, sb);
+	for (i = 0; i < dev->maxchild; i++) {
+                if (!dev->children[i])
+                        continue;
+		recurse_new_dev_inode(dev->children[i], sb);
+	}
+}
+
+static void new_bus_inode(struct usb_bus *bus, struct super_block *sb)
+{
+	struct inode *inode;
+	unsigned int busnum = bus->busnum;
+
+	if (busnum > 255)
+		return;
+	inode = iget(sb, IBUS | (busnum << 8));
+	if (!inode) {
+		printk(KERN_ERR "usbdevfs: cannot create inode for bus %u\n", busnum);
+		return;
+	}
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_uid = sb->u.usbdevfs_sb.busuid;
+	inode->i_gid = sb->u.usbdevfs_sb.busgid;
+	inode->i_mode = sb->u.usbdevfs_sb.busmode | S_IFDIR;
+	inode->i_op = &usbdevfs_bus_inode_operations;
+	inode->u.usbdev_i.p.bus = bus;
+	list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist);
+	list_add_tail(&inode->u.usbdev_i.dlist, &bus->inodes);
+}
+
+static void free_inode(struct inode *inode)
+{
+	inode->u.usbdev_i.p.bus = NULL;
+	inode->u.usbdev_i.p.dev = NULL;
+	inode->i_mode &= ~S_IRWXUGO;
+	inode->i_uid = inode->i_gid = 0;
+	inode->i_size = 0;
+	list_del(&inode->u.usbdev_i.slist);
+	INIT_LIST_HEAD(&inode->u.usbdev_i.slist);
+	list_del(&inode->u.usbdev_i.dlist);
+	INIT_LIST_HEAD(&inode->u.usbdev_i.dlist);
+	iput(inode);
+}
+
+static struct usb_bus *usbdevfs_findbus(int busnr)
+{
+        struct list_head *list;
+        struct usb_bus *bus;
+
+        for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
+                bus = list_entry(list, struct usb_bus, bus_list);
+                if (bus->busnum == busnr)
+                        return bus;
+        }
+        return NULL;
+}
+
+#if 0
+static struct usb_device *finddev(struct usb_device *dev, int devnr)
+{
+        unsigned int i;
+        struct usb_device *d2;
+
+        if (!dev)
+                return NULL;
+        if (dev->devnum == devnr)
+                return dev;
+        for (i = 0; i < dev->maxchild; i++) {
+                if (!dev->children[i])
+                        continue;
+                if ((d2 = finddev(dev->children[i], devnr)))
+                        return d2;
+        }
+        return NULL;
+}
+
+static struct usb_device *usbdevfs_finddevice(struct usb_bus *bus, int devnr)
+{
+        return finddev(bus->root_hub, devnr);
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static int usbdevfs_revalidate(struct dentry *dentry, int flags)
+{
+	struct inode *inode = dentry->d_inode;
+
+        if (!inode)
+                return 0;
+	if (ITYPE(inode->i_ino) == IBUS && !inode->u.usbdev_i.p.bus)
+		return 0;
+	if (ITYPE(inode->i_ino) == IDEVICE && !inode->u.usbdev_i.p.dev)
+		return 0;
+	return 1;
+}
+
+static struct dentry_operations usbdevfs_dentry_operations = {
+	d_revalidate:	usbdevfs_revalidate,
+};
+
+static struct dentry *usbdevfs_root_lookup(struct inode *dir, struct dentry *dentry)
+{
+	int busnr;
+	unsigned long ino = 0;
+	unsigned int i;
+	struct inode *inode;
+
+	/* sanity check */
+	if (dir->i_ino != IROOT)
+		return ERR_PTR(-EINVAL);
+	dentry->d_op = &usbdevfs_dentry_operations;
+	busnr = dnumber(dentry);
+	if (busnr >= 0 && busnr <= 255)
+		ino = IBUS | (busnr << 8);
+	if (!ino) {
+		for (i = 0; i < NRSPECIAL; i++) {
+			if (strlen(special[i].name) == dentry->d_name.len && 
+			    !strncmp(special[i].name, dentry->d_name.name, dentry->d_name.len)) {
+				ino = ISPECIAL | (i + IROOT + 1);
+				break;
+			}
+		}
+	}
+	if (!ino)
+		return ERR_PTR(-ENOENT);
+	inode = iget(dir->i_sb, ino);
+	if (!inode)
+		return ERR_PTR(-EINVAL);
+	if (inode && ITYPE(ino) == IBUS && inode->u.usbdev_i.p.bus == NULL) {
+		iput(inode);
+		inode = NULL;
+	}
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static struct dentry *usbdevfs_bus_lookup(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode;
+	int devnr;
+
+	/* sanity check */
+	if (ITYPE(dir->i_ino) != IBUS)
+		return ERR_PTR(-EINVAL);
+	dentry->d_op = &usbdevfs_dentry_operations;
+	devnr = dnumber(dentry);
+	if (devnr < 1 || devnr > 127)
+		return ERR_PTR(-ENOENT);
+	inode = iget(dir->i_sb, IDEVICE | (dir->i_ino & (0xff << 8)) | devnr);
+	if (!inode)
+		return ERR_PTR(-EINVAL);
+	if (inode && inode->u.usbdev_i.p.dev == NULL) {
+		iput(inode);
+		inode = NULL;
+	}
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	unsigned long ino = inode->i_ino;
+	struct special *spec;
+	struct list_head *list;
+	struct usb_bus *bus;
+	char numbuf[8];
+	unsigned int i;
+
+	/* sanity check */
+	if (ino != IROOT)
+		return -EINVAL;
+	i = filp->f_pos;
+	switch (i) {
+	case 0:
+		if (filldir(dirent, ".", 1, i, IROOT) < 0)
+			return 0;
+		filp->f_pos++;
+		i++;
+		/* fall through */
+
+	case 1:
+		if (filldir(dirent, "..", 2, i, IROOT) < 0)
+			return 0;
+		filp->f_pos++;
+		i++;
+		/* fall through */
+
+	default:
+		
+		while (i >= 2 && i < 2+NRSPECIAL) {
+			spec = &special[filp->f_pos-2];
+			if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT)) < 0)
+				return 0;
+			filp->f_pos++;
+			i++;
+		}
+		if (i < 2+NRSPECIAL)
+			return 0;
+		i -= 2+NRSPECIAL;
+		lock_kernel();
+		for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
+			if (i > 0) {
+				i--;
+				continue;
+			}
+			bus = list_entry(list, struct usb_bus, bus_list);
+			sprintf(numbuf, "%03d", bus->busnum);
+			if (filldir(dirent, numbuf, 3, filp->f_pos, IBUS | ((bus->busnum & 0xff) << 8)) < 0)
+				break;
+			filp->f_pos++;
+		}
+		unlock_kernel();
+		return 0;
+	}
+}
+
+static int bus_readdir(struct usb_device *dev, unsigned long ino, int pos, struct file *filp, void *dirent, filldir_t filldir)
+{
+	char numbuf[8];
+	unsigned int i;
+
+	if (!dev)
+		return pos;
+	sprintf(numbuf, "%03d", dev->devnum);
+	if (pos > 0)
+		pos--;
+	else {
+		if (filldir(dirent, numbuf, 3, filp->f_pos, ino | (dev->devnum & 0xff)) < 0)
+			return -1;
+		filp->f_pos++;
+	}
+	for (i = 0; i < dev->maxchild; i++) {
+		if (!dev->children[i])
+			continue;
+		pos = bus_readdir(dev->children[i], ino, pos, filp, dirent, filldir);
+		if (pos < 0)
+			return -1;
+	}
+	return pos;
+}
+
+static int usbdevfs_bus_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	unsigned long ino = inode->i_ino;
+	struct usb_bus *bus;
+
+	/* sanity check */
+	if (ITYPE(ino) != IBUS)
+		return -EINVAL;
+	switch ((unsigned int)filp->f_pos) {
+	case 0:
+		if (filldir(dirent, ".", 1, filp->f_pos, ino) < 0)
+			return 0;
+		filp->f_pos++;
+		/* fall through */
+
+	case 1:
+		if (filldir(dirent, "..", 2, filp->f_pos, IROOT) < 0)
+			return 0;
+		filp->f_pos++;
+		/* fall through */
+
+	default:
+		lock_kernel();
+		bus = usbdevfs_findbus(IBUSNR(ino));
+		bus_readdir(bus->root_hub, IDEVICE | ((bus->busnum & 0xff) << 8), filp->f_pos-2, filp, dirent, filldir);
+		unlock_kernel();
+		return 0;
+	}
+}
+
+static struct file_operations usbdevfs_root_file_operations = {
+	readdir: usbdevfs_root_readdir,
+};
+
+static struct inode_operations usbdevfs_root_inode_operations = {
+	default_file_ops: &usbdevfs_root_file_operations,
+	lookup: usbdevfs_root_lookup,
+};
+
+static struct file_operations usbdevfs_bus_file_operations = {
+	readdir: usbdevfs_bus_readdir,
+};
+
+static struct inode_operations usbdevfs_bus_inode_operations = {
+	default_file_ops: &usbdevfs_bus_file_operations,
+	lookup: usbdevfs_bus_lookup,
+};
+
+static void usbdevfs_read_inode(struct inode *inode)
+{
+	struct special *spec;
+
+	inode->i_ctime = inode->i_mtime = inode->i_atime = CURRENT_TIME;
+	inode->i_mode = S_IFREG;
+	inode->i_gid = inode->i_uid = 0;
+	INIT_LIST_HEAD(&inode->u.usbdev_i.dlist);
+	INIT_LIST_HEAD(&inode->u.usbdev_i.slist);
+	inode->u.usbdev_i.p.dev = NULL;
+	inode->u.usbdev_i.p.bus = NULL;
+	switch (ITYPE(inode->i_ino)) {
+	case ISPECIAL:
+		if (inode->i_ino == IROOT) {
+			inode->i_op = &usbdevfs_root_inode_operations;
+			inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+			return;
+		}
+		if (inode->i_ino <= IROOT || inode->i_ino > IROOT+NRSPECIAL)
+			return;
+		spec = &special[inode->i_ino-(IROOT+1)];
+		inode->i_op = spec->iops;
+		return;
+
+	case IDEVICE:
+		return;
+
+	case IBUS:
+		return;
+
+	default:
+		return;
+        }
+}
+
+static void usbdevfs_put_super(struct super_block *sb)
+{
+	list_del(&sb->u.usbdevfs_sb.slist);
+	INIT_LIST_HEAD(&sb->u.usbdevfs_sb.slist);
+	while (!list_empty(&sb->u.usbdevfs_sb.ilist))
+		free_inode(list_entry(sb->u.usbdevfs_sb.ilist.next, struct inode, u.usbdev_i.slist));
+	MOD_DEC_USE_COUNT;
+}
+
+static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf, int x)
+{
+        buf->f_type = USBDEVICE_SUPER_MAGIC;
+        buf->f_bsize = PAGE_SIZE/sizeof(long);   /* ??? */
+        buf->f_bfree = 0;
+        buf->f_bavail = 0;
+        buf->f_ffree = 0;
+        buf->f_namelen = NAME_MAX;
+        return 0;
+}
+
+static struct super_operations usbdevfs_sops = { 
+	read_inode:	usbdevfs_read_inode,
+	put_super:	usbdevfs_put_super,
+	statfs:		usbdevfs_statfs,
+};
+
+struct super_block *usbdevfs_read_super(struct super_block *s, void *data, int silent)
+{
+        struct inode *root_inode, *inode;
+	struct list_head *blist;
+	struct usb_bus *bus;
+	unsigned int i;
+	uid_t devuid = 0, busuid = 0, listuid = 0;
+	gid_t devgid = 0, busgid = 0, listgid = 0;
+	umode_t devmode = S_IWUSR | S_IRUGO, busmode = S_IXUGO | S_IRUGO, listmode = S_IRUGO;
+	char *curopt = NULL, *value;
+
+	MOD_INC_USE_COUNT;
+	/* parse options */
+	if (data)
+		curopt = strtok(data, ",");
+	for (; curopt; curopt = strtok(NULL, ",")) {
+		if ((value = strchr(curopt, '=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(curopt, "devuid")) {
+			if (!value || !value[0])
+				goto opterr;
+			devuid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "devgid")) {
+			if (!value || !value[0])
+				goto opterr;
+			devgid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "devmode")) {
+			if (!value || !value[0])
+				goto opterr;
+			devmode = simple_strtoul(value, &value, 0) & S_IRWXUGO;
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "busuid")) {
+			if (!value || !value[0])
+				goto opterr;
+			busuid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "busgid")) {
+			if (!value || !value[0])
+				goto opterr;
+			busgid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "busmode")) {
+			if (!value || !value[0])
+				goto opterr;
+			busmode = simple_strtoul(value, &value, 0) & S_IRWXUGO;
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "listuid")) {
+			if (!value || !value[0])
+				goto opterr;
+			listuid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "listgid")) {
+			if (!value || !value[0])
+				goto opterr;
+			listgid = simple_strtoul(value, &value, 0);
+			if (*value)
+				goto opterr;
+		}
+		if (!strcmp(curopt, "listmode")) {
+			if (!value || !value[0])
+				goto opterr;
+			listmode = simple_strtoul(value, &value, 0) & S_IRWXUGO;
+			if (*value)
+				goto opterr;
+		}
+	}
+	/* fill superblock */
+        s->s_blocksize = 1024;
+        s->s_blocksize_bits = 10;
+        s->s_magic = USBDEVICE_SUPER_MAGIC;
+        s->s_op = &usbdevfs_sops;
+	INIT_LIST_HEAD(&s->u.usbdevfs_sb.slist);
+	INIT_LIST_HEAD(&s->u.usbdevfs_sb.ilist);
+	s->u.usbdevfs_sb.devuid = devuid;
+	s->u.usbdevfs_sb.devgid = devgid;
+	s->u.usbdevfs_sb.devmode = devmode;
+	s->u.usbdevfs_sb.busuid = busuid;
+	s->u.usbdevfs_sb.busgid = busgid;
+	s->u.usbdevfs_sb.busmode = busmode;
+	root_inode = iget(s, IROOT);
+        if (!root_inode)
+                goto out_no_root;
+        s->s_root = d_alloc_root(root_inode, NULL);
+        if (!s->s_root)
+                goto out_no_root;
+	list_add_tail(&s->u.usbdevfs_sb.slist, &superlist);
+	for (i = 0; i < NRSPECIAL; i++) {
+		if (!(inode = iget(s, IROOT+1+i)))
+			continue;
+		inode->i_uid = listuid;
+		inode->i_gid = listgid;
+		inode->i_mode = listmode | S_IFREG;
+		special[i].inode = inode;
+		list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist);
+		list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes);
+	}
+	lock_kernel();
+	for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) {
+		bus = list_entry(blist, struct usb_bus, bus_list);
+		new_bus_inode(bus, s);
+		recurse_new_dev_inode(bus->root_hub, s);
+	}
+	unlock_kernel();
+        return s;
+
+ out_no_root:
+        printk("usbdevfs_read_super: get root inode failed\n");
+        iput(root_inode);
+	MOD_DEC_USE_COUNT;
+        return NULL;
+
+ opterr:
+        printk(KERN_WARNING "usbdevfs: mount parameter error\n");
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbdevfs_read_super, 0);
+
+/* --------------------------------------------------------------------- */
+
+static void update_special_inodes (void)
+{
+	int i;
+	for (i = 0; i < NRSPECIAL; i++) {
+		struct inode *inode = special[i].inode;
+		if (inode)
+			inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	}
+}
+
+
+void usbdevfs_add_bus(struct usb_bus *bus)
+{
+	struct list_head *slist;
+
+	lock_kernel();
+	for (slist = superlist.next; slist != &superlist; slist = slist->next)
+		new_bus_inode(bus, list_entry(slist, struct super_block, u.usbdevfs_sb.slist));
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+void usbdevfs_remove_bus(struct usb_bus *bus)
+{
+	lock_kernel();
+	while (!list_empty(&bus->inodes))
+		free_inode(list_entry(bus->inodes.next, struct inode, u.usbdev_i.dlist));
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+void usbdevfs_add_device(struct usb_device *dev)
+{
+	struct list_head *slist;
+
+	lock_kernel();
+	for (slist = superlist.next; slist != &superlist; slist = slist->next)
+		new_dev_inode(dev, list_entry(slist, struct super_block, u.usbdevfs_sb.slist));
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+void usbdevfs_remove_device(struct usb_device *dev)
+{
+	struct dev_state *ds;
+	struct siginfo sinfo;
+
+	lock_kernel();
+	while (!list_empty(&dev->inodes))
+		free_inode(list_entry(dev->inodes.next, struct inode, u.usbdev_i.dlist));
+	while (!list_empty(&dev->filelist)) {
+		ds = list_entry(dev->filelist.next, struct dev_state, list);
+		list_del(&ds->list);
+		INIT_LIST_HEAD(&ds->list);
+		down_write(&ds->devsem);
+		ds->dev = NULL;
+		up_write(&ds->devsem);
+		if (ds->discsignr) {
+			sinfo.si_signo = SIGPIPE;
+			sinfo.si_errno = EPIPE;
+			sinfo.si_code = SI_ASYNCIO;
+			sinfo.si_addr = ds->disccontext;
+			send_sig_info(ds->discsignr, &sinfo, ds->disctask);
+		}
+	}
+	
+	update_special_inodes();
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef CONFIG_PROC_FS		
+static struct proc_dir_entry *usbdir = NULL;
+#endif	
+
+int __init usbdevfs_init(void)
+{
+	int ret;
+
+	for (ret = 0; ret < NRSPECIAL; ret++) {
+		INIT_LIST_HEAD(&special[ret].inodes);
+	}
+	if ((ret = usb_register(&usbdevfs_driver)))
+		return ret;
+	if ((ret = register_filesystem(&usbdevice_fs_type)))
+		usb_deregister(&usbdevfs_driver);
+#ifdef CONFIG_PROC_FS		
+	/* create mount point for usbdevfs */
+	usbdir = proc_mkdir("usb", proc_bus);
+#endif	
+	return ret;
+}
+
+void __exit usbdevfs_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS	
+        if (usbdir)
+                remove_proc_entry("usb", proc_bus);
+#endif
+	usb_deregister(&usbdevfs_driver);
+	unregister_filesystem(&usbdevice_fs_type);
+}
+
+#if 0
+module_init(usbdevfs_init);
+module_exit(usbdevfs_cleanup);
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)