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);
|