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> 24cac6fb01SLoic Poulain #include <linux/skbuff.h> 25afba937eSOliver Neukum #include <linux/usb.h> 26afba937eSOliver Neukum #include <linux/usb/cdc.h> 27cac6fb01SLoic Poulain #include <linux/wwan.h> 28afba937eSOliver Neukum #include <asm/byteorder.h> 29afba937eSOliver Neukum #include <asm/unaligned.h> 303cc36157SBjørn Mork #include <linux/usb/cdc-wdm.h> 31afba937eSOliver Neukum 32afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum" 3387d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" 34afba937eSOliver Neukum 356ef4852bSNémeth Márton static const struct usb_device_id wdm_ids[] = { 36afba937eSOliver Neukum { 37afba937eSOliver Neukum .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | 38afba937eSOliver Neukum USB_DEVICE_ID_MATCH_INT_SUBCLASS, 39afba937eSOliver Neukum .bInterfaceClass = USB_CLASS_COMM, 40afba937eSOliver Neukum .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM 41afba937eSOliver Neukum }, 42afba937eSOliver Neukum { } 43afba937eSOliver Neukum }; 44afba937eSOliver Neukum 45aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids); 46aa5380b9SOliver Neukum 47afba937eSOliver Neukum #define WDM_MINOR_BASE 176 48afba937eSOliver Neukum 49afba937eSOliver Neukum 50afba937eSOliver Neukum #define WDM_IN_USE 1 51afba937eSOliver Neukum #define WDM_DISCONNECTING 2 52afba937eSOliver Neukum #define WDM_RESULT 3 53afba937eSOliver Neukum #define WDM_READ 4 54afba937eSOliver Neukum #define WDM_INT_STALL 5 55afba937eSOliver Neukum #define WDM_POLL_RUNNING 6 56922a5eadSOliver Neukum #define WDM_RESPONDING 7 57beb1d35fSOliver Neukum #define WDM_SUSPENDING 8 5888044202SBjørn Mork #define WDM_RESETTING 9 59c0f5eceeSOliver Neukum #define WDM_OVERFLOW 10 60cac6fb01SLoic Poulain #define WDM_WWAN_IN_USE 11 61afba937eSOliver Neukum 62afba937eSOliver Neukum #define WDM_MAX 16 63afba937eSOliver Neukum 6437d2a363SOliver Neukum /* we cannot wait forever at flush() */ 6537d2a363SOliver Neukum #define WDM_FLUSH_TIMEOUT (30 * HZ) 6637d2a363SOliver Neukum 677e3054a0SBjørn Mork /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ 687e3054a0SBjørn Mork #define WDM_DEFAULT_BUFSIZE 256 69afba937eSOliver Neukum 70afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex); 71b0c13860SBjørn Mork static DEFINE_SPINLOCK(wdm_device_list_lock); 72b0c13860SBjørn Mork static LIST_HEAD(wdm_device_list); 73afba937eSOliver Neukum 74afba937eSOliver Neukum /* --- method tables --- */ 75afba937eSOliver Neukum 76afba937eSOliver Neukum struct wdm_device { 77afba937eSOliver Neukum u8 *inbuf; /* buffer for response */ 78afba937eSOliver Neukum u8 *outbuf; /* buffer for command */ 79afba937eSOliver Neukum u8 *sbuf; /* buffer for status */ 80afba937eSOliver Neukum u8 *ubuf; /* buffer for copy to user space */ 81afba937eSOliver Neukum 82afba937eSOliver Neukum struct urb *command; 83afba937eSOliver Neukum struct urb *response; 84afba937eSOliver Neukum struct urb *validity; 85afba937eSOliver Neukum struct usb_interface *intf; 86afba937eSOliver Neukum struct usb_ctrlrequest *orq; 87afba937eSOliver Neukum struct usb_ctrlrequest *irq; 88afba937eSOliver Neukum spinlock_t iuspin; 89afba937eSOliver Neukum 90afba937eSOliver Neukum unsigned long flags; 91afba937eSOliver Neukum u16 bufsize; 92afba937eSOliver Neukum u16 wMaxCommand; 93afba937eSOliver Neukum u16 wMaxPacketSize; 94afba937eSOliver Neukum __le16 inum; 95afba937eSOliver Neukum int reslength; 96afba937eSOliver Neukum int length; 97afba937eSOliver Neukum int read; 98afba937eSOliver Neukum int count; 99afba937eSOliver Neukum dma_addr_t shandle; 100afba937eSOliver Neukum dma_addr_t ihandle; 101e8537bd2SBjørn Mork struct mutex wlock; 102e8537bd2SBjørn Mork struct mutex rlock; 103afba937eSOliver Neukum wait_queue_head_t wait; 104afba937eSOliver Neukum struct work_struct rxwork; 1052df69484SSebastian Andrzej Siewior struct work_struct service_outs_intr; 106afba937eSOliver Neukum int werr; 107afba937eSOliver Neukum int rerr; 10873e06865SGreg Suarez int resp_count; 109b0c13860SBjørn Mork 110b0c13860SBjørn Mork struct list_head device_list; 1113cc36157SBjørn Mork int (*manage_power)(struct usb_interface *, int); 112cac6fb01SLoic Poulain 113cac6fb01SLoic Poulain enum wwan_port_type wwanp_type; 114cac6fb01SLoic Poulain struct wwan_port *wwanp; 115afba937eSOliver Neukum }; 116afba937eSOliver Neukum 117afba937eSOliver Neukum static struct usb_driver wdm_driver; 118afba937eSOliver Neukum 119b0c13860SBjørn Mork /* return intfdata if we own the interface, else look up intf in the list */ 120b0c13860SBjørn Mork static struct wdm_device *wdm_find_device(struct usb_interface *intf) 121b0c13860SBjørn Mork { 1226a448868SBjørn Mork struct wdm_device *desc; 123b0c13860SBjørn Mork 124b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 125b0c13860SBjørn Mork list_for_each_entry(desc, &wdm_device_list, device_list) 126b0c13860SBjørn Mork if (desc->intf == intf) 1276a448868SBjørn Mork goto found; 1286a448868SBjørn Mork desc = NULL; 1296a448868SBjørn Mork found: 130b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 131b0c13860SBjørn Mork 132b0c13860SBjørn Mork return desc; 133b0c13860SBjørn Mork } 134b0c13860SBjørn Mork 135b0c13860SBjørn Mork static struct wdm_device *wdm_find_device_by_minor(int minor) 136b0c13860SBjørn Mork { 1376a448868SBjørn Mork struct wdm_device *desc; 138b0c13860SBjørn Mork 139b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 140b0c13860SBjørn Mork list_for_each_entry(desc, &wdm_device_list, device_list) 141b0c13860SBjørn Mork if (desc->intf->minor == minor) 1426a448868SBjørn Mork goto found; 1436a448868SBjørn Mork desc = NULL; 1446a448868SBjørn Mork found: 145b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 146b0c13860SBjørn Mork 147b0c13860SBjørn Mork return desc; 148b0c13860SBjørn Mork } 149b0c13860SBjørn Mork 150afba937eSOliver Neukum /* --- callbacks --- */ 151afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb) 152afba937eSOliver Neukum { 153afba937eSOliver Neukum struct wdm_device *desc; 154579b9ccaSSebastian Andrzej Siewior unsigned long flags; 155579b9ccaSSebastian Andrzej Siewior 156afba937eSOliver Neukum desc = urb->context; 157579b9ccaSSebastian Andrzej Siewior spin_lock_irqsave(&desc->iuspin, flags); 158afba937eSOliver Neukum desc->werr = urb->status; 159579b9ccaSSebastian Andrzej Siewior spin_unlock_irqrestore(&desc->iuspin, flags); 160afba937eSOliver Neukum kfree(desc->outbuf); 1615c22837aSOliver Neukum desc->outbuf = NULL; 1625c22837aSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 16337d2a363SOliver Neukum wake_up_all(&desc->wait); 164afba937eSOliver Neukum } 165afba937eSOliver Neukum 166cac6fb01SLoic Poulain static void wdm_wwan_rx(struct wdm_device *desc, int length); 167cac6fb01SLoic Poulain 168afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb) 169afba937eSOliver Neukum { 170579b9ccaSSebastian Andrzej Siewior unsigned long flags; 171afba937eSOliver Neukum struct wdm_device *desc = urb->context; 172afba937eSOliver Neukum int status = urb->status; 173c0f5eceeSOliver Neukum int length = urb->actual_length; 174afba937eSOliver Neukum 175579b9ccaSSebastian Andrzej Siewior spin_lock_irqsave(&desc->iuspin, flags); 176922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 177afba937eSOliver Neukum 178afba937eSOliver Neukum if (status) { 179afba937eSOliver Neukum switch (status) { 180afba937eSOliver Neukum case -ENOENT: 181afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 182ce8bb344SOliver Neukum "nonzero urb status received: -ENOENT\n"); 183922a5eadSOliver Neukum goto skip_error; 184afba937eSOliver Neukum case -ECONNRESET: 185afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 186ce8bb344SOliver Neukum "nonzero urb status received: -ECONNRESET\n"); 187922a5eadSOliver Neukum goto skip_error; 188afba937eSOliver Neukum case -ESHUTDOWN: 189afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 190ce8bb344SOliver Neukum "nonzero urb status received: -ESHUTDOWN\n"); 191922a5eadSOliver Neukum goto skip_error; 192afba937eSOliver Neukum case -EPIPE: 19319445816SBjørn Mork dev_err(&desc->intf->dev, 1949908a32eSGreg Kroah-Hartman "nonzero urb status received: -EPIPE\n"); 195afba937eSOliver Neukum break; 196afba937eSOliver Neukum default: 1979908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 1989908a32eSGreg Kroah-Hartman "Unexpected error %d\n", status); 199afba937eSOliver Neukum break; 200afba937eSOliver Neukum } 201afba937eSOliver Neukum } 202afba937eSOliver Neukum 203cac6fb01SLoic Poulain if (test_bit(WDM_WWAN_IN_USE, &desc->flags)) { 204cac6fb01SLoic Poulain wdm_wwan_rx(desc, length); 205cac6fb01SLoic Poulain goto out; 206cac6fb01SLoic Poulain } 207cac6fb01SLoic Poulain 208c1da59daSRobert Foss /* 209c1da59daSRobert Foss * only set a new error if there is no previous error. 210c1da59daSRobert Foss * Errors are only cleared during read/open 2118fec9355SBjørn Mork * Avoid propagating -EPIPE (stall) to userspace since it is 2128fec9355SBjørn Mork * better handled as an empty read 213c1da59daSRobert Foss */ 2148fec9355SBjørn Mork if (desc->rerr == 0 && status != -EPIPE) 215afba937eSOliver Neukum desc->rerr = status; 216c1da59daSRobert Foss 217c0f5eceeSOliver Neukum if (length + desc->length > desc->wMaxCommand) { 218c0f5eceeSOliver Neukum /* The buffer would overflow */ 219c0f5eceeSOliver Neukum set_bit(WDM_OVERFLOW, &desc->flags); 220c0f5eceeSOliver Neukum } else { 221c0f5eceeSOliver Neukum /* we may already be in overflow */ 222c0f5eceeSOliver Neukum if (!test_bit(WDM_OVERFLOW, &desc->flags)) { 223c0f5eceeSOliver Neukum memmove(desc->ubuf + desc->length, desc->inbuf, length); 224c0f5eceeSOliver Neukum desc->length += length; 225c0f5eceeSOliver Neukum desc->reslength = length; 226c0f5eceeSOliver Neukum } 227c0f5eceeSOliver Neukum } 228922a5eadSOliver Neukum skip_error: 229afba937eSOliver Neukum 230c1da59daSRobert Foss if (desc->rerr) { 231c1da59daSRobert Foss /* 232c1da59daSRobert Foss * Since there was an error, userspace may decide to not read 233c1da59daSRobert Foss * any data after poll'ing. 234c1da59daSRobert Foss * We should respond to further attempts from the device to send 235c1da59daSRobert Foss * data, so that we can get unstuck. 236c1da59daSRobert Foss */ 2372df69484SSebastian Andrzej Siewior schedule_work(&desc->service_outs_intr); 2382df69484SSebastian Andrzej Siewior } else { 2392df69484SSebastian Andrzej Siewior set_bit(WDM_READ, &desc->flags); 2402df69484SSebastian Andrzej Siewior wake_up(&desc->wait); 241c1da59daSRobert Foss } 242cac6fb01SLoic Poulain out: 243579b9ccaSSebastian Andrzej Siewior spin_unlock_irqrestore(&desc->iuspin, flags); 244afba937eSOliver Neukum } 245afba937eSOliver Neukum 246afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb) 247afba937eSOliver Neukum { 248579b9ccaSSebastian Andrzej Siewior unsigned long flags; 249afba937eSOliver Neukum int rv = 0; 2506dd433e6SOliver Neukum int responding; 251afba937eSOliver Neukum int status = urb->status; 252afba937eSOliver Neukum struct wdm_device *desc; 253afba937eSOliver Neukum struct usb_cdc_notification *dr; 254afba937eSOliver Neukum 255afba937eSOliver Neukum desc = urb->context; 256afba937eSOliver Neukum dr = (struct usb_cdc_notification *)desc->sbuf; 257afba937eSOliver Neukum 258afba937eSOliver Neukum if (status) { 259afba937eSOliver Neukum switch (status) { 260afba937eSOliver Neukum case -ESHUTDOWN: 261afba937eSOliver Neukum case -ENOENT: 262afba937eSOliver Neukum case -ECONNRESET: 263afba937eSOliver Neukum return; /* unplug */ 264afba937eSOliver Neukum case -EPIPE: 265afba937eSOliver Neukum set_bit(WDM_INT_STALL, &desc->flags); 2669908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Stall on int endpoint\n"); 267afba937eSOliver Neukum goto sw; /* halt is cleared in work */ 268afba937eSOliver Neukum default: 269*22f00812SAlan Stern dev_err_ratelimited(&desc->intf->dev, 2709908a32eSGreg Kroah-Hartman "nonzero urb status received: %d\n", status); 271afba937eSOliver Neukum break; 272afba937eSOliver Neukum } 273afba937eSOliver Neukum } 274afba937eSOliver Neukum 275afba937eSOliver Neukum if (urb->actual_length < sizeof(struct usb_cdc_notification)) { 276*22f00812SAlan Stern dev_err_ratelimited(&desc->intf->dev, "wdm_int_callback - %d bytes\n", 2779908a32eSGreg Kroah-Hartman urb->actual_length); 278afba937eSOliver Neukum goto exit; 279afba937eSOliver Neukum } 280afba937eSOliver Neukum 281afba937eSOliver Neukum switch (dr->bNotificationType) { 282afba937eSOliver Neukum case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: 283afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 284ce8bb344SOliver Neukum "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d\n", 285323ece54SOliver Neukum le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength)); 286afba937eSOliver Neukum break; 287afba937eSOliver Neukum 288afba937eSOliver Neukum case USB_CDC_NOTIFY_NETWORK_CONNECTION: 289afba937eSOliver Neukum 290afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 291ce8bb344SOliver Neukum "NOTIFY_NETWORK_CONNECTION %s network\n", 292afba937eSOliver Neukum dr->wValue ? "connected to" : "disconnected from"); 293afba937eSOliver Neukum goto exit; 2949983d6dcSBjørn Mork case USB_CDC_NOTIFY_SPEED_CHANGE: 295ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)\n", 2969983d6dcSBjørn Mork urb->actual_length); 2979983d6dcSBjørn Mork goto exit; 298afba937eSOliver Neukum default: 299afba937eSOliver Neukum clear_bit(WDM_POLL_RUNNING, &desc->flags); 3009908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 3019908a32eSGreg Kroah-Hartman "unknown notification %d received: index %d len %d\n", 302323ece54SOliver Neukum dr->bNotificationType, 303323ece54SOliver Neukum le16_to_cpu(dr->wIndex), 304323ece54SOliver Neukum le16_to_cpu(dr->wLength)); 305afba937eSOliver Neukum goto exit; 306afba937eSOliver Neukum } 307afba937eSOliver Neukum 308579b9ccaSSebastian Andrzej Siewior spin_lock_irqsave(&desc->iuspin, flags); 3096dd433e6SOliver Neukum responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); 31073e06865SGreg Suarez if (!desc->resp_count++ && !responding 31173e06865SGreg Suarez && !test_bit(WDM_DISCONNECTING, &desc->flags) 312beb1d35fSOliver Neukum && !test_bit(WDM_SUSPENDING, &desc->flags)) { 313afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_ATOMIC); 314ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv); 315afba937eSOliver Neukum } 316579b9ccaSSebastian Andrzej Siewior spin_unlock_irqrestore(&desc->iuspin, flags); 317afba937eSOliver Neukum if (rv < 0) { 318922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 319afba937eSOliver Neukum if (rv == -EPERM) 320afba937eSOliver Neukum return; 321afba937eSOliver Neukum if (rv == -ENOMEM) { 322afba937eSOliver Neukum sw: 323afba937eSOliver Neukum rv = schedule_work(&desc->rxwork); 324afba937eSOliver Neukum if (rv) 3259908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 3269908a32eSGreg Kroah-Hartman "Cannot schedule work\n"); 327afba937eSOliver Neukum } 328afba937eSOliver Neukum } 329afba937eSOliver Neukum exit: 330afba937eSOliver Neukum rv = usb_submit_urb(urb, GFP_ATOMIC); 331afba937eSOliver Neukum if (rv) 3329908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 3339908a32eSGreg Kroah-Hartman "%s - usb_submit_urb failed with result %d\n", 334afba937eSOliver Neukum __func__, rv); 335afba937eSOliver Neukum 336afba937eSOliver Neukum } 337afba937eSOliver Neukum 33818abf874SOliver Neukum static void poison_urbs(struct wdm_device *desc) 339afba937eSOliver Neukum { 34017d80d56SOliver Neukum /* the order here is essential */ 34118abf874SOliver Neukum usb_poison_urb(desc->command); 34218abf874SOliver Neukum usb_poison_urb(desc->validity); 34318abf874SOliver Neukum usb_poison_urb(desc->response); 34418abf874SOliver Neukum } 34518abf874SOliver Neukum 34618abf874SOliver Neukum static void unpoison_urbs(struct wdm_device *desc) 34718abf874SOliver Neukum { 34818abf874SOliver Neukum /* 34918abf874SOliver Neukum * the order here is not essential 35018abf874SOliver Neukum * it is symmetrical just to be nice 35118abf874SOliver Neukum */ 35218abf874SOliver Neukum usb_unpoison_urb(desc->response); 35318abf874SOliver Neukum usb_unpoison_urb(desc->validity); 35418abf874SOliver Neukum usb_unpoison_urb(desc->command); 355afba937eSOliver Neukum } 356afba937eSOliver Neukum 357afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc) 358afba937eSOliver Neukum { 359afba937eSOliver Neukum usb_free_urb(desc->validity); 360afba937eSOliver Neukum usb_free_urb(desc->response); 361afba937eSOliver Neukum usb_free_urb(desc->command); 362afba937eSOliver Neukum } 363afba937eSOliver Neukum 364afba937eSOliver Neukum static void cleanup(struct wdm_device *desc) 365afba937eSOliver Neukum { 3668457d99cSBjørn Mork kfree(desc->sbuf); 3678457d99cSBjørn Mork kfree(desc->inbuf); 368afba937eSOliver Neukum kfree(desc->orq); 369afba937eSOliver Neukum kfree(desc->irq); 370afba937eSOliver Neukum kfree(desc->ubuf); 371afba937eSOliver Neukum free_urbs(desc); 372afba937eSOliver Neukum kfree(desc); 373afba937eSOliver Neukum } 374afba937eSOliver Neukum 375afba937eSOliver Neukum static ssize_t wdm_write 376afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 377afba937eSOliver Neukum { 378afba937eSOliver Neukum u8 *buf; 379afba937eSOliver Neukum int rv = -EMSGSIZE, r, we; 380afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 381afba937eSOliver Neukum struct usb_ctrlrequest *req; 382afba937eSOliver Neukum 383afba937eSOliver Neukum if (count > desc->wMaxCommand) 384afba937eSOliver Neukum count = desc->wMaxCommand; 385afba937eSOliver Neukum 386afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 387afba937eSOliver Neukum we = desc->werr; 388afba937eSOliver Neukum desc->werr = 0; 389afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 390afba937eSOliver Neukum if (we < 0) 39176cb03e7SOliver Neukum return usb_translate_errors(we); 392afba937eSOliver Neukum 39364b9533eSGeliang Tang buf = memdup_user(buffer, count); 39464b9533eSGeliang Tang if (IS_ERR(buf)) 39564b9533eSGeliang Tang return PTR_ERR(buf); 396860e41a7SOliver Neukum 397860e41a7SOliver Neukum /* concurrent writes and disconnect */ 398e8537bd2SBjørn Mork r = mutex_lock_interruptible(&desc->wlock); 399860e41a7SOliver Neukum rv = -ERESTARTSYS; 40028965e17SOliver Neukum if (r) 40128965e17SOliver Neukum goto out_free_mem; 402860e41a7SOliver Neukum 403860e41a7SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 404860e41a7SOliver Neukum rv = -ENODEV; 40528965e17SOliver Neukum goto out_free_mem_lock; 406860e41a7SOliver Neukum } 407afba937eSOliver Neukum 40817d80d56SOliver Neukum r = usb_autopm_get_interface(desc->intf); 409860e41a7SOliver Neukum if (r < 0) { 41012a98b2bSOliver Neukum rv = usb_translate_errors(r); 41128965e17SOliver Neukum goto out_free_mem_lock; 412860e41a7SOliver Neukum } 4137f1dc313SOliver Neukum 4140cdfb819SDavid Sterba if (!(file->f_flags & O_NONBLOCK)) 415afba937eSOliver Neukum r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, 416afba937eSOliver Neukum &desc->flags)); 4177f1dc313SOliver Neukum else 4187f1dc313SOliver Neukum if (test_bit(WDM_IN_USE, &desc->flags)) 4197f1dc313SOliver Neukum r = -EAGAIN; 42088044202SBjørn Mork 42188044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) 42288044202SBjørn Mork r = -EIO; 42388044202SBjørn Mork 42437d2a363SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 42537d2a363SOliver Neukum r = -ENODEV; 42637d2a363SOliver Neukum 427860e41a7SOliver Neukum if (r < 0) { 42812a98b2bSOliver Neukum rv = r; 42928965e17SOliver Neukum goto out_free_mem_pm; 430afba937eSOliver Neukum } 431afba937eSOliver Neukum 432afba937eSOliver Neukum req = desc->orq; 433afba937eSOliver Neukum usb_fill_control_urb( 434afba937eSOliver Neukum desc->command, 435afba937eSOliver Neukum interface_to_usbdev(desc->intf), 436afba937eSOliver Neukum /* using common endpoint 0 */ 437afba937eSOliver Neukum usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0), 438afba937eSOliver Neukum (unsigned char *)req, 439afba937eSOliver Neukum buf, 440afba937eSOliver Neukum count, 441afba937eSOliver Neukum wdm_out_callback, 442afba937eSOliver Neukum desc 443afba937eSOliver Neukum ); 444afba937eSOliver Neukum 445afba937eSOliver Neukum req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | 446afba937eSOliver Neukum USB_RECIP_INTERFACE); 447afba937eSOliver Neukum req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 448afba937eSOliver Neukum req->wValue = 0; 449323ece54SOliver Neukum req->wIndex = desc->inum; /* already converted */ 450afba937eSOliver Neukum req->wLength = cpu_to_le16(count); 451afba937eSOliver Neukum set_bit(WDM_IN_USE, &desc->flags); 4525c22837aSOliver Neukum desc->outbuf = buf; 453afba937eSOliver Neukum 454afba937eSOliver Neukum rv = usb_submit_urb(desc->command, GFP_KERNEL); 455afba937eSOliver Neukum if (rv < 0) { 4565c22837aSOliver Neukum desc->outbuf = NULL; 457afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 45837d2a363SOliver Neukum wake_up_all(&desc->wait); /* for wdm_wait_for_response() */ 4599908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); 46012a98b2bSOliver Neukum rv = usb_translate_errors(rv); 46128965e17SOliver Neukum goto out_free_mem_pm; 462afba937eSOliver Neukum } else { 463ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d\n", 464323ece54SOliver Neukum le16_to_cpu(req->wIndex)); 465afba937eSOliver Neukum } 46628965e17SOliver Neukum 46717d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 468e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 46964b9533eSGeliang Tang return count; 47028965e17SOliver Neukum 47128965e17SOliver Neukum out_free_mem_pm: 47228965e17SOliver Neukum usb_autopm_put_interface(desc->intf); 47328965e17SOliver Neukum out_free_mem_lock: 47428965e17SOliver Neukum mutex_unlock(&desc->wlock); 47528965e17SOliver Neukum out_free_mem: 47628965e17SOliver Neukum kfree(buf); 47728965e17SOliver Neukum return rv; 478afba937eSOliver Neukum } 479afba937eSOliver Neukum 4808dd5cd53SBjørn Mork /* 481c1da59daSRobert Foss * Submit the read urb if resp_count is non-zero. 4828dd5cd53SBjørn Mork * 4838dd5cd53SBjørn Mork * Called with desc->iuspin locked 4848dd5cd53SBjørn Mork */ 485c1da59daSRobert Foss static int service_outstanding_interrupt(struct wdm_device *desc) 4868dd5cd53SBjørn Mork { 4878dd5cd53SBjørn Mork int rv = 0; 4888dd5cd53SBjørn Mork 4898dd5cd53SBjørn Mork /* submit read urb only if the device is waiting for it */ 490f563926fSBjørn Mork if (!desc->resp_count || !--desc->resp_count) 4918dd5cd53SBjørn Mork goto out; 4928dd5cd53SBjørn Mork 4935e5ff0b4STetsuo Handa if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 4945e5ff0b4STetsuo Handa rv = -ENODEV; 4955e5ff0b4STetsuo Handa goto out; 4965e5ff0b4STetsuo Handa } 4975e5ff0b4STetsuo Handa if (test_bit(WDM_RESETTING, &desc->flags)) { 4985e5ff0b4STetsuo Handa rv = -EIO; 4995e5ff0b4STetsuo Handa goto out; 5005e5ff0b4STetsuo Handa } 5015e5ff0b4STetsuo Handa 5021607830dSGreg Kroah-Hartman set_bit(WDM_RESPONDING, &desc->flags); 5038dd5cd53SBjørn Mork spin_unlock_irq(&desc->iuspin); 504e871db8dSSebastian Andrzej Siewior rv = usb_submit_urb(desc->response, GFP_KERNEL); 5058dd5cd53SBjørn Mork spin_lock_irq(&desc->iuspin); 5068dd5cd53SBjørn Mork if (rv) { 5075e5ff0b4STetsuo Handa if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 5088dd5cd53SBjørn Mork dev_err(&desc->intf->dev, 5098dd5cd53SBjørn Mork "usb_submit_urb failed with result %d\n", rv); 5108dd5cd53SBjørn Mork 5118dd5cd53SBjørn Mork /* make sure the next notification trigger a submit */ 5128dd5cd53SBjørn Mork clear_bit(WDM_RESPONDING, &desc->flags); 5138dd5cd53SBjørn Mork desc->resp_count = 0; 5148dd5cd53SBjørn Mork } 5158dd5cd53SBjørn Mork out: 5168dd5cd53SBjørn Mork return rv; 5178dd5cd53SBjørn Mork } 5188dd5cd53SBjørn Mork 519afba937eSOliver Neukum static ssize_t wdm_read 520afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos) 521afba937eSOliver Neukum { 522711c68b3SBen Hutchings int rv, cntr; 523afba937eSOliver Neukum int i = 0; 524afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 525afba937eSOliver Neukum 526afba937eSOliver Neukum 527e8537bd2SBjørn Mork rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ 528afba937eSOliver Neukum if (rv < 0) 529afba937eSOliver Neukum return -ERESTARTSYS; 530afba937eSOliver Neukum 5316aa7de05SMark Rutland cntr = READ_ONCE(desc->length); 532711c68b3SBen Hutchings if (cntr == 0) { 533afba937eSOliver Neukum desc->read = 0; 534afba937eSOliver Neukum retry: 5357f1dc313SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 5367f1dc313SOliver Neukum rv = -ENODEV; 5377f1dc313SOliver Neukum goto err; 5387f1dc313SOliver Neukum } 539c0f5eceeSOliver Neukum if (test_bit(WDM_OVERFLOW, &desc->flags)) { 540c0f5eceeSOliver Neukum clear_bit(WDM_OVERFLOW, &desc->flags); 541c0f5eceeSOliver Neukum rv = -ENOBUFS; 542c0f5eceeSOliver Neukum goto err; 543c0f5eceeSOliver Neukum } 544afba937eSOliver Neukum i++; 5457f1dc313SOliver Neukum if (file->f_flags & O_NONBLOCK) { 5467f1dc313SOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { 54753b7f7b5SGustavo A. R. Silva rv = -EAGAIN; 5487f1dc313SOliver Neukum goto err; 5497f1dc313SOliver Neukum } 5507f1dc313SOliver Neukum rv = 0; 5517f1dc313SOliver Neukum } else { 552afba937eSOliver Neukum rv = wait_event_interruptible(desc->wait, 553afba937eSOliver Neukum test_bit(WDM_READ, &desc->flags)); 5547f1dc313SOliver Neukum } 555afba937eSOliver Neukum 5567f1dc313SOliver Neukum /* may have happened while we slept */ 55717d80d56SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 55817d80d56SOliver Neukum rv = -ENODEV; 55917d80d56SOliver Neukum goto err; 56017d80d56SOliver Neukum } 56188044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) { 56288044202SBjørn Mork rv = -EIO; 56388044202SBjørn Mork goto err; 56488044202SBjørn Mork } 56517d80d56SOliver Neukum usb_mark_last_busy(interface_to_usbdev(desc->intf)); 566afba937eSOliver Neukum if (rv < 0) { 567afba937eSOliver Neukum rv = -ERESTARTSYS; 568afba937eSOliver Neukum goto err; 569afba937eSOliver Neukum } 570afba937eSOliver Neukum 571afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 572afba937eSOliver Neukum 573afba937eSOliver Neukum if (desc->rerr) { /* read completed, error happened */ 57485e8a0b9SOliver Neukum rv = usb_translate_errors(desc->rerr); 575afba937eSOliver Neukum desc->rerr = 0; 576afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 577afba937eSOliver Neukum goto err; 578afba937eSOliver Neukum } 579afba937eSOliver Neukum /* 580afba937eSOliver Neukum * recheck whether we've lost the race 581afba937eSOliver Neukum * against the completion handler 582afba937eSOliver Neukum */ 583afba937eSOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */ 584afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 585afba937eSOliver Neukum goto retry; 586afba937eSOliver Neukum } 587c0f5eceeSOliver Neukum 588afba937eSOliver Neukum if (!desc->reslength) { /* zero length read */ 589ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n"); 590c1da59daSRobert Foss clear_bit(WDM_READ, &desc->flags); 591c1da59daSRobert Foss rv = service_outstanding_interrupt(desc); 592afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 5938dd5cd53SBjørn Mork if (rv < 0) 5948dd5cd53SBjørn Mork goto err; 595afba937eSOliver Neukum goto retry; 596afba937eSOliver Neukum } 597711c68b3SBen Hutchings cntr = desc->length; 598afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 599afba937eSOliver Neukum } 600afba937eSOliver Neukum 601711c68b3SBen Hutchings if (cntr > count) 602711c68b3SBen Hutchings cntr = count; 603afba937eSOliver Neukum rv = copy_to_user(buffer, desc->ubuf, cntr); 604afba937eSOliver Neukum if (rv > 0) { 605afba937eSOliver Neukum rv = -EFAULT; 606afba937eSOliver Neukum goto err; 607afba937eSOliver Neukum } 608afba937eSOliver Neukum 609711c68b3SBen Hutchings spin_lock_irq(&desc->iuspin); 610711c68b3SBen Hutchings 611afba937eSOliver Neukum for (i = 0; i < desc->length - cntr; i++) 612afba937eSOliver Neukum desc->ubuf[i] = desc->ubuf[i + cntr]; 613afba937eSOliver Neukum 614afba937eSOliver Neukum desc->length -= cntr; 61587d65e54SOliver Neukum /* in case we had outstanding data */ 616c1da59daSRobert Foss if (!desc->length) { 617c1da59daSRobert Foss clear_bit(WDM_READ, &desc->flags); 618c1da59daSRobert Foss service_outstanding_interrupt(desc); 619c1da59daSRobert Foss } 62073e06865SGreg Suarez spin_unlock_irq(&desc->iuspin); 621afba937eSOliver Neukum rv = cntr; 622afba937eSOliver Neukum 623afba937eSOliver Neukum err: 624e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 625afba937eSOliver Neukum return rv; 626afba937eSOliver Neukum } 627afba937eSOliver Neukum 62837d2a363SOliver Neukum static int wdm_wait_for_response(struct file *file, long timeout) 629afba937eSOliver Neukum { 630afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 63137d2a363SOliver Neukum long rv; /* Use long here because (int) MAX_SCHEDULE_TIMEOUT < 0. */ 632afba937eSOliver Neukum 6331426bd2cSOliver Neukum /* 63437d2a363SOliver Neukum * Needs both flags. We cannot do with one because resetting it would 63537d2a363SOliver Neukum * cause a race with write() yet we need to signal a disconnect. 6361426bd2cSOliver Neukum */ 63737d2a363SOliver Neukum rv = wait_event_interruptible_timeout(desc->wait, 6381426bd2cSOliver Neukum !test_bit(WDM_IN_USE, &desc->flags) || 63937d2a363SOliver Neukum test_bit(WDM_DISCONNECTING, &desc->flags), 64037d2a363SOliver Neukum timeout); 6416b0b79d3SBjørn Mork 64237d2a363SOliver Neukum /* 64337d2a363SOliver Neukum * To report the correct error. This is best effort. 64437d2a363SOliver Neukum * We are inevitably racing with the hardware. 64537d2a363SOliver Neukum */ 6461426bd2cSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 6471426bd2cSOliver Neukum return -ENODEV; 64837d2a363SOliver Neukum if (!rv) 64937d2a363SOliver Neukum return -EIO; 65037d2a363SOliver Neukum if (rv < 0) 65137d2a363SOliver Neukum return -EINTR; 652afba937eSOliver Neukum 65337d2a363SOliver Neukum spin_lock_irq(&desc->iuspin); 65437d2a363SOliver Neukum rv = desc->werr; 65537d2a363SOliver Neukum desc->werr = 0; 65637d2a363SOliver Neukum spin_unlock_irq(&desc->iuspin); 65737d2a363SOliver Neukum 65837d2a363SOliver Neukum return usb_translate_errors(rv); 65937d2a363SOliver Neukum 66037d2a363SOliver Neukum } 66137d2a363SOliver Neukum 66237d2a363SOliver Neukum /* 66337d2a363SOliver Neukum * You need to send a signal when you react to malicious or defective hardware. 66437d2a363SOliver Neukum * Also, don't abort when fsync() returned -EINVAL, for older kernels which do 66537d2a363SOliver Neukum * not implement wdm_flush() will return -EINVAL. 66637d2a363SOliver Neukum */ 66737d2a363SOliver Neukum static int wdm_fsync(struct file *file, loff_t start, loff_t end, int datasync) 66837d2a363SOliver Neukum { 66937d2a363SOliver Neukum return wdm_wait_for_response(file, MAX_SCHEDULE_TIMEOUT); 67037d2a363SOliver Neukum } 67137d2a363SOliver Neukum 67237d2a363SOliver Neukum /* 67337d2a363SOliver Neukum * Same with wdm_fsync(), except it uses finite timeout in order to react to 67437d2a363SOliver Neukum * malicious or defective hardware which ceased communication after close() was 67537d2a363SOliver Neukum * implicitly called due to process termination. 67637d2a363SOliver Neukum */ 67737d2a363SOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id) 67837d2a363SOliver Neukum { 67937d2a363SOliver Neukum return wdm_wait_for_response(file, WDM_FLUSH_TIMEOUT); 680afba937eSOliver Neukum } 681afba937eSOliver Neukum 682afc9a42bSAl Viro static __poll_t wdm_poll(struct file *file, struct poll_table_struct *wait) 683afba937eSOliver Neukum { 684afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 685afba937eSOliver Neukum unsigned long flags; 686afc9a42bSAl Viro __poll_t mask = 0; 687afba937eSOliver Neukum 688afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 689afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 690a9a08845SLinus Torvalds mask = EPOLLHUP | EPOLLERR; 691afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 692afba937eSOliver Neukum goto desc_out; 693afba937eSOliver Neukum } 694afba937eSOliver Neukum if (test_bit(WDM_READ, &desc->flags)) 695a9a08845SLinus Torvalds mask = EPOLLIN | EPOLLRDNORM; 696afba937eSOliver Neukum if (desc->rerr || desc->werr) 697a9a08845SLinus Torvalds mask |= EPOLLERR; 698afba937eSOliver Neukum if (!test_bit(WDM_IN_USE, &desc->flags)) 699a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM; 700afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 701afba937eSOliver Neukum 702afba937eSOliver Neukum poll_wait(file, &desc->wait, wait); 703afba937eSOliver Neukum 704afba937eSOliver Neukum desc_out: 705afba937eSOliver Neukum return mask; 706afba937eSOliver Neukum } 707afba937eSOliver Neukum 708afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file) 709afba937eSOliver Neukum { 710afba937eSOliver Neukum int minor = iminor(inode); 711afba937eSOliver Neukum int rv = -ENODEV; 712afba937eSOliver Neukum struct usb_interface *intf; 713afba937eSOliver Neukum struct wdm_device *desc; 714afba937eSOliver Neukum 715afba937eSOliver Neukum mutex_lock(&wdm_mutex); 716b0c13860SBjørn Mork desc = wdm_find_device_by_minor(minor); 717b0c13860SBjørn Mork if (!desc) 718afba937eSOliver Neukum goto out; 719afba937eSOliver Neukum 720b0c13860SBjørn Mork intf = desc->intf; 721afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 722afba937eSOliver Neukum goto out; 723afba937eSOliver Neukum file->private_data = desc; 724afba937eSOliver Neukum 725cac6fb01SLoic Poulain if (test_bit(WDM_WWAN_IN_USE, &desc->flags)) { 726cac6fb01SLoic Poulain rv = -EBUSY; 727cac6fb01SLoic Poulain goto out; 728cac6fb01SLoic Poulain } 729cac6fb01SLoic Poulain 73017d80d56SOliver Neukum rv = usb_autopm_get_interface(desc->intf); 73117d80d56SOliver Neukum if (rv < 0) { 7329908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); 73317d80d56SOliver Neukum goto out; 73417d80d56SOliver Neukum } 735afba937eSOliver Neukum 736e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 737e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 73817d80d56SOliver Neukum if (!desc->count++) { 739d771d8aaSOliver Neukum desc->werr = 0; 740d771d8aaSOliver Neukum desc->rerr = 0; 74117d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_KERNEL); 742afba937eSOliver Neukum if (rv < 0) { 743afba937eSOliver Neukum desc->count--; 7449908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 7459908a32eSGreg Kroah-Hartman "Error submitting int urb - %d\n", rv); 74612a98b2bSOliver Neukum rv = usb_translate_errors(rv); 747afba937eSOliver Neukum } 74817d80d56SOliver Neukum } else { 749afba937eSOliver Neukum rv = 0; 75017d80d56SOliver Neukum } 751e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 7523cc36157SBjørn Mork if (desc->count == 1) 7533cc36157SBjørn Mork desc->manage_power(intf, 1); 75417d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 755afba937eSOliver Neukum out: 756afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 757afba937eSOliver Neukum return rv; 758afba937eSOliver Neukum } 759afba937eSOliver Neukum 760afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file) 761afba937eSOliver Neukum { 762afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 763afba937eSOliver Neukum 764afba937eSOliver Neukum mutex_lock(&wdm_mutex); 765e8537bd2SBjørn Mork 766e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 767e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 768afba937eSOliver Neukum desc->count--; 769e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 77017d80d56SOliver Neukum 771afba937eSOliver Neukum if (!desc->count) { 7726b0b79d3SBjørn Mork if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { 773ce8bb344SOliver Neukum dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n"); 77418abf874SOliver Neukum poison_urbs(desc); 77573e06865SGreg Suarez spin_lock_irq(&desc->iuspin); 77673e06865SGreg Suarez desc->resp_count = 0; 77701e01f5cSSergey Ryazanov clear_bit(WDM_RESPONDING, &desc->flags); 77873e06865SGreg Suarez spin_unlock_irq(&desc->iuspin); 7793cc36157SBjørn Mork desc->manage_power(desc->intf, 0); 78018abf874SOliver Neukum unpoison_urbs(desc); 781880bca3aSBjørn Mork } else { 7826b0b79d3SBjørn Mork /* must avoid dev_printk here as desc->intf is invalid */ 7836b0b79d3SBjørn Mork pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__); 7842f338c8aSOliver Neukum cleanup(desc); 785afba937eSOliver Neukum } 786880bca3aSBjørn Mork } 787afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 788afba937eSOliver Neukum return 0; 789afba937eSOliver Neukum } 790afba937eSOliver Neukum 7913edce1cfSBjørn Mork static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 7923edce1cfSBjørn Mork { 7933edce1cfSBjørn Mork struct wdm_device *desc = file->private_data; 7943edce1cfSBjørn Mork int rv = 0; 7953edce1cfSBjørn Mork 7963edce1cfSBjørn Mork switch (cmd) { 7973edce1cfSBjørn Mork case IOCTL_WDM_MAX_COMMAND: 7983edce1cfSBjørn Mork if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand))) 7993edce1cfSBjørn Mork rv = -EFAULT; 8003edce1cfSBjørn Mork break; 8013edce1cfSBjørn Mork default: 8023edce1cfSBjørn Mork rv = -ENOTTY; 8033edce1cfSBjørn Mork } 8043edce1cfSBjørn Mork return rv; 8053edce1cfSBjørn Mork } 8063edce1cfSBjørn Mork 807afba937eSOliver Neukum static const struct file_operations wdm_fops = { 808afba937eSOliver Neukum .owner = THIS_MODULE, 809afba937eSOliver Neukum .read = wdm_read, 810afba937eSOliver Neukum .write = wdm_write, 81137d2a363SOliver Neukum .fsync = wdm_fsync, 812afba937eSOliver Neukum .open = wdm_open, 813afba937eSOliver Neukum .flush = wdm_flush, 814afba937eSOliver Neukum .release = wdm_release, 8156038f373SArnd Bergmann .poll = wdm_poll, 8163edce1cfSBjørn Mork .unlocked_ioctl = wdm_ioctl, 8171832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl, 8186038f373SArnd Bergmann .llseek = noop_llseek, 819afba937eSOliver Neukum }; 820afba937eSOliver Neukum 821afba937eSOliver Neukum static struct usb_class_driver wdm_class = { 822afba937eSOliver Neukum .name = "cdc-wdm%d", 823afba937eSOliver Neukum .fops = &wdm_fops, 824afba937eSOliver Neukum .minor_base = WDM_MINOR_BASE, 825afba937eSOliver Neukum }; 826afba937eSOliver Neukum 827cac6fb01SLoic Poulain /* --- WWAN framework integration --- */ 82804d2b755SRikard Falkeborn #ifdef CONFIG_WWAN 829cac6fb01SLoic Poulain static int wdm_wwan_port_start(struct wwan_port *port) 830cac6fb01SLoic Poulain { 831cac6fb01SLoic Poulain struct wdm_device *desc = wwan_port_get_drvdata(port); 832cac6fb01SLoic Poulain 833cac6fb01SLoic Poulain /* The interface is both exposed via the WWAN framework and as a 834cac6fb01SLoic Poulain * legacy usbmisc chardev. If chardev is already open, just fail 835cac6fb01SLoic Poulain * to prevent concurrent usage. Otherwise, switch to WWAN mode. 836cac6fb01SLoic Poulain */ 837cac6fb01SLoic Poulain mutex_lock(&wdm_mutex); 838cac6fb01SLoic Poulain if (desc->count) { 839cac6fb01SLoic Poulain mutex_unlock(&wdm_mutex); 840cac6fb01SLoic Poulain return -EBUSY; 841cac6fb01SLoic Poulain } 842cac6fb01SLoic Poulain set_bit(WDM_WWAN_IN_USE, &desc->flags); 843cac6fb01SLoic Poulain mutex_unlock(&wdm_mutex); 844cac6fb01SLoic Poulain 845cac6fb01SLoic Poulain desc->manage_power(desc->intf, 1); 846cac6fb01SLoic Poulain 847cac6fb01SLoic Poulain /* tx is allowed */ 848cac6fb01SLoic Poulain wwan_port_txon(port); 849cac6fb01SLoic Poulain 850cac6fb01SLoic Poulain /* Start getting events */ 851cac6fb01SLoic Poulain return usb_submit_urb(desc->validity, GFP_KERNEL); 852cac6fb01SLoic Poulain } 853cac6fb01SLoic Poulain 854cac6fb01SLoic Poulain static void wdm_wwan_port_stop(struct wwan_port *port) 855cac6fb01SLoic Poulain { 856cac6fb01SLoic Poulain struct wdm_device *desc = wwan_port_get_drvdata(port); 857cac6fb01SLoic Poulain 858cac6fb01SLoic Poulain /* Stop all transfers and disable WWAN mode */ 8595ada57a9SJakub Kicinski poison_urbs(desc); 860cac6fb01SLoic Poulain desc->manage_power(desc->intf, 0); 861cac6fb01SLoic Poulain clear_bit(WDM_READ, &desc->flags); 862cac6fb01SLoic Poulain clear_bit(WDM_WWAN_IN_USE, &desc->flags); 8635ada57a9SJakub Kicinski unpoison_urbs(desc); 864cac6fb01SLoic Poulain } 865cac6fb01SLoic Poulain 866cac6fb01SLoic Poulain static void wdm_wwan_port_tx_complete(struct urb *urb) 867cac6fb01SLoic Poulain { 868cac6fb01SLoic Poulain struct sk_buff *skb = urb->context; 869cac6fb01SLoic Poulain struct wdm_device *desc = skb_shinfo(skb)->destructor_arg; 870cac6fb01SLoic Poulain 871cac6fb01SLoic Poulain usb_autopm_put_interface(desc->intf); 872cac6fb01SLoic Poulain wwan_port_txon(desc->wwanp); 873cac6fb01SLoic Poulain kfree_skb(skb); 874cac6fb01SLoic Poulain } 875cac6fb01SLoic Poulain 876cac6fb01SLoic Poulain static int wdm_wwan_port_tx(struct wwan_port *port, struct sk_buff *skb) 877cac6fb01SLoic Poulain { 878cac6fb01SLoic Poulain struct wdm_device *desc = wwan_port_get_drvdata(port); 879cac6fb01SLoic Poulain struct usb_interface *intf = desc->intf; 880cac6fb01SLoic Poulain struct usb_ctrlrequest *req = desc->orq; 881cac6fb01SLoic Poulain int rv; 882cac6fb01SLoic Poulain 883cac6fb01SLoic Poulain rv = usb_autopm_get_interface(intf); 884cac6fb01SLoic Poulain if (rv) 885cac6fb01SLoic Poulain return rv; 886cac6fb01SLoic Poulain 887cac6fb01SLoic Poulain usb_fill_control_urb( 888cac6fb01SLoic Poulain desc->command, 889cac6fb01SLoic Poulain interface_to_usbdev(intf), 890cac6fb01SLoic Poulain usb_sndctrlpipe(interface_to_usbdev(intf), 0), 891cac6fb01SLoic Poulain (unsigned char *)req, 892cac6fb01SLoic Poulain skb->data, 893cac6fb01SLoic Poulain skb->len, 894cac6fb01SLoic Poulain wdm_wwan_port_tx_complete, 895cac6fb01SLoic Poulain skb 896cac6fb01SLoic Poulain ); 897cac6fb01SLoic Poulain 898cac6fb01SLoic Poulain req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); 899cac6fb01SLoic Poulain req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 900cac6fb01SLoic Poulain req->wValue = 0; 901cac6fb01SLoic Poulain req->wIndex = desc->inum; 902cac6fb01SLoic Poulain req->wLength = cpu_to_le16(skb->len); 903cac6fb01SLoic Poulain 904cac6fb01SLoic Poulain skb_shinfo(skb)->destructor_arg = desc; 905cac6fb01SLoic Poulain 906cac6fb01SLoic Poulain rv = usb_submit_urb(desc->command, GFP_KERNEL); 907cac6fb01SLoic Poulain if (rv) 908cac6fb01SLoic Poulain usb_autopm_put_interface(intf); 909cac6fb01SLoic Poulain else /* One transfer at a time, stop TX until URB completion */ 910cac6fb01SLoic Poulain wwan_port_txoff(port); 911cac6fb01SLoic Poulain 912cac6fb01SLoic Poulain return rv; 913cac6fb01SLoic Poulain } 914cac6fb01SLoic Poulain 915c608dc10SRikard Falkeborn static const struct wwan_port_ops wdm_wwan_port_ops = { 916cac6fb01SLoic Poulain .start = wdm_wwan_port_start, 917cac6fb01SLoic Poulain .stop = wdm_wwan_port_stop, 918cac6fb01SLoic Poulain .tx = wdm_wwan_port_tx, 919cac6fb01SLoic Poulain }; 920cac6fb01SLoic Poulain 921cac6fb01SLoic Poulain static void wdm_wwan_init(struct wdm_device *desc) 922cac6fb01SLoic Poulain { 923cac6fb01SLoic Poulain struct usb_interface *intf = desc->intf; 924cac6fb01SLoic Poulain struct wwan_port *port; 925cac6fb01SLoic Poulain 926cac6fb01SLoic Poulain /* Only register to WWAN core if protocol/type is known */ 927cac6fb01SLoic Poulain if (desc->wwanp_type == WWAN_PORT_UNKNOWN) { 928cac6fb01SLoic Poulain dev_info(&intf->dev, "Unknown control protocol\n"); 929cac6fb01SLoic Poulain return; 930cac6fb01SLoic Poulain } 931cac6fb01SLoic Poulain 93236bd28c1Shaozhe chang port = wwan_create_port(&intf->dev, desc->wwanp_type, &wdm_wwan_port_ops, 93336bd28c1Shaozhe chang NULL, desc); 934cac6fb01SLoic Poulain if (IS_ERR(port)) { 935cac6fb01SLoic Poulain dev_err(&intf->dev, "%s: Unable to create WWAN port\n", 936cac6fb01SLoic Poulain dev_name(intf->usb_dev)); 937cac6fb01SLoic Poulain return; 938cac6fb01SLoic Poulain } 939cac6fb01SLoic Poulain 940cac6fb01SLoic Poulain desc->wwanp = port; 941cac6fb01SLoic Poulain } 942cac6fb01SLoic Poulain 943cac6fb01SLoic Poulain static void wdm_wwan_deinit(struct wdm_device *desc) 944cac6fb01SLoic Poulain { 945cac6fb01SLoic Poulain if (!desc->wwanp) 946cac6fb01SLoic Poulain return; 947cac6fb01SLoic Poulain 948cac6fb01SLoic Poulain wwan_remove_port(desc->wwanp); 949cac6fb01SLoic Poulain desc->wwanp = NULL; 950cac6fb01SLoic Poulain } 951cac6fb01SLoic Poulain 952cac6fb01SLoic Poulain static void wdm_wwan_rx(struct wdm_device *desc, int length) 953cac6fb01SLoic Poulain { 954cac6fb01SLoic Poulain struct wwan_port *port = desc->wwanp; 955cac6fb01SLoic Poulain struct sk_buff *skb; 956cac6fb01SLoic Poulain 957cac6fb01SLoic Poulain /* Forward data to WWAN port */ 958cac6fb01SLoic Poulain skb = alloc_skb(length, GFP_ATOMIC); 959cac6fb01SLoic Poulain if (!skb) 960cac6fb01SLoic Poulain return; 961cac6fb01SLoic Poulain 9621abf6ab4SShang XiaoJing skb_put_data(skb, desc->inbuf, length); 963cac6fb01SLoic Poulain wwan_port_rx(port, skb); 964cac6fb01SLoic Poulain 965cac6fb01SLoic Poulain /* inbuf has been copied, it is safe to check for outstanding data */ 966cac6fb01SLoic Poulain schedule_work(&desc->service_outs_intr); 967cac6fb01SLoic Poulain } 96804d2b755SRikard Falkeborn #else /* CONFIG_WWAN */ 969cac6fb01SLoic Poulain static void wdm_wwan_init(struct wdm_device *desc) {} 970cac6fb01SLoic Poulain static void wdm_wwan_deinit(struct wdm_device *desc) {} 971cac6fb01SLoic Poulain static void wdm_wwan_rx(struct wdm_device *desc, int length) {} 97204d2b755SRikard Falkeborn #endif /* CONFIG_WWAN */ 973cac6fb01SLoic Poulain 974afba937eSOliver Neukum /* --- error handling --- */ 975afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work) 976afba937eSOliver Neukum { 977afba937eSOliver Neukum struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); 978afba937eSOliver Neukum unsigned long flags; 9796dd433e6SOliver Neukum int rv = 0; 9806dd433e6SOliver Neukum int responding; 981afba937eSOliver Neukum 982afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 983afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 984afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 985afba937eSOliver Neukum } else { 9866dd433e6SOliver Neukum responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); 987afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 9886dd433e6SOliver Neukum if (!responding) 989afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_KERNEL); 990afba937eSOliver Neukum if (rv < 0 && rv != -EPERM) { 991afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 9926dd433e6SOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 993afba937eSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 994afba937eSOliver Neukum schedule_work(&desc->rxwork); 995afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 996afba937eSOliver Neukum } 997afba937eSOliver Neukum } 998afba937eSOliver Neukum } 999afba937eSOliver Neukum 10002df69484SSebastian Andrzej Siewior static void service_interrupt_work(struct work_struct *work) 10012df69484SSebastian Andrzej Siewior { 10022df69484SSebastian Andrzej Siewior struct wdm_device *desc; 10032df69484SSebastian Andrzej Siewior 10042df69484SSebastian Andrzej Siewior desc = container_of(work, struct wdm_device, service_outs_intr); 10052df69484SSebastian Andrzej Siewior 10062df69484SSebastian Andrzej Siewior spin_lock_irq(&desc->iuspin); 10072df69484SSebastian Andrzej Siewior service_outstanding_interrupt(desc); 10082df69484SSebastian Andrzej Siewior if (!desc->resp_count) { 10092df69484SSebastian Andrzej Siewior set_bit(WDM_READ, &desc->flags); 10102df69484SSebastian Andrzej Siewior wake_up(&desc->wait); 10112df69484SSebastian Andrzej Siewior } 10122df69484SSebastian Andrzej Siewior spin_unlock_irq(&desc->iuspin); 10132df69484SSebastian Andrzej Siewior } 10142df69484SSebastian Andrzej Siewior 1015afba937eSOliver Neukum /* --- hotplug --- */ 1016afba937eSOliver Neukum 10173cc36157SBjørn Mork static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, 1018cac6fb01SLoic Poulain u16 bufsize, enum wwan_port_type type, 1019cac6fb01SLoic Poulain int (*manage_power)(struct usb_interface *, int)) 1020afba937eSOliver Neukum { 10210dffb486SBjørn Mork int rv = -ENOMEM; 1022afba937eSOliver Neukum struct wdm_device *desc; 1023afba937eSOliver Neukum 1024afba937eSOliver Neukum desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); 1025afba937eSOliver Neukum if (!desc) 1026afba937eSOliver Neukum goto out; 1027b0c13860SBjørn Mork INIT_LIST_HEAD(&desc->device_list); 1028e8537bd2SBjørn Mork mutex_init(&desc->rlock); 1029e8537bd2SBjørn Mork mutex_init(&desc->wlock); 1030afba937eSOliver Neukum spin_lock_init(&desc->iuspin); 1031afba937eSOliver Neukum init_waitqueue_head(&desc->wait); 10320dffb486SBjørn Mork desc->wMaxCommand = bufsize; 1033052fbc0dSOliver Neukum /* this will be expanded and needed in hardware endianness */ 1034afba937eSOliver Neukum desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); 1035afba937eSOliver Neukum desc->intf = intf; 1036cac6fb01SLoic Poulain desc->wwanp_type = type; 1037afba937eSOliver Neukum INIT_WORK(&desc->rxwork, wdm_rxwork); 10382df69484SSebastian Andrzej Siewior INIT_WORK(&desc->service_outs_intr, service_interrupt_work); 1039afba937eSOliver Neukum 104042601e35SJunlin Yang if (!usb_endpoint_is_int_in(ep)) { 1041afba937eSOliver Neukum rv = -EINVAL; 1042052fbc0dSOliver Neukum goto err; 104342601e35SJunlin Yang } 1044afba937eSOliver Neukum 104529cc8897SKuninori Morimoto desc->wMaxPacketSize = usb_endpoint_maxp(ep); 1046afba937eSOliver Neukum 1047afba937eSOliver Neukum desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 1048afba937eSOliver Neukum if (!desc->orq) 1049afba937eSOliver Neukum goto err; 1050afba937eSOliver Neukum desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 1051afba937eSOliver Neukum if (!desc->irq) 1052afba937eSOliver Neukum goto err; 1053afba937eSOliver Neukum 1054afba937eSOliver Neukum desc->validity = usb_alloc_urb(0, GFP_KERNEL); 1055afba937eSOliver Neukum if (!desc->validity) 1056afba937eSOliver Neukum goto err; 1057afba937eSOliver Neukum 1058afba937eSOliver Neukum desc->response = usb_alloc_urb(0, GFP_KERNEL); 1059afba937eSOliver Neukum if (!desc->response) 1060afba937eSOliver Neukum goto err; 1061afba937eSOliver Neukum 1062afba937eSOliver Neukum desc->command = usb_alloc_urb(0, GFP_KERNEL); 1063afba937eSOliver Neukum if (!desc->command) 1064afba937eSOliver Neukum goto err; 1065afba937eSOliver Neukum 1066afba937eSOliver Neukum desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 1067afba937eSOliver Neukum if (!desc->ubuf) 1068afba937eSOliver Neukum goto err; 1069afba937eSOliver Neukum 10708457d99cSBjørn Mork desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL); 1071afba937eSOliver Neukum if (!desc->sbuf) 1072afba937eSOliver Neukum goto err; 1073afba937eSOliver Neukum 10748457d99cSBjørn Mork desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 1075afba937eSOliver Neukum if (!desc->inbuf) 10768457d99cSBjørn Mork goto err; 1077afba937eSOliver Neukum 1078afba937eSOliver Neukum usb_fill_int_urb( 1079afba937eSOliver Neukum desc->validity, 1080afba937eSOliver Neukum interface_to_usbdev(intf), 1081afba937eSOliver Neukum usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress), 1082afba937eSOliver Neukum desc->sbuf, 1083afba937eSOliver Neukum desc->wMaxPacketSize, 1084afba937eSOliver Neukum wdm_int_callback, 1085afba937eSOliver Neukum desc, 1086afba937eSOliver Neukum ep->bInterval 1087afba937eSOliver Neukum ); 1088afba937eSOliver Neukum 108919b85b3bSBjørn Mork desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); 109019b85b3bSBjørn Mork desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; 109119b85b3bSBjørn Mork desc->irq->wValue = 0; 1092323ece54SOliver Neukum desc->irq->wIndex = desc->inum; /* already converted */ 109319b85b3bSBjørn Mork desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); 109419b85b3bSBjørn Mork 109519b85b3bSBjørn Mork usb_fill_control_urb( 109619b85b3bSBjørn Mork desc->response, 10978143a896SBjørn Mork interface_to_usbdev(intf), 109819b85b3bSBjørn Mork /* using common endpoint 0 */ 109919b85b3bSBjørn Mork usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), 110019b85b3bSBjørn Mork (unsigned char *)desc->irq, 110119b85b3bSBjørn Mork desc->inbuf, 110219b85b3bSBjørn Mork desc->wMaxCommand, 110319b85b3bSBjørn Mork wdm_in_callback, 110419b85b3bSBjørn Mork desc 110519b85b3bSBjørn Mork ); 110619b85b3bSBjørn Mork 11073cc36157SBjørn Mork desc->manage_power = manage_power; 11083cc36157SBjørn Mork 1109b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 1110b0c13860SBjørn Mork list_add(&desc->device_list, &wdm_device_list); 1111b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 1112b0c13860SBjørn Mork 1113afba937eSOliver Neukum rv = usb_register_dev(intf, &wdm_class); 1114052fbc0dSOliver Neukum if (rv < 0) 1115b0c13860SBjørn Mork goto err; 1116052fbc0dSOliver Neukum else 1117820c629aSBjørn Mork dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev)); 1118cac6fb01SLoic Poulain 1119cac6fb01SLoic Poulain wdm_wwan_init(desc); 1120cac6fb01SLoic Poulain 1121afba937eSOliver Neukum out: 1122afba937eSOliver Neukum return rv; 1123afba937eSOliver Neukum err: 11246286d85eSBjørn Mork spin_lock(&wdm_device_list_lock); 11256286d85eSBjørn Mork list_del(&desc->device_list); 11266286d85eSBjørn Mork spin_unlock(&wdm_device_list_lock); 11270dffb486SBjørn Mork cleanup(desc); 11280dffb486SBjørn Mork return rv; 11290dffb486SBjørn Mork } 11300dffb486SBjørn Mork 11313cc36157SBjørn Mork static int wdm_manage_power(struct usb_interface *intf, int on) 11323cc36157SBjørn Mork { 11333cc36157SBjørn Mork /* need autopm_get/put here to ensure the usbcore sees the new value */ 11343cc36157SBjørn Mork int rv = usb_autopm_get_interface(intf); 11353cc36157SBjørn Mork 11363cc36157SBjørn Mork intf->needs_remote_wakeup = on; 11374144bc86SBjørn Mork if (!rv) 11383cc36157SBjørn Mork usb_autopm_put_interface(intf); 11394144bc86SBjørn Mork return 0; 11403cc36157SBjørn Mork } 11413cc36157SBjørn Mork 11420dffb486SBjørn Mork static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) 11430dffb486SBjørn Mork { 11440dffb486SBjørn Mork int rv = -EINVAL; 11450dffb486SBjørn Mork struct usb_host_interface *iface; 11460dffb486SBjørn Mork struct usb_endpoint_descriptor *ep; 11477fae7bfbSOliver Neukum struct usb_cdc_parsed_header hdr; 11480dffb486SBjørn Mork u8 *buffer = intf->altsetting->extra; 11490dffb486SBjørn Mork int buflen = intf->altsetting->extralen; 11500dffb486SBjørn Mork u16 maxcom = WDM_DEFAULT_BUFSIZE; 11510dffb486SBjørn Mork 11520dffb486SBjørn Mork if (!buffer) 11530dffb486SBjørn Mork goto err; 11540dffb486SBjørn Mork 11557fae7bfbSOliver Neukum cdc_parse_cdc_header(&hdr, intf, buffer, buflen); 11567fae7bfbSOliver Neukum 11577fae7bfbSOliver Neukum if (hdr.usb_cdc_dmm_desc) 11587fae7bfbSOliver Neukum maxcom = le16_to_cpu(hdr.usb_cdc_dmm_desc->wMaxCommand); 11590dffb486SBjørn Mork 11600dffb486SBjørn Mork iface = intf->cur_altsetting; 11610dffb486SBjørn Mork if (iface->desc.bNumEndpoints != 1) 11620dffb486SBjørn Mork goto err; 11630dffb486SBjørn Mork ep = &iface->endpoint[0].desc; 11640dffb486SBjørn Mork 1165cac6fb01SLoic Poulain rv = wdm_create(intf, ep, maxcom, WWAN_PORT_UNKNOWN, &wdm_manage_power); 11660dffb486SBjørn Mork 11670dffb486SBjørn Mork err: 1168afba937eSOliver Neukum return rv; 1169afba937eSOliver Neukum } 1170afba937eSOliver Neukum 11713cc36157SBjørn Mork /** 11723cc36157SBjørn Mork * usb_cdc_wdm_register - register a WDM subdriver 11733cc36157SBjørn Mork * @intf: usb interface the subdriver will associate with 11743cc36157SBjørn Mork * @ep: interrupt endpoint to monitor for notifications 11753cc36157SBjørn Mork * @bufsize: maximum message size to support for read/write 1176cac6fb01SLoic Poulain * @type: Type/protocol of the transported data (MBIM, QMI...) 1177ddcb6c6aSLee Jones * @manage_power: call-back invoked during open and release to 1178ddcb6c6aSLee Jones * manage the device's power 11793cc36157SBjørn Mork * Create WDM usb class character device and associate it with intf 11803cc36157SBjørn Mork * without binding, allowing another driver to manage the interface. 11813cc36157SBjørn Mork * 11823cc36157SBjørn Mork * The subdriver will manage the given interrupt endpoint exclusively 11833cc36157SBjørn Mork * and will issue control requests referring to the given intf. It 11843cc36157SBjørn Mork * will otherwise avoid interferring, and in particular not do 11853cc36157SBjørn Mork * usb_set_intfdata/usb_get_intfdata on intf. 11863cc36157SBjørn Mork * 11873cc36157SBjørn Mork * The return value is a pointer to the subdriver's struct usb_driver. 11883cc36157SBjørn Mork * The registering driver is responsible for calling this subdriver's 11893cc36157SBjørn Mork * disconnect, suspend, resume, pre_reset and post_reset methods from 11903cc36157SBjørn Mork * its own. 11913cc36157SBjørn Mork */ 11923cc36157SBjørn Mork struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, 11933cc36157SBjørn Mork struct usb_endpoint_descriptor *ep, 1194cac6fb01SLoic Poulain int bufsize, enum wwan_port_type type, 11953cc36157SBjørn Mork int (*manage_power)(struct usb_interface *, int)) 11963cc36157SBjørn Mork { 11976dade7adSColin Ian King int rv; 11983cc36157SBjørn Mork 1199cac6fb01SLoic Poulain rv = wdm_create(intf, ep, bufsize, type, manage_power); 12003cc36157SBjørn Mork if (rv < 0) 12013cc36157SBjørn Mork goto err; 12023cc36157SBjørn Mork 12033cc36157SBjørn Mork return &wdm_driver; 12043cc36157SBjørn Mork err: 12053cc36157SBjørn Mork return ERR_PTR(rv); 12063cc36157SBjørn Mork } 12073cc36157SBjørn Mork EXPORT_SYMBOL(usb_cdc_wdm_register); 12083cc36157SBjørn Mork 1209afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf) 1210afba937eSOliver Neukum { 1211afba937eSOliver Neukum struct wdm_device *desc; 1212afba937eSOliver Neukum unsigned long flags; 1213afba937eSOliver Neukum 1214afba937eSOliver Neukum usb_deregister_dev(intf, &wdm_class); 1215b0c13860SBjørn Mork desc = wdm_find_device(intf); 1216afba937eSOliver Neukum mutex_lock(&wdm_mutex); 1217afba937eSOliver Neukum 1218cac6fb01SLoic Poulain wdm_wwan_deinit(desc); 1219cac6fb01SLoic Poulain 1220afba937eSOliver Neukum /* the spinlock makes sure no new urbs are generated in the callbacks */ 1221afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 1222afba937eSOliver Neukum set_bit(WDM_DISCONNECTING, &desc->flags); 1223afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 1224afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 122562aaf24dSBjørn Mork wake_up_all(&desc->wait); 1226e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 1227e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 122818abf874SOliver Neukum poison_urbs(desc); 1229d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 12302df69484SSebastian Andrzej Siewior cancel_work_sync(&desc->service_outs_intr); 1231e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 1232e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 12336286d85eSBjørn Mork 12346286d85eSBjørn Mork /* the desc->intf pointer used as list key is now invalid */ 12356286d85eSBjørn Mork spin_lock(&wdm_device_list_lock); 12366286d85eSBjørn Mork list_del(&desc->device_list); 12376286d85eSBjørn Mork spin_unlock(&wdm_device_list_lock); 12386286d85eSBjørn Mork 1239afba937eSOliver Neukum if (!desc->count) 1240afba937eSOliver Neukum cleanup(desc); 1241880bca3aSBjørn Mork else 124213a88bf5SOliver Neukum dev_dbg(&intf->dev, "%d open files - postponing cleanup\n", desc->count); 1243afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 1244afba937eSOliver Neukum } 1245afba937eSOliver Neukum 1246d93d16e9SOliver Neukum #ifdef CONFIG_PM 124717d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message) 124817d80d56SOliver Neukum { 1249b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 125017d80d56SOliver Neukum int rv = 0; 125117d80d56SOliver Neukum 125217d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); 125317d80d56SOliver Neukum 1254d93d16e9SOliver Neukum /* if this is an autosuspend the caller does the locking */ 1255e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 1256e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 1257e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 1258e8537bd2SBjørn Mork } 125962e66854SOliver Neukum spin_lock_irq(&desc->iuspin); 1260d93d16e9SOliver Neukum 12615b1b0b81SAlan Stern if (PMSG_IS_AUTO(message) && 1262922a5eadSOliver Neukum (test_bit(WDM_IN_USE, &desc->flags) 1263922a5eadSOliver Neukum || test_bit(WDM_RESPONDING, &desc->flags))) { 126462e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 126517d80d56SOliver Neukum rv = -EBUSY; 126617d80d56SOliver Neukum } else { 1267d93d16e9SOliver Neukum 1268beb1d35fSOliver Neukum set_bit(WDM_SUSPENDING, &desc->flags); 126962e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 1270d93d16e9SOliver Neukum /* callback submits work - order is essential */ 127118abf874SOliver Neukum poison_urbs(desc); 1272d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 12732df69484SSebastian Andrzej Siewior cancel_work_sync(&desc->service_outs_intr); 127418abf874SOliver Neukum unpoison_urbs(desc); 127517d80d56SOliver Neukum } 1276e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 1277e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 1278e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 1279e8537bd2SBjørn Mork } 128017d80d56SOliver Neukum 128117d80d56SOliver Neukum return rv; 128217d80d56SOliver Neukum } 1283d93d16e9SOliver Neukum #endif 128417d80d56SOliver Neukum 128517d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc) 128617d80d56SOliver Neukum { 128717d80d56SOliver Neukum int rv = 0; 128817d80d56SOliver Neukum 128917d80d56SOliver Neukum if (desc->count) { 129017d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_NOIO); 129117d80d56SOliver Neukum if (rv < 0) 12929908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 12939908a32eSGreg Kroah-Hartman "Error resume submitting int urb - %d\n", rv); 129417d80d56SOliver Neukum } 129517d80d56SOliver Neukum return rv; 129617d80d56SOliver Neukum } 1297d93d16e9SOliver Neukum 1298d93d16e9SOliver Neukum #ifdef CONFIG_PM 129917d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf) 130017d80d56SOliver Neukum { 1301b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 130217d80d56SOliver Neukum int rv; 130317d80d56SOliver Neukum 130417d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); 1305338124c1SOliver Neukum 1306beb1d35fSOliver Neukum clear_bit(WDM_SUSPENDING, &desc->flags); 130762e66854SOliver Neukum rv = recover_from_urb_loss(desc); 1308338124c1SOliver Neukum 130917d80d56SOliver Neukum return rv; 131017d80d56SOliver Neukum } 1311d93d16e9SOliver Neukum #endif 131217d80d56SOliver Neukum 131317d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf) 131417d80d56SOliver Neukum { 1315b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 131617d80d56SOliver Neukum 1317d771d8aaSOliver Neukum /* 1318d771d8aaSOliver Neukum * we notify everybody using poll of 1319d771d8aaSOliver Neukum * an exceptional situation 1320d771d8aaSOliver Neukum * must be done before recovery lest a spontaneous 1321d771d8aaSOliver Neukum * message from the device is lost 1322d771d8aaSOliver Neukum */ 1323d771d8aaSOliver Neukum spin_lock_irq(&desc->iuspin); 132488044202SBjørn Mork set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */ 132588044202SBjørn Mork set_bit(WDM_READ, &desc->flags); /* unblock read */ 132688044202SBjørn Mork clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */ 1327d771d8aaSOliver Neukum desc->rerr = -EINTR; 1328d771d8aaSOliver Neukum spin_unlock_irq(&desc->iuspin); 1329d771d8aaSOliver Neukum wake_up_all(&desc->wait); 133088044202SBjørn Mork mutex_lock(&desc->rlock); 133188044202SBjørn Mork mutex_lock(&desc->wlock); 133218abf874SOliver Neukum poison_urbs(desc); 133388044202SBjørn Mork cancel_work_sync(&desc->rxwork); 13342df69484SSebastian Andrzej Siewior cancel_work_sync(&desc->service_outs_intr); 133517d80d56SOliver Neukum return 0; 133617d80d56SOliver Neukum } 133717d80d56SOliver Neukum 133817d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf) 133917d80d56SOliver Neukum { 1340b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 134117d80d56SOliver Neukum int rv; 134217d80d56SOliver Neukum 134318abf874SOliver Neukum unpoison_urbs(desc); 1344c0f5eceeSOliver Neukum clear_bit(WDM_OVERFLOW, &desc->flags); 134588044202SBjørn Mork clear_bit(WDM_RESETTING, &desc->flags); 134617d80d56SOliver Neukum rv = recover_from_urb_loss(desc); 1347e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 1348e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 13490742a338SYueHaibing return rv; 135017d80d56SOliver Neukum } 135117d80d56SOliver Neukum 1352afba937eSOliver Neukum static struct usb_driver wdm_driver = { 1353afba937eSOliver Neukum .name = "cdc_wdm", 1354afba937eSOliver Neukum .probe = wdm_probe, 1355afba937eSOliver Neukum .disconnect = wdm_disconnect, 1356d93d16e9SOliver Neukum #ifdef CONFIG_PM 135717d80d56SOliver Neukum .suspend = wdm_suspend, 135817d80d56SOliver Neukum .resume = wdm_resume, 135917d80d56SOliver Neukum .reset_resume = wdm_resume, 1360d93d16e9SOliver Neukum #endif 136117d80d56SOliver Neukum .pre_reset = wdm_pre_reset, 136217d80d56SOliver Neukum .post_reset = wdm_post_reset, 1363afba937eSOliver Neukum .id_table = wdm_ids, 136417d80d56SOliver Neukum .supports_autosuspend = 1, 1365e1f12eb6SSarah Sharp .disable_hub_initiated_lpm = 1, 1366afba937eSOliver Neukum }; 1367afba937eSOliver Neukum 136865db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver); 1369afba937eSOliver Neukum 1370afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR); 137187d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC); 1372afba937eSOliver Neukum MODULE_LICENSE("GPL"); 1373