1afba937eSOliver Neukum /* 2afba937eSOliver Neukum * cdc-wdm.c 3afba937eSOliver Neukum * 4afba937eSOliver Neukum * This driver supports USB CDC WCM Device Management. 5afba937eSOliver Neukum * 6afba937eSOliver Neukum * Copyright (c) 2007-2008 Oliver Neukum 7afba937eSOliver Neukum * 8afba937eSOliver Neukum * Some code taken from cdc-acm.c 9afba937eSOliver Neukum * 10afba937eSOliver Neukum * Released under the GPLv2. 11afba937eSOliver Neukum * 12afba937eSOliver Neukum * Many thanks to Carl Nordbeck 13afba937eSOliver Neukum */ 14afba937eSOliver Neukum #include <linux/kernel.h> 15afba937eSOliver Neukum #include <linux/errno.h> 16afba937eSOliver Neukum #include <linux/slab.h> 17afba937eSOliver Neukum #include <linux/module.h> 18afba937eSOliver Neukum #include <linux/smp_lock.h> 19afba937eSOliver Neukum #include <linux/mutex.h> 20afba937eSOliver Neukum #include <linux/uaccess.h> 21afba937eSOliver Neukum #include <linux/bitops.h> 22afba937eSOliver Neukum #include <linux/poll.h> 23afba937eSOliver Neukum #include <linux/usb.h> 24afba937eSOliver Neukum #include <linux/usb/cdc.h> 25afba937eSOliver Neukum #include <asm/byteorder.h> 26afba937eSOliver Neukum #include <asm/unaligned.h> 27afba937eSOliver Neukum 28afba937eSOliver Neukum /* 29afba937eSOliver Neukum * Version Information 30afba937eSOliver Neukum */ 3187d65e54SOliver Neukum #define DRIVER_VERSION "v0.03" 32afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum" 3387d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" 34afba937eSOliver Neukum 35afba937eSOliver Neukum static struct usb_device_id wdm_ids[] = { 36afba937eSOliver Neukum { 37afba937eSOliver Neukum .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | 38afba937eSOliver Neukum USB_DEVICE_ID_MATCH_INT_SUBCLASS, 39afba937eSOliver Neukum .bInterfaceClass = USB_CLASS_COMM, 40afba937eSOliver Neukum .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM 41afba937eSOliver Neukum }, 42afba937eSOliver Neukum { } 43afba937eSOliver Neukum }; 44afba937eSOliver Neukum 45afba937eSOliver Neukum #define WDM_MINOR_BASE 176 46afba937eSOliver Neukum 47afba937eSOliver Neukum 48afba937eSOliver Neukum #define WDM_IN_USE 1 49afba937eSOliver Neukum #define WDM_DISCONNECTING 2 50afba937eSOliver Neukum #define WDM_RESULT 3 51afba937eSOliver Neukum #define WDM_READ 4 52afba937eSOliver Neukum #define WDM_INT_STALL 5 53afba937eSOliver Neukum #define WDM_POLL_RUNNING 6 54afba937eSOliver Neukum 55afba937eSOliver Neukum 56afba937eSOliver Neukum #define WDM_MAX 16 57afba937eSOliver Neukum 58afba937eSOliver Neukum 59afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex); 60afba937eSOliver Neukum 61afba937eSOliver Neukum /* --- method tables --- */ 62afba937eSOliver Neukum 63afba937eSOliver Neukum struct wdm_device { 64afba937eSOliver Neukum u8 *inbuf; /* buffer for response */ 65afba937eSOliver Neukum u8 *outbuf; /* buffer for command */ 66afba937eSOliver Neukum u8 *sbuf; /* buffer for status */ 67afba937eSOliver Neukum u8 *ubuf; /* buffer for copy to user space */ 68afba937eSOliver Neukum 69afba937eSOliver Neukum struct urb *command; 70afba937eSOliver Neukum struct urb *response; 71afba937eSOliver Neukum struct urb *validity; 72afba937eSOliver Neukum struct usb_interface *intf; 73afba937eSOliver Neukum struct usb_ctrlrequest *orq; 74afba937eSOliver Neukum struct usb_ctrlrequest *irq; 75afba937eSOliver Neukum spinlock_t iuspin; 76afba937eSOliver Neukum 77afba937eSOliver Neukum unsigned long flags; 78afba937eSOliver Neukum u16 bufsize; 79afba937eSOliver Neukum u16 wMaxCommand; 80afba937eSOliver Neukum u16 wMaxPacketSize; 81afba937eSOliver Neukum u16 bMaxPacketSize0; 82afba937eSOliver Neukum __le16 inum; 83afba937eSOliver Neukum int reslength; 84afba937eSOliver Neukum int length; 85afba937eSOliver Neukum int read; 86afba937eSOliver Neukum int count; 87afba937eSOliver Neukum dma_addr_t shandle; 88afba937eSOliver Neukum dma_addr_t ihandle; 89afba937eSOliver Neukum struct mutex wlock; 90afba937eSOliver Neukum struct mutex rlock; 9117d80d56SOliver Neukum struct mutex plock; 92afba937eSOliver Neukum wait_queue_head_t wait; 93afba937eSOliver Neukum struct work_struct rxwork; 94afba937eSOliver Neukum int werr; 95afba937eSOliver Neukum int rerr; 96afba937eSOliver Neukum }; 97afba937eSOliver Neukum 98afba937eSOliver Neukum static struct usb_driver wdm_driver; 99afba937eSOliver Neukum 100afba937eSOliver Neukum /* --- callbacks --- */ 101afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb) 102afba937eSOliver Neukum { 103afba937eSOliver Neukum struct wdm_device *desc; 104afba937eSOliver Neukum desc = urb->context; 105afba937eSOliver Neukum spin_lock(&desc->iuspin); 106afba937eSOliver Neukum desc->werr = urb->status; 107afba937eSOliver Neukum spin_unlock(&desc->iuspin); 108afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 109afba937eSOliver Neukum kfree(desc->outbuf); 110afba937eSOliver Neukum wake_up(&desc->wait); 111afba937eSOliver Neukum } 112afba937eSOliver Neukum 113afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb) 114afba937eSOliver Neukum { 115afba937eSOliver Neukum struct wdm_device *desc = urb->context; 116afba937eSOliver Neukum int status = urb->status; 117afba937eSOliver Neukum 118afba937eSOliver Neukum spin_lock(&desc->iuspin); 119afba937eSOliver Neukum 120afba937eSOliver Neukum if (status) { 121afba937eSOliver Neukum switch (status) { 122afba937eSOliver Neukum case -ENOENT: 123afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 124afba937eSOliver Neukum "nonzero urb status received: -ENOENT"); 125afba937eSOliver Neukum break; 126afba937eSOliver Neukum case -ECONNRESET: 127afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 128afba937eSOliver Neukum "nonzero urb status received: -ECONNRESET"); 129afba937eSOliver Neukum break; 130afba937eSOliver Neukum case -ESHUTDOWN: 131afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 132afba937eSOliver Neukum "nonzero urb status received: -ESHUTDOWN"); 133afba937eSOliver Neukum break; 134afba937eSOliver Neukum case -EPIPE: 135afba937eSOliver Neukum err("nonzero urb status received: -EPIPE"); 136afba937eSOliver Neukum break; 137afba937eSOliver Neukum default: 138afba937eSOliver Neukum err("Unexpected error %d", status); 139afba937eSOliver Neukum break; 140afba937eSOliver Neukum } 141afba937eSOliver Neukum } 142afba937eSOliver Neukum 143afba937eSOliver Neukum desc->rerr = status; 144afba937eSOliver Neukum desc->reslength = urb->actual_length; 145afba937eSOliver Neukum memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); 146afba937eSOliver Neukum desc->length += desc->reslength; 147afba937eSOliver Neukum wake_up(&desc->wait); 148afba937eSOliver Neukum 149afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 150afba937eSOliver Neukum spin_unlock(&desc->iuspin); 151afba937eSOliver Neukum } 152afba937eSOliver Neukum 153afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb) 154afba937eSOliver Neukum { 155afba937eSOliver Neukum int rv = 0; 156afba937eSOliver Neukum int status = urb->status; 157afba937eSOliver Neukum struct wdm_device *desc; 158afba937eSOliver Neukum struct usb_ctrlrequest *req; 159afba937eSOliver Neukum struct usb_cdc_notification *dr; 160afba937eSOliver Neukum 161afba937eSOliver Neukum desc = urb->context; 162afba937eSOliver Neukum req = desc->irq; 163afba937eSOliver Neukum dr = (struct usb_cdc_notification *)desc->sbuf; 164afba937eSOliver Neukum 165afba937eSOliver Neukum if (status) { 166afba937eSOliver Neukum switch (status) { 167afba937eSOliver Neukum case -ESHUTDOWN: 168afba937eSOliver Neukum case -ENOENT: 169afba937eSOliver Neukum case -ECONNRESET: 170afba937eSOliver Neukum return; /* unplug */ 171afba937eSOliver Neukum case -EPIPE: 172afba937eSOliver Neukum set_bit(WDM_INT_STALL, &desc->flags); 173afba937eSOliver Neukum err("Stall on int endpoint"); 174afba937eSOliver Neukum goto sw; /* halt is cleared in work */ 175afba937eSOliver Neukum default: 176afba937eSOliver Neukum err("nonzero urb status received: %d", status); 177afba937eSOliver Neukum break; 178afba937eSOliver Neukum } 179afba937eSOliver Neukum } 180afba937eSOliver Neukum 181afba937eSOliver Neukum if (urb->actual_length < sizeof(struct usb_cdc_notification)) { 182afba937eSOliver Neukum err("wdm_int_callback - %d bytes", urb->actual_length); 183afba937eSOliver Neukum goto exit; 184afba937eSOliver Neukum } 185afba937eSOliver Neukum 186afba937eSOliver Neukum switch (dr->bNotificationType) { 187afba937eSOliver Neukum case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: 188afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 189afba937eSOliver Neukum "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d", 190afba937eSOliver Neukum dr->wIndex, dr->wLength); 191afba937eSOliver Neukum break; 192afba937eSOliver Neukum 193afba937eSOliver Neukum case USB_CDC_NOTIFY_NETWORK_CONNECTION: 194afba937eSOliver Neukum 195afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 196afba937eSOliver Neukum "NOTIFY_NETWORK_CONNECTION %s network", 197afba937eSOliver Neukum dr->wValue ? "connected to" : "disconnected from"); 198afba937eSOliver Neukum goto exit; 199afba937eSOliver Neukum default: 200afba937eSOliver Neukum clear_bit(WDM_POLL_RUNNING, &desc->flags); 201afba937eSOliver Neukum err("unknown notification %d received: index %d len %d", 202afba937eSOliver Neukum dr->bNotificationType, dr->wIndex, dr->wLength); 203afba937eSOliver Neukum goto exit; 204afba937eSOliver Neukum } 205afba937eSOliver Neukum 206afba937eSOliver Neukum req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); 207afba937eSOliver Neukum req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; 208afba937eSOliver Neukum req->wValue = 0; 209afba937eSOliver Neukum req->wIndex = desc->inum; 21087d65e54SOliver Neukum req->wLength = cpu_to_le16(desc->wMaxCommand); 211afba937eSOliver Neukum 212afba937eSOliver Neukum usb_fill_control_urb( 213afba937eSOliver Neukum desc->response, 214afba937eSOliver Neukum interface_to_usbdev(desc->intf), 215afba937eSOliver Neukum /* using common endpoint 0 */ 216afba937eSOliver Neukum usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), 217afba937eSOliver Neukum (unsigned char *)req, 218afba937eSOliver Neukum desc->inbuf, 21987d65e54SOliver Neukum desc->wMaxCommand, 220afba937eSOliver Neukum wdm_in_callback, 221afba937eSOliver Neukum desc 222afba937eSOliver Neukum ); 223afba937eSOliver Neukum desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 224afba937eSOliver Neukum spin_lock(&desc->iuspin); 225afba937eSOliver Neukum clear_bit(WDM_READ, &desc->flags); 226afba937eSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { 227afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_ATOMIC); 228afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", 229afba937eSOliver Neukum __func__, rv); 230afba937eSOliver Neukum } 231afba937eSOliver Neukum spin_unlock(&desc->iuspin); 232afba937eSOliver Neukum if (rv < 0) { 233afba937eSOliver Neukum if (rv == -EPERM) 234afba937eSOliver Neukum return; 235afba937eSOliver Neukum if (rv == -ENOMEM) { 236afba937eSOliver Neukum sw: 237afba937eSOliver Neukum rv = schedule_work(&desc->rxwork); 238afba937eSOliver Neukum if (rv) 239afba937eSOliver Neukum err("Cannot schedule work"); 240afba937eSOliver Neukum } 241afba937eSOliver Neukum } 242afba937eSOliver Neukum exit: 243afba937eSOliver Neukum rv = usb_submit_urb(urb, GFP_ATOMIC); 244afba937eSOliver Neukum if (rv) 245afba937eSOliver Neukum err("%s - usb_submit_urb failed with result %d", 246afba937eSOliver Neukum __func__, rv); 247afba937eSOliver Neukum 248afba937eSOliver Neukum } 249afba937eSOliver Neukum 250afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc) 251afba937eSOliver Neukum { 25217d80d56SOliver Neukum /* the order here is essential */ 253afba937eSOliver Neukum usb_kill_urb(desc->command); 254afba937eSOliver Neukum usb_kill_urb(desc->validity); 255afba937eSOliver Neukum usb_kill_urb(desc->response); 256afba937eSOliver Neukum } 257afba937eSOliver Neukum 258afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc) 259afba937eSOliver Neukum { 260afba937eSOliver Neukum usb_free_urb(desc->validity); 261afba937eSOliver Neukum usb_free_urb(desc->response); 262afba937eSOliver Neukum usb_free_urb(desc->command); 263afba937eSOliver Neukum } 264afba937eSOliver Neukum 265afba937eSOliver Neukum static void cleanup(struct wdm_device *desc) 266afba937eSOliver Neukum { 267afba937eSOliver Neukum usb_buffer_free(interface_to_usbdev(desc->intf), 268afba937eSOliver Neukum desc->wMaxPacketSize, 269afba937eSOliver Neukum desc->sbuf, 270afba937eSOliver Neukum desc->validity->transfer_dma); 271afba937eSOliver Neukum usb_buffer_free(interface_to_usbdev(desc->intf), 27287d65e54SOliver Neukum desc->wMaxCommand, 273afba937eSOliver Neukum desc->inbuf, 274afba937eSOliver Neukum desc->response->transfer_dma); 275afba937eSOliver Neukum kfree(desc->orq); 276afba937eSOliver Neukum kfree(desc->irq); 277afba937eSOliver Neukum kfree(desc->ubuf); 278afba937eSOliver Neukum free_urbs(desc); 279afba937eSOliver Neukum kfree(desc); 280afba937eSOliver Neukum } 281afba937eSOliver Neukum 282afba937eSOliver Neukum static ssize_t wdm_write 283afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 284afba937eSOliver Neukum { 285afba937eSOliver Neukum u8 *buf; 286afba937eSOliver Neukum int rv = -EMSGSIZE, r, we; 287afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 288afba937eSOliver Neukum struct usb_ctrlrequest *req; 289afba937eSOliver Neukum 290afba937eSOliver Neukum if (count > desc->wMaxCommand) 291afba937eSOliver Neukum count = desc->wMaxCommand; 292afba937eSOliver Neukum 293afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 294afba937eSOliver Neukum we = desc->werr; 295afba937eSOliver Neukum desc->werr = 0; 296afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 297afba937eSOliver Neukum if (we < 0) 298afba937eSOliver Neukum return -EIO; 299afba937eSOliver Neukum 300afba937eSOliver Neukum r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */ 301afba937eSOliver Neukum rv = -ERESTARTSYS; 302afba937eSOliver Neukum if (r) 303afba937eSOliver Neukum goto outnl; 304afba937eSOliver Neukum 30517d80d56SOliver Neukum r = usb_autopm_get_interface(desc->intf); 30617d80d56SOliver Neukum if (r < 0) 30717d80d56SOliver Neukum goto outnp; 308afba937eSOliver Neukum r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, 309afba937eSOliver Neukum &desc->flags)); 310afba937eSOliver Neukum if (r < 0) 311afba937eSOliver Neukum goto out; 312afba937eSOliver Neukum 313afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 314afba937eSOliver Neukum rv = -ENODEV; 315afba937eSOliver Neukum goto out; 316afba937eSOliver Neukum } 317afba937eSOliver Neukum 318afba937eSOliver Neukum desc->outbuf = buf = kmalloc(count, GFP_KERNEL); 319afba937eSOliver Neukum if (!buf) { 320afba937eSOliver Neukum rv = -ENOMEM; 321afba937eSOliver Neukum goto out; 322afba937eSOliver Neukum } 323afba937eSOliver Neukum 324afba937eSOliver Neukum r = copy_from_user(buf, buffer, count); 325afba937eSOliver Neukum if (r > 0) { 326afba937eSOliver Neukum kfree(buf); 327afba937eSOliver Neukum rv = -EFAULT; 328afba937eSOliver Neukum goto out; 329afba937eSOliver Neukum } 330afba937eSOliver Neukum 331afba937eSOliver Neukum req = desc->orq; 332afba937eSOliver Neukum usb_fill_control_urb( 333afba937eSOliver Neukum desc->command, 334afba937eSOliver Neukum interface_to_usbdev(desc->intf), 335afba937eSOliver Neukum /* using common endpoint 0 */ 336afba937eSOliver Neukum usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0), 337afba937eSOliver Neukum (unsigned char *)req, 338afba937eSOliver Neukum buf, 339afba937eSOliver Neukum count, 340afba937eSOliver Neukum wdm_out_callback, 341afba937eSOliver Neukum desc 342afba937eSOliver Neukum ); 343afba937eSOliver Neukum 344afba937eSOliver Neukum req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | 345afba937eSOliver Neukum USB_RECIP_INTERFACE); 346afba937eSOliver Neukum req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 347afba937eSOliver Neukum req->wValue = 0; 348afba937eSOliver Neukum req->wIndex = desc->inum; 349afba937eSOliver Neukum req->wLength = cpu_to_le16(count); 350afba937eSOliver Neukum set_bit(WDM_IN_USE, &desc->flags); 351afba937eSOliver Neukum 352afba937eSOliver Neukum rv = usb_submit_urb(desc->command, GFP_KERNEL); 353afba937eSOliver Neukum if (rv < 0) { 354afba937eSOliver Neukum kfree(buf); 355afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 35687d65e54SOliver Neukum err("Tx URB error: %d", rv); 357afba937eSOliver Neukum } else { 358afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", 359afba937eSOliver Neukum req->wIndex); 360afba937eSOliver Neukum } 361afba937eSOliver Neukum out: 36217d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 36317d80d56SOliver Neukum outnp: 364afba937eSOliver Neukum mutex_unlock(&desc->wlock); 365afba937eSOliver Neukum outnl: 366afba937eSOliver Neukum return rv < 0 ? rv : count; 367afba937eSOliver Neukum } 368afba937eSOliver Neukum 369afba937eSOliver Neukum static ssize_t wdm_read 370afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos) 371afba937eSOliver Neukum { 372afba937eSOliver Neukum int rv, cntr; 373afba937eSOliver Neukum int i = 0; 374afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 375afba937eSOliver Neukum 376afba937eSOliver Neukum 377afba937eSOliver Neukum rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ 378afba937eSOliver Neukum if (rv < 0) 379afba937eSOliver Neukum return -ERESTARTSYS; 380afba937eSOliver Neukum 381afba937eSOliver Neukum if (desc->length == 0) { 382afba937eSOliver Neukum desc->read = 0; 383afba937eSOliver Neukum retry: 384afba937eSOliver Neukum i++; 385afba937eSOliver Neukum rv = wait_event_interruptible(desc->wait, 386afba937eSOliver Neukum test_bit(WDM_READ, &desc->flags)); 387afba937eSOliver Neukum 38817d80d56SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 38917d80d56SOliver Neukum rv = -ENODEV; 39017d80d56SOliver Neukum goto err; 39117d80d56SOliver Neukum } 39217d80d56SOliver Neukum usb_mark_last_busy(interface_to_usbdev(desc->intf)); 393afba937eSOliver Neukum if (rv < 0) { 394afba937eSOliver Neukum rv = -ERESTARTSYS; 395afba937eSOliver Neukum goto err; 396afba937eSOliver Neukum } 397afba937eSOliver Neukum 398afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 399afba937eSOliver Neukum 400afba937eSOliver Neukum if (desc->rerr) { /* read completed, error happened */ 401afba937eSOliver Neukum int t = desc->rerr; 402afba937eSOliver Neukum desc->rerr = 0; 403afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 404afba937eSOliver Neukum err("reading had resulted in %d", t); 405afba937eSOliver Neukum rv = -EIO; 406afba937eSOliver Neukum goto err; 407afba937eSOliver Neukum } 408afba937eSOliver Neukum /* 409afba937eSOliver Neukum * recheck whether we've lost the race 410afba937eSOliver Neukum * against the completion handler 411afba937eSOliver Neukum */ 412afba937eSOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */ 413afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 414afba937eSOliver Neukum goto retry; 415afba937eSOliver Neukum } 416afba937eSOliver Neukum if (!desc->reslength) { /* zero length read */ 417afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 418afba937eSOliver Neukum goto retry; 419afba937eSOliver Neukum } 420afba937eSOliver Neukum clear_bit(WDM_READ, &desc->flags); 421afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 422afba937eSOliver Neukum } 423afba937eSOliver Neukum 424afba937eSOliver Neukum cntr = count > desc->length ? desc->length : count; 425afba937eSOliver Neukum rv = copy_to_user(buffer, desc->ubuf, cntr); 426afba937eSOliver Neukum if (rv > 0) { 427afba937eSOliver Neukum rv = -EFAULT; 428afba937eSOliver Neukum goto err; 429afba937eSOliver Neukum } 430afba937eSOliver Neukum 431afba937eSOliver Neukum for (i = 0; i < desc->length - cntr; i++) 432afba937eSOliver Neukum desc->ubuf[i] = desc->ubuf[i + cntr]; 433afba937eSOliver Neukum 434afba937eSOliver Neukum desc->length -= cntr; 43587d65e54SOliver Neukum /* in case we had outstanding data */ 43687d65e54SOliver Neukum if (!desc->length) 43787d65e54SOliver Neukum clear_bit(WDM_READ, &desc->flags); 438afba937eSOliver Neukum rv = cntr; 439afba937eSOliver Neukum 440afba937eSOliver Neukum err: 441afba937eSOliver Neukum mutex_unlock(&desc->rlock); 442afba937eSOliver Neukum if (rv < 0) 443afba937eSOliver Neukum err("wdm_read: exit error"); 444afba937eSOliver Neukum return rv; 445afba937eSOliver Neukum } 446afba937eSOliver Neukum 447afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id) 448afba937eSOliver Neukum { 449afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 450afba937eSOliver Neukum 451afba937eSOliver Neukum wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); 452afba937eSOliver Neukum if (desc->werr < 0) 453afba937eSOliver Neukum err("Error in flush path: %d", desc->werr); 454afba937eSOliver Neukum 455afba937eSOliver Neukum return desc->werr; 456afba937eSOliver Neukum } 457afba937eSOliver Neukum 458afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) 459afba937eSOliver Neukum { 460afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 461afba937eSOliver Neukum unsigned long flags; 462afba937eSOliver Neukum unsigned int mask = 0; 463afba937eSOliver Neukum 464afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 465afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 466afba937eSOliver Neukum mask = POLLERR; 467afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 468afba937eSOliver Neukum goto desc_out; 469afba937eSOliver Neukum } 470afba937eSOliver Neukum if (test_bit(WDM_READ, &desc->flags)) 471afba937eSOliver Neukum mask = POLLIN | POLLRDNORM; 472afba937eSOliver Neukum if (desc->rerr || desc->werr) 473afba937eSOliver Neukum mask |= POLLERR; 474afba937eSOliver Neukum if (!test_bit(WDM_IN_USE, &desc->flags)) 475afba937eSOliver Neukum mask |= POLLOUT | POLLWRNORM; 476afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 477afba937eSOliver Neukum 478afba937eSOliver Neukum poll_wait(file, &desc->wait, wait); 479afba937eSOliver Neukum 480afba937eSOliver Neukum desc_out: 481afba937eSOliver Neukum return mask; 482afba937eSOliver Neukum } 483afba937eSOliver Neukum 484afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file) 485afba937eSOliver Neukum { 486afba937eSOliver Neukum int minor = iminor(inode); 487afba937eSOliver Neukum int rv = -ENODEV; 488afba937eSOliver Neukum struct usb_interface *intf; 489afba937eSOliver Neukum struct wdm_device *desc; 490afba937eSOliver Neukum 491afba937eSOliver Neukum mutex_lock(&wdm_mutex); 492afba937eSOliver Neukum intf = usb_find_interface(&wdm_driver, minor); 493afba937eSOliver Neukum if (!intf) 494afba937eSOliver Neukum goto out; 495afba937eSOliver Neukum 496afba937eSOliver Neukum desc = usb_get_intfdata(intf); 497afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 498afba937eSOliver Neukum goto out; 499afba937eSOliver Neukum 50017d80d56SOliver Neukum ; 501afba937eSOliver Neukum file->private_data = desc; 502afba937eSOliver Neukum 50317d80d56SOliver Neukum rv = usb_autopm_get_interface(desc->intf); 50417d80d56SOliver Neukum if (rv < 0) { 50517d80d56SOliver Neukum err("Error autopm - %d", rv); 50617d80d56SOliver Neukum goto out; 50717d80d56SOliver Neukum } 50817d80d56SOliver Neukum intf->needs_remote_wakeup = 1; 509afba937eSOliver Neukum 51017d80d56SOliver Neukum mutex_lock(&desc->plock); 51117d80d56SOliver Neukum if (!desc->count++) { 51217d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_KERNEL); 513afba937eSOliver Neukum if (rv < 0) { 514afba937eSOliver Neukum desc->count--; 515afba937eSOliver Neukum err("Error submitting int urb - %d", rv); 516afba937eSOliver Neukum } 51717d80d56SOliver Neukum } else { 518afba937eSOliver Neukum rv = 0; 51917d80d56SOliver Neukum } 52017d80d56SOliver Neukum mutex_unlock(&desc->plock); 52117d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 522afba937eSOliver Neukum out: 523afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 524afba937eSOliver Neukum return rv; 525afba937eSOliver Neukum } 526afba937eSOliver Neukum 527afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file) 528afba937eSOliver Neukum { 529afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 530afba937eSOliver Neukum 531afba937eSOliver Neukum mutex_lock(&wdm_mutex); 53217d80d56SOliver Neukum mutex_lock(&desc->plock); 533afba937eSOliver Neukum desc->count--; 53417d80d56SOliver Neukum mutex_unlock(&desc->plock); 53517d80d56SOliver Neukum 536afba937eSOliver Neukum if (!desc->count) { 537afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); 538afba937eSOliver Neukum kill_urbs(desc); 53917d80d56SOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 54017d80d56SOliver Neukum desc->intf->needs_remote_wakeup = 0; 541afba937eSOliver Neukum } 542afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 543afba937eSOliver Neukum return 0; 544afba937eSOliver Neukum } 545afba937eSOliver Neukum 546afba937eSOliver Neukum static const struct file_operations wdm_fops = { 547afba937eSOliver Neukum .owner = THIS_MODULE, 548afba937eSOliver Neukum .read = wdm_read, 549afba937eSOliver Neukum .write = wdm_write, 550afba937eSOliver Neukum .open = wdm_open, 551afba937eSOliver Neukum .flush = wdm_flush, 552afba937eSOliver Neukum .release = wdm_release, 553afba937eSOliver Neukum .poll = wdm_poll 554afba937eSOliver Neukum }; 555afba937eSOliver Neukum 556afba937eSOliver Neukum static struct usb_class_driver wdm_class = { 557afba937eSOliver Neukum .name = "cdc-wdm%d", 558afba937eSOliver Neukum .fops = &wdm_fops, 559afba937eSOliver Neukum .minor_base = WDM_MINOR_BASE, 560afba937eSOliver Neukum }; 561afba937eSOliver Neukum 562afba937eSOliver Neukum /* --- error handling --- */ 563afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work) 564afba937eSOliver Neukum { 565afba937eSOliver Neukum struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); 566afba937eSOliver Neukum unsigned long flags; 567afba937eSOliver Neukum int rv; 568afba937eSOliver Neukum 569afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 570afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 571afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 572afba937eSOliver Neukum } else { 573afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 574afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_KERNEL); 575afba937eSOliver Neukum if (rv < 0 && rv != -EPERM) { 576afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 577afba937eSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 578afba937eSOliver Neukum schedule_work(&desc->rxwork); 579afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 580afba937eSOliver Neukum } 581afba937eSOliver Neukum } 582afba937eSOliver Neukum } 583afba937eSOliver Neukum 584afba937eSOliver Neukum /* --- hotplug --- */ 585afba937eSOliver Neukum 586afba937eSOliver Neukum static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) 587afba937eSOliver Neukum { 588afba937eSOliver Neukum int rv = -EINVAL; 589afba937eSOliver Neukum struct usb_device *udev = interface_to_usbdev(intf); 590afba937eSOliver Neukum struct wdm_device *desc; 591afba937eSOliver Neukum struct usb_host_interface *iface; 592afba937eSOliver Neukum struct usb_endpoint_descriptor *ep; 593afba937eSOliver Neukum struct usb_cdc_dmm_desc *dmhd; 594afba937eSOliver Neukum u8 *buffer = intf->altsetting->extra; 595afba937eSOliver Neukum int buflen = intf->altsetting->extralen; 596afba937eSOliver Neukum u16 maxcom = 0; 597afba937eSOliver Neukum 598afba937eSOliver Neukum if (!buffer) 599afba937eSOliver Neukum goto out; 600afba937eSOliver Neukum 601afba937eSOliver Neukum while (buflen > 0) { 602afba937eSOliver Neukum if (buffer [1] != USB_DT_CS_INTERFACE) { 603afba937eSOliver Neukum err("skipping garbage"); 604afba937eSOliver Neukum goto next_desc; 605afba937eSOliver Neukum } 606afba937eSOliver Neukum 607afba937eSOliver Neukum switch (buffer [2]) { 608afba937eSOliver Neukum case USB_CDC_HEADER_TYPE: 609afba937eSOliver Neukum break; 610afba937eSOliver Neukum case USB_CDC_DMM_TYPE: 611afba937eSOliver Neukum dmhd = (struct usb_cdc_dmm_desc *)buffer; 612afba937eSOliver Neukum maxcom = le16_to_cpu(dmhd->wMaxCommand); 613afba937eSOliver Neukum dev_dbg(&intf->dev, 614afba937eSOliver Neukum "Finding maximum buffer length: %d", maxcom); 615afba937eSOliver Neukum break; 616afba937eSOliver Neukum default: 617afba937eSOliver Neukum err("Ignoring extra header, type %d, length %d", 618afba937eSOliver Neukum buffer[2], buffer[0]); 619afba937eSOliver Neukum break; 620afba937eSOliver Neukum } 621afba937eSOliver Neukum next_desc: 622afba937eSOliver Neukum buflen -= buffer[0]; 623afba937eSOliver Neukum buffer += buffer[0]; 624afba937eSOliver Neukum } 625afba937eSOliver Neukum 626afba937eSOliver Neukum rv = -ENOMEM; 627afba937eSOliver Neukum desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); 628afba937eSOliver Neukum if (!desc) 629afba937eSOliver Neukum goto out; 630afba937eSOliver Neukum mutex_init(&desc->wlock); 631afba937eSOliver Neukum mutex_init(&desc->rlock); 63217d80d56SOliver Neukum mutex_init(&desc->plock); 633afba937eSOliver Neukum spin_lock_init(&desc->iuspin); 634afba937eSOliver Neukum init_waitqueue_head(&desc->wait); 635afba937eSOliver Neukum desc->wMaxCommand = maxcom; 636afba937eSOliver Neukum desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); 637afba937eSOliver Neukum desc->intf = intf; 638afba937eSOliver Neukum INIT_WORK(&desc->rxwork, wdm_rxwork); 639afba937eSOliver Neukum 640afba937eSOliver Neukum iface = &intf->altsetting[0]; 641afba937eSOliver Neukum ep = &iface->endpoint[0].desc; 642afba937eSOliver Neukum if (!usb_endpoint_is_int_in(ep)) { 643afba937eSOliver Neukum rv = -EINVAL; 644afba937eSOliver Neukum goto err; 645afba937eSOliver Neukum } 646afba937eSOliver Neukum 647fa4144b7SAl Viro desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); 648fa4144b7SAl Viro desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0; 649afba937eSOliver Neukum 650afba937eSOliver Neukum desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 651afba937eSOliver Neukum if (!desc->orq) 652afba937eSOliver Neukum goto err; 653afba937eSOliver Neukum desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 654afba937eSOliver Neukum if (!desc->irq) 655afba937eSOliver Neukum goto err; 656afba937eSOliver Neukum 657afba937eSOliver Neukum desc->validity = usb_alloc_urb(0, GFP_KERNEL); 658afba937eSOliver Neukum if (!desc->validity) 659afba937eSOliver Neukum goto err; 660afba937eSOliver Neukum 661afba937eSOliver Neukum desc->response = usb_alloc_urb(0, GFP_KERNEL); 662afba937eSOliver Neukum if (!desc->response) 663afba937eSOliver Neukum goto err; 664afba937eSOliver Neukum 665afba937eSOliver Neukum desc->command = usb_alloc_urb(0, GFP_KERNEL); 666afba937eSOliver Neukum if (!desc->command) 667afba937eSOliver Neukum goto err; 668afba937eSOliver Neukum 669afba937eSOliver Neukum desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 670afba937eSOliver Neukum if (!desc->ubuf) 671afba937eSOliver Neukum goto err; 672afba937eSOliver Neukum 673afba937eSOliver Neukum desc->sbuf = usb_buffer_alloc(interface_to_usbdev(intf), 674afba937eSOliver Neukum desc->wMaxPacketSize, 675afba937eSOliver Neukum GFP_KERNEL, 676afba937eSOliver Neukum &desc->validity->transfer_dma); 677afba937eSOliver Neukum if (!desc->sbuf) 678afba937eSOliver Neukum goto err; 679afba937eSOliver Neukum 680afba937eSOliver Neukum desc->inbuf = usb_buffer_alloc(interface_to_usbdev(intf), 681afba937eSOliver Neukum desc->bMaxPacketSize0, 682afba937eSOliver Neukum GFP_KERNEL, 683afba937eSOliver Neukum &desc->response->transfer_dma); 684afba937eSOliver Neukum if (!desc->inbuf) 685afba937eSOliver Neukum goto err2; 686afba937eSOliver Neukum 687afba937eSOliver Neukum usb_fill_int_urb( 688afba937eSOliver Neukum desc->validity, 689afba937eSOliver Neukum interface_to_usbdev(intf), 690afba937eSOliver Neukum usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress), 691afba937eSOliver Neukum desc->sbuf, 692afba937eSOliver Neukum desc->wMaxPacketSize, 693afba937eSOliver Neukum wdm_int_callback, 694afba937eSOliver Neukum desc, 695afba937eSOliver Neukum ep->bInterval 696afba937eSOliver Neukum ); 697afba937eSOliver Neukum desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 698afba937eSOliver Neukum 699afba937eSOliver Neukum usb_set_intfdata(intf, desc); 700afba937eSOliver Neukum rv = usb_register_dev(intf, &wdm_class); 701afba937eSOliver Neukum dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n", 702afba937eSOliver Neukum intf->minor - WDM_MINOR_BASE); 703afba937eSOliver Neukum if (rv < 0) 704afba937eSOliver Neukum goto err; 705afba937eSOliver Neukum out: 706afba937eSOliver Neukum return rv; 707afba937eSOliver Neukum err2: 708afba937eSOliver Neukum usb_buffer_free(interface_to_usbdev(desc->intf), 709afba937eSOliver Neukum desc->wMaxPacketSize, 710afba937eSOliver Neukum desc->sbuf, 711afba937eSOliver Neukum desc->validity->transfer_dma); 712afba937eSOliver Neukum err: 713afba937eSOliver Neukum free_urbs(desc); 714afba937eSOliver Neukum kfree(desc->ubuf); 715afba937eSOliver Neukum kfree(desc->orq); 716afba937eSOliver Neukum kfree(desc->irq); 717afba937eSOliver Neukum kfree(desc); 718afba937eSOliver Neukum return rv; 719afba937eSOliver Neukum } 720afba937eSOliver Neukum 721afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf) 722afba937eSOliver Neukum { 723afba937eSOliver Neukum struct wdm_device *desc; 724afba937eSOliver Neukum unsigned long flags; 725afba937eSOliver Neukum 726afba937eSOliver Neukum usb_deregister_dev(intf, &wdm_class); 727afba937eSOliver Neukum mutex_lock(&wdm_mutex); 728afba937eSOliver Neukum desc = usb_get_intfdata(intf); 729afba937eSOliver Neukum 730afba937eSOliver Neukum /* the spinlock makes sure no new urbs are generated in the callbacks */ 731afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 732afba937eSOliver Neukum set_bit(WDM_DISCONNECTING, &desc->flags); 733afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 73417d80d56SOliver Neukum /* to terminate pending flushes */ 735afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 736afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 737afba937eSOliver Neukum cancel_work_sync(&desc->rxwork); 738afba937eSOliver Neukum kill_urbs(desc); 739afba937eSOliver Neukum wake_up_all(&desc->wait); 740afba937eSOliver Neukum if (!desc->count) 741afba937eSOliver Neukum cleanup(desc); 742afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 743afba937eSOliver Neukum } 744afba937eSOliver Neukum 74517d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message) 74617d80d56SOliver Neukum { 74717d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 74817d80d56SOliver Neukum int rv = 0; 74917d80d56SOliver Neukum 75017d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); 75117d80d56SOliver Neukum 75217d80d56SOliver Neukum mutex_lock(&desc->plock); 75335758589SOliver Neukum #ifdef CONFIG_PM 75417d80d56SOliver Neukum if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { 75517d80d56SOliver Neukum rv = -EBUSY; 75617d80d56SOliver Neukum } else { 75735758589SOliver Neukum #endif 75817d80d56SOliver Neukum cancel_work_sync(&desc->rxwork); 75917d80d56SOliver Neukum kill_urbs(desc); 76035758589SOliver Neukum #ifdef CONFIG_PM 76117d80d56SOliver Neukum } 76235758589SOliver Neukum #endif 76317d80d56SOliver Neukum mutex_unlock(&desc->plock); 76417d80d56SOliver Neukum 76517d80d56SOliver Neukum return rv; 76617d80d56SOliver Neukum } 76717d80d56SOliver Neukum 76817d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc) 76917d80d56SOliver Neukum { 77017d80d56SOliver Neukum int rv = 0; 77117d80d56SOliver Neukum 77217d80d56SOliver Neukum if (desc->count) { 77317d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_NOIO); 77417d80d56SOliver Neukum if (rv < 0) 77517d80d56SOliver Neukum err("Error resume submitting int urb - %d", rv); 77617d80d56SOliver Neukum } 77717d80d56SOliver Neukum return rv; 77817d80d56SOliver Neukum } 77917d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf) 78017d80d56SOliver Neukum { 78117d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 78217d80d56SOliver Neukum int rv; 78317d80d56SOliver Neukum 78417d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); 78517d80d56SOliver Neukum mutex_lock(&desc->plock); 78617d80d56SOliver Neukum rv = recover_from_urb_loss(desc); 78717d80d56SOliver Neukum mutex_unlock(&desc->plock); 78817d80d56SOliver Neukum return rv; 78917d80d56SOliver Neukum } 79017d80d56SOliver Neukum 79117d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf) 79217d80d56SOliver Neukum { 79317d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 79417d80d56SOliver Neukum 79517d80d56SOliver Neukum mutex_lock(&desc->plock); 79617d80d56SOliver Neukum return 0; 79717d80d56SOliver Neukum } 79817d80d56SOliver Neukum 79917d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf) 80017d80d56SOliver Neukum { 80117d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 80217d80d56SOliver Neukum int rv; 80317d80d56SOliver Neukum 80417d80d56SOliver Neukum rv = recover_from_urb_loss(desc); 80517d80d56SOliver Neukum mutex_unlock(&desc->plock); 80617d80d56SOliver Neukum return 0; 80717d80d56SOliver Neukum } 80817d80d56SOliver Neukum 809afba937eSOliver Neukum static struct usb_driver wdm_driver = { 810afba937eSOliver Neukum .name = "cdc_wdm", 811afba937eSOliver Neukum .probe = wdm_probe, 812afba937eSOliver Neukum .disconnect = wdm_disconnect, 81317d80d56SOliver Neukum .suspend = wdm_suspend, 81417d80d56SOliver Neukum .resume = wdm_resume, 81517d80d56SOliver Neukum .reset_resume = wdm_resume, 81617d80d56SOliver Neukum .pre_reset = wdm_pre_reset, 81717d80d56SOliver Neukum .post_reset = wdm_post_reset, 818afba937eSOliver Neukum .id_table = wdm_ids, 81917d80d56SOliver Neukum .supports_autosuspend = 1, 820afba937eSOliver Neukum }; 821afba937eSOliver Neukum 822afba937eSOliver Neukum /* --- low level module stuff --- */ 823afba937eSOliver Neukum 824afba937eSOliver Neukum static int __init wdm_init(void) 825afba937eSOliver Neukum { 826afba937eSOliver Neukum int rv; 827afba937eSOliver Neukum 828afba937eSOliver Neukum rv = usb_register(&wdm_driver); 829afba937eSOliver Neukum 830afba937eSOliver Neukum return rv; 831afba937eSOliver Neukum } 832afba937eSOliver Neukum 833afba937eSOliver Neukum static void __exit wdm_exit(void) 834afba937eSOliver Neukum { 835afba937eSOliver Neukum usb_deregister(&wdm_driver); 836afba937eSOliver Neukum } 837afba937eSOliver Neukum 838afba937eSOliver Neukum module_init(wdm_init); 839afba937eSOliver Neukum module_exit(wdm_exit); 840afba937eSOliver Neukum 841afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR); 84287d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC); 843afba937eSOliver Neukum MODULE_LICENSE("GPL"); 844