Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | /*
* drivers/usb/file.c
*
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */
EXPORT_SYMBOL(usb_devfs_handle);
#define MAX_USB_MINORS 256
static struct file_operations *usb_minors[MAX_USB_MINORS];
static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED;
static int usb_open(struct inode * inode, struct file * file)
{
int minor = minor(inode->i_rdev);
struct file_operations *c;
int err = -ENODEV;
struct file_operations *old_fops, *new_fops = NULL;
spin_lock (&minor_lock);
c = usb_minors[minor];
if (!c || !(new_fops = fops_get(c))) {
spin_unlock(&minor_lock);
return err;
}
spin_unlock(&minor_lock);
old_fops = file->f_op;
file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
err = file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
static struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
};
int usb_major_init(void)
{
if (register_chrdev(USB_MAJOR, "usb", &usb_fops)) {
err("unable to get major %d for usb devices", USB_MAJOR);
return -EBUSY;
}
usb_devfs_handle = devfs_mk_dir(NULL, "usb", NULL);
return 0;
}
void usb_major_cleanup(void)
{
devfs_unregister(usb_devfs_handle);
unregister_chrdev(USB_MAJOR, "usb");
}
/**
* usb_register_dev - register a USB device, and ask for a minor number
* @fops: the file operations for this USB device
* @minor: the requested starting minor for this device.
* @num_minors: number of minor numbers requested for this device
* @start_minor: place to put the new starting minor number
*
* This should be called by all USB drivers that use the USB major number.
* If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be
* dynamically allocated out of the list of available ones. If it is not
* enabled, the minor number will be based on the next available free minor,
* starting at the requested @minor.
*
* usb_deregister_dev() must be called when the driver is done with
* the minor numbers given out by this function.
*
* Returns -EINVAL if something bad happens with trying to register a
* device, and 0 on success, alone with a value that the driver should
* use in start_minor.
*/
int usb_register_dev (struct file_operations *fops, int minor, int num_minors, int *start_minor)
{
int i;
int j;
int good_spot;
int retval = -EINVAL;
#ifdef CONFIG_USB_DYNAMIC_MINORS
/*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.
*/
minor = 0;
#endif
dbg ("asking for %d minors, starting at %d", num_minors, minor);
if (fops == NULL)
goto exit;
*start_minor = 0;
spin_lock (&minor_lock);
for (i = minor; 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;
dbg("found a minor chunk free, starting at %d", i);
for (i = *start_minor; i < (*start_minor + num_minors); ++i)
usb_minors[i] = fops;
retval = 0;
goto exit;
}
exit:
spin_unlock (&minor_lock);
return retval;
}
EXPORT_SYMBOL(usb_register_dev);
/**
* usb_deregister_dev - deregister a USB device's dynamic minor.
* @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 (int num_minors, int start_minor)
{
int i;
dbg ("removing %d minors starting at %d", 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);
}
EXPORT_SYMBOL(usb_deregister_dev);
|