diff options
author | Sarah Bailey <saharabeara@gmail.com> | 2006-11-19 01:30:16 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-12-01 17:25:52 -0500 |
commit | 7e27780ffdf5fcf8675dfb278ca316a1f2b7e179 (patch) | |
tree | 0e65f7afd10ef3fae8daac86a4327a1b3bd481dc /drivers/usb | |
parent | 10c8211c634f759633ffcdc3b0e2913963b32abc (diff) |
USB: added dynamic major number for USB endpoints
This patch is an update for Greg K-H's proposed usbfs2:
http://sourceforge.net/mailarchive/message.php?msg_id=19295229
It creates a dynamic major for USB endpoints and fixes
the endpoint minor calculation.
Signed-off-by: Sarah Bailey <saharabeara@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/endpoint.c | 98 |
1 files changed, 85 insertions, 13 deletions
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 3b2d137912be..c505b767cee1 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c | |||
@@ -10,15 +10,20 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/spinlock.h> | ||
14 | #include <linux/idr.h> | ||
13 | #include <linux/usb.h> | 15 | #include <linux/usb.h> |
14 | #include "usb.h" | 16 | #include "usb.h" |
15 | 17 | ||
16 | /* endpoint stuff */ | 18 | #define MAX_ENDPOINT_MINORS (64*128*32) |
19 | static int usb_endpoint_major; | ||
20 | static DEFINE_IDR(endpoint_idr); | ||
17 | 21 | ||
18 | struct ep_device { | 22 | struct ep_device { |
19 | struct usb_endpoint_descriptor *desc; | 23 | struct usb_endpoint_descriptor *desc; |
20 | struct usb_device *udev; | 24 | struct usb_device *udev; |
21 | struct device dev; | 25 | struct device dev; |
26 | int minor; | ||
22 | }; | 27 | }; |
23 | #define to_ep_device(_dev) \ | 28 | #define to_ep_device(_dev) \ |
24 | container_of(_dev, struct ep_device, dev) | 29 | container_of(_dev, struct ep_device, dev) |
@@ -152,6 +157,55 @@ static struct attribute_group ep_dev_attr_grp = { | |||
152 | .attrs = ep_dev_attrs, | 157 | .attrs = ep_dev_attrs, |
153 | }; | 158 | }; |
154 | 159 | ||
160 | static int usb_endpoint_major_init(void) | ||
161 | { | ||
162 | dev_t dev; | ||
163 | int error; | ||
164 | |||
165 | error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS, | ||
166 | "usb_endpoint"); | ||
167 | if (error) { | ||
168 | err("unable to get a dynamic major for usb endpoints"); | ||
169 | return error; | ||
170 | } | ||
171 | usb_endpoint_major = MAJOR(dev); | ||
172 | |||
173 | return error; | ||
174 | } | ||
175 | |||
176 | static void usb_endpoint_major_cleanup(void) | ||
177 | { | ||
178 | unregister_chrdev_region(MKDEV(usb_endpoint_major, 0), | ||
179 | MAX_ENDPOINT_MINORS); | ||
180 | } | ||
181 | |||
182 | static int endpoint_get_minor(struct ep_device *ep_dev) | ||
183 | { | ||
184 | static DEFINE_MUTEX(minor_lock); | ||
185 | int retval = -ENOMEM; | ||
186 | int id; | ||
187 | |||
188 | mutex_lock(&minor_lock); | ||
189 | if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0) | ||
190 | goto exit; | ||
191 | |||
192 | retval = idr_get_new(&endpoint_idr, ep_dev, &id); | ||
193 | if (retval < 0) { | ||
194 | if (retval == -EAGAIN) | ||
195 | retval = -ENOMEM; | ||
196 | goto exit; | ||
197 | } | ||
198 | ep_dev->minor = id & MAX_ID_MASK; | ||
199 | exit: | ||
200 | mutex_unlock(&minor_lock); | ||
201 | return retval; | ||
202 | } | ||
203 | |||
204 | static void endpoint_free_minor(struct ep_device *ep_dev) | ||
205 | { | ||
206 | idr_remove(&endpoint_idr, ep_dev->minor); | ||
207 | } | ||
208 | |||
155 | static struct endpoint_class { | 209 | static struct endpoint_class { |
156 | struct kref kref; | 210 | struct kref kref; |
157 | struct class *class; | 211 | struct class *class; |
@@ -176,11 +230,20 @@ static int init_endpoint_class(void) | |||
176 | ep_class->class = class_create(THIS_MODULE, "usb_endpoint"); | 230 | ep_class->class = class_create(THIS_MODULE, "usb_endpoint"); |
177 | if (IS_ERR(ep_class->class)) { | 231 | if (IS_ERR(ep_class->class)) { |
178 | result = IS_ERR(ep_class->class); | 232 | result = IS_ERR(ep_class->class); |
179 | kfree(ep_class); | 233 | goto class_create_error; |
180 | ep_class = NULL; | ||
181 | goto exit; | ||
182 | } | 234 | } |
183 | 235 | ||
236 | result = usb_endpoint_major_init(); | ||
237 | if (result) | ||
238 | goto endpoint_major_error; | ||
239 | |||
240 | goto exit; | ||
241 | |||
242 | endpoint_major_error: | ||
243 | class_destroy(ep_class->class); | ||
244 | class_create_error: | ||
245 | kfree(ep_class); | ||
246 | ep_class = NULL; | ||
184 | exit: | 247 | exit: |
185 | return result; | 248 | return result; |
186 | } | 249 | } |
@@ -191,6 +254,7 @@ static void release_endpoint_class(struct kref *kref) | |||
191 | class_destroy(ep_class->class); | 254 | class_destroy(ep_class->class); |
192 | kfree(ep_class); | 255 | kfree(ep_class); |
193 | ep_class = NULL; | 256 | ep_class = NULL; |
257 | usb_endpoint_major_cleanup(); | ||
194 | } | 258 | } |
195 | 259 | ||
196 | static void destroy_endpoint_class(void) | 260 | static void destroy_endpoint_class(void) |
@@ -213,7 +277,6 @@ int usb_create_ep_files(struct device *parent, | |||
213 | { | 277 | { |
214 | char name[8]; | 278 | char name[8]; |
215 | struct ep_device *ep_dev; | 279 | struct ep_device *ep_dev; |
216 | int minor; | ||
217 | int retval; | 280 | int retval; |
218 | 281 | ||
219 | retval = init_endpoint_class(); | 282 | retval = init_endpoint_class(); |
@@ -226,12 +289,16 @@ int usb_create_ep_files(struct device *parent, | |||
226 | goto error_alloc; | 289 | goto error_alloc; |
227 | } | 290 | } |
228 | 291 | ||
229 | /* fun calculation to determine the minor of this endpoint */ | 292 | retval = endpoint_get_minor(ep_dev); |
230 | minor = (((udev->bus->busnum - 1) * 128) * 16) + (udev->devnum - 1); | 293 | if (retval) { |
294 | dev_err(parent, "can not allocate minor number for %s", | ||
295 | ep_dev->dev.bus_id); | ||
296 | goto error_register; | ||
297 | } | ||
231 | 298 | ||
232 | ep_dev->desc = &endpoint->desc; | 299 | ep_dev->desc = &endpoint->desc; |
233 | ep_dev->udev = udev; | 300 | ep_dev->udev = udev; |
234 | ep_dev->dev.devt = MKDEV(442, minor); // FIXME fake number... | 301 | ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor); |
235 | ep_dev->dev.class = ep_class->class; | 302 | ep_dev->dev.class = ep_class->class; |
236 | ep_dev->dev.parent = parent; | 303 | ep_dev->dev.parent = parent; |
237 | ep_dev->dev.release = ep_device_release; | 304 | ep_dev->dev.release = ep_device_release; |
@@ -241,7 +308,7 @@ int usb_create_ep_files(struct device *parent, | |||
241 | 308 | ||
242 | retval = device_register(&ep_dev->dev); | 309 | retval = device_register(&ep_dev->dev); |
243 | if (retval) | 310 | if (retval) |
244 | goto error_register; | 311 | goto error_chrdev; |
245 | retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); | 312 | retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); |
246 | if (retval) | 313 | if (retval) |
247 | goto error_group; | 314 | goto error_group; |
@@ -261,6 +328,9 @@ error_group: | |||
261 | destroy_endpoint_class(); | 328 | destroy_endpoint_class(); |
262 | return retval; | 329 | return retval; |
263 | 330 | ||
331 | error_chrdev: | ||
332 | endpoint_free_minor(ep_dev); | ||
333 | |||
264 | error_register: | 334 | error_register: |
265 | kfree(ep_dev); | 335 | kfree(ep_dev); |
266 | error_alloc: | 336 | error_alloc: |
@@ -271,14 +341,16 @@ exit: | |||
271 | 341 | ||
272 | void usb_remove_ep_files(struct usb_host_endpoint *endpoint) | 342 | void usb_remove_ep_files(struct usb_host_endpoint *endpoint) |
273 | { | 343 | { |
344 | struct ep_device *ep_dev = endpoint->ep_dev; | ||
274 | 345 | ||
275 | if (endpoint->ep_dev) { | 346 | if (ep_dev) { |
276 | char name[8]; | 347 | char name[8]; |
277 | 348 | ||
278 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); | 349 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); |
279 | sysfs_remove_link(&endpoint->ep_dev->dev.parent->kobj, name); | 350 | sysfs_remove_link(&ep_dev->dev.parent->kobj, name); |
280 | sysfs_remove_group(&endpoint->ep_dev->dev.kobj, &ep_dev_attr_grp); | 351 | sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); |
281 | device_unregister(&endpoint->ep_dev->dev); | 352 | endpoint_free_minor(ep_dev); |
353 | device_unregister(&ep_dev->dev); | ||
282 | endpoint->ep_dev = NULL; | 354 | endpoint->ep_dev = NULL; |
283 | destroy_endpoint_class(); | 355 | destroy_endpoint_class(); |
284 | } | 356 | } |