]> git.neil.brown.name Git - history.git/commitdiff
added CONFIG_USB_DYNAMIC_MINORS support to the USB core
authorGreg Kroah-Hartman <greg@kroah.com>
Thu, 25 Apr 2002 08:11:42 +0000 (01:11 -0700)
committerGreg Kroah-Hartman <greg@kroah.com>
Thu, 25 Apr 2002 08:11:42 +0000 (01:11 -0700)
Here's a patch that finishes off my previous patch that enabled us to
use less than 16 minor numbers per USB device that uses the USB major
number.  This patch allows all such devices to share all 256 minor
numbers at once, much like the usbserial core shares the USB serial
major with all usb-serial drivers.  This also solves Oliver's problem of
having 30 printers :)

drivers/usb/Config.help
drivers/usb/core/usb.c
include/linux/usb.h

index 65b90af25c326779c7d5b098346bb08cd0d6d3cc..fa161a19725b041dfccb31fdb1041198eac80552 100644 (file)
@@ -69,3 +69,11 @@ CONFIG_USB_BANDWIDTH
   If you say N here, these conditions will cause warning messages
   about USB bandwidth usage to be logged and some devices or
   drivers may not work correctly.
+
+CONFIG_USB_DYNAMIC_MINORS
+  If you say Y here, the USB subsystem will use dynamic minor
+  allocation for any device that uses the USB major number.
+  This means that you can have more than 16 of a single type
+  of device (like USB printers).
+
+  If you are unsure about this, say N here.
index 0e21346c08cb7b33a5b3a8b5358c6d86fd109adb..3ec1f3796e0c9b69b9e385fedff01d1c6b4b1179 100644 (file)
@@ -11,6 +11,7 @@
        more docs, etc)
  * (C) Copyright Yggdrasil Computing, Inc. 2000
  *     (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002
  *
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
@@ -59,7 +60,47 @@ LIST_HEAD(usb_driver_list);
 
 devfs_handle_t usb_devfs_handle;       /* /dev/usb dir. */
 
-static struct usb_driver *usb_minors[256];
+#define MAX_USB_MINORS 256
+static struct usb_driver *usb_minors[MAX_USB_MINORS];
+static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED;
+
+static int usb_register_minors (struct usb_driver *driver, int num_minors, int start_minor)
+{
+       int i;
+
+       dbg("registering %d minors, starting at %d", num_minors, start_minor);
+
+       if (start_minor + num_minors >= MAX_USB_MINORS)
+               return -EINVAL;
+
+       spin_lock (&minor_lock);
+       for (i = start_minor; i < (start_minor + num_minors); ++i)
+               if (usb_minors[i]) {
+                       spin_unlock (&minor_lock);
+                       err("minor %d is already in use, error registering %s driver",
+                           i, driver->name);
+                       return -EINVAL;
+               }
+               
+       for (i = start_minor; i < (start_minor + num_minors); ++i)
+               usb_minors[i] = driver;
+
+       spin_unlock (&minor_lock);
+       return 0;
+}
+
+static void usb_deregister_minors (struct usb_driver *driver, int num_minors, int start_minor)
+{
+       int i;
+
+       dbg ("%s is removing %d minors starting at %d", driver->name,
+            num_minors, start_minor);
+
+       spin_lock (&minor_lock);
+       for (i = start_minor; i < (start_minor + num_minors); ++i)
+               usb_minors[i] = NULL;
+       spin_unlock (&minor_lock);
+}
 
 /**
  *     usb_register - register a USB driver
@@ -72,18 +113,15 @@ static struct usb_driver *usb_minors[256];
  */
 int usb_register(struct usb_driver *new_driver)
 {
-       int i;
-
+       int retval = 0;
+       
+#ifndef CONFIG_USB_DYNAMIC_MINORS
        if (new_driver->fops != NULL) {
-               for (i = new_driver->minor; i < new_driver->minor + new_driver->num_minors; ++i) {
-                       if (usb_minors[i]) {
-                               err("error registering %s driver", new_driver->name);
-                               return -EINVAL;
-                       }
-               }
-               for (i = new_driver->minor; i < new_driver->minor + new_driver->num_minors; ++i)
-                       usb_minors[i] = new_driver;
+               retval = usb_register_minors (new_driver, new_driver->num_minors, new_driver->minor);
+               if (retval)
+                       return retval;
        }
+#endif
 
        info("registered new driver %s", new_driver->name);
 
@@ -96,8 +134,93 @@ int usb_register(struct usb_driver *new_driver)
 
        usbfs_update_special();
 
-       return 0;
+       return retval;
+}
+
+
+/**
+ * usb_register_dev - register a USB device, and ask for a minor number
+ * @new_driver: USB operations for the driver
+ * @num_minors: number of minor numbers requested for this device
+ * @start_minor: place to put the new starting minor number
+ *
+ * Used to ask the USB core for a new minor number for a device that has
+ * just showed up.  This is used to dynamically allocate minor numbers
+ * from the pool of USB reserved minor numbers.
+ *
+ * This should be called by all drivers that use the USB major number.
+ * This only returns a good value of CONFIG_USB_DYNAMIC_MINORS is
+ * selected by the user.
+ *
+ * usb_deregister_dev() should be called when the driver is done with
+ * the minor numbers given out by this function.
+ *
+ * Returns a negative error code on failure and 0 on success, alone with
+ * a value that the driver should use in start_minor.
+ */
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+int usb_register_dev (struct usb_driver *new_driver, int num_minors, int *start_minor)
+{
+       int i;
+       int j;
+       int good_spot;
+       int retval = -EINVAL;
+
+       dbg ("%s is asking for %d minors", new_driver->name, num_minors);
+
+       if (new_driver->fops == NULL)
+               goto exit;
+
+       *start_minor = 0; 
+       spin_lock (&minor_lock);
+       for (i = 0; i < MAX_USB_MINORS; ++i) {
+               if (usb_minors[i])
+                       continue;
+
+               good_spot = 1;
+               for (j = 1; j <= num_minors-1; ++j)
+                       if (usb_minors[i+j]) {
+                               good_spot = 0;
+                               break;
+                       }
+               if (good_spot == 0)
+                       continue;
+
+               *start_minor = i;
+               spin_unlock (&minor_lock);
+               retval = usb_register_minors (new_driver, num_minors, *start_minor);
+               if (retval) {
+                       /* someone snuck in here, so let's start looking all over again */
+                       spin_lock (&minor_lock);
+                       i = 0;
+                       continue;
+               }
+               goto exit;
+       }
+       spin_unlock (&minor_lock);
+exit:
+       return retval;
+}
+
+/**
+ * usb_deregister_dev - deregister a USB device's dynamic minor.
+ * @driver: USB operations for the driver
+ * @num_minors: number of minor numbers to put back.
+ * @start_minor: the starting minor number
+ *
+ * Used in conjunction with usb_register_dev().  This function is called
+ * when the USB driver is finished with the minor numbers gotten from a
+ * call to usb_register_dev() (usually when the device is disconnected
+ * from the system.)
+ * 
+ * This should be called by all drivers that use the USB major number.
+ */
+void usb_deregister_dev (struct usb_driver *driver, int num_minors, int start_minor)
+{
+       usb_deregister_minors (driver, num_minors, start_minor);
 }
+#endif /* CONFIG_USB_DYNAMIC_MINORS */
+
 
 /**
  *     usb_scan_devices - scans all unclaimed USB interfaces
@@ -177,12 +300,13 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
 void usb_deregister(struct usb_driver *driver)
 {
        struct list_head *tmp;
-       int i;
 
        info("deregistering driver %s", driver->name);
+
+#ifndef CONFIG_USB_DYNAMIC_MINORS
        if (driver->fops != NULL)
-               for (i = driver->minor; i < driver->minor + driver->num_minors; ++i)
-                       usb_minors[i] = NULL;
+               usb_deregister_minors (driver, driver->num_minors, driver->minor);
+#endif
 
        /*
         * first we remove the driver, to be sure it doesn't get used by
@@ -2524,14 +2648,14 @@ int usb_new_device(struct usb_device *dev)
 static int usb_open(struct inode * inode, struct file * file)
 {
        int minor = minor(inode->i_rdev);
-       struct usb_driver *c = usb_minors[minor];
+       struct usb_driver *c;
        int err = -ENODEV;
        struct file_operations *old_fops, *new_fops = NULL;
 
-       /*
-        * No load-on-demand? Randy, could you ACK that it's really not
-        * supposed to be done?                                 -- AV
-        */
+       spin_lock (&minor_lock);
+       c = usb_minors[minor];
+       spin_unlock (&minor_lock);
+
        if (!c || !(new_fops = fops_get(c->fops)))
                return err;
        old_fops = file->f_op;
@@ -2623,6 +2747,11 @@ EXPORT_SYMBOL(usb_register);
 EXPORT_SYMBOL(usb_deregister);
 EXPORT_SYMBOL(usb_scan_devices);
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+EXPORT_SYMBOL(usb_register_dev);
+EXPORT_SYMBOL(usb_deregister_dev);
+#endif
+
 EXPORT_SYMBOL(usb_alloc_dev);
 EXPORT_SYMBOL(usb_free_dev);
 
index 7b706c88da9f946386a4a1a63c80d3c7b3162fbf..78ff81cdc4b4a99457d770bde79ebc2622ccf8ca 100644 (file)
@@ -2,6 +2,7 @@
 #define __LINUX_USB_H
 
 #include <linux/device.h>
+#include <linux/errno.h>
 
 /* USB constants */
 
@@ -776,6 +777,14 @@ struct usb_driver {
 extern int usb_register(struct usb_driver *);
 extern void usb_deregister(struct usb_driver *);
 
+#ifndef CONFIG_USB_DYNAMIC_MINORS
+static inline int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor) { return -ENODEV; }
+static inline void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor) {}
+#else
+extern int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor);
+extern void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor);
+#endif
+
 /* -------------------------------------------------------------------------- */
 
 /*