1afba937eSOliver Neukum /* 2afba937eSOliver Neukum * cdc-wdm.c 3afba937eSOliver Neukum * 4afba937eSOliver Neukum * This driver supports USB CDC WCM Device Management. 5afba937eSOliver Neukum * 6052fbc0dSOliver Neukum * Copyright (c) 2007-2009 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/mutex.h> 19afba937eSOliver Neukum #include <linux/uaccess.h> 20afba937eSOliver Neukum #include <linux/bitops.h> 21afba937eSOliver Neukum #include <linux/poll.h> 22afba937eSOliver Neukum #include <linux/usb.h> 23afba937eSOliver Neukum #include <linux/usb/cdc.h> 24afba937eSOliver Neukum #include <asm/byteorder.h> 25afba937eSOliver Neukum #include <asm/unaligned.h> 26afba937eSOliver Neukum 27afba937eSOliver Neukum /* 28afba937eSOliver Neukum * Version Information 29afba937eSOliver Neukum */ 3087d65e54SOliver Neukum #define DRIVER_VERSION "v0.03" 31afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum" 3287d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" 33afba937eSOliver Neukum 346ef4852bSNémeth Márton static const struct usb_device_id wdm_ids[] = { 35afba937eSOliver Neukum { 36afba937eSOliver Neukum .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | 37afba937eSOliver Neukum USB_DEVICE_ID_MATCH_INT_SUBCLASS, 38afba937eSOliver Neukum .bInterfaceClass = USB_CLASS_COMM, 39afba937eSOliver Neukum .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM 40afba937eSOliver Neukum }, 41afba937eSOliver Neukum { } 42afba937eSOliver Neukum }; 43afba937eSOliver Neukum 44aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids); 45aa5380b9SOliver Neukum 46afba937eSOliver Neukum #define WDM_MINOR_BASE 176 47afba937eSOliver Neukum 48afba937eSOliver Neukum 49afba937eSOliver Neukum #define WDM_IN_USE 1 50afba937eSOliver Neukum #define WDM_DISCONNECTING 2 51afba937eSOliver Neukum #define WDM_RESULT 3 52afba937eSOliver Neukum #define WDM_READ 4 53afba937eSOliver Neukum #define WDM_INT_STALL 5 54afba937eSOliver Neukum #define WDM_POLL_RUNNING 6 55922a5eadSOliver Neukum #define WDM_RESPONDING 7 56beb1d35fSOliver Neukum #define WDM_SUSPENDING 8 57afba937eSOliver Neukum 58afba937eSOliver Neukum #define WDM_MAX 16 59afba937eSOliver Neukum 60afba937eSOliver Neukum 61afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex); 62afba937eSOliver Neukum 63afba937eSOliver Neukum /* --- method tables --- */ 64afba937eSOliver Neukum 65afba937eSOliver Neukum struct wdm_device { 66afba937eSOliver Neukum u8 *inbuf; /* buffer for response */ 67afba937eSOliver Neukum u8 *outbuf; /* buffer for command */ 68afba937eSOliver Neukum u8 *sbuf; /* buffer for status */ 69afba937eSOliver Neukum u8 *ubuf; /* buffer for copy to user space */ 70afba937eSOliver Neukum 71afba937eSOliver Neukum struct urb *command; 72afba937eSOliver Neukum struct urb *response; 73afba937eSOliver Neukum struct urb *validity; 74afba937eSOliver Neukum struct usb_interface *intf; 75afba937eSOliver Neukum struct usb_ctrlrequest *orq; 76afba937eSOliver Neukum struct usb_ctrlrequest *irq; 77afba937eSOliver Neukum spinlock_t iuspin; 78afba937eSOliver Neukum 79afba937eSOliver Neukum unsigned long flags; 80afba937eSOliver Neukum u16 bufsize; 81afba937eSOliver Neukum u16 wMaxCommand; 82afba937eSOliver Neukum u16 wMaxPacketSize; 83afba937eSOliver Neukum u16 bMaxPacketSize0; 84afba937eSOliver Neukum __le16 inum; 85afba937eSOliver Neukum int reslength; 86afba937eSOliver Neukum int length; 87afba937eSOliver Neukum int read; 88afba937eSOliver Neukum int count; 89afba937eSOliver Neukum dma_addr_t shandle; 90afba937eSOliver Neukum dma_addr_t ihandle; 91860e41a7SOliver Neukum struct mutex lock; 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); 119922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 120afba937eSOliver Neukum 121afba937eSOliver Neukum if (status) { 122afba937eSOliver Neukum switch (status) { 123afba937eSOliver Neukum case -ENOENT: 124afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 125afba937eSOliver Neukum "nonzero urb status received: -ENOENT"); 126922a5eadSOliver Neukum goto skip_error; 127afba937eSOliver Neukum case -ECONNRESET: 128afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 129afba937eSOliver Neukum "nonzero urb status received: -ECONNRESET"); 130922a5eadSOliver Neukum goto skip_error; 131afba937eSOliver Neukum case -ESHUTDOWN: 132afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 133afba937eSOliver Neukum "nonzero urb status received: -ESHUTDOWN"); 134922a5eadSOliver Neukum goto skip_error; 135afba937eSOliver Neukum case -EPIPE: 1369908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 1379908a32eSGreg Kroah-Hartman "nonzero urb status received: -EPIPE\n"); 138afba937eSOliver Neukum break; 139afba937eSOliver Neukum default: 1409908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 1419908a32eSGreg Kroah-Hartman "Unexpected error %d\n", status); 142afba937eSOliver Neukum break; 143afba937eSOliver Neukum } 144afba937eSOliver Neukum } 145afba937eSOliver Neukum 146afba937eSOliver Neukum desc->rerr = status; 147afba937eSOliver Neukum desc->reslength = urb->actual_length; 148afba937eSOliver Neukum memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); 149afba937eSOliver Neukum desc->length += desc->reslength; 150922a5eadSOliver Neukum skip_error: 151afba937eSOliver Neukum wake_up(&desc->wait); 152afba937eSOliver Neukum 153afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 154afba937eSOliver Neukum spin_unlock(&desc->iuspin); 155afba937eSOliver Neukum } 156afba937eSOliver Neukum 157afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb) 158afba937eSOliver Neukum { 159afba937eSOliver Neukum int rv = 0; 160afba937eSOliver Neukum int status = urb->status; 161afba937eSOliver Neukum struct wdm_device *desc; 162afba937eSOliver Neukum struct usb_ctrlrequest *req; 163afba937eSOliver Neukum struct usb_cdc_notification *dr; 164afba937eSOliver Neukum 165afba937eSOliver Neukum desc = urb->context; 166afba937eSOliver Neukum req = desc->irq; 167afba937eSOliver Neukum dr = (struct usb_cdc_notification *)desc->sbuf; 168afba937eSOliver Neukum 169afba937eSOliver Neukum if (status) { 170afba937eSOliver Neukum switch (status) { 171afba937eSOliver Neukum case -ESHUTDOWN: 172afba937eSOliver Neukum case -ENOENT: 173afba937eSOliver Neukum case -ECONNRESET: 174afba937eSOliver Neukum return; /* unplug */ 175afba937eSOliver Neukum case -EPIPE: 176afba937eSOliver Neukum set_bit(WDM_INT_STALL, &desc->flags); 1779908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Stall on int endpoint\n"); 178afba937eSOliver Neukum goto sw; /* halt is cleared in work */ 179afba937eSOliver Neukum default: 1809908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 1819908a32eSGreg Kroah-Hartman "nonzero urb status received: %d\n", status); 182afba937eSOliver Neukum break; 183afba937eSOliver Neukum } 184afba937eSOliver Neukum } 185afba937eSOliver Neukum 186afba937eSOliver Neukum if (urb->actual_length < sizeof(struct usb_cdc_notification)) { 1879908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n", 1889908a32eSGreg Kroah-Hartman urb->actual_length); 189afba937eSOliver Neukum goto exit; 190afba937eSOliver Neukum } 191afba937eSOliver Neukum 192afba937eSOliver Neukum switch (dr->bNotificationType) { 193afba937eSOliver Neukum case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: 194afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 195afba937eSOliver Neukum "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d", 196afba937eSOliver Neukum dr->wIndex, dr->wLength); 197afba937eSOliver Neukum break; 198afba937eSOliver Neukum 199afba937eSOliver Neukum case USB_CDC_NOTIFY_NETWORK_CONNECTION: 200afba937eSOliver Neukum 201afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 202afba937eSOliver Neukum "NOTIFY_NETWORK_CONNECTION %s network", 203afba937eSOliver Neukum dr->wValue ? "connected to" : "disconnected from"); 204afba937eSOliver Neukum goto exit; 205afba937eSOliver Neukum default: 206afba937eSOliver Neukum clear_bit(WDM_POLL_RUNNING, &desc->flags); 2079908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2089908a32eSGreg Kroah-Hartman "unknown notification %d received: index %d len %d\n", 209afba937eSOliver Neukum dr->bNotificationType, dr->wIndex, dr->wLength); 210afba937eSOliver Neukum goto exit; 211afba937eSOliver Neukum } 212afba937eSOliver Neukum 213afba937eSOliver Neukum req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); 214afba937eSOliver Neukum req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; 215afba937eSOliver Neukum req->wValue = 0; 216afba937eSOliver Neukum req->wIndex = desc->inum; 21787d65e54SOliver Neukum req->wLength = cpu_to_le16(desc->wMaxCommand); 218afba937eSOliver Neukum 219afba937eSOliver Neukum usb_fill_control_urb( 220afba937eSOliver Neukum desc->response, 221afba937eSOliver Neukum interface_to_usbdev(desc->intf), 222afba937eSOliver Neukum /* using common endpoint 0 */ 223afba937eSOliver Neukum usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), 224afba937eSOliver Neukum (unsigned char *)req, 225afba937eSOliver Neukum desc->inbuf, 22687d65e54SOliver Neukum desc->wMaxCommand, 227afba937eSOliver Neukum wdm_in_callback, 228afba937eSOliver Neukum desc 229afba937eSOliver Neukum ); 230afba937eSOliver Neukum desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 231afba937eSOliver Neukum spin_lock(&desc->iuspin); 232afba937eSOliver Neukum clear_bit(WDM_READ, &desc->flags); 233922a5eadSOliver Neukum set_bit(WDM_RESPONDING, &desc->flags); 234beb1d35fSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags) 235beb1d35fSOliver Neukum && !test_bit(WDM_SUSPENDING, &desc->flags)) { 236afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_ATOMIC); 237afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", 238afba937eSOliver Neukum __func__, rv); 239afba937eSOliver Neukum } 240afba937eSOliver Neukum spin_unlock(&desc->iuspin); 241afba937eSOliver Neukum if (rv < 0) { 242922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 243afba937eSOliver Neukum if (rv == -EPERM) 244afba937eSOliver Neukum return; 245afba937eSOliver Neukum if (rv == -ENOMEM) { 246afba937eSOliver Neukum sw: 247afba937eSOliver Neukum rv = schedule_work(&desc->rxwork); 248afba937eSOliver Neukum if (rv) 2499908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2509908a32eSGreg Kroah-Hartman "Cannot schedule work\n"); 251afba937eSOliver Neukum } 252afba937eSOliver Neukum } 253afba937eSOliver Neukum exit: 254afba937eSOliver Neukum rv = usb_submit_urb(urb, GFP_ATOMIC); 255afba937eSOliver Neukum if (rv) 2569908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2579908a32eSGreg Kroah-Hartman "%s - usb_submit_urb failed with result %d\n", 258afba937eSOliver Neukum __func__, rv); 259afba937eSOliver Neukum 260afba937eSOliver Neukum } 261afba937eSOliver Neukum 262afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc) 263afba937eSOliver Neukum { 26417d80d56SOliver Neukum /* the order here is essential */ 265afba937eSOliver Neukum usb_kill_urb(desc->command); 266afba937eSOliver Neukum usb_kill_urb(desc->validity); 267afba937eSOliver Neukum usb_kill_urb(desc->response); 268afba937eSOliver Neukum } 269afba937eSOliver Neukum 270afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc) 271afba937eSOliver Neukum { 272afba937eSOliver Neukum usb_free_urb(desc->validity); 273afba937eSOliver Neukum usb_free_urb(desc->response); 274afba937eSOliver Neukum usb_free_urb(desc->command); 275afba937eSOliver Neukum } 276afba937eSOliver Neukum 277afba937eSOliver Neukum static void cleanup(struct wdm_device *desc) 278afba937eSOliver Neukum { 279997ea58eSDaniel Mack usb_free_coherent(interface_to_usbdev(desc->intf), 280afba937eSOliver Neukum desc->wMaxPacketSize, 281afba937eSOliver Neukum desc->sbuf, 282afba937eSOliver Neukum desc->validity->transfer_dma); 283997ea58eSDaniel Mack usb_free_coherent(interface_to_usbdev(desc->intf), 284878b753eSRobert Lukassen desc->bMaxPacketSize0, 285afba937eSOliver Neukum desc->inbuf, 286afba937eSOliver Neukum desc->response->transfer_dma); 287afba937eSOliver Neukum kfree(desc->orq); 288afba937eSOliver Neukum kfree(desc->irq); 289afba937eSOliver Neukum kfree(desc->ubuf); 290afba937eSOliver Neukum free_urbs(desc); 291afba937eSOliver Neukum kfree(desc); 292afba937eSOliver Neukum } 293afba937eSOliver Neukum 294afba937eSOliver Neukum static ssize_t wdm_write 295afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 296afba937eSOliver Neukum { 297afba937eSOliver Neukum u8 *buf; 298afba937eSOliver Neukum int rv = -EMSGSIZE, r, we; 299afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 300afba937eSOliver Neukum struct usb_ctrlrequest *req; 301afba937eSOliver Neukum 302afba937eSOliver Neukum if (count > desc->wMaxCommand) 303afba937eSOliver Neukum count = desc->wMaxCommand; 304afba937eSOliver Neukum 305afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 306afba937eSOliver Neukum we = desc->werr; 307afba937eSOliver Neukum desc->werr = 0; 308afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 309afba937eSOliver Neukum if (we < 0) 310afba937eSOliver Neukum return -EIO; 311afba937eSOliver Neukum 312860e41a7SOliver Neukum desc->outbuf = buf = kmalloc(count, GFP_KERNEL); 313860e41a7SOliver Neukum if (!buf) { 314860e41a7SOliver Neukum rv = -ENOMEM; 315afba937eSOliver Neukum goto outnl; 316860e41a7SOliver Neukum } 317860e41a7SOliver Neukum 318860e41a7SOliver Neukum r = copy_from_user(buf, buffer, count); 319860e41a7SOliver Neukum if (r > 0) { 320860e41a7SOliver Neukum kfree(buf); 321860e41a7SOliver Neukum rv = -EFAULT; 322860e41a7SOliver Neukum goto outnl; 323860e41a7SOliver Neukum } 324860e41a7SOliver Neukum 325860e41a7SOliver Neukum /* concurrent writes and disconnect */ 326860e41a7SOliver Neukum r = mutex_lock_interruptible(&desc->lock); 327860e41a7SOliver Neukum rv = -ERESTARTSYS; 328860e41a7SOliver Neukum if (r) { 329860e41a7SOliver Neukum kfree(buf); 330860e41a7SOliver Neukum goto outnl; 331860e41a7SOliver Neukum } 332860e41a7SOliver Neukum 333860e41a7SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 334860e41a7SOliver Neukum kfree(buf); 335860e41a7SOliver Neukum rv = -ENODEV; 336860e41a7SOliver Neukum goto outnp; 337860e41a7SOliver Neukum } 338afba937eSOliver Neukum 33917d80d56SOliver Neukum r = usb_autopm_get_interface(desc->intf); 340860e41a7SOliver Neukum if (r < 0) { 341860e41a7SOliver Neukum kfree(buf); 34217d80d56SOliver Neukum goto outnp; 343860e41a7SOliver Neukum } 3447f1dc313SOliver Neukum 3450cdfb819SDavid Sterba if (!(file->f_flags & O_NONBLOCK)) 346afba937eSOliver Neukum r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, 347afba937eSOliver Neukum &desc->flags)); 3487f1dc313SOliver Neukum else 3497f1dc313SOliver Neukum if (test_bit(WDM_IN_USE, &desc->flags)) 3507f1dc313SOliver Neukum r = -EAGAIN; 351860e41a7SOliver Neukum if (r < 0) { 352afba937eSOliver Neukum kfree(buf); 353afba937eSOliver Neukum goto out; 354afba937eSOliver Neukum } 355afba937eSOliver Neukum 356afba937eSOliver Neukum req = desc->orq; 357afba937eSOliver Neukum usb_fill_control_urb( 358afba937eSOliver Neukum desc->command, 359afba937eSOliver Neukum interface_to_usbdev(desc->intf), 360afba937eSOliver Neukum /* using common endpoint 0 */ 361afba937eSOliver Neukum usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0), 362afba937eSOliver Neukum (unsigned char *)req, 363afba937eSOliver Neukum buf, 364afba937eSOliver Neukum count, 365afba937eSOliver Neukum wdm_out_callback, 366afba937eSOliver Neukum desc 367afba937eSOliver Neukum ); 368afba937eSOliver Neukum 369afba937eSOliver Neukum req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | 370afba937eSOliver Neukum USB_RECIP_INTERFACE); 371afba937eSOliver Neukum req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 372afba937eSOliver Neukum req->wValue = 0; 373afba937eSOliver Neukum req->wIndex = desc->inum; 374afba937eSOliver Neukum req->wLength = cpu_to_le16(count); 375afba937eSOliver Neukum set_bit(WDM_IN_USE, &desc->flags); 376afba937eSOliver Neukum 377afba937eSOliver Neukum rv = usb_submit_urb(desc->command, GFP_KERNEL); 378afba937eSOliver Neukum if (rv < 0) { 379afba937eSOliver Neukum kfree(buf); 380afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 3819908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); 382afba937eSOliver Neukum } else { 383afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", 384afba937eSOliver Neukum req->wIndex); 385afba937eSOliver Neukum } 386afba937eSOliver Neukum out: 38717d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 38817d80d56SOliver Neukum outnp: 389860e41a7SOliver Neukum mutex_unlock(&desc->lock); 390afba937eSOliver Neukum outnl: 391afba937eSOliver Neukum return rv < 0 ? rv : count; 392afba937eSOliver Neukum } 393afba937eSOliver Neukum 394afba937eSOliver Neukum static ssize_t wdm_read 395afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos) 396afba937eSOliver Neukum { 3977f1dc313SOliver Neukum int rv, cntr = 0; 398afba937eSOliver Neukum int i = 0; 399afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 400afba937eSOliver Neukum 401afba937eSOliver Neukum 402860e41a7SOliver Neukum rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */ 403afba937eSOliver Neukum if (rv < 0) 404afba937eSOliver Neukum return -ERESTARTSYS; 405afba937eSOliver Neukum 406afba937eSOliver Neukum if (desc->length == 0) { 407afba937eSOliver Neukum desc->read = 0; 408afba937eSOliver Neukum retry: 4097f1dc313SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 4107f1dc313SOliver Neukum rv = -ENODEV; 4117f1dc313SOliver Neukum goto err; 4127f1dc313SOliver Neukum } 413afba937eSOliver Neukum i++; 4147f1dc313SOliver Neukum if (file->f_flags & O_NONBLOCK) { 4157f1dc313SOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { 4167f1dc313SOliver Neukum rv = cntr ? cntr : -EAGAIN; 4177f1dc313SOliver Neukum goto err; 4187f1dc313SOliver Neukum } 4197f1dc313SOliver Neukum rv = 0; 4207f1dc313SOliver Neukum } else { 421afba937eSOliver Neukum rv = wait_event_interruptible(desc->wait, 422afba937eSOliver Neukum test_bit(WDM_READ, &desc->flags)); 4237f1dc313SOliver Neukum } 424afba937eSOliver Neukum 4257f1dc313SOliver Neukum /* may have happened while we slept */ 42617d80d56SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 42717d80d56SOliver Neukum rv = -ENODEV; 42817d80d56SOliver Neukum goto err; 42917d80d56SOliver Neukum } 43017d80d56SOliver Neukum usb_mark_last_busy(interface_to_usbdev(desc->intf)); 431afba937eSOliver Neukum if (rv < 0) { 432afba937eSOliver Neukum rv = -ERESTARTSYS; 433afba937eSOliver Neukum goto err; 434afba937eSOliver Neukum } 435afba937eSOliver Neukum 436afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 437afba937eSOliver Neukum 438afba937eSOliver Neukum if (desc->rerr) { /* read completed, error happened */ 439afba937eSOliver Neukum desc->rerr = 0; 440afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 441afba937eSOliver Neukum rv = -EIO; 442afba937eSOliver Neukum goto err; 443afba937eSOliver Neukum } 444afba937eSOliver Neukum /* 445afba937eSOliver Neukum * recheck whether we've lost the race 446afba937eSOliver Neukum * against the completion handler 447afba937eSOliver Neukum */ 448afba937eSOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */ 449afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 450afba937eSOliver Neukum goto retry; 451afba937eSOliver Neukum } 452afba937eSOliver Neukum if (!desc->reslength) { /* zero length read */ 453afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 454afba937eSOliver Neukum goto retry; 455afba937eSOliver Neukum } 456afba937eSOliver Neukum clear_bit(WDM_READ, &desc->flags); 457afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 458afba937eSOliver Neukum } 459afba937eSOliver Neukum 460afba937eSOliver Neukum cntr = count > desc->length ? desc->length : count; 461afba937eSOliver Neukum rv = copy_to_user(buffer, desc->ubuf, cntr); 462afba937eSOliver Neukum if (rv > 0) { 463afba937eSOliver Neukum rv = -EFAULT; 464afba937eSOliver Neukum goto err; 465afba937eSOliver Neukum } 466afba937eSOliver Neukum 467afba937eSOliver Neukum for (i = 0; i < desc->length - cntr; i++) 468afba937eSOliver Neukum desc->ubuf[i] = desc->ubuf[i + cntr]; 469afba937eSOliver Neukum 470afba937eSOliver Neukum desc->length -= cntr; 47187d65e54SOliver Neukum /* in case we had outstanding data */ 47287d65e54SOliver Neukum if (!desc->length) 47387d65e54SOliver Neukum clear_bit(WDM_READ, &desc->flags); 474afba937eSOliver Neukum rv = cntr; 475afba937eSOliver Neukum 476afba937eSOliver Neukum err: 477860e41a7SOliver Neukum mutex_unlock(&desc->lock); 478afba937eSOliver Neukum return rv; 479afba937eSOliver Neukum } 480afba937eSOliver Neukum 481afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id) 482afba937eSOliver Neukum { 483afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 484afba937eSOliver Neukum 485afba937eSOliver Neukum wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); 486afba937eSOliver Neukum if (desc->werr < 0) 4879908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error in flush path: %d\n", 4889908a32eSGreg Kroah-Hartman desc->werr); 489afba937eSOliver Neukum 490afba937eSOliver Neukum return desc->werr; 491afba937eSOliver Neukum } 492afba937eSOliver Neukum 493afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) 494afba937eSOliver Neukum { 495afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 496afba937eSOliver Neukum unsigned long flags; 497afba937eSOliver Neukum unsigned int mask = 0; 498afba937eSOliver Neukum 499afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 500afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 501afba937eSOliver Neukum mask = POLLERR; 502afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 503afba937eSOliver Neukum goto desc_out; 504afba937eSOliver Neukum } 505afba937eSOliver Neukum if (test_bit(WDM_READ, &desc->flags)) 506afba937eSOliver Neukum mask = POLLIN | POLLRDNORM; 507afba937eSOliver Neukum if (desc->rerr || desc->werr) 508afba937eSOliver Neukum mask |= POLLERR; 509afba937eSOliver Neukum if (!test_bit(WDM_IN_USE, &desc->flags)) 510afba937eSOliver Neukum mask |= POLLOUT | POLLWRNORM; 511afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 512afba937eSOliver Neukum 513afba937eSOliver Neukum poll_wait(file, &desc->wait, wait); 514afba937eSOliver Neukum 515afba937eSOliver Neukum desc_out: 516afba937eSOliver Neukum return mask; 517afba937eSOliver Neukum } 518afba937eSOliver Neukum 519afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file) 520afba937eSOliver Neukum { 521afba937eSOliver Neukum int minor = iminor(inode); 522afba937eSOliver Neukum int rv = -ENODEV; 523afba937eSOliver Neukum struct usb_interface *intf; 524afba937eSOliver Neukum struct wdm_device *desc; 525afba937eSOliver Neukum 526afba937eSOliver Neukum mutex_lock(&wdm_mutex); 527afba937eSOliver Neukum intf = usb_find_interface(&wdm_driver, minor); 528afba937eSOliver Neukum if (!intf) 529afba937eSOliver Neukum goto out; 530afba937eSOliver Neukum 531afba937eSOliver Neukum desc = usb_get_intfdata(intf); 532afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 533afba937eSOliver Neukum goto out; 534afba937eSOliver Neukum file->private_data = desc; 535afba937eSOliver Neukum 53617d80d56SOliver Neukum rv = usb_autopm_get_interface(desc->intf); 53717d80d56SOliver Neukum if (rv < 0) { 5389908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); 53917d80d56SOliver Neukum goto out; 54017d80d56SOliver Neukum } 54117d80d56SOliver Neukum intf->needs_remote_wakeup = 1; 542afba937eSOliver Neukum 543860e41a7SOliver Neukum mutex_lock(&desc->lock); 54417d80d56SOliver Neukum if (!desc->count++) { 545d771d8aaSOliver Neukum desc->werr = 0; 546d771d8aaSOliver Neukum desc->rerr = 0; 54717d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_KERNEL); 548afba937eSOliver Neukum if (rv < 0) { 549afba937eSOliver Neukum desc->count--; 5509908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 5519908a32eSGreg Kroah-Hartman "Error submitting int urb - %d\n", rv); 552afba937eSOliver Neukum } 55317d80d56SOliver Neukum } else { 554afba937eSOliver Neukum rv = 0; 55517d80d56SOliver Neukum } 556860e41a7SOliver Neukum mutex_unlock(&desc->lock); 55717d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 558afba937eSOliver Neukum out: 559afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 560afba937eSOliver Neukum return rv; 561afba937eSOliver Neukum } 562afba937eSOliver Neukum 563afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file) 564afba937eSOliver Neukum { 565afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 566afba937eSOliver Neukum 567afba937eSOliver Neukum mutex_lock(&wdm_mutex); 568860e41a7SOliver Neukum mutex_lock(&desc->lock); 569afba937eSOliver Neukum desc->count--; 570860e41a7SOliver Neukum mutex_unlock(&desc->lock); 57117d80d56SOliver Neukum 572afba937eSOliver Neukum if (!desc->count) { 573afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); 574afba937eSOliver Neukum kill_urbs(desc); 57517d80d56SOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 57617d80d56SOliver Neukum desc->intf->needs_remote_wakeup = 0; 577afba937eSOliver Neukum } 578afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 579afba937eSOliver Neukum return 0; 580afba937eSOliver Neukum } 581afba937eSOliver Neukum 582afba937eSOliver Neukum static const struct file_operations wdm_fops = { 583afba937eSOliver Neukum .owner = THIS_MODULE, 584afba937eSOliver Neukum .read = wdm_read, 585afba937eSOliver Neukum .write = wdm_write, 586afba937eSOliver Neukum .open = wdm_open, 587afba937eSOliver Neukum .flush = wdm_flush, 588afba937eSOliver Neukum .release = wdm_release, 5896038f373SArnd Bergmann .poll = wdm_poll, 5906038f373SArnd Bergmann .llseek = noop_llseek, 591afba937eSOliver Neukum }; 592afba937eSOliver Neukum 593afba937eSOliver Neukum static struct usb_class_driver wdm_class = { 594afba937eSOliver Neukum .name = "cdc-wdm%d", 595afba937eSOliver Neukum .fops = &wdm_fops, 596afba937eSOliver Neukum .minor_base = WDM_MINOR_BASE, 597afba937eSOliver Neukum }; 598afba937eSOliver Neukum 599afba937eSOliver Neukum /* --- error handling --- */ 600afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work) 601afba937eSOliver Neukum { 602afba937eSOliver Neukum struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); 603afba937eSOliver Neukum unsigned long flags; 604afba937eSOliver Neukum int rv; 605afba937eSOliver Neukum 606afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 607afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 608afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 609afba937eSOliver Neukum } else { 610afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 611afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_KERNEL); 612afba937eSOliver Neukum if (rv < 0 && rv != -EPERM) { 613afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 614afba937eSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 615afba937eSOliver Neukum schedule_work(&desc->rxwork); 616afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 617afba937eSOliver Neukum } 618afba937eSOliver Neukum } 619afba937eSOliver Neukum } 620afba937eSOliver Neukum 621afba937eSOliver Neukum /* --- hotplug --- */ 622afba937eSOliver Neukum 623afba937eSOliver Neukum static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) 624afba937eSOliver Neukum { 625afba937eSOliver Neukum int rv = -EINVAL; 626afba937eSOliver Neukum struct usb_device *udev = interface_to_usbdev(intf); 627afba937eSOliver Neukum struct wdm_device *desc; 628afba937eSOliver Neukum struct usb_host_interface *iface; 629afba937eSOliver Neukum struct usb_endpoint_descriptor *ep; 630afba937eSOliver Neukum struct usb_cdc_dmm_desc *dmhd; 631afba937eSOliver Neukum u8 *buffer = intf->altsetting->extra; 632afba937eSOliver Neukum int buflen = intf->altsetting->extralen; 633afba937eSOliver Neukum u16 maxcom = 0; 634afba937eSOliver Neukum 635afba937eSOliver Neukum if (!buffer) 636afba937eSOliver Neukum goto out; 637afba937eSOliver Neukum 638052fbc0dSOliver Neukum while (buflen > 2) { 639afba937eSOliver Neukum if (buffer [1] != USB_DT_CS_INTERFACE) { 6409908a32eSGreg Kroah-Hartman dev_err(&intf->dev, "skipping garbage\n"); 641afba937eSOliver Neukum goto next_desc; 642afba937eSOliver Neukum } 643afba937eSOliver Neukum 644afba937eSOliver Neukum switch (buffer [2]) { 645afba937eSOliver Neukum case USB_CDC_HEADER_TYPE: 646afba937eSOliver Neukum break; 647afba937eSOliver Neukum case USB_CDC_DMM_TYPE: 648afba937eSOliver Neukum dmhd = (struct usb_cdc_dmm_desc *)buffer; 649afba937eSOliver Neukum maxcom = le16_to_cpu(dmhd->wMaxCommand); 650afba937eSOliver Neukum dev_dbg(&intf->dev, 651afba937eSOliver Neukum "Finding maximum buffer length: %d", maxcom); 652afba937eSOliver Neukum break; 653afba937eSOliver Neukum default: 6549908a32eSGreg Kroah-Hartman dev_err(&intf->dev, 6559908a32eSGreg Kroah-Hartman "Ignoring extra header, type %d, length %d\n", 656afba937eSOliver Neukum buffer[2], buffer[0]); 657afba937eSOliver Neukum break; 658afba937eSOliver Neukum } 659afba937eSOliver Neukum next_desc: 660afba937eSOliver Neukum buflen -= buffer[0]; 661afba937eSOliver Neukum buffer += buffer[0]; 662afba937eSOliver Neukum } 663afba937eSOliver Neukum 664afba937eSOliver Neukum rv = -ENOMEM; 665afba937eSOliver Neukum desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); 666afba937eSOliver Neukum if (!desc) 667afba937eSOliver Neukum goto out; 668860e41a7SOliver Neukum mutex_init(&desc->lock); 669afba937eSOliver Neukum spin_lock_init(&desc->iuspin); 670afba937eSOliver Neukum init_waitqueue_head(&desc->wait); 671afba937eSOliver Neukum desc->wMaxCommand = maxcom; 672052fbc0dSOliver Neukum /* this will be expanded and needed in hardware endianness */ 673afba937eSOliver Neukum desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); 674afba937eSOliver Neukum desc->intf = intf; 675afba937eSOliver Neukum INIT_WORK(&desc->rxwork, wdm_rxwork); 676afba937eSOliver Neukum 677afba937eSOliver Neukum rv = -EINVAL; 678052fbc0dSOliver Neukum iface = intf->cur_altsetting; 679052fbc0dSOliver Neukum if (iface->desc.bNumEndpoints != 1) 680afba937eSOliver Neukum goto err; 681052fbc0dSOliver Neukum ep = &iface->endpoint[0].desc; 682052fbc0dSOliver Neukum if (!ep || !usb_endpoint_is_int_in(ep)) 683052fbc0dSOliver Neukum goto err; 684afba937eSOliver Neukum 68529cc8897SKuninori Morimoto desc->wMaxPacketSize = usb_endpoint_maxp(ep); 686fa4144b7SAl Viro desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0; 687afba937eSOliver Neukum 688afba937eSOliver Neukum desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 689afba937eSOliver Neukum if (!desc->orq) 690afba937eSOliver Neukum goto err; 691afba937eSOliver Neukum desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 692afba937eSOliver Neukum if (!desc->irq) 693afba937eSOliver Neukum goto err; 694afba937eSOliver Neukum 695afba937eSOliver Neukum desc->validity = usb_alloc_urb(0, GFP_KERNEL); 696afba937eSOliver Neukum if (!desc->validity) 697afba937eSOliver Neukum goto err; 698afba937eSOliver Neukum 699afba937eSOliver Neukum desc->response = usb_alloc_urb(0, GFP_KERNEL); 700afba937eSOliver Neukum if (!desc->response) 701afba937eSOliver Neukum goto err; 702afba937eSOliver Neukum 703afba937eSOliver Neukum desc->command = usb_alloc_urb(0, GFP_KERNEL); 704afba937eSOliver Neukum if (!desc->command) 705afba937eSOliver Neukum goto err; 706afba937eSOliver Neukum 707afba937eSOliver Neukum desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 708afba937eSOliver Neukum if (!desc->ubuf) 709afba937eSOliver Neukum goto err; 710afba937eSOliver Neukum 711997ea58eSDaniel Mack desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf), 712afba937eSOliver Neukum desc->wMaxPacketSize, 713afba937eSOliver Neukum GFP_KERNEL, 714afba937eSOliver Neukum &desc->validity->transfer_dma); 715afba937eSOliver Neukum if (!desc->sbuf) 716afba937eSOliver Neukum goto err; 717afba937eSOliver Neukum 718997ea58eSDaniel Mack desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), 719afba937eSOliver Neukum desc->bMaxPacketSize0, 720afba937eSOliver Neukum GFP_KERNEL, 721afba937eSOliver Neukum &desc->response->transfer_dma); 722afba937eSOliver Neukum if (!desc->inbuf) 723afba937eSOliver Neukum goto err2; 724afba937eSOliver Neukum 725afba937eSOliver Neukum usb_fill_int_urb( 726afba937eSOliver Neukum desc->validity, 727afba937eSOliver Neukum interface_to_usbdev(intf), 728afba937eSOliver Neukum usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress), 729afba937eSOliver Neukum desc->sbuf, 730afba937eSOliver Neukum desc->wMaxPacketSize, 731afba937eSOliver Neukum wdm_int_callback, 732afba937eSOliver Neukum desc, 733afba937eSOliver Neukum ep->bInterval 734afba937eSOliver Neukum ); 735afba937eSOliver Neukum desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 736afba937eSOliver Neukum 737afba937eSOliver Neukum usb_set_intfdata(intf, desc); 738afba937eSOliver Neukum rv = usb_register_dev(intf, &wdm_class); 739052fbc0dSOliver Neukum if (rv < 0) 740052fbc0dSOliver Neukum goto err3; 741052fbc0dSOliver Neukum else 742afba937eSOliver Neukum dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n", 743afba937eSOliver Neukum intf->minor - WDM_MINOR_BASE); 744afba937eSOliver Neukum out: 745afba937eSOliver Neukum return rv; 746052fbc0dSOliver Neukum err3: 747052fbc0dSOliver Neukum usb_set_intfdata(intf, NULL); 748997ea58eSDaniel Mack usb_free_coherent(interface_to_usbdev(desc->intf), 749052fbc0dSOliver Neukum desc->bMaxPacketSize0, 750052fbc0dSOliver Neukum desc->inbuf, 751052fbc0dSOliver Neukum desc->response->transfer_dma); 752afba937eSOliver Neukum err2: 753997ea58eSDaniel Mack usb_free_coherent(interface_to_usbdev(desc->intf), 754afba937eSOliver Neukum desc->wMaxPacketSize, 755afba937eSOliver Neukum desc->sbuf, 756afba937eSOliver Neukum desc->validity->transfer_dma); 757afba937eSOliver Neukum err: 758afba937eSOliver Neukum free_urbs(desc); 759afba937eSOliver Neukum kfree(desc->ubuf); 760afba937eSOliver Neukum kfree(desc->orq); 761afba937eSOliver Neukum kfree(desc->irq); 762afba937eSOliver Neukum kfree(desc); 763afba937eSOliver Neukum return rv; 764afba937eSOliver Neukum } 765afba937eSOliver Neukum 766afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf) 767afba937eSOliver Neukum { 768afba937eSOliver Neukum struct wdm_device *desc; 769afba937eSOliver Neukum unsigned long flags; 770afba937eSOliver Neukum 771afba937eSOliver Neukum usb_deregister_dev(intf, &wdm_class); 772afba937eSOliver Neukum mutex_lock(&wdm_mutex); 773afba937eSOliver Neukum desc = usb_get_intfdata(intf); 774afba937eSOliver Neukum 775afba937eSOliver Neukum /* the spinlock makes sure no new urbs are generated in the callbacks */ 776afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 777afba937eSOliver Neukum set_bit(WDM_DISCONNECTING, &desc->flags); 778afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 77917d80d56SOliver Neukum /* to terminate pending flushes */ 780afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 781afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 782860e41a7SOliver Neukum mutex_lock(&desc->lock); 783afba937eSOliver Neukum kill_urbs(desc); 784d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 785860e41a7SOliver Neukum mutex_unlock(&desc->lock); 786afba937eSOliver Neukum wake_up_all(&desc->wait); 787afba937eSOliver Neukum if (!desc->count) 788afba937eSOliver Neukum cleanup(desc); 789afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 790afba937eSOliver Neukum } 791afba937eSOliver Neukum 792d93d16e9SOliver Neukum #ifdef CONFIG_PM 79317d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message) 79417d80d56SOliver Neukum { 79517d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 79617d80d56SOliver Neukum int rv = 0; 79717d80d56SOliver Neukum 79817d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); 79917d80d56SOliver Neukum 800d93d16e9SOliver Neukum /* if this is an autosuspend the caller does the locking */ 8015b1b0b81SAlan Stern if (!PMSG_IS_AUTO(message)) 802860e41a7SOliver Neukum mutex_lock(&desc->lock); 80362e66854SOliver Neukum spin_lock_irq(&desc->iuspin); 804d93d16e9SOliver Neukum 8055b1b0b81SAlan Stern if (PMSG_IS_AUTO(message) && 806922a5eadSOliver Neukum (test_bit(WDM_IN_USE, &desc->flags) 807922a5eadSOliver Neukum || test_bit(WDM_RESPONDING, &desc->flags))) { 80862e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 80917d80d56SOliver Neukum rv = -EBUSY; 81017d80d56SOliver Neukum } else { 811d93d16e9SOliver Neukum 812beb1d35fSOliver Neukum set_bit(WDM_SUSPENDING, &desc->flags); 81362e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 814d93d16e9SOliver Neukum /* callback submits work - order is essential */ 81517d80d56SOliver Neukum kill_urbs(desc); 816d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 81717d80d56SOliver Neukum } 8185b1b0b81SAlan Stern if (!PMSG_IS_AUTO(message)) 819860e41a7SOliver Neukum mutex_unlock(&desc->lock); 82017d80d56SOliver Neukum 82117d80d56SOliver Neukum return rv; 82217d80d56SOliver Neukum } 823d93d16e9SOliver Neukum #endif 82417d80d56SOliver Neukum 82517d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc) 82617d80d56SOliver Neukum { 82717d80d56SOliver Neukum int rv = 0; 82817d80d56SOliver Neukum 82917d80d56SOliver Neukum if (desc->count) { 83017d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_NOIO); 83117d80d56SOliver Neukum if (rv < 0) 8329908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 8339908a32eSGreg Kroah-Hartman "Error resume submitting int urb - %d\n", rv); 83417d80d56SOliver Neukum } 83517d80d56SOliver Neukum return rv; 83617d80d56SOliver Neukum } 837d93d16e9SOliver Neukum 838d93d16e9SOliver Neukum #ifdef CONFIG_PM 83917d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf) 84017d80d56SOliver Neukum { 84117d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 84217d80d56SOliver Neukum int rv; 84317d80d56SOliver Neukum 84417d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); 845338124c1SOliver Neukum 846beb1d35fSOliver Neukum clear_bit(WDM_SUSPENDING, &desc->flags); 84762e66854SOliver Neukum rv = recover_from_urb_loss(desc); 848338124c1SOliver Neukum 84917d80d56SOliver Neukum return rv; 85017d80d56SOliver Neukum } 851d93d16e9SOliver Neukum #endif 85217d80d56SOliver Neukum 85317d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf) 85417d80d56SOliver Neukum { 85517d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 85617d80d56SOliver Neukum 857860e41a7SOliver Neukum mutex_lock(&desc->lock); 858d771d8aaSOliver Neukum kill_urbs(desc); 859d771d8aaSOliver Neukum 860d771d8aaSOliver Neukum /* 861d771d8aaSOliver Neukum * we notify everybody using poll of 862d771d8aaSOliver Neukum * an exceptional situation 863d771d8aaSOliver Neukum * must be done before recovery lest a spontaneous 864d771d8aaSOliver Neukum * message from the device is lost 865d771d8aaSOliver Neukum */ 866d771d8aaSOliver Neukum spin_lock_irq(&desc->iuspin); 867d771d8aaSOliver Neukum desc->rerr = -EINTR; 868d771d8aaSOliver Neukum spin_unlock_irq(&desc->iuspin); 869d771d8aaSOliver Neukum wake_up_all(&desc->wait); 87017d80d56SOliver Neukum return 0; 87117d80d56SOliver Neukum } 87217d80d56SOliver Neukum 87317d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf) 87417d80d56SOliver Neukum { 87517d80d56SOliver Neukum struct wdm_device *desc = usb_get_intfdata(intf); 87617d80d56SOliver Neukum int rv; 87717d80d56SOliver Neukum 87817d80d56SOliver Neukum rv = recover_from_urb_loss(desc); 879860e41a7SOliver Neukum mutex_unlock(&desc->lock); 88017d80d56SOliver Neukum return 0; 88117d80d56SOliver Neukum } 88217d80d56SOliver Neukum 883afba937eSOliver Neukum static struct usb_driver wdm_driver = { 884afba937eSOliver Neukum .name = "cdc_wdm", 885afba937eSOliver Neukum .probe = wdm_probe, 886afba937eSOliver Neukum .disconnect = wdm_disconnect, 887d93d16e9SOliver Neukum #ifdef CONFIG_PM 88817d80d56SOliver Neukum .suspend = wdm_suspend, 88917d80d56SOliver Neukum .resume = wdm_resume, 89017d80d56SOliver Neukum .reset_resume = wdm_resume, 891d93d16e9SOliver Neukum #endif 89217d80d56SOliver Neukum .pre_reset = wdm_pre_reset, 89317d80d56SOliver Neukum .post_reset = wdm_post_reset, 894afba937eSOliver Neukum .id_table = wdm_ids, 89517d80d56SOliver Neukum .supports_autosuspend = 1, 896afba937eSOliver Neukum }; 897afba937eSOliver Neukum 89865db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver); 899afba937eSOliver Neukum 900afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR); 90187d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC); 902afba937eSOliver Neukum MODULE_LICENSE("GPL"); 903