[LWN Logo]

Date: Fri, 07 Jan 2000 11:51:55 +0100
From: Thomas Sailer <sailer@ife.ee.ethz.ch>
To: randy.dunlap@intel.com, linux-usb@suse.com
Subject: [linux-usb] usbdevfs, against 2.3.38-1

--------------A5A6ED2D61C5E1F5DBFFD617
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

 
--------------A5A6ED2D61C5E1F5DBFFD617
Content-Type: text/plain; charset=us-ascii;
 name="usbdevfs-2.3.38-1.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="usbdevfs-2.3.38-1.diff"

diff -urN linux-orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-orig/Documentation/Configure.help	Fri Jan  7 10:37:24 2000
+++ linux/Documentation/Configure.help	Fri Jan  7 10:33:29 2000
@@ -8115,6 +8115,17 @@
   Note that you must say Y to global "/proc filesystem support" under
   Filesystems for this to work.
 
+USB device file system
+CONFIG_USB_DEVICEFS
+  This file system implements a "devices" file, that lists
+  the currently connected to your USB busses, a "drivers" file
+  that lists the USB kernel client drivers currently loaded,
+  and for every connected device a file named "xxx/yyy", where
+  xxx is the bus number and yyy the device number, that can be used
+  by userspace drivers to talk to the device.
+
+  Most users want to say Y here.
+
 DABUSB driver
 CONFIG_USB_DABUSB
   A Digital Audio Broadcasting (DAB) Receiver for USB and Linux brought
diff -urN linux-orig/Documentation/usb/CREDITS linux/Documentation/usb/CREDITS
--- linux-orig/Documentation/usb/CREDITS	Fri Jan  7 10:31:37 2000
+++ linux/Documentation/usb/CREDITS	Fri Jan  7 10:38:56 2000
@@ -4,13 +4,16 @@
 order by last name).  I'm sure this list should be longer, its
 difficult to maintain, add yourself with a patch if desired.
 
+  Georg Acher <acher@informatik.tu-muenchen.de>
   Alan Cox <alan@lxorguk.ukuu.org.uk>
   Johannes Erdfelt <jerdfelt@sventech.com>
+  Deti Fliegl <deti@fliegl.de>
   ham <ham@unsuave.com>
   Bradley M Keryan <keryan@andrew.cmu.edu>
   Paul Mackerras <paulus@cs.anu.edu.au>
   David E. Nelson <dnelson@jump.net>
   Vojtech Pavlik <vojtech@suse.cz>
+  Thomas Sailer <sailer@ife.ee.ethz.ch>
   Gregory P. Smith <greg@electricrain.com>
   Linus Torvalds <torvalds@transmeta.com>
   Roman Weissgaerber <weissg@vienna.at>
diff -urN linux-orig/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux-orig/drivers/usb/Config.in	Fri Jan  7 10:37:44 2000
+++ linux/drivers/usb/Config.in	Fri Jan  7 10:33:44 2000
@@ -17,6 +17,7 @@
    if [ "$CONFIG_PROC_FS" != "n" ]; then
       bool '  /proc/bus/usb support' CONFIG_USB_PROC
    fi
+   bool '  Preliminary USB device filesystem' CONFIG_USB_DEVICEFS
 
 comment 'USB Devices'
    dep_tristate '  USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
