--- drivers/usb/Kconfig | 2 drivers/usb/Makefile | 2 drivers/usb/usbfs2/Kconfig | 7 drivers/usb/usbfs2/Makefile | 7 drivers/usb/usbfs2/inode.c | 485 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb.h | 2 6 files changed, 505 insertions(+) --- /dev/null +++ gregkh-2.6/drivers/usb/usbfs2/Makefile @@ -0,0 +1,7 @@ +usbfs2-objs := inode.o + +obj-$(CONFIG_USB_USBFS2) += usbfs2.o + +ifeq ($(CONFIG_USB_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif --- gregkh-2.6.orig/drivers/usb/Kconfig +++ gregkh-2.6/drivers/usb/Kconfig @@ -77,6 +77,8 @@ config USB source "drivers/usb/core/Kconfig" +source "drivers/usb/usbfs2/Kconfig" + source "drivers/usb/host/Kconfig" source "drivers/usb/ip/Kconfig" --- gregkh-2.6.orig/drivers/usb/Makefile +++ gregkh-2.6/drivers/usb/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_MON) += mon/ +obj-$(CONFIG_USB_USBFS2) += usbfs2/ + obj-$(CONFIG_PCI) += host/ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_ISP116X_HCD) += host/ --- /dev/null +++ gregkh-2.6/drivers/usb/usbfs2/Kconfig @@ -0,0 +1,7 @@ +config USB_USBFS2 + tristate "USB device filesystem - version 2" + depends on USB + ---help--- + Select this option to get the usb device file system, mark 2 + + Most users want to say Y here. --- /dev/null +++ gregkh-2.6/drivers/usb/usbfs2/inode.c @@ -0,0 +1,485 @@ +/* + * usbfs2.c - usbfs2 + * + * Copyright (C) 2006 Greg Kroah-Hartman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * Based on security/inode.c which had the following copyright notice: + * Copyright (C) 2005 Greg Kroah-Hartman + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/usb.h" + +#define USBFS2_MAGIC 0x75736266 + +static struct vfsmount *mount; +static int mount_count; + +/* + * TODO: + * I think I can get rid of these default_file_ops, but not quite sure... + */ +static ssize_t default_read_file(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t default_write_file(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return count; +} + +static int default_open(struct inode *inode, struct file *file) +{ + if (inode->u.generic_ip) + file->private_data = inode->u.generic_ip; + + return 0; +} + +static struct file_operations default_file_ops = { + .read = default_read_file, + .write = default_write_file, + .open = default_open, +}; + +static struct inode *get_inode(struct super_block *sb, int mode, dev_t dev) +{ + struct inode *inode = new_inode(sb); + + if (inode) { + inode->i_mode = mode; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + switch (mode & S_IFMT) { + default: + init_special_inode(inode, mode, dev); + break; + case S_IFREG: + inode->i_fop = &default_file_ops; + break; + case S_IFDIR: + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; + break; + } + } + return inode; +} + +/* SMP-safe */ +static int mknod(struct inode *dir, struct dentry *dentry, + int mode, dev_t dev) +{ + struct inode *inode; + int error = -EPERM; + + if (dentry->d_inode) + return -EEXIST; + + inode = get_inode(dir->i_sb, mode, dev); + if (inode) { + d_instantiate(dentry, inode); + dget(dentry); + error = 0; + } + return error; +} + +static int mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int res; + + mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR; + res = mknod(dir, dentry, mode, 0); + if (!res) + dir->i_nlink++; + return res; +} + +static int create(struct inode *dir, struct dentry *dentry, int mode) +{ + mode = (mode & S_IALLUGO) | S_IFREG; + return mknod(dir, dentry, mode, 0); +} + +static inline int positive(struct dentry *dentry) +{ + return dentry->d_inode && !d_unhashed(dentry); +} + +static int fill_super(struct super_block *sb, void *data, int silent) +{ + static struct tree_descr files[] = {{""}}; + + return simple_fill_super(sb, USBFS2_MAGIC, files); +} + +static struct super_block *get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_single(fs_type, flags, data, fill_super); +} + +static struct file_system_type fs_type = { + .owner = THIS_MODULE, + .name = "usbfs2", + .get_sb = get_sb, + .kill_sb = kill_litter_super, +}; + +static int create_by_name(const char *name, mode_t mode, + struct dentry *parent, + struct dentry **dentry) +{ + int error = 0; + + *dentry = NULL; + + /* If the parent is not specified, we create it in the root. + * We need the root dentry to do this, which is in the super + * block. A pointer to that is in the struct vfsmount that we + * have around. + */ + if (!parent ) { + if (mount && mount->mnt_sb) { + parent = mount->mnt_sb->s_root; + } + } + if (!parent) { + pr_debug("usbfs2: Ah! can not find a parent!\n"); + return -EFAULT; + } + + mutex_lock(&parent->d_inode->i_mutex); + *dentry = lookup_one_len(name, parent, strlen(name)); + if (!IS_ERR(dentry)) { + if ((mode & S_IFMT) == S_IFDIR) + error = mkdir(parent->d_inode, *dentry, mode); + else + error = create(parent->d_inode, *dentry, mode); + } else + error = PTR_ERR(dentry); + mutex_unlock(&parent->d_inode->i_mutex); + + return error; +} + +/** + * usbfs2_create_file - create a file in the usbfs2 filesystem + * + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this paramater is NULL, then the + * file will be created in the root of the usbfs2 filesystem. + * @data: a pointer to something that the caller will want to get to later + * on. The inode.u.generic_ip pointer will point to this value on + * the open() call. + * @fops: a pointer to a struct file_operations that should be used for + * this file. + * + * This is the basic "create a file" function for usbfs2. It allows for a + * wide range of flexibility in createing a file, or a directory (if you + * want to create a directory, the usbfs2_create_dir() function is + * recommended to be used instead.) + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the usbfs2_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, NULL will be returned. + * + * If usbfs2 is not enabled in the kernel, the value -ENODEV will be + * returned. It is not wise to check for this value, but rather, check for + * NULL or !NULL instead as to eliminate the need for #ifdef in the calling + * code. + */ +static struct dentry *usbfs2_create_file(const char *name, mode_t mode, + struct dentry *parent, void *data, + struct file_operations *fops) +{ + struct dentry *dentry = NULL; + int error; + + pr_debug("usbfs2: creating file '%s'\n",name); + + error = simple_pin_fs("usbfs2", &mount, &mount_count); + if (error) { + dentry = ERR_PTR(error); + goto exit; + } + + error = create_by_name(name, mode, parent, &dentry); + if (error) { + dentry = ERR_PTR(error); + simple_release_fs(&mount, &mount_count); + goto exit; + } + + if (dentry->d_inode) { + if (fops) + dentry->d_inode->i_fop = fops; + if (data) + dentry->d_inode->u.generic_ip = data; + } +exit: + return dentry; +} + +/** + * usbfs2_create_dir - create a directory in the usbfs2 filesystem + * + * @name: a pointer to a string containing the name of the directory to + * create. + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this paramater is NULL, then the + * directory will be created in the root of the usbfs2 filesystem. + * + * This function creates a directory in usbfs2 with the given name. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the usbfs2_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, NULL will be returned. + * + * If usbfs2 is not enabled in the kernel, the value -ENODEV will be + * returned. It is not wise to check for this value, but rather, check for + * NULL or !NULL instead as to eliminate the need for #ifdef in the calling + * code. + */ +static struct dentry *usbfs2_create_dir(const char *name, struct dentry *parent) +{ + return usbfs2_create_file(name, S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, + parent, NULL, NULL); +} + +/** + * usbfs2_remove - removes a file or directory from the usbfs2 filesystem + * + * @dentry: a pointer to a the dentry of the file or directory to be + * removed. + * + * This function removes a file or directory in usbfs2 that was previously + * created with a call to another usbfs2 function (like + * usbfs2_create_file() or variants thereof.) + * + * This function is required to be called in order for the file to be + * removed, no automatic cleanup of files will happen when a module is + * removed, you are responsible here. + */ +static void usbfs2_remove(struct dentry *dentry) +{ + struct dentry *parent; + + if (!dentry) + return; + + parent = dentry->d_parent; + if (!parent || !parent->d_inode) + return; + + mutex_lock(&parent->d_inode->i_mutex); + if (positive(dentry)) { + if (dentry->d_inode) { + if (S_ISDIR(dentry->d_inode->i_mode)) + simple_rmdir(parent->d_inode, dentry); + else + simple_unlink(parent->d_inode, dentry); + dput(dentry); + } + } + mutex_unlock(&parent->d_inode->i_mutex); + simple_release_fs(&mount, &mount_count); +} + + +static void add_bus(struct usb_bus *bus) +{ + struct dentry *parent; + char name[8]; + + sprintf(name, "%03d", bus->busnum); + + parent = mount->mnt_sb->s_root; + bus->usbfs2_dentry = usbfs2_create_dir(name, parent); + if (bus->usbfs2_dentry == NULL) { + dev_err(bus->controller, "error creating usbfs2 bus entry"); + return; + } +} + +static void remove_bus(struct usb_bus *bus) +{ + if (bus->usbfs2_dentry) { + usbfs2_remove(bus->usbfs2_dentry); + bus->usbfs2_dentry = NULL; + } +} + +struct ep_object { + struct usb_endpoint_descriptor *desc; + struct usb_device *udev; + struct dentry *dentry; +}; + +static ssize_t ep_read_file(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct ep_object *ep_obj = file->private_data; + dbg("endpoint %02x read from", ep_obj->desc->bEndpointAddress); + return 0; +} + +static ssize_t ep_write_file(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ep_object *ep_obj = file->private_data; + dbg("endpoint %02x written to", ep_obj->desc->bEndpointAddress); + return count; +} + +static int ep_open(struct inode *inode, struct file *file) +{ + if (inode->u.generic_ip) + file->private_data = inode->u.generic_ip; + return 0; +} + +static struct file_operations ep_file_ops = { + .read = ep_read_file, + .write = ep_write_file, + .open = ep_open, +}; + +static void add_endpoint(struct dentry *parent, struct usb_host_endpoint *endpoint, struct usb_device *udev) +{ + struct ep_object *ep_obj; + char name[8]; + + ep_obj = kzalloc(sizeof(struct ep_object), GFP_KERNEL); + if (!ep_obj) + return; + + ep_obj->desc = &endpoint->desc; + ep_obj->udev = udev; + + sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); + ep_obj->dentry = usbfs2_create_file(name, S_IRWXU | S_IRUGO | S_IXUGO, + parent, ep_obj, &ep_file_ops); +} + +static void add_device(struct usb_device *dev) +{ + char name[8]; + + if (!dev->bus->usbfs2_dentry) { + dev_err(&dev->dev, "no directory created for the device's bus\n"); + return; + } + + sprintf(name, "%03d", dev->devnum); + dev->usbfs2_dentry = usbfs2_create_dir(name, dev->bus->usbfs2_dentry); + if (dev->usbfs2_dentry == NULL) { + dev_err(&dev->dev, "error creating usbfs2 device entry"); + return; + } + add_endpoint(dev->usbfs2_dentry, &dev->ep0, dev); + +} + +static void remove_device(struct usb_device *dev) +{ + if (dev->usbfs2_dentry) { + usbfs2_remove(dev->usbfs2_dentry); + dev->usbfs2_dentry = NULL; + } +#if 0 + /* FIXME need to do something like this here */ + while (!list_empty(&dev->filelist)) { + struct dev_state *ds; + struct siginfo sinfo; + + ds = list_entry(dev->filelist.next, struct dev_state, list); + wake_up_all(&ds->wait); + list_del_init(&ds->list); + if (ds->discsignr) { + sinfo.si_signo = SIGPIPE; + sinfo.si_errno = EPIPE; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = ds->disccontext; + kill_proc_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid); + } + } +#endif +} + +static int usbfs2_notify(struct notifier_block *self, unsigned long action, + void *dev) +{ + switch (action) { + case USB_DEVICE_ADD: + add_device(dev); + break; + case USB_DEVICE_REMOVE: + remove_device(dev); + break; + case USB_BUS_ADD: + add_bus(dev); + break; + case USB_BUS_REMOVE: + remove_bus(dev); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block usbfs2_nb = { + .notifier_call = usbfs2_notify, +}; + + +static int __init usbfs2_init(void) +{ + int retval; + + retval = register_filesystem(&fs_type); + if (retval) + goto exit; + + usb_register_notify(&usbfs2_nb); + +exit: + return retval; +} + +static void __exit usbfs2_exit(void) +{ + usb_unregister_notify(&usbfs2_nb); + simple_release_fs(&mount, &mount_count); + unregister_filesystem(&fs_type); +} + +core_initcall(usbfs2_init); +module_exit(usbfs2_exit); +MODULE_LICENSE("GPL"); + --- gregkh-2.6.orig/include/linux/usb.h +++ gregkh-2.6/include/linux/usb.h @@ -291,6 +291,7 @@ struct usb_bus { int bandwidth_isoc_reqs; /* number of Isoc. requests */ struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */ + struct dentry *usbfs2_dentry; /* usbfs2 dentry entry for the bus */ struct class_device *class_dev; /* class device for this bus */ struct kref kref; /* reference counting for this bus */ @@ -361,6 +362,7 @@ struct usb_device { struct list_head filelist; struct class_device *class_dev; struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ + struct dentry *usbfs2_dentry; /* usbfs2 dentry entry for the device */ /* * Child devices - these can be either new devices