Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: CDC WDM driver

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Oliver Neukum and committed by
Greg Kroah-Hartman
afba937e 129bd474

+761
+11
drivers/usb/class/Kconfig
··· 29 29 To compile this driver as a module, choose M here: the 30 30 module will be called usblp. 31 31 32 + config 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 +
+1
drivers/usb/class/Makefile
··· 5 5 6 6 obj-$(CONFIG_USB_ACM) += cdc-acm.o 7 7 obj-$(CONFIG_USB_PRINTER) += usblp.o 8 + obj-$(CONFIG_USB_WDM) += cdc-wdm.o
+740
drivers/usb/class/cdc-wdm.c
··· 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 + 34 + static 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 + 58 + static DEFINE_MUTEX(wdm_mutex); 59 + 60 + /* --- method tables --- */ 61 + 62 + struct 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 + 96 + static struct usb_driver wdm_driver; 97 + 98 + /* --- callbacks --- */ 99 + static 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 + 111 + static 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 + 151 + static 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) { 234 + sw: 235 + rv = schedule_work(&desc->rxwork); 236 + if (rv) 237 + err("Cannot schedule work"); 238 + } 239 + } 240 + exit: 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 + 248 + static 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 + 255 + static 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 + 262 + static 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 + 279 + static 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 + } 354 + out: 355 + mutex_unlock(&desc->wlock); 356 + outnl: 357 + return rv < 0 ? rv : count; 358 + } 359 + 360 + static 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; 374 + retry: 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 + 423 + err: 424 + mutex_unlock(&desc->rlock); 425 + if (rv < 0) 426 + err("wdm_read: exit error"); 427 + return rv; 428 + } 429 + 430 + static 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 + 441 + static 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 + 463 + desc_out: 464 + return mask; 465 + } 466 + 467 + static 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 + 495 + out: 496 + mutex_unlock(&wdm_mutex); 497 + return rv; 498 + } 499 + 500 + static 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 + 514 + static 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 + 524 + static 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 --- */ 531 + static 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 + 554 + static 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 + } 589 + next_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; 672 + out: 673 + return rv; 674 + err2: 675 + usb_buffer_free(interface_to_usbdev(desc->intf), 676 + desc->wMaxPacketSize, 677 + desc->sbuf, 678 + desc->validity->transfer_dma); 679 + err: 680 + free_urbs(desc); 681 + kfree(desc->ubuf); 682 + kfree(desc->orq); 683 + kfree(desc->irq); 684 + kfree(desc); 685 + return rv; 686 + } 687 + 688 + static 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 + 711 + static 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 + 720 + static int __init wdm_init(void) 721 + { 722 + int rv; 723 + 724 + rv = usb_register(&wdm_driver); 725 + 726 + return rv; 727 + } 728 + 729 + static void __exit wdm_exit(void) 730 + { 731 + usb_deregister(&wdm_driver); 732 + } 733 + 734 + module_init(wdm_init); 735 + module_exit(wdm_exit); 736 + 737 + MODULE_AUTHOR(DRIVER_AUTHOR); 738 + MODULE_DESCRIPTION("USB Abstract Control Model driver for " 739 + "USB WCM Device Management"); 740 + MODULE_LICENSE("GPL");
+9
include/linux/usb/cdc.h
··· 130 130 __u8 bNumberPowerFilters; 131 131 } __attribute__ ((packed)); 132 132 133 + /* "Telephone Control Model Functional Descriptor" from CDC WMC spec 6.3..3 */ 134 + struct usb_cdc_dmm_desc { 135 + __u8 bFunctionLength; 136 + __u8 bDescriptorType; 137 + __u8 bDescriptorSubtype; 138 + __u16 bcdVersion; 139 + __le16 wMaxCommand; 140 + } __attribute__ ((packed)); 141 + 133 142 /* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */ 134 143 struct usb_cdc_mdlm_desc { 135 144 __u8 bLength;