diff -urN linux-orig/drivers/usb/Makefile linux/drivers/usb/Makefile
--- linux-orig/drivers/usb/Makefile	Tue Jan  4 20:17:50 2000
+++ linux/drivers/usb/Makefile	Thu Jan  6 18:23:07 2000
@@ -223,12 +223,14 @@
 usb-ohci-hcd.o: ohci-hcd.o
 	$(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o
 
+COREOBJ :=
 ifeq ($(CONFIG_USB_PROC),y)
-usbcore.o: usb.o usb-debug.o usb-core.o proc_usb.o hub.o
-	$(LD) $(LD_RFLAG) -r -o $@ usb.o usb-debug.o usb-core.o proc_usb.o \
-	hub.o
-else
-usbcore.o: usb.o usb-debug.o usb-core.o hub.o
-	$(LD) $(LD_RFLAG) -r -o $@ usb.o usb-debug.o usb-core.o \
-	hub.o
+  COREOBJ += proc_usb.o
+endif
+ifeq ($(CONFIG_USB_DEVICEFS),y)
+  COREOBJ += devio.o inode.o drivers.o devices.o
 endif
+
+usbcore.o: usb.o usb-debug.o usb-core.o hub.o $(COREOBJ)
+	$(LD) $(LD_RFLAG) -r -o $@ usb.o usb-debug.o usb-core.o \
+	hub.o $(COREOBJ)
diff -urN linux-orig/drivers/usb/dabusb.h linux/drivers/usb/dabusb.h
--- linux-orig/drivers/usb/dabusb.h	Fri Jan  7 10:37:45 2000
+++ linux/drivers/usb/dabusb.h	Fri Jan  7 10:36:38 2000
@@ -14,7 +14,13 @@
 
 #ifdef __KERNEL__
 
+#ifdef MODSTR
+#undef MODSTR
+#endif
+#define MODSTR "dabusb: "
+
 typedef enum { _stopped=0, _started } driver_state_t;
+
 
 typedef struct
 {
diff -urN linux-orig/drivers/usb/devices.c linux/drivers/usb/devices.c
--- linux-orig/drivers/usb/devices.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/usb/devices.c	Thu Jan  6 18:30:56 2000
@@ -0,0 +1,539 @@
+/*
+ * devices.c
+ * (C) Copyright 1999 Randy Dunlap.
+ * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
+ * (C) Copyright 1999 Deti Fliegl (new USB architecture)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *************************************************************
+ *
+ * <mountpoint>/devices contains USB topology, device, config, class,
+ * interface, & endpoint data.
+ *
+ * I considered using /proc/bus/usb/devices/device# for each device
+ * as it is attached or detached, but I didn't like this for some
+ * reason -- maybe it's just too deep of a directory structure.
+ * I also don't like looking in multiple places to gather and view
+ * the data.  Having only one file for ./devices also prevents race
+ * conditions that could arise if a program was reading device info
+ * for devices that are being removed (unplugged).  (That is, the
+ * program may find a directory for devnum_12 then try to open it,
+ * but it was just unplugged, so the directory is now deleted.
+ * But programs would just have to be prepared for situations like
+ * this in any plug-and-play environment.)
+ *
+ * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Converted the whole proc stuff to real
+ *   read methods. Now not the whole device list needs to fit
+ *   into one page, only the device list for one bus.
+ *   Added a poll method to /proc/bus/usb/devices, to wake
+ *   up an eventual usbd
+ * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Turned into its own filesystem
+ *
+ * $Id: proc_usb.c,v 1.16 1999/12/20 11:11:10 fliegl Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>
+
+#include "usb.h"
+#include "usbdevice_fs.h"
+
+#define MAX_TOPO_LEVEL		6
+
+/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
+#define ALLOW_SERIAL_NUMBER
+
+static char *format_topo =
+/* T:  Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */
+  "T:  Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n";
+
+static char *format_string_manufacturer =
+/* S:  Manufacturer=xxxx */
+  "S:  Manufacturer=%s\n";
+
+static char *format_string_product =
+/* S:  Product=xxxx */
+  "S:  Product=%s\n";
+
+#ifdef ALLOW_SERIAL_NUMBER
+static char *format_string_serialnumber =
+/* S:  SerialNumber=xxxx */
+  "S:  SerialNumber=%s\n";
+#endif
+
+static char *format_bandwidth =
+/* B:  Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
+  "B:  Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
+  
+static char *format_device1 =
+/* D:  Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
+  "D:  Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
+
+static char *format_device2 =
+/* P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx */
+  "P:  Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
+
+static char *format_config =
+/* C:  #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
+  "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
+  
+static char *format_iface =
+/* I:  If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
+  "I:  If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
+
+static char *format_endpt =
+/* E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms */
+  "E:  Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%3dms\n";
+
+
+/*
+ * Need access to the driver and USB bus lists.
+ * extern struct list_head usb_driver_list;
+ * extern struct list_head usb_bus_list;
+ * However, these will come from functions that return ptrs to each of them.
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq);
+static unsigned int conndiscevcnt = 0;
+
+/* this struct stores the poll state for <mountpoint>/devices pollers */
+struct usb_device_status {
+	unsigned int lastev;
+};
+
+struct class_info {
+	int class;
+	char *class_name;
+};
+
+static const struct class_info clas_info[] =
+{					/* max. 5 chars. per name string */
+	{USB_CLASS_PER_INTERFACE,	">ifc"},
+	{USB_CLASS_AUDIO,		"audio"},
+	{USB_CLASS_COMM,		"comm."},
+	{USB_CLASS_HID,			"HID"},
+	{USB_CLASS_HUB,			"hub"},
+	{USB_CLASS_PRINTER,		"print"},
+	{USB_CLASS_MASS_STORAGE,	"stor."},
+	{USB_CLASS_DATA,		"data"},
+	{USB_CLASS_VENDOR_SPEC,		"vend."},
+	{-1,				"unk."}		/* leave as last */
+};
+
+/*****************************************************************/
+
+void usbdevfs_conn_disc_event(void)
+{
+	wake_up(&deviceconndiscwq);
+	conndiscevcnt++;
+}
+
+static const char *class_decode(const int class)
+{
+	int ix;
+
+	for (ix = 0; clas_info[ix].class != -1; ix++)
+		if (clas_info[ix].class == class)
+			break;
+	return (clas_info[ix].class_name);
+}
+
+static char *usb_dump_endpoint_descriptor(char *start, char *end, const struct usb_endpoint_descriptor *desc)
+{
+	char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."};
+
+	if (start > end)
+		return start;
+	start += sprintf(start, format_endpt, desc->bEndpointAddress,
+			 (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O',
+			 desc->bmAttributes, EndpointType[desc->bmAttributes & 3],
+			 desc->wMaxPacketSize, desc->bInterval);
+	return start;
+}
+
+static char *usb_dump_endpoint(char *start, char *end, const struct usb_endpoint_descriptor *endpoint)
+{
+	return usb_dump_endpoint_descriptor(start, end, endpoint);
+}
+
+static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno)
+{
+	struct usb_interface_descriptor *desc = &iface->altsetting[setno];
+
+	if (start > end)
+		return start;
+	start += sprintf(start, format_iface,
+			 desc->bInterfaceNumber,
+			 desc->bAlternateSetting,
+			 desc->bNumEndpoints,
+			 desc->bInterfaceClass,
+			 class_decode(desc->bInterfaceClass),
+			 desc->bInterfaceSubClass,
+			 desc->bInterfaceProtocol,
+			 iface->driver ? iface->driver->name : "(none)");
+	return start;
+}
+
+static char *usb_dump_interface(char *start, char *end, const struct usb_interface *iface, int setno)
+{
+	struct usb_interface_descriptor *desc = &iface->altsetting[setno];
+	int i;
+
+	start = usb_dump_interface_descriptor(start, end, iface, setno);
+	for (i = 0; i < desc->bNumEndpoints; i++) {
+		if (start > end)
+			return start;
+		start = usb_dump_endpoint(start, end, desc->endpoint + i);
+	}
+	return start;
+}
+
+/* TBD:
+ * 0. TBDs
+ * 1. marking active config and ifaces (code lists all, but should mark
+ *    which ones are active, if any)
+ * 2. add <halted> status to each endpoint line
+ */
+
+static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, const int active)
+{
+	if (start > end)
+		return start;
+	start += sprintf(start, format_config,
+			 active ? '*' : ' ',	/* mark active/actual/current cfg. */
+			 desc->bNumInterfaces,
+			 desc->bConfigurationValue,
+			 desc->bmAttributes,
+			 desc->MaxPower * 2);
+	return start;
+}
+
+static char *usb_dump_config(char *start, char *end, const struct usb_config_descriptor *config, const int active)
+{
+	int i, j;
+	struct usb_interface *interface;
+
+	if (start > end)
+		return start;
+	if (!config)		/* getting these some in 2.3.7; none in 2.3.6 */
+		return start + sprintf(start, "(null Cfg. desc.)\n");
+	start = usb_dump_config_descriptor(start, end, config, active);
+	for (i = 0; i < config->bNumInterfaces; i++) {
+		interface = config->interface + i;
+		if (!interface)
+			break;
+		for (j = 0; j < interface->num_altsetting; j++) {
+			if (start > end)
+				return start;
+			start = usb_dump_interface(start, end, interface, j);
+		}
+	}
+	return start;
+}
+
+/*
+ * Dump the different USB descriptors.
+ */
+static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc)
+{
+	if (start > end)
+		return start;
+	start += sprintf (start, format_device1,
+			  desc->bcdUSB >> 8, desc->bcdUSB & 0xff,
+			  desc->bDeviceClass,
+			  class_decode (desc->bDeviceClass),
+			  desc->bDeviceSubClass,
+			  desc->bDeviceProtocol,
+			  desc->bMaxPacketSize0,
+			  desc->bNumConfigurations);
+	if (start > end)
+		return start;
+	start += sprintf(start, format_device2,
+			 desc->idVendor, desc->idProduct,
+			 desc->bcdDevice >> 8, desc->bcdDevice & 0xff);
+	return start;
+}
+
+/*
+ * Dump the different strings that this device holds.
+ */
+static char *usb_dump_device_strings (char *start, char *end, const struct usb_device *dev)
+{
+	if (start > end)
+		return start;
+
+	if (dev->descriptor.iManufacturer) {
+		char * string = usb_string ((struct usb_device *)dev, 
+					dev->descriptor.iManufacturer);
+		if (string) {
+			start += sprintf (start, format_string_manufacturer,
+					string
+					);
+		if (start > end)
+			return start;
+								
+		}
+	}
+
+	if (dev->descriptor.iProduct) {
+		char * string = usb_string ((struct usb_device *)dev, 
+					dev->descriptor.iProduct);
+		if (string) {
+			start += sprintf (start, format_string_product,
+					string
+					);
+		if (start > end)
+			return start;
+
+		}
+	}
+
+#ifdef ALLOW_SERIAL_NUMBER
+	if (dev->descriptor.iSerialNumber) {
+		char * string = usb_string ((struct usb_device *)dev, 
+					dev->descriptor.iSerialNumber);
+		if (string) {
+			start += sprintf (start, format_string_serialnumber,
+					string
+					);
+		}
+	}
+#endif
+
+	return start;
+}
+
+static char *usb_dump_desc(char *start, char *end, const struct usb_device *dev)
+{
+	int i;
+
+	if (start > end)
+		return start;
+		
+	start = usb_dump_device_descriptor(start, end, &dev->descriptor);
+
+	if (start > end)
+		return start;
+	
+	start = usb_dump_device_strings (start, end, dev);
+	
+	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+		if (start > end)
+			return start;
+		start = usb_dump_config(start, end, dev->config + i,
+					(dev->config + i) == dev->actconfig); /* active ? */
+	}
+	return start;
+}
+
+
+#ifdef PROC_EXTRA /* TBD: may want to add this code later */
+
+static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc)
+{
+	int leng = USB_DT_HUB_NONVAR_SIZE;
+	unsigned char *ptr = (unsigned char *)desc;
+
+	if (start > end)
+		return start;
+	start += sprintf(start, "Interface:");
+	while (leng) {
+		start += sprintf(start, " %02x", *ptr);
+		ptr++; leng--;
+	}
+	start += sprintf(start, "\n");
+	return start;
+}
+
+static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index)
+{
+	if (start > end)
+		return start;
+	start += sprintf(start, "Interface:");
+	if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
+		start += sprintf(start, "%s: %s ", id, dev->stringindex[index]);
+	return start;
+}
+
+#endif /* PROC_EXTRA */
+
+/*****************************************************************/
+
+static char *usb_device_dump(char *start, char *end, const struct usb_device *usbdev,
+			     int bus, int level, int index, int count)
+{
+	int chix;
+	int cnt = 0;
+	int parent_devnum = 0;
+
+	if (level > MAX_TOPO_LEVEL)
+		return start;
+	if (usbdev->parent && usbdev->parent->devnum != -1)
+		parent_devnum = usbdev->parent->devnum;
+	/*
+	 * So the root hub's parent is 0 and any device that is
+	 * plugged into the root hub has a parent of 0.
+	 */
+	start += sprintf(start, format_topo, bus, level, parent_devnum, index, count,
+			 usbdev->devnum, usbdev->slow ? "1.5" : "12 ", usbdev->maxchild);
+	/*
+	 * level = topology-tier level;
+	 * parent_devnum = parent device number;
+	 * index = parent's connector number;
+	 * count = device count at this level
+	 */
+	/* do not dump descriptors for root hub */
+	if (usbdev->devnum >= 0)
+		start = usb_dump_desc(start, end, usbdev);
+	if (start > end)
+		return start + sprintf(start, "(truncated)\n");
+	/* Now look at all of this device's children. */
+	for (chix = 0; chix < usbdev->maxchild; chix++) {
+		if (start > end)
+			return start;
+		if (usbdev->children[chix])
+			start = usb_device_dump(start, end, usbdev->children[chix], bus, level + 1, chix, ++cnt);
+	}
+	return start;
+}
+
+static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
+{
+	struct list_head *buslist;
+	struct usb_bus *bus;
+	char *page, *end;
+	ssize_t ret = 0;
+	unsigned int pos, len;
+	int busnum = 0;
+
+	if (*ppos < 0)
+		return -EINVAL;
+	if (nbytes <= 0)
+		return 0;
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EFAULT;
+        if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+                return -ENOMEM;
+	pos = *ppos;
+	/* enumerate busses */
+	for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next, ++busnum) {
+		/* print bandwidth allocation */
+		bus = list_entry(buslist, struct usb_bus, bus_list);
+		len = sprintf(page, format_bandwidth, bus->bandwidth_allocated, FRAME_TIME_MAX_USECS_ALLOC,
+			      (100 * bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / FRAME_TIME_MAX_USECS_ALLOC,
+			      bus->bandwidth_int_reqs, bus->bandwidth_isoc_reqs);
+		end = usb_device_dump(page + len, page + (PAGE_SIZE - 100), bus->root_hub, busnum, 0, 0, 0);
+		len = end - page;
+		if (len > pos) {
+			len -= pos;
+			if (len > nbytes)
+				len = nbytes;
+			if (copy_to_user(buf, page + pos, len)) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+			nbytes -= len;
+			buf += len;
+			ret += len;
+			pos = 0;
+			*ppos += len;
+		} else
+			pos -= len;
+	}
+	free_page((unsigned long)page);
+	return ret;
+}
+
+static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct usb_device_status *st = (struct usb_device_status *)file->private_data;
+	unsigned int mask = 0;
+	
+	if (!st) {
+		st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
+		if (!st)
+			return POLLIN;
+		/*
+		 * need to prevent the module from being unloaded, since
+		 * proc_unregister does not call the release method and
+		 * we would have a memory leak
+		 */
+		st->lastev = conndiscevcnt;
+		file->private_data = st;
+		mask = POLLIN;
+	}
+	if (file->f_mode & FMODE_READ)
+                poll_wait(file, &deviceconndiscwq, wait);
+	if (st->lastev != conndiscevcnt)
+		mask |= POLLIN;
+	st->lastev = conndiscevcnt;
+	return mask;
+}
+
+static int usb_device_open(struct inode *inode, struct file *file)
+{
+        file->private_data = NULL;
+        return 0;
+}
+
+static int usb_device_release(struct inode *inode, struct file *file)
+{
+	if (file->private_data) {
+		kfree(file->private_data);
+		file->private_data = NULL;
+	}
+
+        return 0;
+}
+
+static long long usb_device_lseek(struct file * file, long long offset, int orig)
+{
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		return file->f_pos;
+
+	case 1:
+		file->f_pos += offset;
+		return file->f_pos;
+
+	case 2:
+		return -EINVAL;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+struct file_operations usbdevfs_devices_fops = {
+	usb_device_lseek,   /* lseek   */
+	usb_device_read,    /* read    */
+	NULL,               /* write   */
+	NULL,               /* readdir */
+	usb_device_poll,    /* poll    */
+	NULL,               /* ioctl   */
+	NULL,               /* mmap    */
+	usb_device_open,    /* open    */
+	NULL,               /* flush   */
+	usb_device_release, /* release */
+	NULL                /* fsync   */
+};
diff -urN linux-orig/drivers/usb/devio.c linux/drivers/usb/devio.c
--- linux-orig/drivers/usb/devio.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/usb/devio.c	Thu Jan  6 18:30:50 2000
@@ -0,0 +1,889 @@
+/*****************************************************************************/
+
+/*
+ *      devio.c  --  User space communication with USB devices.
+ *
+ *      Copyright (C) 1999-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.
+ *
+ *  This file implements the /proc/bus/usb/x/y files, where
+ *  x is the bus number and y the device number.
+ *
+ *  It allows user space programs/"drivers" to communicate directly
+ *  with USB devices without intervening kernel driver.
+ *
+ *  Revision history
+ *    22.12.1999   0.1   Initial release (split from proc_usb.c)
+ *    04.01.2000   0.2   Turned into its own filesystem
+ */
+
+/*****************************************************************************/
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/signal.h>
+#include <asm/uaccess.h>
+
+#include "usb.h"
+#include "usbdevice_fs.h"
+
+struct async {
+        struct list_head asynclist;
+        struct dev_state *ps;
+	struct task_struct *task;
+	unsigned int signr;
+	void *userbuffer;
+        void *userurb;
+        urb_t urb;
+};
+
+static long long usbdev_lseek(struct file *file, long long offset, int orig)
+{
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		return file->f_pos;
+
+	case 1:
+		file->f_pos += offset;
+		return file->f_pos;
+
+	case 2:
+		return -EINVAL;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static ssize_t usbdev_read(struct file *file, char * buf, size_t nbytes, loff_t *ppos)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	ssize_t ret = 0;
+	unsigned len;
+	loff_t pos;
+
+	pos = *ppos;
+	down_read(&ps->devsem);
+	if (!ps->dev)
+		ret = -ENODEV;
+	else if (pos < 0)
+		ret = -EINVAL;
+	else if (pos < sizeof(struct usb_device_descriptor)) {
+		len = sizeof(struct usb_device_descriptor) - pos;
+		if (len > nbytes)
+			len = nbytes;
+		if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len))
+			ret = -EFAULT;
+		else {
+			*ppos += len;
+			buf += len;
+			nbytes -= len;
+			ret += len;
+		}
+	}
+	up_read(&ps->devsem);
+	return ret;
+}
+
+extern inline unsigned int ld2(unsigned int x)
+{
+        unsigned int r = 0;
+        
+        if (x >= 0x10000) {
+                x >>= 16;
+                r += 16;
+        }
+        if (x >= 0x100) {
+                x >>= 8;
+                r += 8;
+        }
+        if (x >= 0x10) {
+                x >>= 4;
+                r += 4;
+        }
+        if (x >= 4) {
+                x >>= 2;
+                r += 2;
+        }
+        if (x >= 2)
+                r++;
+        return r;
+}
+
+/*
+ * async list handling
+ */
+
+static struct async *alloc_async(unsigned int numisoframes)
+{
+        unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t);
+        struct async *as = kmalloc(assize, GFP_KERNEL);
+        if (!as)
+                return NULL;
+        memset(as, 0, assize);
+        as->urb.number_of_packets = numisoframes;
+        return as;
+}
+
+static void free_async(struct async *as)
+{
+        if (as->urb.transfer_buffer)
+                kfree(as->urb.transfer_buffer);
+        kfree(as);
+}
+
+extern __inline__ void async_newpending(struct async *as)
+{
+        struct dev_state *ps = as->ps;
+        unsigned long flags;
+        
+        spin_lock_irqsave(&ps->lock, flags);
+        list_add_tail(&as->asynclist, &ps->async_pending);
+        spin_unlock_irqrestore(&ps->lock, flags);
+}
+
+extern __inline__ void async_removepending(struct async *as)
+{
+        struct dev_state *ps = as->ps;
+        unsigned long flags;
+        
+        spin_lock_irqsave(&ps->lock, flags);
+        list_del(&as->asynclist);
+        INIT_LIST_HEAD(&as->asynclist);
+        spin_unlock_irqrestore(&ps->lock, flags);
+}
+
+extern __inline__ struct async *async_getcompleted(struct dev_state *ps)
+{
+        unsigned long flags;
+        struct async *as = NULL;
+
+        spin_lock_irqsave(&ps->lock, flags);
+        if (!list_empty(&ps->async_completed)) {
+                as = list_entry(ps->async_completed.next, struct async, asynclist);
+                list_del(&as->asynclist);
+                INIT_LIST_HEAD(&as->asynclist);
+        }
+        spin_unlock_irqrestore(&ps->lock, flags);
+        return as;
+}
+
+extern __inline__ struct async *async_getpending(struct dev_state *ps, void *userurb)
+{
+        unsigned long flags;
+        struct async *as;
+        struct list_head *p;
+
+        spin_lock_irqsave(&ps->lock, flags);
+        for (p = ps->async_pending.next; p != &ps->async_pending; ) {
+                as = list_entry(p, struct async, asynclist);
+                p = p->next;
+                if (as->userurb != userurb)
+                        continue;
+                list_del(&as->asynclist);
+                INIT_LIST_HEAD(&as->asynclist);
+                spin_unlock_irqrestore(&ps->lock, flags);
+                return as;
+        }
+        spin_unlock_irqrestore(&ps->lock, flags);
+        return NULL;
+}
+
+static void async_completed(purb_t urb)
+{
+        struct async *as = (struct async *)urb->context;
+        struct dev_state *ps = as->ps;
+	struct siginfo sinfo;
+
+#if 0
+	printk(KERN_DEBUG "usbdevfs: async_completed: status %d errcount %d actlen %d pipe 0x%x\n", 
+	       urb->status, urb->error_count, urb->actual_length, urb->pipe);
+#endif
+        spin_lock(&ps->lock);
+        list_del(&as->asynclist);
+        list_add_tail(&as->asynclist, &ps->async_completed);
+        spin_unlock(&ps->lock);
+        wake_up(&ps->wait);
+	if (as->signr) {
+		sinfo.si_signo = as->signr;
+		sinfo.si_errno = as->urb.status;
+		sinfo.si_code = SI_ASYNCIO;
+		sinfo.si_addr = as->userurb;
+		send_sig_info(as->signr, &sinfo, as->task);
+	}
+}
+
+static void destroy_all_async(struct dev_state *ps)
+{
+        struct async *as;
+        unsigned long flags;
+
+        spin_lock_irqsave(&ps->lock, flags);
+        if (!list_empty(&ps->async_pending)) {
+                as = list_entry(ps->async_pending.next, struct async, asynclist);
+                list_del(&as->asynclist);
+                INIT_LIST_HEAD(&as->asynclist);
+                spin_unlock_irqrestore(&ps->lock, flags);
+                /* usb_unlink_urb calls the completion handler with status == USB_ST_URB_KILLED */
+                usb_unlink_urb(&as->urb);
+                spin_lock_irqsave(&ps->lock, flags);
+        }
+        spin_unlock_irqrestore(&ps->lock, flags);
+        while ((as = async_getcompleted(ps)))
+                free_async(as);
+}
+
+/*
+ * interface claiming
+ */
+
+static void *driver_probe(struct usb_device *dev, unsigned int intf)
+{
+	return NULL;
+}
+
+static void driver_disconnect(struct usb_device *dev, void *context)
+{
+	struct dev_state *ps = (struct dev_state *)context;
+
+	ps->ifclaimed = 0;
+}
+
+struct usb_driver usbdevfs_driver = {
+	"usbdevfs",
+	driver_probe,
+	driver_disconnect,
+	LIST_HEAD_INIT(usbdevfs_driver.driver_list),
+	NULL,
+	0
+};
+
+static int claimintf(struct dev_state *ps, unsigned int intf)
+{
+	struct usb_device *dev = ps->dev;
+	struct usb_interface *iface;
+	int err;
+
+	if (intf >= 8*sizeof(ps->ifclaimed) || !dev || intf >= dev->actconfig->bNumInterfaces)
+		return -EINVAL;
+	/* already claimed */
+	if (test_bit(intf, &ps->ifclaimed))
+		return 0;
+	iface = &dev->actconfig->interface[intf];
+	err = -EBUSY;
+	lock_kernel();
+	if (!usb_interface_claimed(iface)) {
+		usb_driver_claim_interface(&usbdevfs_driver, iface, ps);
+		set_bit(intf, &ps->ifclaimed);
+		err = 0;
+	}
+	unlock_kernel();
+	return err;
+}
+
+static int releaseintf(struct dev_state *ps, unsigned int intf)
+{
+	struct usb_device *dev;
+	struct usb_interface *iface;
+	int err;
+
+	if (intf >= 8*sizeof(ps->ifclaimed))
+		return -EINVAL;
+	err = -EINVAL;
+	lock_kernel();
+	dev = ps->dev;
+	if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) {
+		iface = &dev->actconfig->interface[intf];
+		usb_driver_release_interface(&usbdevfs_driver, iface);
+		err = 0;
+	}
+	unlock_kernel();
+	return err;
+}
+
+static int checkintf(struct dev_state *ps, unsigned int intf)
+{
+	if (intf >= 8*sizeof(ps->ifclaimed))
+		return -EINVAL;
+	if (test_bit(intf, &ps->ifclaimed))
+		return 0;
+	/* if not yet claimed, claim it for the driver */
+	printk(KERN_WARNING "usbdevfs: process %d (%s) did not claim interface %u before use\n",
+	       current->pid, current->comm, intf);
+	return claimintf(ps, intf);
+}
+
+static int findintfep(struct usb_device *dev, unsigned int ep)
+{
+	unsigned int i, j, e;
+        struct usb_interface *iface;
+	struct usb_interface_descriptor *alts;
+	struct usb_endpoint_descriptor *endpt;
+
+	if (ep & ~(USB_DIR_IN|0xf))
+		return -EINVAL;
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		iface = &dev->actconfig->interface[i];
+		for (j = 0; j < iface->num_altsetting; j++) {
+                        alts = &iface->altsetting[j];
+			for (e = 0; e < alts->bNumEndpoints; e++) {
+				endpt = &alts->endpoint[e];
+				if (endpt->bEndpointAddress == ep)
+					return i;
+			}
+		}
+	}
+	return -ENOENT; 
+}
+
+static int findintfif(struct usb_device *dev, unsigned int ifn)
+{
+	unsigned int i, j;
+        struct usb_interface *iface;
+	struct usb_interface_descriptor *alts;
+
+	if (ifn & ~0xff)
+		return -EINVAL;
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		iface = &dev->actconfig->interface[i];
+		for (j = 0; j < iface->num_altsetting; j++) {
+                        alts = &iface->altsetting[j];
+			if (alts->bInterfaceNumber == ifn)
+				return i;
+		}
+	}
+	return -ENOENT; 
+}
+
+/*
+ * file operations
+ */
+static int usbdev_open(struct inode *inode, struct file *file)
+{
+	struct usb_device *dev;
+	struct dev_state *ps;
+	int ret;
+
+	/* 
+	 * no locking necessary here, as both sys_open (actually filp_open)
+	 * and the hub thread have the kernel lock
+	 * (still acquire the kernel lock for safety)
+	 */
+	lock_kernel();
+	ret = -ENOENT;
+	if (ITYPE(inode->i_ino) != IDEVICE)
+		goto out;
+	dev = inode->u.usbdev_i.p.dev;
+	if (!dev)
+		goto out;
+	ret = -ENOMEM;
+	if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
+		goto out;
+	ret = 0;
+	ps->dev = dev;
+	ps->file = file;
+	spin_lock_init(&ps->lock);
+	INIT_LIST_HEAD(&ps->async_pending);
+	INIT_LIST_HEAD(&ps->async_completed);
+	init_waitqueue_head(&ps->wait);
+	init_rwsem(&ps->devsem);
+	ps->discsignr = 0;
+	ps->disctask = current;
+	ps->disccontext = NULL;
+	ps->ifclaimed = 0;
+	wmb();
+	list_add_tail(&ps->list, &dev->filelist);
+	file->private_data = ps;
+ out:
+	unlock_kernel();
+        return ret;
+}
+
+static int usbdev_release(struct inode *inode, struct file *file)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	unsigned int i;
+
+	lock_kernel();
+	list_del(&ps->list);
+	INIT_LIST_HEAD(&ps->list);
+	if (ps->dev) {
+		for (i = 0; ps->ifclaimed && i < 8*sizeof(ps->ifclaimed); i++)
+			if (test_bit(i, &ps->ifclaimed))
+				releaseintf(ps, i);
+	}
+	unlock_kernel();
+	destroy_all_async(ps);
+	kfree(ps);
+        return 0;
+}
+
+static int proc_control(struct dev_state *ps, void *arg)
+{
+	struct usb_device *dev = ps->dev;
+	struct usbdevfs_ctrltransfer ctrl;
+	unsigned int tmo;
+	unsigned char *tbuf;
+	int i, ret;
+
+	copy_from_user_ret(&ctrl, (void *)arg, sizeof(ctrl), -EFAULT);
+	switch (ctrl.requesttype & 0x1f) {
+	case USB_RECIP_ENDPOINT:
+		if ((ret = findintfep(ps->dev, ctrl.index & 0xff)) < 0)
+			return ret;
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+		break;
+
+	case USB_RECIP_INTERFACE:
+		if ((ret = findintfif(ps->dev, ctrl.index & 0xff)) < 0)
+			return ret;
+		if ((ret = checkintf(ps, ret)))
+			return ret;
+		break;
+	}
+	if (ctrl.length > PAGE_SIZE)
+		return -EINVAL;
+	if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	tmo = (ctrl.timeout * HZ + 999) / 1000;
+	if (ctrl.requesttype & 0x80) {
+		if (ctrl.length && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.length)) {
+			free_page((unsigned long)tbuf);
+			return -EINVAL;
+		}
+		i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
+				    ctrl.value, ctrl.index, tbuf, ctrl.length, tmo);
+		if ((i > 0) && ctrl.length) {
+			copy_to_user_ret(ctrl.data, tbuf, ctrl.length, -EFAULT);
+		}
+	} else {
+		if (ctrl.length) {
+			copy_from_user_ret(tbuf, ctrl.data, ctrl.length, -EFAULT);
+		}
+		i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype,
+				    ctrl.value, ctrl.index, tbuf, ctrl.length, tmo);
+	}
+	free_page((unsigned long)tbuf);
+	if (i<0) {
+		printk(KERN_DEBUG "/proc/bus/usb: USBDEVFS_CONTROL failed dev %d rqt %u rq %u len %u ret %d\n", 
+		       dev->devnum, ctrl.requesttype, ctrl.request, ctrl.length, i);
+	}
+	return i;
+}
+
+static int proc_bulk(struct dev_state *ps, void *arg)
+{
+	struct usb_device *dev = ps->dev;
+	struct usbdevfs_bulktransfer bulk;
+	unsigned int tmo, len1, pipe;
+	unsigned long len2;
+	unsigned char *tbuf;
+	int i, ret;
+
+	copy_from_user_ret(&bulk, (void *)arg, sizeof(bulk), -EFAULT);
+	if ((ret = findintfep(ps->dev, bulk.ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	if (bulk.ep & USB_DIR_IN)
+		pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f);
+	else
+		pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f);
+	if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
+		return -EINVAL;
+	len1 = bulk.len;
+	if (len1 > PAGE_SIZE)
+		len1 = PAGE_SIZE;
+	if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	tmo = (bulk.timeout * HZ + 999) / 1000;
+	if (bulk.ep & 0x80) {
+		if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) {
+			free_page((unsigned long)tbuf);
+			return -EINVAL;
+		}
+		i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+		if (!i && len2) {
+			copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT);
+		}
+	} else {
+		if (len1) {
+			copy_from_user_ret(tbuf, bulk.data, len1, -EFAULT);
+		}
+		i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+	}
+	free_page((unsigned long)tbuf);
+	if (i < 0) {
+		printk(KERN_WARNING "/proc/bus/usb: USBDEVFS_BULK failed dev %d ep 0x%x len %u ret %d\n", 
+		       dev->devnum, bulk.ep, bulk.len, i);
+		return i;
+	}
+	return len2;
+}
+
+static int proc_resetep(struct dev_state *ps, void *arg)
+{
+	unsigned int ep;
+	int ret;
+
+	get_user_ret(ep, (unsigned int *)arg, -EFAULT);
+	if ((ret = findintfep(ps->dev, ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0);
+	return 0;
+}
+
+static int proc_setintf(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_setinterface setintf;
+	int ret;
+
+	copy_from_user_ret(&setintf, arg, sizeof(setintf), -EFAULT);
+	if ((ret = findintfif(ps->dev, setintf.interface)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting))
+		return -EINVAL;
+	return 0;
+}
+
+static int proc_setconfig(struct dev_state *ps, void *arg)
+{
+	unsigned int u;
+
+	get_user_ret(u, (unsigned int *)arg, -EFAULT);
+	if (usb_set_configuration(ps->dev, u) < 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int proc_submiturb(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_urb uurb;
+	struct usbdevfs_iso_packet_desc *isopkt = NULL;
+	struct async *as;
+	unsigned int u, totlen, isofrmlen;
+	int ret;
+
+	copy_from_user_ret(&uurb, arg, sizeof(uurb), -EFAULT);
+	if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD))
+		return -EINVAL;
+	if (!uurb.buffer)
+		return -EINVAL;
+	if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX))
+		return -EINVAL;
+	if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	switch(uurb.type) {
+	case USBDEVFS_URB_TYPE_BULK:
+		uurb.number_of_packets = 0;
+		if (uurb.buffer_length > 8192)
+			return -EINVAL;
+		if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length))
+			return -EFAULT;
+		break;
+
+	case USBDEVFS_URB_TYPE_ISO:
+		/* arbitrary limit */
+		if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128)
+			return -EINVAL;
+		isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets;
+		if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
+			return -ENOMEM;
+		if (copy_from_user(isopkt, &((struct usbdevfs_urb *)arg)->iso_frame_desc, isofrmlen)) {
+			kfree(isopkt);
+			return -EFAULT;
+		}
+		for (totlen = u = 0; u < uurb.number_of_packets; u++) {
+			if (isopkt[u].length > 1023) {
+				kfree(isopkt);
+				return -EINVAL;
+			}
+			totlen += isopkt[u].length;
+		}
+		if (totlen > 8192) {
+			kfree(isopkt);
+			return -ENOMEM;
+		}
+		uurb.buffer_length = totlen;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (!(as = alloc_async(uurb.number_of_packets))) {
+		if (isopkt)
+			kfree(isopkt);
+		return -ENOMEM;
+	}
+	if (!(as->urb.transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) {
+		if (isopkt)
+			kfree(isopkt);
+		free_async(as);
+		return -ENOMEM;
+	}
+        as->urb.next = NULL;
+        as->urb.dev = ps->dev;
+        as->urb.pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN);
+        as->urb.transfer_flags = uurb.flags;
+	as->urb.transfer_buffer_length = uurb.buffer_length;
+	as->urb.start_frame = uurb.start_frame;
+	as->urb.number_of_packets = uurb.number_of_packets;
+        as->urb.context = as;
+        as->urb.complete = async_completed;
+	for (totlen = u = 0; u < uurb.number_of_packets; u++) {
+		as->urb.iso_frame_desc[u].offset = totlen;
+		as->urb.iso_frame_desc[u].length = isopkt[u].length;
+		totlen += isopkt[u].length;
+	}
+	if (isopkt)
+		kfree(isopkt);
+	as->ps = ps;
+        as->userurb = arg;
+	if (uurb.endpoint & USB_DIR_IN)
+		as->userbuffer = uurb.buffer;
+	else
+		as->userbuffer = NULL;
+	as->signr = uurb.signr;
+	as->task = current;
+	if (!(uurb.endpoint & USB_DIR_IN)) {
+		if (copy_from_user(as->urb.transfer_buffer, uurb.buffer, as->urb.transfer_buffer_length)) {
+			free_async(as);
+			return -EFAULT;
+		}
+	}
+        async_newpending(as);
+        if ((ret = usb_submit_urb(&as->urb))) {
+		printk(KERN_DEBUG "/proc/bus/usb: usb_submit_urb returned %d\n", ret);
+                async_removepending(as);
+                free_async(as);
+                return ret;
+        }
+        return 0;
+}
+
+static int proc_unlinkurb(struct dev_state *ps, void *arg)
+{
+	struct async *as;
+
+	as = async_getpending(ps, arg);
+	if (!as)
+		return -EINVAL;
+	usb_unlink_urb(&as->urb);
+	return 0;
+}
+
+static int processcompl(struct async *as)
+{
+	unsigned int i;
+
+	if (as->userbuffer)
+		if (copy_to_user(as->userbuffer, as->urb.transfer_buffer, as->urb.transfer_buffer_length))
+			return -EFAULT;
+	put_user_ret(as->urb.status, &((struct usbdevfs_urb *)as->userurb)->status, -EFAULT);
+	put_user_ret(as->urb.actual_length, &((struct usbdevfs_urb *)as->userurb)->actual_length, -EFAULT);
+	put_user_ret(as->urb.error_count, &((struct usbdevfs_urb *)as->userurb)->error_count, -EFAULT);
+	if (!(usb_pipeisoc(as->urb.pipe)))
+		return 0;
+	for (i = 0; i < as->urb.number_of_packets; i++) {
+		put_user_ret(as->urb.iso_frame_desc[i].actual_length, 
+			     &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].actual_length, 
+			     -EFAULT);
+		put_user_ret(as->urb.iso_frame_desc[i].status, 
+			     &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].status, 
+			     -EFAULT);
+	}
+	return 0;
+}
+
+static int proc_reapurb(struct dev_state *ps, void *arg)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	struct async *as = NULL;
+	void *addr;
+	int ret;
+
+	add_wait_queue(&ps->wait, &wait);
+	while (ps->dev) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if ((as = async_getcompleted(ps)))
+			break;
+		if (signal_pending(current))
+			break;
+		up_read(&ps->devsem);
+		schedule();
+		down_read(&ps->devsem);
+	}
+	remove_wait_queue(&ps->wait, &wait);
+	set_current_state(TASK_RUNNING);
+	if (as) {
+		ret = processcompl(as);
+		addr = as->userurb;
+		free_async(as);
+		if (ret)
+			return ret;
+		put_user_ret(addr, (void **)arg, -EFAULT);
+		return 0;
+	}
+	if (signal_pending(current))
+		return -EINTR;
+	return -EIO;
+}
+
+static int proc_reapurbnonblock(struct dev_state *ps, void *arg)
+{
+	struct async *as;
+	void *addr;
+	int ret;
+
+	if (!(as = async_getcompleted(ps)))
+		return -EAGAIN;
+	ret = processcompl(as);
+	addr = as->userurb;
+	free_async(as);
+	if (ret)
+		return ret;
+	put_user_ret(addr, (void **)arg, -EFAULT);
+	return 0;
+}
+
+static int proc_disconnectsignal(struct dev_state *ps, void *arg)
+{
+	struct usbdevfs_disconnectsignal ds;
+
+	copy_from_user_ret(&ds, arg, sizeof(ds), -EFAULT);
+	if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX))
+		return -EINVAL;
+	ps->discsignr = ds.signr;
+	ps->disccontext = ds.context;
+	return 0;
+}
+
+static int proc_claiminterface(struct dev_state *ps, void *arg)
+{
+	unsigned int intf;
+	int ret;
+
+	get_user_ret(intf, (unsigned int *)arg, -EFAULT);
+	if ((ret = findintfif(ps->dev, intf)) < 0)
+		return ret;
+	return claimintf(ps, ret);
+}
+
+static int proc_releaseinterface(struct dev_state *ps, void *arg)
+{
+	unsigned int intf;
+	int ret;
+
+	get_user_ret(intf, (unsigned int *)arg, -EFAULT);
+	if ((ret = findintfif(ps->dev, intf)) < 0)
+		return ret;
+	return releaseintf(ps, intf);
+}
+
+static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	int ret = -ENOIOCTLCMD;
+
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EPERM;
+	down_read(&ps->devsem);
+	if (!ps->dev) {
+		up_read(&ps->devsem);
+		return -ENODEV;
+	}
+	switch (cmd) {
+	case USBDEVFS_CONTROL:
+		ret = proc_control(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_BULK:
+		ret = proc_bulk(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_RESETEP:
+		ret = proc_resetep(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_SETINTERFACE:
+		ret = proc_setintf(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_SETCONFIGURATION:
+		ret = proc_setconfig(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_SUBMITURB:
+		ret = proc_submiturb(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_DISCARDURB:
+		ret = proc_unlinkurb(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_REAPURB:
+		ret = proc_reapurb(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_REAPURBNDELAY:
+		ret = proc_reapurbnonblock(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_DISCSIGNAL:
+		ret = proc_disconnectsignal(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_CLAIMINTERFACE:
+		ret = proc_claiminterface(ps, (void *)arg);
+		break;
+
+	case USBDEVFS_RELEASEINTERFACE:
+		ret = proc_releaseinterface(ps, (void *)arg);
+		break;
+
+	}
+	up_read(&ps->devsem);
+	return ret;
+}
+
+static struct file_operations usbdevfs_device_file_operations = {
+	usbdev_lseek,    /* lseek   */
+	usbdev_read,     /* read    */
+	NULL,            /* write   */
+	NULL,            /* readdir */
+	NULL,            /* poll    */
+	usbdev_ioctl,    /* ioctl   */
+	NULL,            /* mmap    */
+	usbdev_open,     /* open    */
+	NULL,            /* flush   */
+	usbdev_release,  /* release */
+	NULL             /* fsync   */
+};
+
+struct inode_operations usbdevfs_device_inode_operations = {
+        &usbdevfs_device_file_operations,  /* file-ops */
+};
+
diff -urN linux-orig/drivers/usb/drivers.c linux/drivers/usb/drivers.c
--- linux-orig/drivers/usb/drivers.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/usb/drivers.c	Thu Jan  6 18:31:00 2000
@@ -0,0 +1,124 @@
+/*
+ * drivers.c
+ * (C) Copyright 1999 Randy Dunlap.
+ * (C) Copyright 1999, 2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
+ * (C) Copyright 1999 Deti Fliegl (new USB architecture)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *************************************************************
+ *
+ * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Converted the whole proc stuff to real
+ *   read methods. Now not the whole device list needs to fit
+ *   into one page, only the device list for one bus.
+ *   Added a poll method to /proc/bus/usb/devices, to wake
+ *   up an eventual usbd
+ * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Turned into its own filesystem
+ *
+ * $Id: proc_usb.c,v 1.16 1999/12/20 11:11:10 fliegl Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+#include "usb.h"
+#include "usbdevice_fs.h"
+
+
+/*****************************************************************/
+
+/*
+ * Dump usb_driver_list.
+ *
+ * We now walk the list of registered USB drivers.
+ */
+static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
+{
+	struct list_head *tmp = usb_driver_list.next;
+	char *page, *start, *end;
+	ssize_t ret = 0;
+	unsigned int pos, len;
+
+	if (*ppos < 0)
+		return -EINVAL;
+	if (nbytes <= 0)
+		return 0;
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EFAULT;
+        if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+                return -ENOMEM;
+	start = page;
+	end = page + (PAGE_SIZE - 100);
+	pos = *ppos;
+	for (; tmp != &usb_driver_list; tmp = tmp->next) {
+		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+		start += sprintf (start, "%s\n", driver->name);
+		if (start > end) {
+			start += sprintf(start, "(truncated)\n");
+			break;
+		}
+	}
+	if (start == page)
+		start += sprintf(start, "(none)\n");
+	len = start - page;
+	if (len > pos) {
+		len -= pos;
+		if (len > nbytes)
+			len = nbytes;
+		ret = len;
+		if (copy_to_user(buf, page + pos, len))
+			ret = -EFAULT;
+		else
+			*ppos += len;
+	}
+	free_page((unsigned long)page);
+	return ret;
+}
+
+static long long usb_driver_lseek(struct file * file, long long offset, int orig)
+{
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		return file->f_pos;
+
+	case 1:
+		file->f_pos += offset;
+		return file->f_pos;
+
+	case 2:
+		return -EINVAL;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+struct file_operations usbdevfs_drivers_fops = {
+	usb_driver_lseek, /* lseek   */
+	usb_driver_read,  /* read    */
+	NULL,             /* write   */
+	NULL,             /* readdir */
+	NULL,             /* poll    */
+	NULL,             /* ioctl   */
+	NULL,             /* mmap    */
+	NULL,             /* open    */
+	NULL,             /* flush   */
+	NULL,             /* release */
+	NULL              /* fsync   */
+};
diff -urN linux-orig/drivers/usb/inode.c linux/drivers/usb/inode.c
--- linux-orig/drivers/usb/inode.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/usb/inode.c	Thu Jan  6 18:30:46 2000
@@ -0,0 +1,722 @@
+/*****************************************************************************/
+
+/*
+ *	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.
+ *
+ *  History:
+ *   0.1  04.01.2000  Created
+ *
+ *  $Id: ezusb.c,v 1.22 1999/12/03 15:06:28 tom Exp $
+ */
+
+/*****************************************************************************/
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "usb.h"
+#include "usbdevice_fs.h"
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(superlist);
+
+extern struct inode_operations usbdevfs_bus_inode_operations;
+
+static struct inode_operations devices_inode_operations = {
+        &usbdevfs_devices_fops
+};
+
+static struct inode_operations drivers_inode_operations = {
+        &usbdevfs_drivers_fops
+};
+
+struct special {
+	const char *name;
+	struct inode_operations *iops;
+	struct list_head inodes;
+};
+
+static struct special special[] = { 
+	{ "devices", &devices_inode_operations,  },
+	{ "drivers", &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_op = NULL;
+	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 = {
+        usbdevfs_revalidate,    /* d_revalidate */
+        NULL,                   /* d_hash */
+        NULL,                   /* d_compare */
+};
+
+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_inode(struct inode *inode)
+{
+}
+
+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 bufsiz)
+{
+        struct statfs tmp;
+
+        tmp.f_type = USBDEVICE_SUPER_MAGIC;
+        tmp.f_bsize = PAGE_SIZE/sizeof(long);   /* ??? */
+        tmp.f_blocks = 0;
+        tmp.f_bfree = 0;
+        tmp.f_bavail = 0;
+        tmp.f_files = 0;
+        tmp.f_ffree = 0;
+        tmp.f_namelen = NAME_MAX;
+        return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+static struct super_operations usbdevfs_sops = { 
+        usbdevfs_read_inode,
+        NULL,
+        usbdevfs_put_inode,
+        NULL,
+        NULL,
+        usbdevfs_put_super,
+        NULL,
+        usbdevfs_statfs,
+        NULL
+};
+
+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;
+
+	/* 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 */
+        MOD_INC_USE_COUNT;
+        lock_super(s);
+        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);
+        if (!s->s_root)
+                goto out_no_root;
+	list_add_tail(&s->u.usbdevfs_sb.slist, &superlist);
+	unlock_super(s);
+	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;
+		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);
+        s->s_dev = 0;
+        unlock_super(s);
+	MOD_DEC_USE_COUNT;
+        return NULL;
+
+ opterr:
+        printk(KERN_WARNING "usbdevfs: mount parameter error\n");
+	s->s_dev = 0;
+	return NULL;
+}
+
+static struct file_system_type usbdevice_fs_type = {
+        "usbdevfs",
+        0,
+        usbdevfs_read_super,
+        NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+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));
+	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));
+	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));
+	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);
+		}
+	}
+	unlock_kernel();
+	usbdevfs_conn_disc_event();
+}
+
+/* --------------------------------------------------------------------- */
+
+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);
+	return ret;
+}
+
+void __exit usbdevfs_cleanup(void)
+{
+	usb_deregister(&usbdevfs_driver);
+	unregister_filesystem(&usbdevice_fs_type);
+}
+
+#if 0
+module_init(usbdevfs_init);
+module_exit(usbdevfs_cleanup);
+#endif
diff -urN linux-orig/drivers/usb/proc_usb.c linux/drivers/usb/proc_usb.c
--- linux-orig/drivers/usb/proc_usb.c	Wed Dec 22 20:04:50 1999
+++ linux/drivers/usb/proc_usb.c	Thu Jan  6 18:26:03 2000
@@ -121,9 +121,6 @@
  * However, these will come from functions that return ptrs to each of them.
  */
 
