aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/class/Kconfig11
-rw-r--r--drivers/usb/class/Makefile1
-rw-r--r--drivers/usb/class/cdc-wdm.c740
3 files changed, 752 insertions, 0 deletions
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index 3a9102d2591b..66f17ed88cb5 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -29,3 +29,14 @@ config USB_PRINTER
29 To compile this driver as a module, choose M here: the 29 To compile this driver as a module, choose M here: the
30 module will be called usblp. 30 module will be called usblp.
31 31
32config USB_WDM
33 tristate "USB Wireless Device Management support"
34 depends on USB
35 ---help---
36 This driver supports the WMC Device Management functionality
37 of cell phones compliant to the CDC WMC specification. You can use
38 AT commands over this device.
39
40 To compile this driver as a module, choose M here: the
41 module will be called cdc-wdm.
42
diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile
index cc391e6c2af8..535d59a30600 100644
--- a/drivers/usb/class/Makefile
+++ b/drivers/usb/class/Makefile
@@ -5,3 +5,4 @@
5 5
6obj-$(CONFIG_USB_ACM) += cdc-acm.o 6obj-$(CONFIG_USB_ACM) += cdc-acm.o
7obj-$(CONFIG_USB_PRINTER) += usblp.o 7obj-$(CONFIG_USB_PRINTER) += usblp.o
8obj-$(CONFIG_USB_WDM) += cdc-wdm.o
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
new file mode 100644
index 000000000000..107666d4e2ec
--- /dev/null
+++ b/drivers/usb/class/cdc-wdm.c
@@ -0,0 +1,740 @@
1/*
2 * cdc-wdm.c
3 *
4 * This driver supports USB CDC WCM Device Management.
5 *
6 * Copyright (c) 2007-2008 Oliver Neukum
7 *
8 * Some code taken from cdc-acm.c
9 *
10 * Released under the GPLv2.
11 *
12 * Many thanks to Carl Nordbeck
13 */
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/slab.h>
17#include <linux/module.h>
18#include <linux/smp_lock.h>
19#include <linux/mutex.h>
20#include <linux/uaccess.h>
21#include <linux/bitops.h>
22#include <linux/poll.h>
23#include <linux/usb.h>
24#include <linux/usb/cdc.h>
25#include <asm/byteorder.h>
26#include <asm/unaligned.h>
27
28/*
29 * Version Information
30 */
31#define DRIVER_VERSION "v0.02"
32#define DRIVER_AUTHOR "Oliver Neukum"
33
34static struct usb_device_id wdm_ids[] = {
35 {
36 .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
37 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
38 .bInterfaceClass = USB_CLASS_COMM,
39 .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
40 },
41 { }
42};
43
44#define WDM_MINOR_BASE 176
45
46
47#define WDM_IN_USE 1
48#define WDM_DISCONNECTING 2
49#define WDM_RESULT 3
50#define WDM_READ 4
51#define WDM_INT_STALL 5
52#define WDM_POLL_RUNNING 6
53
54
55#define WDM_MAX 16
56
57
58static DEFINE_MUTEX(wdm_mutex);
59
60/* --- method tables --- */
61
62struct wdm_device {
63 u8 *inbuf; /* buffer for response */
64 u8 *outbuf; /* buffer for command */
65 u8 *sbuf; /* buffer for status */
66 u8 *ubuf; /* buffer for copy to user space */
67
68 struct urb *command;
69 struct urb *response;
70 struct urb *validity;
71 struct usb_interface *intf;
72 struct usb_ctrlrequest *orq;
73 struct usb_ctrlrequest *irq;
74 spinlock_t iuspin;
75
76 unsigned long flags;
77 u16 bufsize;
78 u16 wMaxCommand;
79 u16 wMaxPacketSize;
80 u16 bMaxPacketSize0;
81 __le16 inum;
82 int reslength;
83 int length;
84 int read;
85 int count;
86 dma_addr_t shandle;
87 dma_addr_t ihandle;
88 struct mutex wlock;
89 struct mutex rlock;
90 wait_queue_head_t wait;
91 struct work_struct rxwork;
92 int werr;
93 int rerr;
94};
95
96static struct usb_driver wdm_driver;
97
98/* --- callbacks --- */
99static void wdm_out_callback(struct urb *urb)
100{
101 struct wdm_device *desc;
102 desc = urb->context;
103 spin_lock(&desc->iuspin);
104 desc->werr = urb->status;
105 spin_unlock(&desc->iuspin);
106 clear_bit(WDM_IN_USE, &desc->flags);
107 kfree(desc->outbuf);
108 wake_up(&desc->wait);
109}
110
111static void wdm_in_callback(struct urb *urb)
112{
113 struct wdm_device *desc = urb->context;
114 int status = urb->status;
115
116 spin_lock(&desc->iuspin);
117
118 if (status) {
119 switch (status) {
120 case -ENOENT:
121 dev_dbg(&desc->intf->dev,
122 "nonzero urb status received: -ENOENT");
123 break;
124 case -ECONNRESET:
125 dev_dbg(&desc->intf->dev,
126 "nonzero urb status received: -ECONNRESET");
127 break;
128 case -ESHUTDOWN:
129 dev_dbg(&desc->intf->dev,
130 "nonzero urb status received: -ESHUTDOWN");
131 break;
132 case -EPIPE:
133 err("nonzero urb status received: -EPIPE");
134 break;
135 default:
136 err("Unexpected error %d", status);
137 break;
138 }
139 }
140
141 desc->rerr = status;
142 desc->reslength = urb->actual_length;
143 memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
144 desc->length += desc->reslength;
145 wake_up(&desc->wait);
146
147 set_bit(WDM_READ, &desc->flags);
148 spin_unlock(&desc->iuspin);
149}
150
151static void wdm_int_callback(struct urb *urb)
152{
153 int rv = 0;
154 int status = urb->status;
155 struct wdm_device *desc;
156 struct usb_ctrlrequest *req;
157 struct usb_cdc_notification *dr;
158
159 desc = urb->context;
160 req = desc->irq;
161 dr = (struct usb_cdc_notification *)desc->sbuf;
162
163 if (status) {
164 switch (status) {
165 case -ESHUTDOWN:
166 case -ENOENT:
167 case -ECONNRESET:
168 return; /* unplug */
169 case -EPIPE:
170 set_bit(WDM_INT_STALL, &desc->flags);
171 err("Stall on int endpoint");
172 goto sw; /* halt is cleared in work */
173 default:
174 err("nonzero urb status received: %d", status);
175 break;
176 }
177 }
178
179 if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
180 err("wdm_int_callback - %d bytes", urb->actual_length);
181 goto exit;
182 }
183
184 switch (dr->bNotificationType) {
185 case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
186 dev_dbg(&desc->intf->dev,
187 "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
188 dr->wIndex, dr->wLength);
189 break;
190
191 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
192
193 dev_dbg(&desc->intf->dev,
194 "NOTIFY_NETWORK_CONNECTION %s network",
195 dr->wValue ? "connected to" : "disconnected from");
196 goto exit;
197 default:
198 clear_bit(WDM_POLL_RUNNING, &desc->flags);
199 err("unknown notification %d received: index %d len %d",
200 dr->bNotificationType, dr->wIndex, dr->wLength);
201 goto exit;
202 }
203
204 req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
205 req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
206 req->wValue = 0;
207 req->wIndex = desc->inum;
208 req->wLength = cpu_to_le16(desc->bMaxPacketSize0);
209
210 usb_fill_control_urb(
211 desc->response,
212 interface_to_usbdev(desc->intf),
213 /* using common endpoint 0 */
214 usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
215 (unsigned char *)req,
216 desc->inbuf,
217 desc->bMaxPacketSize0,
218 wdm_in_callback,
219 desc
220 );
221 desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
222 spin_lock(&desc->iuspin);
223 clear_bit(WDM_READ, &desc->flags);
224 if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
225 rv = usb_submit_urb(desc->response, GFP_ATOMIC);
226 dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
227 __func__, rv);
228 }
229 spin_unlock(&desc->iuspin);
230 if (rv < 0) {
231 if (rv == -EPERM)
232 return;
233 if (rv == -ENOMEM) {
234sw:
235 rv = schedule_work(&desc->rxwork);
236 if (rv)
237 err("Cannot schedule work");
238 }
239 }
240exit:
241 rv = usb_submit_urb(urb, GFP_ATOMIC);
242 if (rv)
243 err("%s - usb_submit_urb failed with result %d",
244 __func__, rv);
245
246}
247
248static void kill_urbs(struct wdm_device *desc)
249{
250 usb_kill_urb(desc->command);
251 usb_kill_urb(desc->validity);
252 usb_kill_urb(desc->response);
253}
254
255static void free_urbs(struct wdm_device *desc)
256{
257 usb_free_urb(desc->validity);
258 usb_free_urb(desc->response);
259 usb_free_urb(desc->command);
260}
261
262static void cleanup(struct wdm_device *desc)
263{
264 usb_buffer_free(interface_to_usbdev(desc->intf),
265 desc->wMaxPacketSize,
266 desc->sbuf,
267 desc->validity->transfer_dma);
268 usb_buffer_free(interface_to_usbdev(desc->intf),
269 desc->wMaxPacketSize,
270 desc->inbuf,
271 desc->response->transfer_dma);
272 kfree(desc->orq);
273 kfree(desc->irq);
274 kfree(desc->ubuf);
275 free_urbs(desc);
276 kfree(desc);
277}
278
279static ssize_t wdm_write
280(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
281{
282 u8 *buf;
283 int rv = -EMSGSIZE, r, we;
284 struct wdm_device *desc = file->private_data;
285 struct usb_ctrlrequest *req;
286
287 if (count > desc->wMaxCommand)
288 count = desc->wMaxCommand;
289
290 spin_lock_irq(&desc->iuspin);
291 we = desc->werr;
292 desc->werr = 0;
293 spin_unlock_irq(&desc->iuspin);
294 if (we < 0)
295 return -EIO;
296
297 r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */
298 rv = -ERESTARTSYS;
299 if (r)
300 goto outnl;
301
302 r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
303 &desc->flags));
304 if (r < 0)
305 goto out;
306
307 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
308 rv = -ENODEV;
309 goto out;
310 }
311
312 desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
313 if (!buf) {
314 rv = -ENOMEM;
315 goto out;
316 }
317
318 r = copy_from_user(buf, buffer, count);
319 if (r > 0) {
320 kfree(buf);
321 rv = -EFAULT;
322 goto out;
323 }
324
325 req = desc->orq;
326 usb_fill_control_urb(
327 desc->command,
328 interface_to_usbdev(desc->intf),
329 /* using common endpoint 0 */
330 usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
331 (unsigned char *)req,
332 buf,
333 count,
334 wdm_out_callback,
335 desc
336 );
337
338 req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
339 USB_RECIP_INTERFACE);
340 req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
341 req->wValue = 0;
342 req->wIndex = desc->inum;
343 req->wLength = cpu_to_le16(count);
344 set_bit(WDM_IN_USE, &desc->flags);
345
346 rv = usb_submit_urb(desc->command, GFP_KERNEL);
347 if (rv < 0) {
348 kfree(buf);
349 clear_bit(WDM_IN_USE, &desc->flags);
350 } else {
351 dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
352 req->wIndex);
353 }
354out:
355 mutex_unlock(&desc->wlock);
356outnl:
357 return rv < 0 ? rv : count;
358}
359
360static ssize_t wdm_read
361(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
362{
363 int rv, cntr;
364 int i = 0;
365 struct wdm_device *desc = file->private_data;
366
367
368 rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
369 if (rv < 0)
370 return -ERESTARTSYS;
371
372 if (desc->length == 0) {
373 desc->read = 0;
374retry:
375 i++;
376 rv = wait_event_interruptible(desc->wait,
377 test_bit(WDM_READ, &desc->flags));
378
379 if (rv < 0) {
380 rv = -ERESTARTSYS;
381 goto err;
382 }
383
384 spin_lock_irq(&desc->iuspin);
385
386 if (desc->rerr) { /* read completed, error happened */
387 int t = desc->rerr;
388 desc->rerr = 0;
389 spin_unlock_irq(&desc->iuspin);
390 err("reading had resulted in %d", t);
391 rv = -EIO;
392 goto err;
393 }
394 /*
395 * recheck whether we've lost the race
396 * against the completion handler
397 */
398 if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
399 spin_unlock_irq(&desc->iuspin);
400 goto retry;
401 }
402 if (!desc->reslength) { /* zero length read */
403 spin_unlock_irq(&desc->iuspin);
404 goto retry;
405 }
406 clear_bit(WDM_READ, &desc->flags);
407 spin_unlock_irq(&desc->iuspin);
408 }
409
410 cntr = count > desc->length ? desc->length : count;
411 rv = copy_to_user(buffer, desc->ubuf, cntr);
412 if (rv > 0) {
413 rv = -EFAULT;
414 goto err;
415 }
416
417 for (i = 0; i < desc->length - cntr; i++)
418 desc->ubuf[i] = desc->ubuf[i + cntr];
419
420 desc->length -= cntr;
421 rv = cntr;
422
423err:
424 mutex_unlock(&desc->rlock);
425 if (rv < 0)
426 err("wdm_read: exit error");
427 return rv;
428}
429
430static int wdm_flush(struct file *file, fl_owner_t id)
431{
432 struct wdm_device *desc = file->private_data;
433
434 wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
435 if (desc->werr < 0)
436 err("Error in flush path: %d", desc->werr);
437
438 return desc->werr;
439}
440
441static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
442{
443 struct wdm_device *desc = file->private_data;
444 unsigned long flags;
445 unsigned int mask = 0;
446
447 spin_lock_irqsave(&desc->iuspin, flags);
448 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
449 mask = POLLERR;
450 spin_unlock_irqrestore(&desc->iuspin, flags);
451 goto desc_out;
452 }
453 if (test_bit(WDM_READ, &desc->flags))
454 mask = POLLIN | POLLRDNORM;
455 if (desc->rerr || desc->werr)
456 mask |= POLLERR;
457 if (!test_bit(WDM_IN_USE, &desc->flags))
458 mask |= POLLOUT | POLLWRNORM;
459 spin_unlock_irqrestore(&desc->iuspin, flags);
460
461 poll_wait(file, &desc->wait, wait);
462
463desc_out:
464 return mask;
465}
466
467static int wdm_open(struct inode *inode, struct file *file)
468{
469 int minor = iminor(inode);
470 int rv = -ENODEV;
471 struct usb_interface *intf;
472 struct wdm_device *desc;
473
474 mutex_lock(&wdm_mutex);
475 intf = usb_find_interface(&wdm_driver, minor);
476 if (!intf)
477 goto out;
478
479 desc = usb_get_intfdata(intf);
480 if (test_bit(WDM_DISCONNECTING, &desc->flags))
481 goto out;
482
483 desc->count++;
484 file->private_data = desc;
485
486 rv = usb_submit_urb(desc->validity, GFP_KERNEL);
487
488 if (rv < 0) {
489 desc->count--;
490 err("Error submitting int urb - %d", rv);
491 goto out;
492 }
493 rv = 0;
494
495out:
496 mutex_unlock(&wdm_mutex);
497 return rv;
498}
499
500static int wdm_release(struct inode *inode, struct file *file)
501{
502 struct wdm_device *desc = file->private_data;
503
504 mutex_lock(&wdm_mutex);
505 desc->count--;
506 if (!desc->count) {
507 dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
508 kill_urbs(desc);
509 }
510 mutex_unlock(&wdm_mutex);
511 return 0;
512}
513
514static const struct file_operations wdm_fops = {
515 .owner = THIS_MODULE,
516 .read = wdm_read,
517 .write = wdm_write,
518 .open = wdm_open,
519 .flush = wdm_flush,
520 .release = wdm_release,
521 .poll = wdm_poll
522};
523
524static struct usb_class_driver wdm_class = {
525 .name = "cdc-wdm%d",
526 .fops = &wdm_fops,
527 .minor_base = WDM_MINOR_BASE,
528};
529
530/* --- error handling --- */
531static void wdm_rxwork(struct work_struct *work)
532{
533 struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
534 unsigned long flags;
535 int rv;
536
537 spin_lock_irqsave(&desc->iuspin, flags);
538 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
539 spin_unlock_irqrestore(&desc->iuspin, flags);
540 } else {
541 spin_unlock_irqrestore(&desc->iuspin, flags);
542 rv = usb_submit_urb(desc->response, GFP_KERNEL);
543 if (rv < 0 && rv != -EPERM) {
544 spin_lock_irqsave(&desc->iuspin, flags);
545 if (!test_bit(WDM_DISCONNECTING, &desc->flags))
546 schedule_work(&desc->rxwork);
547 spin_unlock_irqrestore(&desc->iuspin, flags);
548 }
549 }
550}
551
552/* --- hotplug --- */
553
554static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
555{
556 int rv = -EINVAL;
557 struct usb_device *udev = interface_to_usbdev(intf);
558 struct wdm_device *desc;
559 struct usb_host_interface *iface;
560 struct usb_endpoint_descriptor *ep;
561 struct usb_cdc_dmm_desc *dmhd;
562 u8 *buffer = intf->altsetting->extra;
563 int buflen = intf->altsetting->extralen;
564 u16 maxcom = 0;
565
566 if (!buffer)
567 goto out;
568
569 while (buflen > 0) {
570 if (buffer [1] != USB_DT_CS_INTERFACE) {
571 err("skipping garbage");
572 goto next_desc;
573 }
574
575 switch (buffer [2]) {
576 case USB_CDC_HEADER_TYPE:
577 break;
578 case USB_CDC_DMM_TYPE:
579 dmhd = (struct usb_cdc_dmm_desc *)buffer;
580 maxcom = le16_to_cpu(dmhd->wMaxCommand);
581 dev_dbg(&intf->dev,
582 "Finding maximum buffer length: %d", maxcom);
583 break;
584 default:
585 err("Ignoring extra header, type %d, length %d",
586 buffer[2], buffer[0]);
587 break;
588 }
589next_desc:
590 buflen -= buffer[0];
591 buffer += buffer[0];
592 }
593
594 rv = -ENOMEM;
595 desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
596 if (!desc)
597 goto out;
598 mutex_init(&desc->wlock);
599 mutex_init(&desc->rlock);
600 spin_lock_init(&desc->iuspin);
601 init_waitqueue_head(&desc->wait);
602 desc->wMaxCommand = maxcom;
603 desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
604 desc->intf = intf;
605 INIT_WORK(&desc->rxwork, wdm_rxwork);
606
607 iface = &intf->altsetting[0];
608 ep = &iface->endpoint[0].desc;
609 if (!usb_endpoint_is_int_in(ep)) {
610 rv = -EINVAL;
611 goto err;
612 }
613
614 desc->wMaxPacketSize = ep->wMaxPacketSize;
615 desc->bMaxPacketSize0 = cpu_to_le16(udev->descriptor.bMaxPacketSize0);
616
617 desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
618 if (!desc->orq)
619 goto err;
620 desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
621 if (!desc->irq)
622 goto err;
623
624 desc->validity = usb_alloc_urb(0, GFP_KERNEL);
625 if (!desc->validity)
626 goto err;
627
628 desc->response = usb_alloc_urb(0, GFP_KERNEL);
629 if (!desc->response)
630 goto err;
631
632 desc->command = usb_alloc_urb(0, GFP_KERNEL);
633 if (!desc->command)
634 goto err;
635
636 desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
637 if (!desc->ubuf)
638 goto err;
639
640 desc->sbuf = usb_buffer_alloc(interface_to_usbdev(intf),
641 desc->wMaxPacketSize,
642 GFP_KERNEL,
643 &desc->validity->transfer_dma);
644 if (!desc->sbuf)
645 goto err;
646
647 desc->inbuf = usb_buffer_alloc(interface_to_usbdev(intf),
648 desc->bMaxPacketSize0,
649 GFP_KERNEL,
650 &desc->response->transfer_dma);
651 if (!desc->inbuf)
652 goto err2;
653
654 usb_fill_int_urb(
655 desc->validity,
656 interface_to_usbdev(intf),
657 usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
658 desc->sbuf,
659 desc->wMaxPacketSize,
660 wdm_int_callback,
661 desc,
662 ep->bInterval
663 );
664 desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
665
666 usb_set_intfdata(intf, desc);
667 rv = usb_register_dev(intf, &wdm_class);
668 dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
669 intf->minor - WDM_MINOR_BASE);
670 if (rv < 0)
671 goto err;
672out:
673 return rv;
674err2:
675 usb_buffer_free(interface_to_usbdev(desc->intf),
676 desc->wMaxPacketSize,
677 desc->sbuf,
678 desc->validity->transfer_dma);
679err:
680 free_urbs(desc);
681 kfree(desc->ubuf);
682 kfree(desc->orq);
683 kfree(desc->irq);
684 kfree(desc);
685 return rv;
686}
687
688static void wdm_disconnect(struct usb_interface *intf)
689{
690 struct wdm_device *desc;
691 unsigned long flags;
692
693 usb_deregister_dev(intf, &wdm_class);
694 mutex_lock(&wdm_mutex);
695 desc = usb_get_intfdata(intf);
696
697 /* the spinlock makes sure no new urbs are generated in the callbacks */
698 spin_lock_irqsave(&desc->iuspin, flags);
699 set_bit(WDM_DISCONNECTING, &desc->flags);
700 set_bit(WDM_READ, &desc->flags);
701 clear_bit(WDM_IN_USE, &desc->flags);
702 spin_unlock_irqrestore(&desc->iuspin, flags);
703 cancel_work_sync(&desc->rxwork);
704 kill_urbs(desc);
705 wake_up_all(&desc->wait);
706 if (!desc->count)
707 cleanup(desc);
708 mutex_unlock(&wdm_mutex);
709}
710
711static struct usb_driver wdm_driver = {
712 .name = "cdc_wdm",
713 .probe = wdm_probe,
714 .disconnect = wdm_disconnect,
715 .id_table = wdm_ids,
716};
717
718/* --- low level module stuff --- */
719
720static int __init wdm_init(void)
721{
722 int rv;
723
724 rv = usb_register(&wdm_driver);
725
726 return rv;
727}
728
729static void __exit wdm_exit(void)
730{
731 usb_deregister(&wdm_driver);
732}
733
734module_init(wdm_init);
735module_exit(wdm_exit);
736
737MODULE_AUTHOR(DRIVER_AUTHOR);
738MODULE_DESCRIPTION("USB Abstract Control Model driver for "
739 "USB WCM Device Management");
740MODULE_LICENSE("GPL");