From: Greg Kroah-Hartman Date: Thu, 25 Apr 2002 08:11:42 +0000 (-0700) Subject: added CONFIG_USB_DYNAMIC_MINORS support to the USB core X-Git-Tag: v2.5.11~2^2~9 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=5e9b6284068fe596fafdf015e8d9f43576b4cd92;p=history.git 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 --git a/drivers/usb/Config.help b/drivers/usb/Config.help index 65b90af25c32..fa161a19725b 100644 --- a/drivers/usb/Config.help +++ b/drivers/usb/Config.help @@ -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. diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0e21346c08cb..3ec1f3796e0c 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -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); diff --git a/include/linux/usb.h b/include/linux/usb.h index 7b706c88da9f..78ff81cdc4b4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -2,6 +2,7 @@ #define __LINUX_USB_H #include +#include /* 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 + /* -------------------------------------------------------------------------- */ /*