-extern struct list_head *usb_driver_get_list (void);
-extern struct list_head *usb_bus_get_list (void);
-
 extern struct proc_dir_entry *proc_bus;
 
 static struct proc_dir_entry *usbdir = NULL, *driversdir = NULL;
@@ -432,7 +429,7 @@
 
 static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
 {
-	struct list_head *usb_bus_list, *buslist;
+	struct list_head *buslist;
 	struct usb_bus *bus;
 	char *page, *end;
 	ssize_t ret = 0;
@@ -447,9 +444,8 @@
 	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	pos = *ppos;
-	usb_bus_list = usb_bus_get_list();
 	/* enumerate busses */
-	for (buslist = usb_bus_list->next; buslist != usb_bus_list; buslist = buslist->next) {
+	for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) {
 		bus = list_entry(buslist, struct usb_bus, bus_list);
 		end = usb_device_dump(page, page + (PAGE_SIZE - 100), bus->root_hub, bus, 0, 0, 0);
 		len = end - page;
@@ -525,8 +521,7 @@
  */
 static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
 {
-	struct list_head *usb_driver_list = usb_driver_get_list();
-	struct list_head *tmp = usb_driver_list->next;
+	struct list_head *tmp = usb_driver_list.next;
 	char *page, *start, *end;
 	ssize_t ret = 0;
 	unsigned int pos, len;
@@ -542,7 +537,7 @@
 	start = page;
 	end = page + (PAGE_SIZE - 100);
 	pos = *ppos;
-	for (; tmp != usb_driver_list; tmp = tmp->next) {
+	for (; tmp != &usb_driver_list; tmp = tmp->next) {
 		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
 		start += sprintf (start, "%s\n", driver->name);
 		if (start > end) {
diff -urN linux-orig/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c
--- linux-orig/drivers/usb/usb-core.c	Tue Jan  4 20:17:50 2000
+++ linux/drivers/usb/usb-core.c	Thu Jan  6 18:29:39 2000
@@ -65,7 +65,8 @@
 #ifdef CONFIG_USB_PROC
 	proc_usb_cleanup ();
 #endif
-	usb_hub_cleanup();	
+        usbdevfs_cleanup();
+	usb_hub_cleanup();
 
 }
 
@@ -82,6 +83,7 @@
 #ifdef CONFIG_USB_PROC
 	proc_usb_init();
 #endif
+        usbdevfs_init();
 	usb_hub_init();
 
 #ifndef CONFIG_USB_MODULE
diff -urN linux-orig/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux-orig/drivers/usb/usb.c	Fri Jan  7 10:37:47 2000
+++ linux/drivers/usb/usb.c	Fri Jan  7 10:36:40 2000
@@ -241,6 +241,7 @@
 	bus->bandwidth_isoc_reqs = 0;
 
 	INIT_LIST_HEAD(&bus->bus_list);
+        INIT_LIST_HEAD(&bus->inodes);
 
 	return bus;
 }
@@ -269,6 +270,8 @@
 	/* Add it to the list of buses */
 	list_add(&bus->bus_list, &usb_bus_list);
 
+        usbdevfs_add_bus(bus);
+
 	printk("New USB bus registered, assigned bus number %d\n", bus->busnum);
 }
 
@@ -285,6 +288,8 @@
 
 	proc_usb_remove_bus(bus);
 
+        usbdevfs_remove_bus(bus);
+
 	clear_bit(bus->busnum, busmap.busmap);
 }
 
@@ -444,6 +449,8 @@
 	dev->bus = bus;
 	dev->parent = parent;
 	atomic_set(&dev->refcnt, 1);
+        INIT_LIST_HEAD(&dev->inodes);
+        INIT_LIST_HEAD(&dev->filelist);
 
 	dev->bus->op->allocate(dev);
 
@@ -1231,6 +1238,7 @@
 
 	/* remove /proc/bus/usb entry */
 	proc_usb_remove_device(dev);
+        usbdevfs_remove_device(dev);
 
 	/* Free up the device itself, including its device number */
 	if (dev->devnum > 0)
@@ -1682,6 +1690,7 @@
 
 	/* now that the basic setup is over, add a /proc/bus/usb entry */
 	proc_usb_add_device(dev);
+        usbdevfs_add_device(dev);
 
 	/* find drivers willing to handle this device */
 	usb_find_drivers(dev);
diff -urN linux-orig/drivers/usb/usb.h linux/drivers/usb/usb.h
--- linux-orig/drivers/usb/usb.h	Fri Jan  7 10:37:47 2000
+++ linux/drivers/usb/usb.h	Fri Jan  7 10:36:40 2000
@@ -550,6 +550,8 @@
 
 	/* procfs entry */
 	struct proc_dir_entry *proc_entry;
+        /* usbdevfs inode list */
+        struct list_head inodes;
 };
 
 #define USB_MAXCHILDREN (8)	/* This is arbitrary */
@@ -581,6 +583,9 @@
 	
 	/* procfs entry */
 	struct proc_dir_entry *proc_entry;
+        /* usbdevfs inode list */
+        struct list_head inodes;
+        struct list_head filelist;
 
 	/*
 	 * Child devices - these can be either new devices
@@ -794,6 +799,13 @@
 
 
 /*
+ * bus and driver list
+ */
+
+extern struct list_head usb_driver_list;
+extern struct list_head usb_bus_list;
+
+/*
  * procfs stuff
  */
 
@@ -808,6 +820,36 @@
 extern inline void proc_usb_add_device(struct usb_device *dev) {}
 extern inline void proc_usb_remove_device(struct usb_device *dev) {}
 #endif
+
+/*
+ * USB device fs stuff
+ */
+
+#ifdef CONFIG_USB_DEVICEFS
+
+/*
+ * these are expected to be called from the USB core/hub thread
+ * with the kernel lock held
+ */
+extern void usbdevfs_add_bus(struct usb_bus *bus);
+extern void usbdevfs_remove_bus(struct usb_bus *bus);
+extern void usbdevfs_add_device(struct usb_device *dev);
+extern void usbdevfs_remove_device(struct usb_device *dev);
+
+extern int usbdevfs_init(void);
+extern void usbdevfs_cleanup(void);
+
+#else /* CONFIG_USB_DEVICEFS */
+
+extern inline void usbdevfs_add_bus(struct usb_bus *bus) {}
+extern inline void usbdevfs_remove_bus(struct usb_bus *bus) {}
+extern inline void usbdevfs_add_device(struct usb_device *dev) {}
+extern inline void usbdevfs_remove_device(struct usb_device *dev) {}
+
+extern inline int usbdevfs_init(void) { return 0; }
+extern inline void usbdevfs_cleanup(void) { }
+
+#endif /* CONFIG_USB_DEVICEFS */
 
 #endif  /* __KERNEL__ */
 
diff -urN linux-orig/drivers/usb/usbdevice_fs.h linux/drivers/usb/usbdevice_fs.h
--- linux-orig/drivers/usb/usbdevice_fs.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/usb/usbdevice_fs.h	Thu Jan  6 18:50:18 2000
@@ -0,0 +1,168 @@
+/*****************************************************************************/
+
+/*
+ *	usbdevice_fs.h  --  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.
+ *
+ *  History:
+ *   0.1  04.01.2000  Created
+ *
+ *  $Id: ezusb.c,v 1.22 1999/12/03 15:06:28 tom Exp $
+ */
+
+/*****************************************************************************/
+
+#ifndef _LINUX_USBDEVICE_FS_H
+#define _LINUX_USBDEVICE_FS_H
+
+/* --------------------------------------------------------------------- */
+
+#define USBDEVICE_SUPER_MAGIC 0x9fa2
+
+/* usbdevfs ioctl codes */
+
+struct usbdevfs_ctrltransfer {
+	__u8 requesttype;
+	__u8 request;
+	__u16 value;
+	__u16 index;
+	__u16 length;
+	__u32 timeout;  /* in milliseconds */
+ 	void *data;
+};
+
+struct usbdevfs_bulktransfer {
+	unsigned int ep;
+	unsigned int len;
+	unsigned int timeout; /* in milliseconds */
+	void *data;
+};
+
+struct usbdevfs_setinterface {
+	unsigned int interface;
+	unsigned int altsetting;
+};
+
+struct usbdevfs_disconnectsignal {
+	unsigned int signr;
+	void *context;
+};
+
+#define USBDEVFS_URB_DISABLE_SPD           1
+#define USBDEVFS_URB_ISO_ASAP              2
+
+#define USBDEVFS_URB_TYPE_ISO		   0
+#define USBDEVFS_URB_TYPE_INTERRUPT	   1
+#define USBDEVFS_URB_TYPE_CONTROL	   2
+#define USBDEVFS_URB_TYPE_BULK		   3
+
+struct usbdevfs_iso_packet_desc {
+	unsigned int length;
+	unsigned int actual_length;
+	unsigned int status;
+};
+
+struct usbdevfs_urb {
+	unsigned char type;
+	unsigned char endpoint;
+	int status;
+	unsigned int flags;
+	void *buffer;
+	int buffer_length;
+	int actual_length;
+	int start_frame;
+	int number_of_packets;
+	int error_count;
+	unsigned int signr;  /* signal to be sent on error, -1 if none should be sent */
+	void *usercontext;
+	struct usbdevfs_iso_packet_desc iso_frame_desc[0];
+};
+
+#define USBDEVFS_CONTROL           _IOWR('U', 0, struct usbdevfs_ctrltransfer)
+#define USBDEVFS_BULK              _IOWR('U', 2, struct usbdevfs_bulktransfer)
+#define USBDEVFS_RESETEP           _IOR('U', 3, unsigned int)
+#define USBDEVFS_SETINTERFACE      _IOR('U', 4, struct usbdevfs_setinterface)
+#define USBDEVFS_SETCONFIGURATION  _IOR('U', 5, unsigned int)
+#define USBDEVFS_SUBMITURB         _IOR('U', 10, struct usbdevfs_urb)
+#define USBDEVFS_DISCARDURB        _IO('U', 11)
+#define USBDEVFS_REAPURB           _IOW('U', 12, void *)
+#define USBDEVFS_REAPURBNDELAY     _IOW('U', 13, void *)
+#define USBDEVFS_DISCSIGNAL        _IOR('U', 14, struct usbdevfs_disconnectsignal)
+#define USBDEVFS_CLAIMINTERFACE    _IOR('U', 15, unsigned int)
+#define USBDEVFS_RELEASEINTERFACE  _IOR('U', 16, unsigned int)
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/list.h>
+#include <asm/semaphore.h>
+
+/*
+ * inode number macros
+ */
+#define ITYPE(x)   ((x)&(0xf<<28))
+#define ISPECIAL   (0<<28)
+#define IBUS       (1<<28)
+#define IDEVICE    (2<<28)
+#define IBUSNR(x)  (((x)>>8)&0xff)
+#define IDEVNR(x)  ((x)&0xff)
+
+#define IROOT      1
+
+/*
+ * sigh. rwsemaphores do not (yet) work from modules
+ */
+
+#define rw_semaphore semaphore
+#define init_rwsem init_MUTEX
+#define down_read down
+#define down_write down
+#define up_read up
+#define up_write up
+
+
+struct dev_state {
+	struct list_head list;      /* state list */
+	struct rw_semaphore devsem; /* protects modifications to dev (dev == NULL indicating disconnect) */ 
+	struct usb_device *dev;
+	struct file *file;
+	spinlock_t lock;            /* protects the async urb lists */
+	struct list_head async_pending;
+	struct list_head async_completed;
+	wait_queue_head_t wait;     /* wake up if a request completed */
+	unsigned int discsignr;
+	struct task_struct *disctask;
+	void *disccontext;
+	unsigned long ifclaimed;
+};
+
+/* internal methods & data */
+extern struct usb_driver usbdevfs_driver;
+extern struct file_operations usbdevfs_drivers_fops;
+extern struct file_operations usbdevfs_devices_fops;
+extern struct inode_operations usbdevfs_device_inode_operations;
+extern void usbdevfs_conn_disc_event(void);
+
+
+#endif /* __KERNEL__ */
+
+/* --------------------------------------------------------------------- */
+#endif /* _LINUX_USBDEVICE_FS_H */
diff -urN linux-orig/include/linux/fs.h linux/include/linux/fs.h
--- linux-orig/include/linux/fs.h	Fri Jan  7 10:32:01 2000
+++ linux/include/linux/fs.h	Fri Jan  7 10:32:40 2000
@@ -277,6 +277,7 @@
 #include <linux/udf_fs_i.h>
 #include <linux/ncp_fs_i.h>
 #include <linux/proc_fs_i.h>
+#include <linux/usbdev_fs_i.h>
 
 /*
  * Attribute flags.  These should be or-ed together to figure out what
@@ -414,6 +415,7 @@
 		struct ncp_inode_info		ncpfs_i;
 		struct proc_inode_info		proc_i;
 		struct socket			socket_i;
+		struct usbdev_inode_info        usbdev_i;
 		void				*generic_ip;
 	} u;
 };
@@ -545,6 +547,7 @@
 #include <linux/bfs_fs_sb.h>
 #include <linux/udf_fs_sb.h>
 #include <linux/ncp_fs_sb.h>
+#include <linux/usbdev_fs_sb.h>
 
 extern struct list_head super_blocks;
 
@@ -592,6 +595,7 @@
 		struct bfs_sb_info	bfs_sb;
 		struct udf_sb_info	udf_sb;
 		struct ncp_sb_info	ncpfs_sb;
+		struct usbdev_sb_info   usbdevfs_sb;
 		void			*generic_sbp;
 	} u;
 	/*
diff -urN linux-orig/include/linux/usbdev_fs_i.h linux/include/linux/usbdev_fs_i.h
--- linux-orig/include/linux/usbdev_fs_i.h	Thu Jan  1 01:00:00 1970
+++ linux/include/linux/usbdev_fs_i.h	Thu Jan  6 18:21:22 2000
@@ -0,0 +1,11 @@
+struct usb_device;
+struct usb_bus;
+
+struct usbdev_inode_info {
+	struct list_head dlist;
+	struct list_head slist;
+	union {
+		struct usb_device *dev;
+		struct usb_bus *bus;
+	} p;
+};
diff -urN linux-orig/include/linux/usbdev_fs_sb.h linux/include/linux/usbdev_fs_sb.h
--- linux-orig/include/linux/usbdev_fs_sb.h	Thu Jan  1 01:00:00 1970
+++ linux/include/linux/usbdev_fs_sb.h	Thu Jan  6 18:21:22 2000
@@ -0,0 +1,10 @@
+struct usbdev_sb_info {
+	struct list_head slist;
+	struct list_head ilist;
+	uid_t devuid;
+	gid_t devgid;
+	umode_t devmode;
+	uid_t busuid;
+	gid_t busgid;
+	umode_t busmode;
+};


--------------A5A6ED2D61C5E1F5DBFFD617
Content-Type: text/plain; charset=us-ascii

-- 
To unsubscribe, e-mail: linux-usb-unsubscribe@suse.com
For additional commands, e-mail: linux-usb-help@suse.com
--------------A5A6ED2D61C5E1F5DBFFD617--