15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2afba937eSOliver Neukum /* 3afba937eSOliver Neukum * cdc-wdm.c 4afba937eSOliver Neukum * 5afba937eSOliver Neukum * This driver supports USB CDC WCM Device Management. 6afba937eSOliver Neukum * 7052fbc0dSOliver Neukum * Copyright (c) 2007-2009 Oliver Neukum 8afba937eSOliver Neukum * 9afba937eSOliver Neukum * Some code taken from cdc-acm.c 10afba937eSOliver Neukum * 11afba937eSOliver Neukum * Released under the GPLv2. 12afba937eSOliver Neukum * 13afba937eSOliver Neukum * Many thanks to Carl Nordbeck 14afba937eSOliver Neukum */ 15afba937eSOliver Neukum #include <linux/kernel.h> 16afba937eSOliver Neukum #include <linux/errno.h> 173edce1cfSBjørn Mork #include <linux/ioctl.h> 18afba937eSOliver Neukum #include <linux/slab.h> 19afba937eSOliver Neukum #include <linux/module.h> 20afba937eSOliver Neukum #include <linux/mutex.h> 21afba937eSOliver Neukum #include <linux/uaccess.h> 22afba937eSOliver Neukum #include <linux/bitops.h> 23afba937eSOliver Neukum #include <linux/poll.h> 24afba937eSOliver Neukum #include <linux/usb.h> 25afba937eSOliver Neukum #include <linux/usb/cdc.h> 26afba937eSOliver Neukum #include <asm/byteorder.h> 27afba937eSOliver Neukum #include <asm/unaligned.h> 283cc36157SBjørn Mork #include <linux/usb/cdc-wdm.h> 29afba937eSOliver Neukum 30afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum" 3187d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" 32afba937eSOliver Neukum 336ef4852bSNémeth Márton static const struct usb_device_id wdm_ids[] = { 34afba937eSOliver Neukum { 35afba937eSOliver Neukum .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | 36afba937eSOliver Neukum USB_DEVICE_ID_MATCH_INT_SUBCLASS, 37afba937eSOliver Neukum .bInterfaceClass = USB_CLASS_COMM, 38afba937eSOliver Neukum .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM 39afba937eSOliver Neukum }, 40afba937eSOliver Neukum { } 41afba937eSOliver Neukum }; 42afba937eSOliver Neukum 43aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids); 44aa5380b9SOliver 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 54922a5eadSOliver Neukum #define WDM_RESPONDING 7 55beb1d35fSOliver Neukum #define WDM_SUSPENDING 8 5688044202SBjørn Mork #define WDM_RESETTING 9 57c0f5eceeSOliver Neukum #define WDM_OVERFLOW 10 58afba937eSOliver Neukum 59afba937eSOliver Neukum #define WDM_MAX 16 60afba937eSOliver Neukum 6137d2a363SOliver Neukum /* we cannot wait forever at flush() */ 6237d2a363SOliver Neukum #define WDM_FLUSH_TIMEOUT (30 * HZ) 6337d2a363SOliver Neukum 647e3054a0SBjørn Mork /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ 657e3054a0SBjørn Mork #define WDM_DEFAULT_BUFSIZE 256 66afba937eSOliver Neukum 67afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex); 68b0c13860SBjørn Mork static DEFINE_SPINLOCK(wdm_device_list_lock); 69b0c13860SBjørn Mork static LIST_HEAD(wdm_device_list); 70afba937eSOliver Neukum 71afba937eSOliver Neukum /* --- method tables --- */ 72afba937eSOliver Neukum 73afba937eSOliver Neukum struct wdm_device { 74afba937eSOliver Neukum u8 *inbuf; /* buffer for response */ 75afba937eSOliver Neukum u8 *outbuf; /* buffer for command */ 76afba937eSOliver Neukum u8 *sbuf; /* buffer for status */ 77afba937eSOliver Neukum u8 *ubuf; /* buffer for copy to user space */ 78afba937eSOliver Neukum 79afba937eSOliver Neukum struct urb *command; 80afba937eSOliver Neukum struct urb *response; 81afba937eSOliver Neukum struct urb *validity; 82afba937eSOliver Neukum struct usb_interface *intf; 83afba937eSOliver Neukum struct usb_ctrlrequest *orq; 84afba937eSOliver Neukum struct usb_ctrlrequest *irq; 85afba937eSOliver Neukum spinlock_t iuspin; 86afba937eSOliver Neukum 87afba937eSOliver Neukum unsigned long flags; 88afba937eSOliver Neukum u16 bufsize; 89afba937eSOliver Neukum u16 wMaxCommand; 90afba937eSOliver Neukum u16 wMaxPacketSize; 91afba937eSOliver Neukum __le16 inum; 92afba937eSOliver Neukum int reslength; 93afba937eSOliver Neukum int length; 94afba937eSOliver Neukum int read; 95afba937eSOliver Neukum int count; 96afba937eSOliver Neukum dma_addr_t shandle; 97afba937eSOliver Neukum dma_addr_t ihandle; 98e8537bd2SBjørn Mork struct mutex wlock; 99e8537bd2SBjørn Mork struct mutex rlock; 100afba937eSOliver Neukum wait_queue_head_t wait; 101afba937eSOliver Neukum struct work_struct rxwork; 1022df69484SSebastian Andrzej Siewior struct work_struct service_outs_intr; 103afba937eSOliver Neukum int werr; 104afba937eSOliver Neukum int rerr; 10573e06865SGreg Suarez int resp_count; 106b0c13860SBjørn Mork 107b0c13860SBjørn Mork struct list_head device_list; 1083cc36157SBjørn Mork int (*manage_power)(struct usb_interface *, int); 109afba937eSOliver Neukum }; 110afba937eSOliver Neukum 111afba937eSOliver Neukum static struct usb_driver wdm_driver; 112afba937eSOliver Neukum 113b0c13860SBjørn Mork /* return intfdata if we own the interface, else look up intf in the list */ 114b0c13860SBjørn Mork static struct wdm_device *wdm_find_device(struct usb_interface *intf) 115b0c13860SBjørn Mork { 1166a448868SBjørn Mork struct wdm_device *desc; 117b0c13860SBjørn Mork 118b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 119b0c13860SBjørn Mork list_for_each_entry(desc, &wdm_device_list, device_list) 120b0c13860SBjørn Mork if (desc->intf == intf) 1216a448868SBjørn Mork goto found; 1226a448868SBjørn Mork desc = NULL; 1236a448868SBjørn Mork found: 124b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 125b0c13860SBjørn Mork 126b0c13860SBjørn Mork return desc; 127b0c13860SBjørn Mork } 128b0c13860SBjørn Mork 129b0c13860SBjørn Mork static struct wdm_device *wdm_find_device_by_minor(int minor) 130b0c13860SBjørn Mork { 1316a448868SBjørn Mork struct wdm_device *desc; 132b0c13860SBjørn Mork 133b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 134b0c13860SBjørn Mork list_for_each_entry(desc, &wdm_device_list, device_list) 135b0c13860SBjørn Mork if (desc->intf->minor == minor) 1366a448868SBjørn Mork goto found; 1376a448868SBjørn Mork desc = NULL; 1386a448868SBjørn Mork found: 139b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 140b0c13860SBjørn Mork 141b0c13860SBjørn Mork return desc; 142b0c13860SBjørn Mork } 143b0c13860SBjørn Mork 144afba937eSOliver Neukum /* --- callbacks --- */ 145afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb) 146afba937eSOliver Neukum { 147afba937eSOliver Neukum struct wdm_device *desc; 148579b9ccaSSebastian Andrzej Siewior unsigned long flags; 149579b9ccaSSebastian Andrzej Siewior 150afba937eSOliver Neukum desc = urb->context; 151579b9ccaSSebastian Andrzej Siewior spin_lock_irqsave(&desc->iuspin, flags); 152afba937eSOliver Neukum desc->werr = urb->status; 153579b9ccaSSebastian Andrzej Siewior spin_unlock_irqrestore(&desc->iuspin, flags); 154afba937eSOliver Neukum kfree(desc->outbuf); 1555c22837aSOliver Neukum desc->outbuf = NULL; 1565c22837aSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 15737d2a363SOliver Neukum wake_up_all(&desc->wait); 158afba937eSOliver Neukum } 159afba937eSOliver Neukum 160afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb) 161afba937eSOliver Neukum { 162579b9ccaSSebastian Andrzej Siewior unsigned long flags; 163afba937eSOliver Neukum struct wdm_device *desc = urb->context; 164afba937eSOliver Neukum int status = urb->status; 165c0f5eceeSOliver Neukum int length = urb->actual_length; 166afba937eSOliver Neukum 167579b9ccaSSebastian Andrzej Siewior spin_lock_irqsave(&desc->iuspin, flags); 168922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 169afba937eSOliver Neukum 170afba937eSOliver Neukum if (status) { 171afba937eSOliver Neukum switch (status) { 172afba937eSOliver Neukum case -ENOENT: 173afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 174ce8bb344SOliver Neukum "nonzero urb status received: -ENOENT\n"); 175922a5eadSOliver Neukum goto skip_error; 176afba937eSOliver Neukum case -ECONNRESET: 177afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 178ce8bb344SOliver Neukum "nonzero urb status received: -ECONNRESET\n"); 179922a5eadSOliver Neukum goto skip_error; 180afba937eSOliver Neukum case -ESHUTDOWN: 181afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 182ce8bb344SOliver Neukum "nonzero urb status received: -ESHUTDOWN\n"); 183922a5eadSOliver Neukum goto skip_error; 184afba937eSOliver Neukum case -EPIPE: 18519445816SBjørn Mork dev_err(&desc->intf->dev, 1869908a32eSGreg Kroah-Hartman "nonzero urb status received: -EPIPE\n"); 187afba937eSOliver Neukum break; 188afba937eSOliver Neukum default: 1899908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 1909908a32eSGreg Kroah-Hartman "Unexpected error %d\n", status); 191afba937eSOliver Neukum break; 192afba937eSOliver Neukum } 193afba937eSOliver Neukum } 194afba937eSOliver Neukum 195c1da59daSRobert Foss /* 196c1da59daSRobert Foss * only set a new error if there is no previous error. 197c1da59daSRobert Foss * Errors are only cleared during read/open 1988fec9355SBjørn Mork * Avoid propagating -EPIPE (stall) to userspace since it is 1998fec9355SBjørn Mork * better handled as an empty read 200c1da59daSRobert Foss */ 2018fec9355SBjørn Mork if (desc->rerr == 0 && status != -EPIPE) 202afba937eSOliver Neukum desc->rerr = status; 203c1da59daSRobert Foss 204c0f5eceeSOliver Neukum if (length + desc->length > desc->wMaxCommand) { 205c0f5eceeSOliver Neukum /* The buffer would overflow */ 206c0f5eceeSOliver Neukum set_bit(WDM_OVERFLOW, &desc->flags); 207c0f5eceeSOliver Neukum } else { 208c0f5eceeSOliver Neukum /* we may already be in overflow */ 209c0f5eceeSOliver Neukum if (!test_bit(WDM_OVERFLOW, &desc->flags)) { 210c0f5eceeSOliver Neukum memmove(desc->ubuf + desc->length, desc->inbuf, length); 211c0f5eceeSOliver Neukum desc->length += length; 212c0f5eceeSOliver Neukum desc->reslength = length; 213c0f5eceeSOliver Neukum } 214c0f5eceeSOliver Neukum } 215922a5eadSOliver Neukum skip_error: 216afba937eSOliver Neukum 217c1da59daSRobert Foss if (desc->rerr) { 218c1da59daSRobert Foss /* 219c1da59daSRobert Foss * Since there was an error, userspace may decide to not read 220c1da59daSRobert Foss * any data after poll'ing. 221c1da59daSRobert Foss * We should respond to further attempts from the device to send 222c1da59daSRobert Foss * data, so that we can get unstuck. 223c1da59daSRobert Foss */ 2242df69484SSebastian Andrzej Siewior schedule_work(&desc->service_outs_intr); 2252df69484SSebastian Andrzej Siewior } else { 2262df69484SSebastian Andrzej Siewior set_bit(WDM_READ, &desc->flags); 2272df69484SSebastian Andrzej Siewior wake_up(&desc->wait); 228c1da59daSRobert Foss } 229579b9ccaSSebastian Andrzej Siewior spin_unlock_irqrestore(&desc->iuspin, flags); 230afba937eSOliver Neukum } 231afba937eSOliver Neukum 232afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb) 233afba937eSOliver Neukum { 234579b9ccaSSebastian Andrzej Siewior unsigned long flags; 235afba937eSOliver Neukum int rv = 0; 2366dd433e6SOliver Neukum int responding; 237afba937eSOliver Neukum int status = urb->status; 238afba937eSOliver Neukum struct wdm_device *desc; 239afba937eSOliver Neukum struct usb_cdc_notification *dr; 240afba937eSOliver Neukum 241afba937eSOliver Neukum desc = urb->context; 242afba937eSOliver Neukum dr = (struct usb_cdc_notification *)desc->sbuf; 243afba937eSOliver Neukum 244afba937eSOliver Neukum if (status) { 245afba937eSOliver Neukum switch (status) { 246afba937eSOliver Neukum case -ESHUTDOWN: 247afba937eSOliver Neukum case -ENOENT: 248afba937eSOliver Neukum case -ECONNRESET: 249afba937eSOliver Neukum return; /* unplug */ 250afba937eSOliver Neukum case -EPIPE: 251afba937eSOliver Neukum set_bit(WDM_INT_STALL, &desc->flags); 2529908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Stall on int endpoint\n"); 253afba937eSOliver Neukum goto sw; /* halt is cleared in work */ 254afba937eSOliver Neukum default: 2559908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2569908a32eSGreg Kroah-Hartman "nonzero urb status received: %d\n", status); 257afba937eSOliver Neukum break; 258afba937eSOliver Neukum } 259afba937eSOliver Neukum } 260afba937eSOliver Neukum 261afba937eSOliver Neukum if (urb->actual_length < sizeof(struct usb_cdc_notification)) { 2629908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n", 2639908a32eSGreg Kroah-Hartman urb->actual_length); 264afba937eSOliver Neukum goto exit; 265afba937eSOliver Neukum } 266afba937eSOliver Neukum 267afba937eSOliver Neukum switch (dr->bNotificationType) { 268afba937eSOliver Neukum case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: 269afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 270ce8bb344SOliver Neukum "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d\n", 271323ece54SOliver Neukum le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength)); 272afba937eSOliver Neukum break; 273afba937eSOliver Neukum 274afba937eSOliver Neukum case USB_CDC_NOTIFY_NETWORK_CONNECTION: 275afba937eSOliver Neukum 276afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 277ce8bb344SOliver Neukum "NOTIFY_NETWORK_CONNECTION %s network\n", 278afba937eSOliver Neukum dr->wValue ? "connected to" : "disconnected from"); 279afba937eSOliver Neukum goto exit; 2809983d6dcSBjørn Mork case USB_CDC_NOTIFY_SPEED_CHANGE: 281ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)\n", 2829983d6dcSBjørn Mork urb->actual_length); 2839983d6dcSBjørn Mork goto exit; 284afba937eSOliver Neukum default: 285afba937eSOliver Neukum clear_bit(WDM_POLL_RUNNING, &desc->flags); 2869908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2879908a32eSGreg Kroah-Hartman "unknown notification %d received: index %d len %d\n", 288323ece54SOliver Neukum dr->bNotificationType, 289323ece54SOliver Neukum le16_to_cpu(dr->wIndex), 290323ece54SOliver Neukum le16_to_cpu(dr->wLength)); 291afba937eSOliver Neukum goto exit; 292afba937eSOliver Neukum } 293afba937eSOliver Neukum 294579b9ccaSSebastian Andrzej Siewior spin_lock_irqsave(&desc->iuspin, flags); 2956dd433e6SOliver Neukum responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); 29673e06865SGreg Suarez if (!desc->resp_count++ && !responding 29773e06865SGreg Suarez && !test_bit(WDM_DISCONNECTING, &desc->flags) 298beb1d35fSOliver Neukum && !test_bit(WDM_SUSPENDING, &desc->flags)) { 299afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_ATOMIC); 300ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv); 301afba937eSOliver Neukum } 302579b9ccaSSebastian Andrzej Siewior spin_unlock_irqrestore(&desc->iuspin, flags); 303afba937eSOliver Neukum if (rv < 0) { 304922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 305afba937eSOliver Neukum if (rv == -EPERM) 306afba937eSOliver Neukum return; 307afba937eSOliver Neukum if (rv == -ENOMEM) { 308afba937eSOliver Neukum sw: 309afba937eSOliver Neukum rv = schedule_work(&desc->rxwork); 310afba937eSOliver Neukum if (rv) 3119908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 3129908a32eSGreg Kroah-Hartman "Cannot schedule work\n"); 313afba937eSOliver Neukum } 314afba937eSOliver Neukum } 315afba937eSOliver Neukum exit: 316afba937eSOliver Neukum rv = usb_submit_urb(urb, GFP_ATOMIC); 317afba937eSOliver Neukum if (rv) 3189908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 3199908a32eSGreg Kroah-Hartman "%s - usb_submit_urb failed with result %d\n", 320afba937eSOliver Neukum __func__, rv); 321afba937eSOliver Neukum 322afba937eSOliver Neukum } 323afba937eSOliver Neukum 324*18abf874SOliver Neukum static void poison_urbs(struct wdm_device *desc) 325afba937eSOliver Neukum { 32617d80d56SOliver Neukum /* the order here is essential */ 327*18abf874SOliver Neukum usb_poison_urb(desc->command); 328*18abf874SOliver Neukum usb_poison_urb(desc->validity); 329*18abf874SOliver Neukum usb_poison_urb(desc->response); 330*18abf874SOliver Neukum } 331*18abf874SOliver Neukum 332*18abf874SOliver Neukum static void unpoison_urbs(struct wdm_device *desc) 333*18abf874SOliver Neukum { 334*18abf874SOliver Neukum /* 335*18abf874SOliver Neukum * the order here is not essential 336*18abf874SOliver Neukum * it is symmetrical just to be nice 337*18abf874SOliver Neukum */ 338*18abf874SOliver Neukum usb_unpoison_urb(desc->response); 339*18abf874SOliver Neukum usb_unpoison_urb(desc->validity); 340*18abf874SOliver Neukum usb_unpoison_urb(desc->command); 341afba937eSOliver Neukum } 342afba937eSOliver Neukum 343afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc) 344afba937eSOliver Neukum { 345afba937eSOliver Neukum usb_free_urb(desc->validity); 346afba937eSOliver Neukum usb_free_urb(desc->response); 347afba937eSOliver Neukum usb_free_urb(desc->command); 348afba937eSOliver Neukum } 349afba937eSOliver Neukum 350afba937eSOliver Neukum static void cleanup(struct wdm_device *desc) 351afba937eSOliver Neukum { 3528457d99cSBjørn Mork kfree(desc->sbuf); 3538457d99cSBjørn Mork kfree(desc->inbuf); 354afba937eSOliver Neukum kfree(desc->orq); 355afba937eSOliver Neukum kfree(desc->irq); 356afba937eSOliver Neukum kfree(desc->ubuf); 357afba937eSOliver Neukum free_urbs(desc); 358afba937eSOliver Neukum kfree(desc); 359afba937eSOliver Neukum } 360afba937eSOliver Neukum 361afba937eSOliver Neukum static ssize_t wdm_write 362afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 363afba937eSOliver Neukum { 364afba937eSOliver Neukum u8 *buf; 365afba937eSOliver Neukum int rv = -EMSGSIZE, r, we; 366afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 367afba937eSOliver Neukum struct usb_ctrlrequest *req; 368afba937eSOliver Neukum 369afba937eSOliver Neukum if (count > desc->wMaxCommand) 370afba937eSOliver Neukum count = desc->wMaxCommand; 371afba937eSOliver Neukum 372afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 373afba937eSOliver Neukum we = desc->werr; 374afba937eSOliver Neukum desc->werr = 0; 375afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 376afba937eSOliver Neukum if (we < 0) 37776cb03e7SOliver Neukum return usb_translate_errors(we); 378afba937eSOliver Neukum 37964b9533eSGeliang Tang buf = memdup_user(buffer, count); 38064b9533eSGeliang Tang if (IS_ERR(buf)) 38164b9533eSGeliang Tang return PTR_ERR(buf); 382860e41a7SOliver Neukum 383860e41a7SOliver Neukum /* concurrent writes and disconnect */ 384e8537bd2SBjørn Mork r = mutex_lock_interruptible(&desc->wlock); 385860e41a7SOliver Neukum rv = -ERESTARTSYS; 38628965e17SOliver Neukum if (r) 38728965e17SOliver Neukum goto out_free_mem; 388860e41a7SOliver Neukum 389860e41a7SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 390860e41a7SOliver Neukum rv = -ENODEV; 39128965e17SOliver Neukum goto out_free_mem_lock; 392860e41a7SOliver Neukum } 393afba937eSOliver Neukum 39417d80d56SOliver Neukum r = usb_autopm_get_interface(desc->intf); 395860e41a7SOliver Neukum if (r < 0) { 39612a98b2bSOliver Neukum rv = usb_translate_errors(r); 39728965e17SOliver Neukum goto out_free_mem_lock; 398860e41a7SOliver Neukum } 3997f1dc313SOliver Neukum 4000cdfb819SDavid Sterba if (!(file->f_flags & O_NONBLOCK)) 401afba937eSOliver Neukum r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, 402afba937eSOliver Neukum &desc->flags)); 4037f1dc313SOliver Neukum else 4047f1dc313SOliver Neukum if (test_bit(WDM_IN_USE, &desc->flags)) 4057f1dc313SOliver Neukum r = -EAGAIN; 40688044202SBjørn Mork 40788044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) 40888044202SBjørn Mork r = -EIO; 40988044202SBjørn Mork 41037d2a363SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 41137d2a363SOliver Neukum r = -ENODEV; 41237d2a363SOliver Neukum 413860e41a7SOliver Neukum if (r < 0) { 41412a98b2bSOliver Neukum rv = r; 41528965e17SOliver Neukum goto out_free_mem_pm; 416afba937eSOliver Neukum } 417afba937eSOliver Neukum 418afba937eSOliver Neukum req = desc->orq; 419afba937eSOliver Neukum usb_fill_control_urb( 420afba937eSOliver Neukum desc->command, 421afba937eSOliver Neukum interface_to_usbdev(desc->intf), 422afba937eSOliver Neukum /* using common endpoint 0 */ 423afba937eSOliver Neukum usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0), 424afba937eSOliver Neukum (unsigned char *)req, 425afba937eSOliver Neukum buf, 426afba937eSOliver Neukum count, 427afba937eSOliver Neukum wdm_out_callback, 428afba937eSOliver Neukum desc 429afba937eSOliver Neukum ); 430afba937eSOliver Neukum 431afba937eSOliver Neukum req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | 432afba937eSOliver Neukum USB_RECIP_INTERFACE); 433afba937eSOliver Neukum req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 434afba937eSOliver Neukum req->wValue = 0; 435323ece54SOliver Neukum req->wIndex = desc->inum; /* already converted */ 436afba937eSOliver Neukum req->wLength = cpu_to_le16(count); 437afba937eSOliver Neukum set_bit(WDM_IN_USE, &desc->flags); 4385c22837aSOliver Neukum desc->outbuf = buf; 439afba937eSOliver Neukum 440afba937eSOliver Neukum rv = usb_submit_urb(desc->command, GFP_KERNEL); 441afba937eSOliver Neukum if (rv < 0) { 4425c22837aSOliver Neukum desc->outbuf = NULL; 443afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 44437d2a363SOliver Neukum wake_up_all(&desc->wait); /* for wdm_wait_for_response() */ 4459908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); 44612a98b2bSOliver Neukum rv = usb_translate_errors(rv); 44728965e17SOliver Neukum goto out_free_mem_pm; 448afba937eSOliver Neukum } else { 449ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d\n", 450323ece54SOliver Neukum le16_to_cpu(req->wIndex)); 451afba937eSOliver Neukum } 45228965e17SOliver Neukum 45317d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 454e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 45564b9533eSGeliang Tang return count; 45628965e17SOliver Neukum 45728965e17SOliver Neukum out_free_mem_pm: 45828965e17SOliver Neukum usb_autopm_put_interface(desc->intf); 45928965e17SOliver Neukum out_free_mem_lock: 46028965e17SOliver Neukum mutex_unlock(&desc->wlock); 46128965e17SOliver Neukum out_free_mem: 46228965e17SOliver Neukum kfree(buf); 46328965e17SOliver Neukum return rv; 464afba937eSOliver Neukum } 465afba937eSOliver Neukum 4668dd5cd53SBjørn Mork /* 467c1da59daSRobert Foss * Submit the read urb if resp_count is non-zero. 4688dd5cd53SBjørn Mork * 4698dd5cd53SBjørn Mork * Called with desc->iuspin locked 4708dd5cd53SBjørn Mork */ 471c1da59daSRobert Foss static int service_outstanding_interrupt(struct wdm_device *desc) 4728dd5cd53SBjørn Mork { 4738dd5cd53SBjørn Mork int rv = 0; 4748dd5cd53SBjørn Mork 4758dd5cd53SBjørn Mork /* submit read urb only if the device is waiting for it */ 476f563926fSBjørn Mork if (!desc->resp_count || !--desc->resp_count) 4778dd5cd53SBjørn Mork goto out; 4788dd5cd53SBjørn Mork 4795e5ff0b4STetsuo Handa if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 4805e5ff0b4STetsuo Handa rv = -ENODEV; 4815e5ff0b4STetsuo Handa goto out; 4825e5ff0b4STetsuo Handa } 4835e5ff0b4STetsuo Handa if (test_bit(WDM_RESETTING, &desc->flags)) { 4845e5ff0b4STetsuo Handa rv = -EIO; 4855e5ff0b4STetsuo Handa goto out; 4865e5ff0b4STetsuo Handa } 4875e5ff0b4STetsuo Handa 4888dd5cd53SBjørn Mork set_bit(WDM_RESPONDING, &desc->flags); 4898dd5cd53SBjørn Mork spin_unlock_irq(&desc->iuspin); 490e871db8dSSebastian Andrzej Siewior rv = usb_submit_urb(desc->response, GFP_KERNEL); 4918dd5cd53SBjørn Mork spin_lock_irq(&desc->iuspin); 4928dd5cd53SBjørn Mork if (rv) { 4935e5ff0b4STetsuo Handa if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 4948dd5cd53SBjørn Mork dev_err(&desc->intf->dev, 4958dd5cd53SBjørn Mork "usb_submit_urb failed with result %d\n", rv); 4968dd5cd53SBjørn Mork 4978dd5cd53SBjørn Mork /* make sure the next notification trigger a submit */ 4988dd5cd53SBjørn Mork clear_bit(WDM_RESPONDING, &desc->flags); 4998dd5cd53SBjørn Mork desc->resp_count = 0; 5008dd5cd53SBjørn Mork } 5018dd5cd53SBjørn Mork out: 5028dd5cd53SBjørn Mork return rv; 5038dd5cd53SBjørn Mork } 5048dd5cd53SBjørn Mork 505afba937eSOliver Neukum static ssize_t wdm_read 506afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos) 507afba937eSOliver Neukum { 508711c68b3SBen Hutchings int rv, cntr; 509afba937eSOliver Neukum int i = 0; 510afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 511afba937eSOliver Neukum 512afba937eSOliver Neukum 513e8537bd2SBjørn Mork rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ 514afba937eSOliver Neukum if (rv < 0) 515afba937eSOliver Neukum return -ERESTARTSYS; 516afba937eSOliver Neukum 5176aa7de05SMark Rutland cntr = READ_ONCE(desc->length); 518711c68b3SBen Hutchings if (cntr == 0) { 519afba937eSOliver Neukum desc->read = 0; 520afba937eSOliver Neukum retry: 5217f1dc313SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 5227f1dc313SOliver Neukum rv = -ENODEV; 5237f1dc313SOliver Neukum goto err; 5247f1dc313SOliver Neukum } 525c0f5eceeSOliver Neukum if (test_bit(WDM_OVERFLOW, &desc->flags)) { 526c0f5eceeSOliver Neukum clear_bit(WDM_OVERFLOW, &desc->flags); 527c0f5eceeSOliver Neukum rv = -ENOBUFS; 528c0f5eceeSOliver Neukum goto err; 529c0f5eceeSOliver Neukum } 530afba937eSOliver Neukum i++; 5317f1dc313SOliver Neukum if (file->f_flags & O_NONBLOCK) { 5327f1dc313SOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { 53353b7f7b5SGustavo A. R. Silva rv = -EAGAIN; 5347f1dc313SOliver Neukum goto err; 5357f1dc313SOliver Neukum } 5367f1dc313SOliver Neukum rv = 0; 5377f1dc313SOliver Neukum } else { 538afba937eSOliver Neukum rv = wait_event_interruptible(desc->wait, 539afba937eSOliver Neukum test_bit(WDM_READ, &desc->flags)); 5407f1dc313SOliver Neukum } 541afba937eSOliver Neukum 5427f1dc313SOliver Neukum /* may have happened while we slept */ 54317d80d56SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 54417d80d56SOliver Neukum rv = -ENODEV; 54517d80d56SOliver Neukum goto err; 54617d80d56SOliver Neukum } 54788044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) { 54888044202SBjørn Mork rv = -EIO; 54988044202SBjørn Mork goto err; 55088044202SBjørn Mork } 55117d80d56SOliver Neukum usb_mark_last_busy(interface_to_usbdev(desc->intf)); 552afba937eSOliver Neukum if (rv < 0) { 553afba937eSOliver Neukum rv = -ERESTARTSYS; 554afba937eSOliver Neukum goto err; 555afba937eSOliver Neukum } 556afba937eSOliver Neukum 557afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 558afba937eSOliver Neukum 559afba937eSOliver Neukum if (desc->rerr) { /* read completed, error happened */ 56085e8a0b9SOliver Neukum rv = usb_translate_errors(desc->rerr); 561afba937eSOliver Neukum desc->rerr = 0; 562afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 563afba937eSOliver Neukum goto err; 564afba937eSOliver Neukum } 565afba937eSOliver Neukum /* 566afba937eSOliver Neukum * recheck whether we've lost the race 567afba937eSOliver Neukum * against the completion handler 568afba937eSOliver Neukum */ 569afba937eSOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */ 570afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 571afba937eSOliver Neukum goto retry; 572afba937eSOliver Neukum } 573c0f5eceeSOliver Neukum 574afba937eSOliver Neukum if (!desc->reslength) { /* zero length read */ 575ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n"); 576c1da59daSRobert Foss clear_bit(WDM_READ, &desc->flags); 577c1da59daSRobert Foss rv = service_outstanding_interrupt(desc); 578afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 5798dd5cd53SBjørn Mork if (rv < 0) 5808dd5cd53SBjørn Mork goto err; 581afba937eSOliver Neukum goto retry; 582afba937eSOliver Neukum } 583711c68b3SBen Hutchings cntr = desc->length; 584afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 585afba937eSOliver Neukum } 586afba937eSOliver Neukum 587711c68b3SBen Hutchings if (cntr > count) 588711c68b3SBen Hutchings cntr = count; 589afba937eSOliver Neukum rv = copy_to_user(buffer, desc->ubuf, cntr); 590afba937eSOliver Neukum if (rv > 0) { 591afba937eSOliver Neukum rv = -EFAULT; 592afba937eSOliver Neukum goto err; 593afba937eSOliver Neukum } 594afba937eSOliver Neukum 595711c68b3SBen Hutchings spin_lock_irq(&desc->iuspin); 596711c68b3SBen Hutchings 597afba937eSOliver Neukum for (i = 0; i < desc->length - cntr; i++) 598afba937eSOliver Neukum desc->ubuf[i] = desc->ubuf[i + cntr]; 599afba937eSOliver Neukum 600afba937eSOliver Neukum desc->length -= cntr; 60187d65e54SOliver Neukum /* in case we had outstanding data */ 602c1da59daSRobert Foss if (!desc->length) { 603c1da59daSRobert Foss clear_bit(WDM_READ, &desc->flags); 604c1da59daSRobert Foss service_outstanding_interrupt(desc); 605c1da59daSRobert Foss } 60673e06865SGreg Suarez spin_unlock_irq(&desc->iuspin); 607afba937eSOliver Neukum rv = cntr; 608afba937eSOliver Neukum 609afba937eSOliver Neukum err: 610e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 611afba937eSOliver Neukum return rv; 612afba937eSOliver Neukum } 613afba937eSOliver Neukum 61437d2a363SOliver Neukum static int wdm_wait_for_response(struct file *file, long timeout) 615afba937eSOliver Neukum { 616afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 61737d2a363SOliver Neukum long rv; /* Use long here because (int) MAX_SCHEDULE_TIMEOUT < 0. */ 618afba937eSOliver Neukum 6191426bd2cSOliver Neukum /* 62037d2a363SOliver Neukum * Needs both flags. We cannot do with one because resetting it would 62137d2a363SOliver Neukum * cause a race with write() yet we need to signal a disconnect. 6221426bd2cSOliver Neukum */ 62337d2a363SOliver Neukum rv = wait_event_interruptible_timeout(desc->wait, 6241426bd2cSOliver Neukum !test_bit(WDM_IN_USE, &desc->flags) || 62537d2a363SOliver Neukum test_bit(WDM_DISCONNECTING, &desc->flags), 62637d2a363SOliver Neukum timeout); 6276b0b79d3SBjørn Mork 62837d2a363SOliver Neukum /* 62937d2a363SOliver Neukum * To report the correct error. This is best effort. 63037d2a363SOliver Neukum * We are inevitably racing with the hardware. 63137d2a363SOliver Neukum */ 6321426bd2cSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 6331426bd2cSOliver Neukum return -ENODEV; 63437d2a363SOliver Neukum if (!rv) 63537d2a363SOliver Neukum return -EIO; 63637d2a363SOliver Neukum if (rv < 0) 63737d2a363SOliver Neukum return -EINTR; 638afba937eSOliver Neukum 63937d2a363SOliver Neukum spin_lock_irq(&desc->iuspin); 64037d2a363SOliver Neukum rv = desc->werr; 64137d2a363SOliver Neukum desc->werr = 0; 64237d2a363SOliver Neukum spin_unlock_irq(&desc->iuspin); 64337d2a363SOliver Neukum 64437d2a363SOliver Neukum return usb_translate_errors(rv); 64537d2a363SOliver Neukum 64637d2a363SOliver Neukum } 64737d2a363SOliver Neukum 64837d2a363SOliver Neukum /* 64937d2a363SOliver Neukum * You need to send a signal when you react to malicious or defective hardware. 65037d2a363SOliver Neukum * Also, don't abort when fsync() returned -EINVAL, for older kernels which do 65137d2a363SOliver Neukum * not implement wdm_flush() will return -EINVAL. 65237d2a363SOliver Neukum */ 65337d2a363SOliver Neukum static int wdm_fsync(struct file *file, loff_t start, loff_t end, int datasync) 65437d2a363SOliver Neukum { 65537d2a363SOliver Neukum return wdm_wait_for_response(file, MAX_SCHEDULE_TIMEOUT); 65637d2a363SOliver Neukum } 65737d2a363SOliver Neukum 65837d2a363SOliver Neukum /* 65937d2a363SOliver Neukum * Same with wdm_fsync(), except it uses finite timeout in order to react to 66037d2a363SOliver Neukum * malicious or defective hardware which ceased communication after close() was 66137d2a363SOliver Neukum * implicitly called due to process termination. 66237d2a363SOliver Neukum */ 66337d2a363SOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id) 66437d2a363SOliver Neukum { 66537d2a363SOliver Neukum return wdm_wait_for_response(file, WDM_FLUSH_TIMEOUT); 666afba937eSOliver Neukum } 667afba937eSOliver Neukum 668afc9a42bSAl Viro static __poll_t wdm_poll(struct file *file, struct poll_table_struct *wait) 669afba937eSOliver Neukum { 670afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 671afba937eSOliver Neukum unsigned long flags; 672afc9a42bSAl Viro __poll_t mask = 0; 673afba937eSOliver Neukum 674afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 675afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 676a9a08845SLinus Torvalds mask = EPOLLHUP | EPOLLERR; 677afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 678afba937eSOliver Neukum goto desc_out; 679afba937eSOliver Neukum } 680afba937eSOliver Neukum if (test_bit(WDM_READ, &desc->flags)) 681a9a08845SLinus Torvalds mask = EPOLLIN | EPOLLRDNORM; 682afba937eSOliver Neukum if (desc->rerr || desc->werr) 683a9a08845SLinus Torvalds mask |= EPOLLERR; 684afba937eSOliver Neukum if (!test_bit(WDM_IN_USE, &desc->flags)) 685a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM; 686afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 687afba937eSOliver Neukum 688afba937eSOliver Neukum poll_wait(file, &desc->wait, wait); 689afba937eSOliver Neukum 690afba937eSOliver Neukum desc_out: 691afba937eSOliver Neukum return mask; 692afba937eSOliver Neukum } 693afba937eSOliver Neukum 694afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file) 695afba937eSOliver Neukum { 696afba937eSOliver Neukum int minor = iminor(inode); 697afba937eSOliver Neukum int rv = -ENODEV; 698afba937eSOliver Neukum struct usb_interface *intf; 699afba937eSOliver Neukum struct wdm_device *desc; 700afba937eSOliver Neukum 701afba937eSOliver Neukum mutex_lock(&wdm_mutex); 702b0c13860SBjørn Mork desc = wdm_find_device_by_minor(minor); 703b0c13860SBjørn Mork if (!desc) 704afba937eSOliver Neukum goto out; 705afba937eSOliver Neukum 706b0c13860SBjørn Mork intf = desc->intf; 707afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 708afba937eSOliver Neukum goto out; 709afba937eSOliver Neukum file->private_data = desc; 710afba937eSOliver Neukum 71117d80d56SOliver Neukum rv = usb_autopm_get_interface(desc->intf); 71217d80d56SOliver Neukum if (rv < 0) { 7139908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); 71417d80d56SOliver Neukum goto out; 71517d80d56SOliver Neukum } 716afba937eSOliver Neukum 717e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 718e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 71917d80d56SOliver Neukum if (!desc->count++) { 720d771d8aaSOliver Neukum desc->werr = 0; 721d771d8aaSOliver Neukum desc->rerr = 0; 72217d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_KERNEL); 723afba937eSOliver Neukum if (rv < 0) { 724afba937eSOliver Neukum desc->count--; 7259908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 7269908a32eSGreg Kroah-Hartman "Error submitting int urb - %d\n", rv); 72712a98b2bSOliver Neukum rv = usb_translate_errors(rv); 728afba937eSOliver Neukum } 72917d80d56SOliver Neukum } else { 730afba937eSOliver Neukum rv = 0; 73117d80d56SOliver Neukum } 732e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 7333cc36157SBjørn Mork if (desc->count == 1) 7343cc36157SBjørn Mork desc->manage_power(intf, 1); 73517d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 736afba937eSOliver Neukum out: 737afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 738afba937eSOliver Neukum return rv; 739afba937eSOliver Neukum } 740afba937eSOliver Neukum 741afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file) 742afba937eSOliver Neukum { 743afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 744afba937eSOliver Neukum 745afba937eSOliver Neukum mutex_lock(&wdm_mutex); 746e8537bd2SBjørn Mork 747e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 748e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 749afba937eSOliver Neukum desc->count--; 750e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 75117d80d56SOliver Neukum 752afba937eSOliver Neukum if (!desc->count) { 7536b0b79d3SBjørn Mork if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { 754ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n"); 755*18abf874SOliver Neukum poison_urbs(desc); 75673e06865SGreg Suarez spin_lock_irq(&desc->iuspin); 75773e06865SGreg Suarez desc->resp_count = 0; 75873e06865SGreg Suarez spin_unlock_irq(&desc->iuspin); 7593cc36157SBjørn Mork desc->manage_power(desc->intf, 0); 760*18abf874SOliver Neukum unpoison_urbs(desc); 761880bca3aSBjørn Mork } else { 7626b0b79d3SBjørn Mork /* must avoid dev_printk here as desc->intf is invalid */ 7636b0b79d3SBjørn Mork pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__); 7642f338c8aSOliver Neukum cleanup(desc); 765afba937eSOliver Neukum } 766880bca3aSBjørn Mork } 767afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 768afba937eSOliver Neukum return 0; 769afba937eSOliver Neukum } 770afba937eSOliver Neukum 7713edce1cfSBjørn Mork static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 7723edce1cfSBjørn Mork { 7733edce1cfSBjørn Mork struct wdm_device *desc = file->private_data; 7743edce1cfSBjørn Mork int rv = 0; 7753edce1cfSBjørn Mork 7763edce1cfSBjørn Mork switch (cmd) { 7773edce1cfSBjørn Mork case IOCTL_WDM_MAX_COMMAND: 7783edce1cfSBjørn Mork if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand))) 7793edce1cfSBjørn Mork rv = -EFAULT; 7803edce1cfSBjørn Mork break; 7813edce1cfSBjørn Mork default: 7823edce1cfSBjørn Mork rv = -ENOTTY; 7833edce1cfSBjørn Mork } 7843edce1cfSBjørn Mork return rv; 7853edce1cfSBjørn Mork } 7863edce1cfSBjørn Mork 787afba937eSOliver Neukum static const struct file_operations wdm_fops = { 788afba937eSOliver Neukum .owner = THIS_MODULE, 789afba937eSOliver Neukum .read = wdm_read, 790afba937eSOliver Neukum .write = wdm_write, 79137d2a363SOliver Neukum .fsync = wdm_fsync, 792afba937eSOliver Neukum .open = wdm_open, 793afba937eSOliver Neukum .flush = wdm_flush, 794afba937eSOliver Neukum .release = wdm_release, 7956038f373SArnd Bergmann .poll = wdm_poll, 7963edce1cfSBjørn Mork .unlocked_ioctl = wdm_ioctl, 7971832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl, 7986038f373SArnd Bergmann .llseek = noop_llseek, 799afba937eSOliver Neukum }; 800afba937eSOliver Neukum 801afba937eSOliver Neukum static struct usb_class_driver wdm_class = { 802afba937eSOliver Neukum .name = "cdc-wdm%d", 803afba937eSOliver Neukum .fops = &wdm_fops, 804afba937eSOliver Neukum .minor_base = WDM_MINOR_BASE, 805afba937eSOliver Neukum }; 806afba937eSOliver Neukum 807afba937eSOliver Neukum /* --- error handling --- */ 808afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work) 809afba937eSOliver Neukum { 810afba937eSOliver Neukum struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); 811afba937eSOliver Neukum unsigned long flags; 8126dd433e6SOliver Neukum int rv = 0; 8136dd433e6SOliver Neukum int responding; 814afba937eSOliver Neukum 815afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 816afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 817afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 818afba937eSOliver Neukum } else { 8196dd433e6SOliver Neukum responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); 820afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 8216dd433e6SOliver Neukum if (!responding) 822afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_KERNEL); 823afba937eSOliver Neukum if (rv < 0 && rv != -EPERM) { 824afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 8256dd433e6SOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 826afba937eSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 827afba937eSOliver Neukum schedule_work(&desc->rxwork); 828afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 829afba937eSOliver Neukum } 830afba937eSOliver Neukum } 831afba937eSOliver Neukum } 832afba937eSOliver Neukum 8332df69484SSebastian Andrzej Siewior static void service_interrupt_work(struct work_struct *work) 8342df69484SSebastian Andrzej Siewior { 8352df69484SSebastian Andrzej Siewior struct wdm_device *desc; 8362df69484SSebastian Andrzej Siewior 8372df69484SSebastian Andrzej Siewior desc = container_of(work, struct wdm_device, service_outs_intr); 8382df69484SSebastian Andrzej Siewior 8392df69484SSebastian Andrzej Siewior spin_lock_irq(&desc->iuspin); 8402df69484SSebastian Andrzej Siewior service_outstanding_interrupt(desc); 8412df69484SSebastian Andrzej Siewior if (!desc->resp_count) { 8422df69484SSebastian Andrzej Siewior set_bit(WDM_READ, &desc->flags); 8432df69484SSebastian Andrzej Siewior wake_up(&desc->wait); 8442df69484SSebastian Andrzej Siewior } 8452df69484SSebastian Andrzej Siewior spin_unlock_irq(&desc->iuspin); 8462df69484SSebastian Andrzej Siewior } 8472df69484SSebastian Andrzej Siewior 848afba937eSOliver Neukum /* --- hotplug --- */ 849afba937eSOliver Neukum 8503cc36157SBjørn Mork static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, 85119445816SBjørn Mork u16 bufsize, int (*manage_power)(struct usb_interface *, int)) 852afba937eSOliver Neukum { 8530dffb486SBjørn Mork int rv = -ENOMEM; 854afba937eSOliver Neukum struct wdm_device *desc; 855afba937eSOliver Neukum 856afba937eSOliver Neukum desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); 857afba937eSOliver Neukum if (!desc) 858afba937eSOliver Neukum goto out; 859b0c13860SBjørn Mork INIT_LIST_HEAD(&desc->device_list); 860e8537bd2SBjørn Mork mutex_init(&desc->rlock); 861e8537bd2SBjørn Mork mutex_init(&desc->wlock); 862afba937eSOliver Neukum spin_lock_init(&desc->iuspin); 863afba937eSOliver Neukum init_waitqueue_head(&desc->wait); 8640dffb486SBjørn Mork desc->wMaxCommand = bufsize; 865052fbc0dSOliver Neukum /* this will be expanded and needed in hardware endianness */ 866afba937eSOliver Neukum desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); 867afba937eSOliver Neukum desc->intf = intf; 868afba937eSOliver Neukum INIT_WORK(&desc->rxwork, wdm_rxwork); 8692df69484SSebastian Andrzej Siewior INIT_WORK(&desc->service_outs_intr, service_interrupt_work); 870afba937eSOliver Neukum 871afba937eSOliver Neukum rv = -EINVAL; 8720dffb486SBjørn Mork if (!usb_endpoint_is_int_in(ep)) 873052fbc0dSOliver Neukum goto err; 874afba937eSOliver Neukum 87529cc8897SKuninori Morimoto desc->wMaxPacketSize = usb_endpoint_maxp(ep); 876afba937eSOliver Neukum 877afba937eSOliver Neukum desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 878afba937eSOliver Neukum if (!desc->orq) 879afba937eSOliver Neukum goto err; 880afba937eSOliver Neukum desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 881afba937eSOliver Neukum if (!desc->irq) 882afba937eSOliver Neukum goto err; 883afba937eSOliver Neukum 884afba937eSOliver Neukum desc->validity = usb_alloc_urb(0, GFP_KERNEL); 885afba937eSOliver Neukum if (!desc->validity) 886afba937eSOliver Neukum goto err; 887afba937eSOliver Neukum 888afba937eSOliver Neukum desc->response = usb_alloc_urb(0, GFP_KERNEL); 889afba937eSOliver Neukum if (!desc->response) 890afba937eSOliver Neukum goto err; 891afba937eSOliver Neukum 892afba937eSOliver Neukum desc->command = usb_alloc_urb(0, GFP_KERNEL); 893afba937eSOliver Neukum if (!desc->command) 894afba937eSOliver Neukum goto err; 895afba937eSOliver Neukum 896afba937eSOliver Neukum desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 897afba937eSOliver Neukum if (!desc->ubuf) 898afba937eSOliver Neukum goto err; 899afba937eSOliver Neukum 9008457d99cSBjørn Mork desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL); 901afba937eSOliver Neukum if (!desc->sbuf) 902afba937eSOliver Neukum goto err; 903afba937eSOliver Neukum 9048457d99cSBjørn Mork desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 905afba937eSOliver Neukum if (!desc->inbuf) 9068457d99cSBjørn Mork goto err; 907afba937eSOliver Neukum 908afba937eSOliver Neukum usb_fill_int_urb( 909afba937eSOliver Neukum desc->validity, 910afba937eSOliver Neukum interface_to_usbdev(intf), 911afba937eSOliver Neukum usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress), 912afba937eSOliver Neukum desc->sbuf, 913afba937eSOliver Neukum desc->wMaxPacketSize, 914afba937eSOliver Neukum wdm_int_callback, 915afba937eSOliver Neukum desc, 916afba937eSOliver Neukum ep->bInterval 917afba937eSOliver Neukum ); 918afba937eSOliver Neukum 91919b85b3bSBjørn Mork desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); 92019b85b3bSBjørn Mork desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; 92119b85b3bSBjørn Mork desc->irq->wValue = 0; 922323ece54SOliver Neukum desc->irq->wIndex = desc->inum; /* already converted */ 92319b85b3bSBjørn Mork desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); 92419b85b3bSBjørn Mork 92519b85b3bSBjørn Mork usb_fill_control_urb( 92619b85b3bSBjørn Mork desc->response, 9278143a896SBjørn Mork interface_to_usbdev(intf), 92819b85b3bSBjørn Mork /* using common endpoint 0 */ 92919b85b3bSBjørn Mork usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), 93019b85b3bSBjørn Mork (unsigned char *)desc->irq, 93119b85b3bSBjørn Mork desc->inbuf, 93219b85b3bSBjørn Mork desc->wMaxCommand, 93319b85b3bSBjørn Mork wdm_in_callback, 93419b85b3bSBjørn Mork desc 93519b85b3bSBjørn Mork ); 93619b85b3bSBjørn Mork 9373cc36157SBjørn Mork desc->manage_power = manage_power; 9383cc36157SBjørn Mork 939b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 940b0c13860SBjørn Mork list_add(&desc->device_list, &wdm_device_list); 941b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 942b0c13860SBjørn Mork 943afba937eSOliver Neukum rv = usb_register_dev(intf, &wdm_class); 944052fbc0dSOliver Neukum if (rv < 0) 945b0c13860SBjørn Mork goto err; 946052fbc0dSOliver Neukum else 947820c629aSBjørn Mork dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev)); 948afba937eSOliver Neukum out: 949afba937eSOliver Neukum return rv; 950afba937eSOliver Neukum err: 9516286d85eSBjørn Mork spin_lock(&wdm_device_list_lock); 9526286d85eSBjørn Mork list_del(&desc->device_list); 9536286d85eSBjørn Mork spin_unlock(&wdm_device_list_lock); 9540dffb486SBjørn Mork cleanup(desc); 9550dffb486SBjørn Mork return rv; 9560dffb486SBjørn Mork } 9570dffb486SBjørn Mork 9583cc36157SBjørn Mork static int wdm_manage_power(struct usb_interface *intf, int on) 9593cc36157SBjørn Mork { 9603cc36157SBjørn Mork /* need autopm_get/put here to ensure the usbcore sees the new value */ 9613cc36157SBjørn Mork int rv = usb_autopm_get_interface(intf); 9623cc36157SBjørn Mork 9633cc36157SBjørn Mork intf->needs_remote_wakeup = on; 9644144bc86SBjørn Mork if (!rv) 9653cc36157SBjørn Mork usb_autopm_put_interface(intf); 9664144bc86SBjørn Mork return 0; 9673cc36157SBjørn Mork } 9683cc36157SBjørn Mork 9690dffb486SBjørn Mork static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) 9700dffb486SBjørn Mork { 9710dffb486SBjørn Mork int rv = -EINVAL; 9720dffb486SBjørn Mork struct usb_host_interface *iface; 9730dffb486SBjørn Mork struct usb_endpoint_descriptor *ep; 9747fae7bfbSOliver Neukum struct usb_cdc_parsed_header hdr; 9750dffb486SBjørn Mork u8 *buffer = intf->altsetting->extra; 9760dffb486SBjørn Mork int buflen = intf->altsetting->extralen; 9770dffb486SBjørn Mork u16 maxcom = WDM_DEFAULT_BUFSIZE; 9780dffb486SBjørn Mork 9790dffb486SBjørn Mork if (!buffer) 9800dffb486SBjørn Mork goto err; 9810dffb486SBjørn Mork 9827fae7bfbSOliver Neukum cdc_parse_cdc_header(&hdr, intf, buffer, buflen); 9837fae7bfbSOliver Neukum 9847fae7bfbSOliver Neukum if (hdr.usb_cdc_dmm_desc) 9857fae7bfbSOliver Neukum maxcom = le16_to_cpu(hdr.usb_cdc_dmm_desc->wMaxCommand); 9860dffb486SBjørn Mork 9870dffb486SBjørn Mork iface = intf->cur_altsetting; 9880dffb486SBjørn Mork if (iface->desc.bNumEndpoints != 1) 9890dffb486SBjørn Mork goto err; 9900dffb486SBjørn Mork ep = &iface->endpoint[0].desc; 9910dffb486SBjørn Mork 99219445816SBjørn Mork rv = wdm_create(intf, ep, maxcom, &wdm_manage_power); 9930dffb486SBjørn Mork 9940dffb486SBjørn Mork err: 995afba937eSOliver Neukum return rv; 996afba937eSOliver Neukum } 997afba937eSOliver Neukum 9983cc36157SBjørn Mork /** 9993cc36157SBjørn Mork * usb_cdc_wdm_register - register a WDM subdriver 10003cc36157SBjørn Mork * @intf: usb interface the subdriver will associate with 10013cc36157SBjørn Mork * @ep: interrupt endpoint to monitor for notifications 10023cc36157SBjørn Mork * @bufsize: maximum message size to support for read/write 1003ddcb6c6aSLee Jones * @manage_power: call-back invoked during open and release to 1004ddcb6c6aSLee Jones * manage the device's power 10053cc36157SBjørn Mork * Create WDM usb class character device and associate it with intf 10063cc36157SBjørn Mork * without binding, allowing another driver to manage the interface. 10073cc36157SBjørn Mork * 10083cc36157SBjørn Mork * The subdriver will manage the given interrupt endpoint exclusively 10093cc36157SBjørn Mork * and will issue control requests referring to the given intf. It 10103cc36157SBjørn Mork * will otherwise avoid interferring, and in particular not do 10113cc36157SBjørn Mork * usb_set_intfdata/usb_get_intfdata on intf. 10123cc36157SBjørn Mork * 10133cc36157SBjørn Mork * The return value is a pointer to the subdriver's struct usb_driver. 10143cc36157SBjørn Mork * The registering driver is responsible for calling this subdriver's 10153cc36157SBjørn Mork * disconnect, suspend, resume, pre_reset and post_reset methods from 10163cc36157SBjørn Mork * its own. 10173cc36157SBjørn Mork */ 10183cc36157SBjørn Mork struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, 10193cc36157SBjørn Mork struct usb_endpoint_descriptor *ep, 10203cc36157SBjørn Mork int bufsize, 10213cc36157SBjørn Mork int (*manage_power)(struct usb_interface *, int)) 10223cc36157SBjørn Mork { 10236dade7adSColin Ian King int rv; 10243cc36157SBjørn Mork 102519445816SBjørn Mork rv = wdm_create(intf, ep, bufsize, manage_power); 10263cc36157SBjørn Mork if (rv < 0) 10273cc36157SBjørn Mork goto err; 10283cc36157SBjørn Mork 10293cc36157SBjørn Mork return &wdm_driver; 10303cc36157SBjørn Mork err: 10313cc36157SBjørn Mork return ERR_PTR(rv); 10323cc36157SBjørn Mork } 10333cc36157SBjørn Mork EXPORT_SYMBOL(usb_cdc_wdm_register); 10343cc36157SBjørn Mork 1035afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf) 1036afba937eSOliver Neukum { 1037afba937eSOliver Neukum struct wdm_device *desc; 1038afba937eSOliver Neukum unsigned long flags; 1039afba937eSOliver Neukum 1040afba937eSOliver Neukum usb_deregister_dev(intf, &wdm_class); 1041b0c13860SBjørn Mork desc = wdm_find_device(intf); 1042afba937eSOliver Neukum mutex_lock(&wdm_mutex); 1043afba937eSOliver Neukum 1044afba937eSOliver Neukum /* the spinlock makes sure no new urbs are generated in the callbacks */ 1045afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 1046afba937eSOliver Neukum set_bit(WDM_DISCONNECTING, &desc->flags); 1047afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 1048afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 104962aaf24dSBjørn Mork wake_up_all(&desc->wait); 1050e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 1051e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 1052*18abf874SOliver Neukum poison_urbs(desc); 1053d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 10542df69484SSebastian Andrzej Siewior cancel_work_sync(&desc->service_outs_intr); 1055e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 1056e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 10576286d85eSBjørn Mork 10586286d85eSBjørn Mork /* the desc->intf pointer used as list key is now invalid */ 10596286d85eSBjørn Mork spin_lock(&wdm_device_list_lock); 10606286d85eSBjørn Mork list_del(&desc->device_list); 10616286d85eSBjørn Mork spin_unlock(&wdm_device_list_lock); 10626286d85eSBjørn Mork 1063afba937eSOliver Neukum if (!desc->count) 1064afba937eSOliver Neukum cleanup(desc); 1065880bca3aSBjørn Mork else 106613a88bf5SOliver Neukum dev_dbg(&intf->dev, "%d open files - postponing cleanup\n", desc->count); 1067afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 1068afba937eSOliver Neukum } 1069afba937eSOliver Neukum 1070d93d16e9SOliver Neukum #ifdef CONFIG_PM 107117d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message) 107217d80d56SOliver Neukum { 1073b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 107417d80d56SOliver Neukum int rv = 0; 107517d80d56SOliver Neukum 107617d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); 107717d80d56SOliver Neukum 1078d93d16e9SOliver Neukum /* if this is an autosuspend the caller does the locking */ 1079e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 1080e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 1081e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 1082e8537bd2SBjørn Mork } 108362e66854SOliver Neukum spin_lock_irq(&desc->iuspin); 1084d93d16e9SOliver Neukum 10855b1b0b81SAlan Stern if (PMSG_IS_AUTO(message) && 1086922a5eadSOliver Neukum (test_bit(WDM_IN_USE, &desc->flags) 1087922a5eadSOliver Neukum || test_bit(WDM_RESPONDING, &desc->flags))) { 108862e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 108917d80d56SOliver Neukum rv = -EBUSY; 109017d80d56SOliver Neukum } else { 1091d93d16e9SOliver Neukum 1092beb1d35fSOliver Neukum set_bit(WDM_SUSPENDING, &desc->flags); 109362e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 1094d93d16e9SOliver Neukum /* callback submits work - order is essential */ 1095*18abf874SOliver Neukum poison_urbs(desc); 1096d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 10972df69484SSebastian Andrzej Siewior cancel_work_sync(&desc->service_outs_intr); 1098*18abf874SOliver Neukum unpoison_urbs(desc); 109917d80d56SOliver Neukum } 1100e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 1101e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 1102e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 1103e8537bd2SBjørn Mork } 110417d80d56SOliver Neukum 110517d80d56SOliver Neukum return rv; 110617d80d56SOliver Neukum } 1107d93d16e9SOliver Neukum #endif 110817d80d56SOliver Neukum 110917d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc) 111017d80d56SOliver Neukum { 111117d80d56SOliver Neukum int rv = 0; 111217d80d56SOliver Neukum 111317d80d56SOliver Neukum if (desc->count) { 111417d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_NOIO); 111517d80d56SOliver Neukum if (rv < 0) 11169908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 11179908a32eSGreg Kroah-Hartman "Error resume submitting int urb - %d\n", rv); 111817d80d56SOliver Neukum } 111917d80d56SOliver Neukum return rv; 112017d80d56SOliver Neukum } 1121d93d16e9SOliver Neukum 1122d93d16e9SOliver Neukum #ifdef CONFIG_PM 112317d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf) 112417d80d56SOliver Neukum { 1125b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 112617d80d56SOliver Neukum int rv; 112717d80d56SOliver Neukum 112817d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); 1129338124c1SOliver Neukum 1130beb1d35fSOliver Neukum clear_bit(WDM_SUSPENDING, &desc->flags); 113162e66854SOliver Neukum rv = recover_from_urb_loss(desc); 1132338124c1SOliver Neukum 113317d80d56SOliver Neukum return rv; 113417d80d56SOliver Neukum } 1135d93d16e9SOliver Neukum #endif 113617d80d56SOliver Neukum 113717d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf) 113817d80d56SOliver Neukum { 1139b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 114017d80d56SOliver Neukum 1141d771d8aaSOliver Neukum /* 1142d771d8aaSOliver Neukum * we notify everybody using poll of 1143d771d8aaSOliver Neukum * an exceptional situation 1144d771d8aaSOliver Neukum * must be done before recovery lest a spontaneous 1145d771d8aaSOliver Neukum * message from the device is lost 1146d771d8aaSOliver Neukum */ 1147d771d8aaSOliver Neukum spin_lock_irq(&desc->iuspin); 114888044202SBjørn Mork set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */ 114988044202SBjørn Mork set_bit(WDM_READ, &desc->flags); /* unblock read */ 115088044202SBjørn Mork clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */ 1151d771d8aaSOliver Neukum desc->rerr = -EINTR; 1152d771d8aaSOliver Neukum spin_unlock_irq(&desc->iuspin); 1153d771d8aaSOliver Neukum wake_up_all(&desc->wait); 115488044202SBjørn Mork mutex_lock(&desc->rlock); 115588044202SBjørn Mork mutex_lock(&desc->wlock); 1156*18abf874SOliver Neukum poison_urbs(desc); 115788044202SBjørn Mork cancel_work_sync(&desc->rxwork); 11582df69484SSebastian Andrzej Siewior cancel_work_sync(&desc->service_outs_intr); 115917d80d56SOliver Neukum return 0; 116017d80d56SOliver Neukum } 116117d80d56SOliver Neukum 116217d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf) 116317d80d56SOliver Neukum { 1164b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 116517d80d56SOliver Neukum int rv; 116617d80d56SOliver Neukum 1167*18abf874SOliver Neukum unpoison_urbs(desc); 1168c0f5eceeSOliver Neukum clear_bit(WDM_OVERFLOW, &desc->flags); 116988044202SBjørn Mork clear_bit(WDM_RESETTING, &desc->flags); 117017d80d56SOliver Neukum rv = recover_from_urb_loss(desc); 1171e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 1172e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 11730742a338SYueHaibing return rv; 117417d80d56SOliver Neukum } 117517d80d56SOliver Neukum 1176afba937eSOliver Neukum static struct usb_driver wdm_driver = { 1177afba937eSOliver Neukum .name = "cdc_wdm", 1178afba937eSOliver Neukum .probe = wdm_probe, 1179afba937eSOliver Neukum .disconnect = wdm_disconnect, 1180d93d16e9SOliver Neukum #ifdef CONFIG_PM 118117d80d56SOliver Neukum .suspend = wdm_suspend, 118217d80d56SOliver Neukum .resume = wdm_resume, 118317d80d56SOliver Neukum .reset_resume = wdm_resume, 1184d93d16e9SOliver Neukum #endif 118517d80d56SOliver Neukum .pre_reset = wdm_pre_reset, 118617d80d56SOliver Neukum .post_reset = wdm_post_reset, 1187afba937eSOliver Neukum .id_table = wdm_ids, 118817d80d56SOliver Neukum .supports_autosuspend = 1, 1189e1f12eb6SSarah Sharp .disable_hub_initiated_lpm = 1, 1190afba937eSOliver Neukum }; 1191afba937eSOliver Neukum 119265db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver); 1193afba937eSOliver Neukum 1194afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR); 119587d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC); 1196afba937eSOliver Neukum MODULE_LICENSE("GPL"); 1197