Commit 5e9b6284 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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 :)
parent 28c6c451
......@@ -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.
......@@ -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,9 +134,94 @@ 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
* Context: !in_interrupt ()
......@@ -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);
......
......@@ -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
/* -------------------------------------------------------------------------- */
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment