|
@@ -43,6 +43,7 @@
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/usb.h>
|
|
|
#include <linux/usbdevice_fs.h>
|
|
|
+#include <linux/cdev.h>
|
|
|
#include <asm/uaccess.h>
|
|
|
#include <asm/byteorder.h>
|
|
|
#include <linux/moduleparam.h>
|
|
@@ -50,6 +51,10 @@
|
|
|
#include "hcd.h" /* for usbcore internals */
|
|
|
#include "usb.h"
|
|
|
|
|
|
+#define USB_MAXBUS 64
|
|
|
+#define USB_DEVICE_MAX USB_MAXBUS * 128
|
|
|
+static struct class *usb_device_class;
|
|
|
+
|
|
|
struct async {
|
|
|
struct list_head asynclist;
|
|
|
struct dev_state *ps;
|
|
@@ -487,7 +492,7 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
|
|
|
*/
|
|
|
static int usbdev_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
- struct usb_device *dev;
|
|
|
+ struct usb_device *dev = NULL;
|
|
|
struct dev_state *ps;
|
|
|
int ret;
|
|
|
|
|
@@ -501,11 +506,16 @@ static int usbdev_open(struct inode *inode, struct file *file)
|
|
|
|
|
|
lock_kernel();
|
|
|
ret = -ENOENT;
|
|
|
- dev = usb_get_dev(inode->u.generic_ip);
|
|
|
+ /* check if we are called from a real node or usbfs */
|
|
|
+ if (imajor(inode) == USB_DEVICE_MAJOR)
|
|
|
+ dev = usbdev_lookup_minor(iminor(inode));
|
|
|
+ if (!dev)
|
|
|
+ dev = inode->u.generic_ip;
|
|
|
if (!dev) {
|
|
|
kfree(ps);
|
|
|
goto out;
|
|
|
}
|
|
|
+ usb_get_dev(dev);
|
|
|
ret = 0;
|
|
|
ps->dev = dev;
|
|
|
ps->file = file;
|
|
@@ -1477,3 +1487,80 @@ struct file_operations usbfs_device_file_operations = {
|
|
|
.open = usbdev_open,
|
|
|
.release = usbdev_release,
|
|
|
};
|
|
|
+
|
|
|
+struct usb_device *usbdev_lookup_minor(int minor)
|
|
|
+{
|
|
|
+ struct class_device *class_dev;
|
|
|
+ struct usb_device *dev = NULL;
|
|
|
+
|
|
|
+ down(&usb_device_class->sem);
|
|
|
+ list_for_each_entry(class_dev, &usb_device_class->children, node) {
|
|
|
+ if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
|
|
|
+ dev = class_dev->class_data;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ up(&usb_device_class->sem);
|
|
|
+
|
|
|
+ return dev;
|
|
|
+};
|
|
|
+
|
|
|
+void usbdev_add(struct usb_device *dev)
|
|
|
+{
|
|
|
+ int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
|
|
|
+
|
|
|
+ dev->class_dev = class_device_create(usb_device_class,
|
|
|
+ MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
|
|
|
+ "usbdev%d.%d", dev->bus->busnum, dev->devnum);
|
|
|
+
|
|
|
+ dev->class_dev->class_data = dev;
|
|
|
+}
|
|
|
+
|
|
|
+void usbdev_remove(struct usb_device *dev)
|
|
|
+{
|
|
|
+ class_device_unregister(dev->class_dev);
|
|
|
+}
|
|
|
+
|
|
|
+static struct cdev usb_device_cdev = {
|
|
|
+ .kobj = {.name = "usb_device", },
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+};
|
|
|
+
|
|
|
+int __init usbdev_init(void)
|
|
|
+{
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ retval = register_chrdev_region(MKDEV(USB_DEVICE_MAJOR, 0),
|
|
|
+ USB_DEVICE_MAX, "usb_device");
|
|
|
+ if (retval) {
|
|
|
+ err("unable to register minors for usb_device");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
|
|
|
+ retval = cdev_add(&usb_device_cdev,
|
|
|
+ MKDEV(USB_DEVICE_MAJOR, 0), USB_DEVICE_MAX);
|
|
|
+ if (retval) {
|
|
|
+ err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
|
|
|
+ unregister_chrdev_region(USB_DEVICE_MAJOR, USB_DEVICE_MAX);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ usb_device_class = class_create(THIS_MODULE, "usb_device");
|
|
|
+ if (IS_ERR(usb_device_class)) {
|
|
|
+ err("unable to register usb_device class");
|
|
|
+ retval = PTR_ERR(usb_device_class);
|
|
|
+ usb_device_class = NULL;
|
|
|
+ cdev_del(&usb_device_cdev);
|
|
|
+ unregister_chrdev_region(USB_DEVICE_MAJOR, USB_DEVICE_MAX);
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+void usbdev_cleanup(void)
|
|
|
+{
|
|
|
+ class_destroy(usb_device_class);
|
|
|
+ cdev_del(&usb_device_cdev);
|
|
|
+ unregister_chrdev_region(USB_DEVICE_MAJOR, USB_DEVICE_MAX);
|
|
|
+}
|
|
|
+
|