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--