# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.560 -> 1.561 # drivers/usb/Config.help 1.11 -> 1.12 # include/linux/usb.h 1.29 -> 1.30 # drivers/usb/core/usb.c 1.46 -> 1.47 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/04/25 greg@kroah.com 1.561 # added CONFIG_USB_DYNAMIC_MINORS support to the USB core # # 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 :) # -------------------------------------------- # diff -Nru a/drivers/usb/Config.help b/drivers/usb/Config.help --- a/drivers/usb/Config.help Thu Apr 25 16:26:56 2002 +++ b/drivers/usb/Config.help Thu Apr 25 16:26:56 2002 @@ -69,3 +69,11 @@ 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. diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Thu Apr 25 16:26:56 2002 +++ b/drivers/usb/core/usb.c Thu Apr 25 16:26:57 2002 @@ -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 @@ 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 @@ */ 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,9 +134,94 @@ 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 * Context: !in_interrupt () @@ -177,12 +300,13 @@ 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 @@ 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; @@ -2622,6 +2746,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); diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Thu Apr 25 16:26:56 2002 +++ b/include/linux/usb.h Thu Apr 25 16:26:56 2002 @@ -2,6 +2,7 @@ #define __LINUX_USB_H #include +#include /* USB constants */ @@ -775,6 +776,14 @@ */ 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 /* -------------------------------------------------------------------------- */