1afba937eSOliver Neukum /* 2afba937eSOliver Neukum * cdc-wdm.c 3afba937eSOliver Neukum * 4afba937eSOliver Neukum * This driver supports USB CDC WCM Device Management. 5afba937eSOliver Neukum * 6052fbc0dSOliver Neukum * Copyright (c) 2007-2009 Oliver Neukum 7afba937eSOliver Neukum * 8afba937eSOliver Neukum * Some code taken from cdc-acm.c 9afba937eSOliver Neukum * 10afba937eSOliver Neukum * Released under the GPLv2. 11afba937eSOliver Neukum * 12afba937eSOliver Neukum * Many thanks to Carl Nordbeck 13afba937eSOliver Neukum */ 14afba937eSOliver Neukum #include <linux/kernel.h> 15afba937eSOliver Neukum #include <linux/errno.h> 16afba937eSOliver Neukum #include <linux/slab.h> 17afba937eSOliver Neukum #include <linux/module.h> 18afba937eSOliver Neukum #include <linux/mutex.h> 19afba937eSOliver Neukum #include <linux/uaccess.h> 20afba937eSOliver Neukum #include <linux/bitops.h> 21afba937eSOliver Neukum #include <linux/poll.h> 22afba937eSOliver Neukum #include <linux/usb.h> 23afba937eSOliver Neukum #include <linux/usb/cdc.h> 24afba937eSOliver Neukum #include <asm/byteorder.h> 25afba937eSOliver Neukum #include <asm/unaligned.h> 263cc36157SBjørn Mork #include <linux/usb/cdc-wdm.h> 27afba937eSOliver Neukum 28afba937eSOliver Neukum /* 29afba937eSOliver Neukum * Version Information 30afba937eSOliver Neukum */ 3187d65e54SOliver Neukum #define DRIVER_VERSION "v0.03" 32afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum" 3387d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" 34afba937eSOliver Neukum 35fec67b45SBjørn Mork #define HUAWEI_VENDOR_ID 0x12D1 36fec67b45SBjørn Mork 376ef4852bSNémeth Márton static const struct usb_device_id wdm_ids[] = { 38afba937eSOliver Neukum { 39afba937eSOliver Neukum .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | 40afba937eSOliver Neukum USB_DEVICE_ID_MATCH_INT_SUBCLASS, 41afba937eSOliver Neukum .bInterfaceClass = USB_CLASS_COMM, 42afba937eSOliver Neukum .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM 43afba937eSOliver Neukum }, 44fec67b45SBjørn Mork { 45fec67b45SBjørn Mork /* 46fec67b45SBjørn Mork * Huawei E392, E398 and possibly other Qualcomm based modems 47fec67b45SBjørn Mork * embed the Qualcomm QMI protocol inside CDC on CDC ECM like 48fec67b45SBjørn Mork * control interfaces. Userspace access to this is required 49fec67b45SBjørn Mork * to configure the accompanying data interface 50fec67b45SBjørn Mork */ 51fec67b45SBjørn Mork .match_flags = USB_DEVICE_ID_MATCH_VENDOR | 52fec67b45SBjørn Mork USB_DEVICE_ID_MATCH_INT_INFO, 53fec67b45SBjørn Mork .idVendor = HUAWEI_VENDOR_ID, 54fec67b45SBjørn Mork .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 55fec67b45SBjørn Mork .bInterfaceSubClass = 1, 56fec67b45SBjørn Mork .bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */ 57fec67b45SBjørn Mork }, 58afba937eSOliver Neukum { } 59afba937eSOliver Neukum }; 60afba937eSOliver Neukum 61aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids); 62aa5380b9SOliver Neukum 63afba937eSOliver Neukum #define WDM_MINOR_BASE 176 64afba937eSOliver Neukum 65afba937eSOliver Neukum 66afba937eSOliver Neukum #define WDM_IN_USE 1 67afba937eSOliver Neukum #define WDM_DISCONNECTING 2 68afba937eSOliver Neukum #define WDM_RESULT 3 69afba937eSOliver Neukum #define WDM_READ 4 70afba937eSOliver Neukum #define WDM_INT_STALL 5 71afba937eSOliver Neukum #define WDM_POLL_RUNNING 6 72922a5eadSOliver Neukum #define WDM_RESPONDING 7 73beb1d35fSOliver Neukum #define WDM_SUSPENDING 8 7488044202SBjørn Mork #define WDM_RESETTING 9 75afba937eSOliver Neukum 76afba937eSOliver Neukum #define WDM_MAX 16 77afba937eSOliver Neukum 787e3054a0SBjørn Mork /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ 797e3054a0SBjørn Mork #define WDM_DEFAULT_BUFSIZE 256 80afba937eSOliver Neukum 81afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex); 82b0c13860SBjørn Mork static DEFINE_SPINLOCK(wdm_device_list_lock); 83b0c13860SBjørn Mork static LIST_HEAD(wdm_device_list); 84afba937eSOliver Neukum 85afba937eSOliver Neukum /* --- method tables --- */ 86afba937eSOliver Neukum 87afba937eSOliver Neukum struct wdm_device { 88afba937eSOliver Neukum u8 *inbuf; /* buffer for response */ 89afba937eSOliver Neukum u8 *outbuf; /* buffer for command */ 90afba937eSOliver Neukum u8 *sbuf; /* buffer for status */ 91afba937eSOliver Neukum u8 *ubuf; /* buffer for copy to user space */ 92afba937eSOliver Neukum 93afba937eSOliver Neukum struct urb *command; 94afba937eSOliver Neukum struct urb *response; 95afba937eSOliver Neukum struct urb *validity; 96afba937eSOliver Neukum struct usb_interface *intf; 97afba937eSOliver Neukum struct usb_ctrlrequest *orq; 98afba937eSOliver Neukum struct usb_ctrlrequest *irq; 99afba937eSOliver Neukum spinlock_t iuspin; 100afba937eSOliver Neukum 101afba937eSOliver Neukum unsigned long flags; 102afba937eSOliver Neukum u16 bufsize; 103afba937eSOliver Neukum u16 wMaxCommand; 104afba937eSOliver Neukum u16 wMaxPacketSize; 105afba937eSOliver Neukum __le16 inum; 106afba937eSOliver Neukum int reslength; 107afba937eSOliver Neukum int length; 108afba937eSOliver Neukum int read; 109afba937eSOliver Neukum int count; 110afba937eSOliver Neukum dma_addr_t shandle; 111afba937eSOliver Neukum dma_addr_t ihandle; 112e8537bd2SBjørn Mork struct mutex wlock; 113e8537bd2SBjørn Mork struct mutex rlock; 114afba937eSOliver Neukum wait_queue_head_t wait; 115afba937eSOliver Neukum struct work_struct rxwork; 116afba937eSOliver Neukum int werr; 117afba937eSOliver Neukum int rerr; 118b0c13860SBjørn Mork 119b0c13860SBjørn Mork struct list_head device_list; 1203cc36157SBjørn Mork int (*manage_power)(struct usb_interface *, int); 121afba937eSOliver Neukum }; 122afba937eSOliver Neukum 123afba937eSOliver Neukum static struct usb_driver wdm_driver; 124afba937eSOliver Neukum 125b0c13860SBjørn Mork /* return intfdata if we own the interface, else look up intf in the list */ 126b0c13860SBjørn Mork static struct wdm_device *wdm_find_device(struct usb_interface *intf) 127b0c13860SBjørn Mork { 128b0c13860SBjørn Mork struct wdm_device *desc = NULL; 129b0c13860SBjørn Mork 130b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 131b0c13860SBjørn Mork list_for_each_entry(desc, &wdm_device_list, device_list) 132b0c13860SBjørn Mork if (desc->intf == intf) 133b0c13860SBjørn Mork break; 134b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 135b0c13860SBjørn Mork 136b0c13860SBjørn Mork return desc; 137b0c13860SBjørn Mork } 138b0c13860SBjørn Mork 139b0c13860SBjørn Mork static struct wdm_device *wdm_find_device_by_minor(int minor) 140b0c13860SBjørn Mork { 141b0c13860SBjørn Mork struct wdm_device *desc = NULL; 142b0c13860SBjørn Mork 143b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 144b0c13860SBjørn Mork list_for_each_entry(desc, &wdm_device_list, device_list) 145b0c13860SBjørn Mork if (desc->intf->minor == minor) 146b0c13860SBjørn Mork break; 147b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 148b0c13860SBjørn Mork 149b0c13860SBjørn Mork return desc; 150b0c13860SBjørn Mork } 151b0c13860SBjørn Mork 152afba937eSOliver Neukum /* --- callbacks --- */ 153afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb) 154afba937eSOliver Neukum { 155afba937eSOliver Neukum struct wdm_device *desc; 156afba937eSOliver Neukum desc = urb->context; 157afba937eSOliver Neukum spin_lock(&desc->iuspin); 158afba937eSOliver Neukum desc->werr = urb->status; 159afba937eSOliver Neukum spin_unlock(&desc->iuspin); 160afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 161afba937eSOliver Neukum kfree(desc->outbuf); 162afba937eSOliver Neukum wake_up(&desc->wait); 163afba937eSOliver Neukum } 164afba937eSOliver Neukum 165afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb) 166afba937eSOliver Neukum { 167afba937eSOliver Neukum struct wdm_device *desc = urb->context; 168afba937eSOliver Neukum int status = urb->status; 169afba937eSOliver Neukum 170afba937eSOliver Neukum spin_lock(&desc->iuspin); 171922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 172afba937eSOliver Neukum 173afba937eSOliver Neukum if (status) { 174afba937eSOliver Neukum switch (status) { 175afba937eSOliver Neukum case -ENOENT: 176afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 177afba937eSOliver Neukum "nonzero urb status received: -ENOENT"); 178922a5eadSOliver Neukum goto skip_error; 179afba937eSOliver Neukum case -ECONNRESET: 180afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 181afba937eSOliver Neukum "nonzero urb status received: -ECONNRESET"); 182922a5eadSOliver Neukum goto skip_error; 183afba937eSOliver Neukum case -ESHUTDOWN: 184afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 185afba937eSOliver Neukum "nonzero urb status received: -ESHUTDOWN"); 186922a5eadSOliver Neukum goto skip_error; 187afba937eSOliver Neukum case -EPIPE: 1889908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 1899908a32eSGreg Kroah-Hartman "nonzero urb status received: -EPIPE\n"); 190afba937eSOliver Neukum break; 191afba937eSOliver Neukum default: 1929908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 1939908a32eSGreg Kroah-Hartman "Unexpected error %d\n", status); 194afba937eSOliver Neukum break; 195afba937eSOliver Neukum } 196afba937eSOliver Neukum } 197afba937eSOliver Neukum 198afba937eSOliver Neukum desc->rerr = status; 199afba937eSOliver Neukum desc->reslength = urb->actual_length; 200afba937eSOliver Neukum memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); 201afba937eSOliver Neukum desc->length += desc->reslength; 202922a5eadSOliver Neukum skip_error: 203afba937eSOliver Neukum wake_up(&desc->wait); 204afba937eSOliver Neukum 205afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 206afba937eSOliver Neukum spin_unlock(&desc->iuspin); 207afba937eSOliver Neukum } 208afba937eSOliver Neukum 209afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb) 210afba937eSOliver Neukum { 211afba937eSOliver Neukum int rv = 0; 212afba937eSOliver Neukum int status = urb->status; 213afba937eSOliver Neukum struct wdm_device *desc; 214afba937eSOliver Neukum struct usb_cdc_notification *dr; 215afba937eSOliver Neukum 216afba937eSOliver Neukum desc = urb->context; 217afba937eSOliver Neukum dr = (struct usb_cdc_notification *)desc->sbuf; 218afba937eSOliver Neukum 219afba937eSOliver Neukum if (status) { 220afba937eSOliver Neukum switch (status) { 221afba937eSOliver Neukum case -ESHUTDOWN: 222afba937eSOliver Neukum case -ENOENT: 223afba937eSOliver Neukum case -ECONNRESET: 224afba937eSOliver Neukum return; /* unplug */ 225afba937eSOliver Neukum case -EPIPE: 226afba937eSOliver Neukum set_bit(WDM_INT_STALL, &desc->flags); 2279908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Stall on int endpoint\n"); 228afba937eSOliver Neukum goto sw; /* halt is cleared in work */ 229afba937eSOliver Neukum default: 2309908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2319908a32eSGreg Kroah-Hartman "nonzero urb status received: %d\n", status); 232afba937eSOliver Neukum break; 233afba937eSOliver Neukum } 234afba937eSOliver Neukum } 235afba937eSOliver Neukum 236afba937eSOliver Neukum if (urb->actual_length < sizeof(struct usb_cdc_notification)) { 2379908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n", 2389908a32eSGreg Kroah-Hartman urb->actual_length); 239afba937eSOliver Neukum goto exit; 240afba937eSOliver Neukum } 241afba937eSOliver Neukum 242afba937eSOliver Neukum switch (dr->bNotificationType) { 243afba937eSOliver Neukum case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: 244afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 245afba937eSOliver Neukum "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d", 246afba937eSOliver Neukum dr->wIndex, dr->wLength); 247afba937eSOliver Neukum break; 248afba937eSOliver Neukum 249afba937eSOliver Neukum case USB_CDC_NOTIFY_NETWORK_CONNECTION: 250afba937eSOliver Neukum 251afba937eSOliver Neukum dev_dbg(&desc->intf->dev, 252afba937eSOliver Neukum "NOTIFY_NETWORK_CONNECTION %s network", 253afba937eSOliver Neukum dr->wValue ? "connected to" : "disconnected from"); 254afba937eSOliver Neukum goto exit; 255afba937eSOliver Neukum default: 256afba937eSOliver Neukum clear_bit(WDM_POLL_RUNNING, &desc->flags); 2579908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2589908a32eSGreg Kroah-Hartman "unknown notification %d received: index %d len %d\n", 259afba937eSOliver Neukum dr->bNotificationType, dr->wIndex, dr->wLength); 260afba937eSOliver Neukum goto exit; 261afba937eSOliver Neukum } 262afba937eSOliver Neukum 263afba937eSOliver Neukum spin_lock(&desc->iuspin); 264afba937eSOliver Neukum clear_bit(WDM_READ, &desc->flags); 265922a5eadSOliver Neukum set_bit(WDM_RESPONDING, &desc->flags); 266beb1d35fSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags) 267beb1d35fSOliver Neukum && !test_bit(WDM_SUSPENDING, &desc->flags)) { 268afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_ATOMIC); 269afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", 270afba937eSOliver Neukum __func__, rv); 271afba937eSOliver Neukum } 272afba937eSOliver Neukum spin_unlock(&desc->iuspin); 273afba937eSOliver Neukum if (rv < 0) { 274922a5eadSOliver Neukum clear_bit(WDM_RESPONDING, &desc->flags); 275afba937eSOliver Neukum if (rv == -EPERM) 276afba937eSOliver Neukum return; 277afba937eSOliver Neukum if (rv == -ENOMEM) { 278afba937eSOliver Neukum sw: 279afba937eSOliver Neukum rv = schedule_work(&desc->rxwork); 280afba937eSOliver Neukum if (rv) 2819908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2829908a32eSGreg Kroah-Hartman "Cannot schedule work\n"); 283afba937eSOliver Neukum } 284afba937eSOliver Neukum } 285afba937eSOliver Neukum exit: 286afba937eSOliver Neukum rv = usb_submit_urb(urb, GFP_ATOMIC); 287afba937eSOliver Neukum if (rv) 2889908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 2899908a32eSGreg Kroah-Hartman "%s - usb_submit_urb failed with result %d\n", 290afba937eSOliver Neukum __func__, rv); 291afba937eSOliver Neukum 292afba937eSOliver Neukum } 293afba937eSOliver Neukum 294afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc) 295afba937eSOliver Neukum { 29617d80d56SOliver Neukum /* the order here is essential */ 297afba937eSOliver Neukum usb_kill_urb(desc->command); 298afba937eSOliver Neukum usb_kill_urb(desc->validity); 299afba937eSOliver Neukum usb_kill_urb(desc->response); 300afba937eSOliver Neukum } 301afba937eSOliver Neukum 302afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc) 303afba937eSOliver Neukum { 304afba937eSOliver Neukum usb_free_urb(desc->validity); 305afba937eSOliver Neukum usb_free_urb(desc->response); 306afba937eSOliver Neukum usb_free_urb(desc->command); 307afba937eSOliver Neukum } 308afba937eSOliver Neukum 309afba937eSOliver Neukum static void cleanup(struct wdm_device *desc) 310afba937eSOliver Neukum { 311b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 312b0c13860SBjørn Mork list_del(&desc->device_list); 313b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 3148457d99cSBjørn Mork kfree(desc->sbuf); 3158457d99cSBjørn Mork kfree(desc->inbuf); 316afba937eSOliver Neukum kfree(desc->orq); 317afba937eSOliver Neukum kfree(desc->irq); 318afba937eSOliver Neukum kfree(desc->ubuf); 319afba937eSOliver Neukum free_urbs(desc); 320afba937eSOliver Neukum kfree(desc); 321afba937eSOliver Neukum } 322afba937eSOliver Neukum 323afba937eSOliver Neukum static ssize_t wdm_write 324afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 325afba937eSOliver Neukum { 326afba937eSOliver Neukum u8 *buf; 327afba937eSOliver Neukum int rv = -EMSGSIZE, r, we; 328afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 329afba937eSOliver Neukum struct usb_ctrlrequest *req; 330afba937eSOliver Neukum 331afba937eSOliver Neukum if (count > desc->wMaxCommand) 332afba937eSOliver Neukum count = desc->wMaxCommand; 333afba937eSOliver Neukum 334afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 335afba937eSOliver Neukum we = desc->werr; 336afba937eSOliver Neukum desc->werr = 0; 337afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 338afba937eSOliver Neukum if (we < 0) 339afba937eSOliver Neukum return -EIO; 340afba937eSOliver Neukum 341860e41a7SOliver Neukum desc->outbuf = buf = kmalloc(count, GFP_KERNEL); 342860e41a7SOliver Neukum if (!buf) { 343860e41a7SOliver Neukum rv = -ENOMEM; 344afba937eSOliver Neukum goto outnl; 345860e41a7SOliver Neukum } 346860e41a7SOliver Neukum 347860e41a7SOliver Neukum r = copy_from_user(buf, buffer, count); 348860e41a7SOliver Neukum if (r > 0) { 349860e41a7SOliver Neukum kfree(buf); 350860e41a7SOliver Neukum rv = -EFAULT; 351860e41a7SOliver Neukum goto outnl; 352860e41a7SOliver Neukum } 353860e41a7SOliver Neukum 354860e41a7SOliver Neukum /* concurrent writes and disconnect */ 355e8537bd2SBjørn Mork r = mutex_lock_interruptible(&desc->wlock); 356860e41a7SOliver Neukum rv = -ERESTARTSYS; 357860e41a7SOliver Neukum if (r) { 358860e41a7SOliver Neukum kfree(buf); 359860e41a7SOliver Neukum goto outnl; 360860e41a7SOliver Neukum } 361860e41a7SOliver Neukum 362860e41a7SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 363860e41a7SOliver Neukum kfree(buf); 364860e41a7SOliver Neukum rv = -ENODEV; 365860e41a7SOliver Neukum goto outnp; 366860e41a7SOliver Neukum } 367afba937eSOliver Neukum 36817d80d56SOliver Neukum r = usb_autopm_get_interface(desc->intf); 369860e41a7SOliver Neukum if (r < 0) { 370860e41a7SOliver Neukum kfree(buf); 37112a98b2bSOliver Neukum rv = usb_translate_errors(r); 37217d80d56SOliver Neukum goto outnp; 373860e41a7SOliver Neukum } 3747f1dc313SOliver Neukum 3750cdfb819SDavid Sterba if (!(file->f_flags & O_NONBLOCK)) 376afba937eSOliver Neukum r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, 377afba937eSOliver Neukum &desc->flags)); 3787f1dc313SOliver Neukum else 3797f1dc313SOliver Neukum if (test_bit(WDM_IN_USE, &desc->flags)) 3807f1dc313SOliver Neukum r = -EAGAIN; 38188044202SBjørn Mork 38288044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) 38388044202SBjørn Mork r = -EIO; 38488044202SBjørn Mork 385860e41a7SOliver Neukum if (r < 0) { 386afba937eSOliver Neukum kfree(buf); 38712a98b2bSOliver Neukum rv = r; 388afba937eSOliver Neukum goto out; 389afba937eSOliver Neukum } 390afba937eSOliver Neukum 391afba937eSOliver Neukum req = desc->orq; 392afba937eSOliver Neukum usb_fill_control_urb( 393afba937eSOliver Neukum desc->command, 394afba937eSOliver Neukum interface_to_usbdev(desc->intf), 395afba937eSOliver Neukum /* using common endpoint 0 */ 396afba937eSOliver Neukum usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0), 397afba937eSOliver Neukum (unsigned char *)req, 398afba937eSOliver Neukum buf, 399afba937eSOliver Neukum count, 400afba937eSOliver Neukum wdm_out_callback, 401afba937eSOliver Neukum desc 402afba937eSOliver Neukum ); 403afba937eSOliver Neukum 404afba937eSOliver Neukum req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | 405afba937eSOliver Neukum USB_RECIP_INTERFACE); 406afba937eSOliver Neukum req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 407afba937eSOliver Neukum req->wValue = 0; 408afba937eSOliver Neukum req->wIndex = desc->inum; 409afba937eSOliver Neukum req->wLength = cpu_to_le16(count); 410afba937eSOliver Neukum set_bit(WDM_IN_USE, &desc->flags); 411afba937eSOliver Neukum 412afba937eSOliver Neukum rv = usb_submit_urb(desc->command, GFP_KERNEL); 413afba937eSOliver Neukum if (rv < 0) { 414afba937eSOliver Neukum kfree(buf); 415afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 4169908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); 41712a98b2bSOliver Neukum rv = usb_translate_errors(rv); 418afba937eSOliver Neukum } else { 419afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", 420afba937eSOliver Neukum req->wIndex); 421afba937eSOliver Neukum } 422afba937eSOliver Neukum out: 42317d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 42417d80d56SOliver Neukum outnp: 425e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 426afba937eSOliver Neukum outnl: 427afba937eSOliver Neukum return rv < 0 ? rv : count; 428afba937eSOliver Neukum } 429afba937eSOliver Neukum 430afba937eSOliver Neukum static ssize_t wdm_read 431afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos) 432afba937eSOliver Neukum { 433711c68b3SBen Hutchings int rv, cntr; 434afba937eSOliver Neukum int i = 0; 435afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 436afba937eSOliver Neukum 437afba937eSOliver Neukum 438e8537bd2SBjørn Mork rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ 439afba937eSOliver Neukum if (rv < 0) 440afba937eSOliver Neukum return -ERESTARTSYS; 441afba937eSOliver Neukum 442711c68b3SBen Hutchings cntr = ACCESS_ONCE(desc->length); 443711c68b3SBen Hutchings if (cntr == 0) { 444afba937eSOliver Neukum desc->read = 0; 445afba937eSOliver Neukum retry: 4467f1dc313SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 4477f1dc313SOliver Neukum rv = -ENODEV; 4487f1dc313SOliver Neukum goto err; 4497f1dc313SOliver Neukum } 450afba937eSOliver Neukum i++; 4517f1dc313SOliver Neukum if (file->f_flags & O_NONBLOCK) { 4527f1dc313SOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { 4537f1dc313SOliver Neukum rv = cntr ? cntr : -EAGAIN; 4547f1dc313SOliver Neukum goto err; 4557f1dc313SOliver Neukum } 4567f1dc313SOliver Neukum rv = 0; 4577f1dc313SOliver Neukum } else { 458afba937eSOliver Neukum rv = wait_event_interruptible(desc->wait, 459afba937eSOliver Neukum test_bit(WDM_READ, &desc->flags)); 4607f1dc313SOliver Neukum } 461afba937eSOliver Neukum 4627f1dc313SOliver Neukum /* may have happened while we slept */ 46317d80d56SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 46417d80d56SOliver Neukum rv = -ENODEV; 46517d80d56SOliver Neukum goto err; 46617d80d56SOliver Neukum } 46788044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) { 46888044202SBjørn Mork rv = -EIO; 46988044202SBjørn Mork goto err; 47088044202SBjørn Mork } 47117d80d56SOliver Neukum usb_mark_last_busy(interface_to_usbdev(desc->intf)); 472afba937eSOliver Neukum if (rv < 0) { 473afba937eSOliver Neukum rv = -ERESTARTSYS; 474afba937eSOliver Neukum goto err; 475afba937eSOliver Neukum } 476afba937eSOliver Neukum 477afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 478afba937eSOliver Neukum 479afba937eSOliver Neukum if (desc->rerr) { /* read completed, error happened */ 480afba937eSOliver Neukum desc->rerr = 0; 481afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 482afba937eSOliver Neukum rv = -EIO; 483afba937eSOliver Neukum goto err; 484afba937eSOliver Neukum } 485afba937eSOliver Neukum /* 486afba937eSOliver Neukum * recheck whether we've lost the race 487afba937eSOliver Neukum * against the completion handler 488afba937eSOliver Neukum */ 489afba937eSOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */ 490afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 491afba937eSOliver Neukum goto retry; 492afba937eSOliver Neukum } 493afba937eSOliver Neukum if (!desc->reslength) { /* zero length read */ 494afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 495afba937eSOliver Neukum goto retry; 496afba937eSOliver Neukum } 497711c68b3SBen Hutchings cntr = desc->length; 498afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 499afba937eSOliver Neukum } 500afba937eSOliver Neukum 501711c68b3SBen Hutchings if (cntr > count) 502711c68b3SBen Hutchings cntr = count; 503afba937eSOliver Neukum rv = copy_to_user(buffer, desc->ubuf, cntr); 504afba937eSOliver Neukum if (rv > 0) { 505afba937eSOliver Neukum rv = -EFAULT; 506afba937eSOliver Neukum goto err; 507afba937eSOliver Neukum } 508afba937eSOliver Neukum 509711c68b3SBen Hutchings spin_lock_irq(&desc->iuspin); 510711c68b3SBen Hutchings 511afba937eSOliver Neukum for (i = 0; i < desc->length - cntr; i++) 512afba937eSOliver Neukum desc->ubuf[i] = desc->ubuf[i + cntr]; 513afba937eSOliver Neukum 514afba937eSOliver Neukum desc->length -= cntr; 51587d65e54SOliver Neukum /* in case we had outstanding data */ 51687d65e54SOliver Neukum if (!desc->length) 51787d65e54SOliver Neukum clear_bit(WDM_READ, &desc->flags); 518711c68b3SBen Hutchings 519711c68b3SBen Hutchings spin_unlock_irq(&desc->iuspin); 520711c68b3SBen Hutchings 521afba937eSOliver Neukum rv = cntr; 522afba937eSOliver Neukum 523afba937eSOliver Neukum err: 524e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 525afba937eSOliver Neukum return rv; 526afba937eSOliver Neukum } 527afba937eSOliver Neukum 528afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id) 529afba937eSOliver Neukum { 530afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 531afba937eSOliver Neukum 532afba937eSOliver Neukum wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); 533afba937eSOliver Neukum if (desc->werr < 0) 5349908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error in flush path: %d\n", 5359908a32eSGreg Kroah-Hartman desc->werr); 536afba937eSOliver Neukum 53724a85baeSOliver Neukum return usb_translate_errors(desc->werr); 538afba937eSOliver Neukum } 539afba937eSOliver Neukum 540afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) 541afba937eSOliver Neukum { 542afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 543afba937eSOliver Neukum unsigned long flags; 544afba937eSOliver Neukum unsigned int mask = 0; 545afba937eSOliver Neukum 546afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 547afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 548afba937eSOliver Neukum mask = POLLERR; 549afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 550afba937eSOliver Neukum goto desc_out; 551afba937eSOliver Neukum } 552afba937eSOliver Neukum if (test_bit(WDM_READ, &desc->flags)) 553afba937eSOliver Neukum mask = POLLIN | POLLRDNORM; 554afba937eSOliver Neukum if (desc->rerr || desc->werr) 555afba937eSOliver Neukum mask |= POLLERR; 556afba937eSOliver Neukum if (!test_bit(WDM_IN_USE, &desc->flags)) 557afba937eSOliver Neukum mask |= POLLOUT | POLLWRNORM; 558afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 559afba937eSOliver Neukum 560afba937eSOliver Neukum poll_wait(file, &desc->wait, wait); 561afba937eSOliver Neukum 562afba937eSOliver Neukum desc_out: 563afba937eSOliver Neukum return mask; 564afba937eSOliver Neukum } 565afba937eSOliver Neukum 566afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file) 567afba937eSOliver Neukum { 568afba937eSOliver Neukum int minor = iminor(inode); 569afba937eSOliver Neukum int rv = -ENODEV; 570afba937eSOliver Neukum struct usb_interface *intf; 571afba937eSOliver Neukum struct wdm_device *desc; 572afba937eSOliver Neukum 573afba937eSOliver Neukum mutex_lock(&wdm_mutex); 574b0c13860SBjørn Mork desc = wdm_find_device_by_minor(minor); 575b0c13860SBjørn Mork if (!desc) 576afba937eSOliver Neukum goto out; 577afba937eSOliver Neukum 578b0c13860SBjørn Mork intf = desc->intf; 579afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 580afba937eSOliver Neukum goto out; 581afba937eSOliver Neukum file->private_data = desc; 582afba937eSOliver Neukum 58317d80d56SOliver Neukum rv = usb_autopm_get_interface(desc->intf); 58417d80d56SOliver Neukum if (rv < 0) { 5859908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); 58617d80d56SOliver Neukum goto out; 58717d80d56SOliver Neukum } 588afba937eSOliver Neukum 589e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 590e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 59117d80d56SOliver Neukum if (!desc->count++) { 592d771d8aaSOliver Neukum desc->werr = 0; 593d771d8aaSOliver Neukum desc->rerr = 0; 59417d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_KERNEL); 595afba937eSOliver Neukum if (rv < 0) { 596afba937eSOliver Neukum desc->count--; 5979908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 5989908a32eSGreg Kroah-Hartman "Error submitting int urb - %d\n", rv); 59912a98b2bSOliver Neukum rv = usb_translate_errors(rv); 600afba937eSOliver Neukum } 60117d80d56SOliver Neukum } else { 602afba937eSOliver Neukum rv = 0; 60317d80d56SOliver Neukum } 604e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 6053cc36157SBjørn Mork if (desc->count == 1) 6063cc36157SBjørn Mork desc->manage_power(intf, 1); 60717d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 608afba937eSOliver Neukum out: 609afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 610afba937eSOliver Neukum return rv; 611afba937eSOliver Neukum } 612afba937eSOliver Neukum 613afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file) 614afba937eSOliver Neukum { 615afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 616afba937eSOliver Neukum 617afba937eSOliver Neukum mutex_lock(&wdm_mutex); 618e8537bd2SBjørn Mork 619e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 620e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 621afba937eSOliver Neukum desc->count--; 622e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 62317d80d56SOliver Neukum 624afba937eSOliver Neukum if (!desc->count) { 625afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); 626afba937eSOliver Neukum kill_urbs(desc); 627880bca3aSBjørn Mork if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { 6283cc36157SBjørn Mork desc->manage_power(desc->intf, 0); 629880bca3aSBjørn Mork } else { 630880bca3aSBjørn Mork dev_dbg(&desc->intf->dev, "%s: device gone - cleaning up\n", __func__); 6312f338c8aSOliver Neukum cleanup(desc); 632afba937eSOliver Neukum } 633880bca3aSBjørn Mork } 634afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 635afba937eSOliver Neukum return 0; 636afba937eSOliver Neukum } 637afba937eSOliver Neukum 638afba937eSOliver Neukum static const struct file_operations wdm_fops = { 639afba937eSOliver Neukum .owner = THIS_MODULE, 640afba937eSOliver Neukum .read = wdm_read, 641afba937eSOliver Neukum .write = wdm_write, 642afba937eSOliver Neukum .open = wdm_open, 643afba937eSOliver Neukum .flush = wdm_flush, 644afba937eSOliver Neukum .release = wdm_release, 6456038f373SArnd Bergmann .poll = wdm_poll, 6466038f373SArnd Bergmann .llseek = noop_llseek, 647afba937eSOliver Neukum }; 648afba937eSOliver Neukum 649afba937eSOliver Neukum static struct usb_class_driver wdm_class = { 650afba937eSOliver Neukum .name = "cdc-wdm%d", 651afba937eSOliver Neukum .fops = &wdm_fops, 652afba937eSOliver Neukum .minor_base = WDM_MINOR_BASE, 653afba937eSOliver Neukum }; 654afba937eSOliver Neukum 655afba937eSOliver Neukum /* --- error handling --- */ 656afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work) 657afba937eSOliver Neukum { 658afba937eSOliver Neukum struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); 659afba937eSOliver Neukum unsigned long flags; 660afba937eSOliver Neukum int rv; 661afba937eSOliver Neukum 662afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 663afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 664afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 665afba937eSOliver Neukum } else { 666afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 667afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_KERNEL); 668afba937eSOliver Neukum if (rv < 0 && rv != -EPERM) { 669afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 670afba937eSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 671afba937eSOliver Neukum schedule_work(&desc->rxwork); 672afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 673afba937eSOliver Neukum } 674afba937eSOliver Neukum } 675afba937eSOliver Neukum } 676afba937eSOliver Neukum 677afba937eSOliver Neukum /* --- hotplug --- */ 678afba937eSOliver Neukum 6793cc36157SBjørn Mork static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, 6803cc36157SBjørn Mork u16 bufsize, int (*manage_power)(struct usb_interface *, int)) 681afba937eSOliver Neukum { 6820dffb486SBjørn Mork int rv = -ENOMEM; 683afba937eSOliver Neukum struct wdm_device *desc; 684afba937eSOliver Neukum 685afba937eSOliver Neukum desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); 686afba937eSOliver Neukum if (!desc) 687afba937eSOliver Neukum goto out; 688b0c13860SBjørn Mork INIT_LIST_HEAD(&desc->device_list); 689e8537bd2SBjørn Mork mutex_init(&desc->rlock); 690e8537bd2SBjørn Mork mutex_init(&desc->wlock); 691afba937eSOliver Neukum spin_lock_init(&desc->iuspin); 692afba937eSOliver Neukum init_waitqueue_head(&desc->wait); 6930dffb486SBjørn Mork desc->wMaxCommand = bufsize; 694052fbc0dSOliver Neukum /* this will be expanded and needed in hardware endianness */ 695afba937eSOliver Neukum desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); 696afba937eSOliver Neukum desc->intf = intf; 697afba937eSOliver Neukum INIT_WORK(&desc->rxwork, wdm_rxwork); 698afba937eSOliver Neukum 699afba937eSOliver Neukum rv = -EINVAL; 7000dffb486SBjørn Mork if (!usb_endpoint_is_int_in(ep)) 701052fbc0dSOliver Neukum goto err; 702afba937eSOliver Neukum 70329cc8897SKuninori Morimoto desc->wMaxPacketSize = usb_endpoint_maxp(ep); 704afba937eSOliver Neukum 705afba937eSOliver Neukum desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 706afba937eSOliver Neukum if (!desc->orq) 707afba937eSOliver Neukum goto err; 708afba937eSOliver Neukum desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 709afba937eSOliver Neukum if (!desc->irq) 710afba937eSOliver Neukum goto err; 711afba937eSOliver Neukum 712afba937eSOliver Neukum desc->validity = usb_alloc_urb(0, GFP_KERNEL); 713afba937eSOliver Neukum if (!desc->validity) 714afba937eSOliver Neukum goto err; 715afba937eSOliver Neukum 716afba937eSOliver Neukum desc->response = usb_alloc_urb(0, GFP_KERNEL); 717afba937eSOliver Neukum if (!desc->response) 718afba937eSOliver Neukum goto err; 719afba937eSOliver Neukum 720afba937eSOliver Neukum desc->command = usb_alloc_urb(0, GFP_KERNEL); 721afba937eSOliver Neukum if (!desc->command) 722afba937eSOliver Neukum goto err; 723afba937eSOliver Neukum 724afba937eSOliver Neukum desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 725afba937eSOliver Neukum if (!desc->ubuf) 726afba937eSOliver Neukum goto err; 727afba937eSOliver Neukum 7288457d99cSBjørn Mork desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL); 729afba937eSOliver Neukum if (!desc->sbuf) 730afba937eSOliver Neukum goto err; 731afba937eSOliver Neukum 7328457d99cSBjørn Mork desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 733afba937eSOliver Neukum if (!desc->inbuf) 7348457d99cSBjørn Mork goto err; 735afba937eSOliver Neukum 736afba937eSOliver Neukum usb_fill_int_urb( 737afba937eSOliver Neukum desc->validity, 738afba937eSOliver Neukum interface_to_usbdev(intf), 739afba937eSOliver Neukum usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress), 740afba937eSOliver Neukum desc->sbuf, 741afba937eSOliver Neukum desc->wMaxPacketSize, 742afba937eSOliver Neukum wdm_int_callback, 743afba937eSOliver Neukum desc, 744afba937eSOliver Neukum ep->bInterval 745afba937eSOliver Neukum ); 746afba937eSOliver Neukum 74719b85b3bSBjørn Mork desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); 74819b85b3bSBjørn Mork desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; 74919b85b3bSBjørn Mork desc->irq->wValue = 0; 75019b85b3bSBjørn Mork desc->irq->wIndex = desc->inum; 75119b85b3bSBjørn Mork desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); 75219b85b3bSBjørn Mork 75319b85b3bSBjørn Mork usb_fill_control_urb( 75419b85b3bSBjørn Mork desc->response, 7558143a896SBjørn Mork interface_to_usbdev(intf), 75619b85b3bSBjørn Mork /* using common endpoint 0 */ 75719b85b3bSBjørn Mork usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), 75819b85b3bSBjørn Mork (unsigned char *)desc->irq, 75919b85b3bSBjørn Mork desc->inbuf, 76019b85b3bSBjørn Mork desc->wMaxCommand, 76119b85b3bSBjørn Mork wdm_in_callback, 76219b85b3bSBjørn Mork desc 76319b85b3bSBjørn Mork ); 76419b85b3bSBjørn Mork 7653cc36157SBjørn Mork desc->manage_power = manage_power; 7663cc36157SBjørn Mork 767b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 768b0c13860SBjørn Mork list_add(&desc->device_list, &wdm_device_list); 769b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 770b0c13860SBjørn Mork 771afba937eSOliver Neukum rv = usb_register_dev(intf, &wdm_class); 772052fbc0dSOliver Neukum if (rv < 0) 773b0c13860SBjørn Mork goto err; 774052fbc0dSOliver Neukum else 775820c629aSBjørn Mork dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev)); 776afba937eSOliver Neukum out: 777afba937eSOliver Neukum return rv; 778afba937eSOliver Neukum err: 7790dffb486SBjørn Mork cleanup(desc); 7800dffb486SBjørn Mork return rv; 7810dffb486SBjørn Mork } 7820dffb486SBjørn Mork 7833cc36157SBjørn Mork static int wdm_manage_power(struct usb_interface *intf, int on) 7843cc36157SBjørn Mork { 7853cc36157SBjørn Mork /* need autopm_get/put here to ensure the usbcore sees the new value */ 7863cc36157SBjørn Mork int rv = usb_autopm_get_interface(intf); 7873cc36157SBjørn Mork if (rv < 0) 7883cc36157SBjørn Mork goto err; 7893cc36157SBjørn Mork 7903cc36157SBjørn Mork intf->needs_remote_wakeup = on; 7913cc36157SBjørn Mork usb_autopm_put_interface(intf); 7923cc36157SBjørn Mork err: 7933cc36157SBjørn Mork return rv; 7943cc36157SBjørn Mork } 7953cc36157SBjørn Mork 7960dffb486SBjørn Mork static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) 7970dffb486SBjørn Mork { 7980dffb486SBjørn Mork int rv = -EINVAL; 7990dffb486SBjørn Mork struct usb_host_interface *iface; 8000dffb486SBjørn Mork struct usb_endpoint_descriptor *ep; 8010dffb486SBjørn Mork struct usb_cdc_dmm_desc *dmhd; 8020dffb486SBjørn Mork u8 *buffer = intf->altsetting->extra; 8030dffb486SBjørn Mork int buflen = intf->altsetting->extralen; 8040dffb486SBjørn Mork u16 maxcom = WDM_DEFAULT_BUFSIZE; 8050dffb486SBjørn Mork 8060dffb486SBjørn Mork if (!buffer) 8070dffb486SBjørn Mork goto err; 8080dffb486SBjørn Mork while (buflen > 2) { 8090dffb486SBjørn Mork if (buffer[1] != USB_DT_CS_INTERFACE) { 8100dffb486SBjørn Mork dev_err(&intf->dev, "skipping garbage\n"); 8110dffb486SBjørn Mork goto next_desc; 8120dffb486SBjørn Mork } 8130dffb486SBjørn Mork 8140dffb486SBjørn Mork switch (buffer[2]) { 8150dffb486SBjørn Mork case USB_CDC_HEADER_TYPE: 8160dffb486SBjørn Mork break; 8170dffb486SBjørn Mork case USB_CDC_DMM_TYPE: 8180dffb486SBjørn Mork dmhd = (struct usb_cdc_dmm_desc *)buffer; 8190dffb486SBjørn Mork maxcom = le16_to_cpu(dmhd->wMaxCommand); 8200dffb486SBjørn Mork dev_dbg(&intf->dev, 8210dffb486SBjørn Mork "Finding maximum buffer length: %d", maxcom); 8220dffb486SBjørn Mork break; 8230dffb486SBjørn Mork default: 8240dffb486SBjørn Mork dev_err(&intf->dev, 8250dffb486SBjørn Mork "Ignoring extra header, type %d, length %d\n", 8260dffb486SBjørn Mork buffer[2], buffer[0]); 8270dffb486SBjørn Mork break; 8280dffb486SBjørn Mork } 8290dffb486SBjørn Mork next_desc: 8300dffb486SBjørn Mork buflen -= buffer[0]; 8310dffb486SBjørn Mork buffer += buffer[0]; 8320dffb486SBjørn Mork } 8330dffb486SBjørn Mork 8340dffb486SBjørn Mork iface = intf->cur_altsetting; 8350dffb486SBjørn Mork if (iface->desc.bNumEndpoints != 1) 8360dffb486SBjørn Mork goto err; 8370dffb486SBjørn Mork ep = &iface->endpoint[0].desc; 8380dffb486SBjørn Mork 8393cc36157SBjørn Mork rv = wdm_create(intf, ep, maxcom, &wdm_manage_power); 8400dffb486SBjørn Mork 8410dffb486SBjørn Mork err: 842afba937eSOliver Neukum return rv; 843afba937eSOliver Neukum } 844afba937eSOliver Neukum 8453cc36157SBjørn Mork /** 8463cc36157SBjørn Mork * usb_cdc_wdm_register - register a WDM subdriver 8473cc36157SBjørn Mork * @intf: usb interface the subdriver will associate with 8483cc36157SBjørn Mork * @ep: interrupt endpoint to monitor for notifications 8493cc36157SBjørn Mork * @bufsize: maximum message size to support for read/write 8503cc36157SBjørn Mork * 8513cc36157SBjørn Mork * Create WDM usb class character device and associate it with intf 8523cc36157SBjørn Mork * without binding, allowing another driver to manage the interface. 8533cc36157SBjørn Mork * 8543cc36157SBjørn Mork * The subdriver will manage the given interrupt endpoint exclusively 8553cc36157SBjørn Mork * and will issue control requests referring to the given intf. It 8563cc36157SBjørn Mork * will otherwise avoid interferring, and in particular not do 8573cc36157SBjørn Mork * usb_set_intfdata/usb_get_intfdata on intf. 8583cc36157SBjørn Mork * 8593cc36157SBjørn Mork * The return value is a pointer to the subdriver's struct usb_driver. 8603cc36157SBjørn Mork * The registering driver is responsible for calling this subdriver's 8613cc36157SBjørn Mork * disconnect, suspend, resume, pre_reset and post_reset methods from 8623cc36157SBjørn Mork * its own. 8633cc36157SBjørn Mork */ 8643cc36157SBjørn Mork struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, 8653cc36157SBjørn Mork struct usb_endpoint_descriptor *ep, 8663cc36157SBjørn Mork int bufsize, 8673cc36157SBjørn Mork int (*manage_power)(struct usb_interface *, int)) 8683cc36157SBjørn Mork { 8693cc36157SBjørn Mork int rv = -EINVAL; 8703cc36157SBjørn Mork 8713cc36157SBjørn Mork rv = wdm_create(intf, ep, bufsize, manage_power); 8723cc36157SBjørn Mork if (rv < 0) 8733cc36157SBjørn Mork goto err; 8743cc36157SBjørn Mork 8753cc36157SBjørn Mork return &wdm_driver; 8763cc36157SBjørn Mork err: 8773cc36157SBjørn Mork return ERR_PTR(rv); 8783cc36157SBjørn Mork } 8793cc36157SBjørn Mork EXPORT_SYMBOL(usb_cdc_wdm_register); 8803cc36157SBjørn Mork 881afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf) 882afba937eSOliver Neukum { 883afba937eSOliver Neukum struct wdm_device *desc; 884afba937eSOliver Neukum unsigned long flags; 885afba937eSOliver Neukum 886afba937eSOliver Neukum usb_deregister_dev(intf, &wdm_class); 887b0c13860SBjørn Mork desc = wdm_find_device(intf); 888afba937eSOliver Neukum mutex_lock(&wdm_mutex); 889afba937eSOliver Neukum 890afba937eSOliver Neukum /* the spinlock makes sure no new urbs are generated in the callbacks */ 891afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 892afba937eSOliver Neukum set_bit(WDM_DISCONNECTING, &desc->flags); 893afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 89417d80d56SOliver Neukum /* to terminate pending flushes */ 895afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 896afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 89762aaf24dSBjørn Mork wake_up_all(&desc->wait); 898e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 899e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 900afba937eSOliver Neukum kill_urbs(desc); 901d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 902e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 903e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 904afba937eSOliver Neukum if (!desc->count) 905afba937eSOliver Neukum cleanup(desc); 906880bca3aSBjørn Mork else 907880bca3aSBjørn Mork dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count); 908afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 909afba937eSOliver Neukum } 910afba937eSOliver Neukum 911d93d16e9SOliver Neukum #ifdef CONFIG_PM 91217d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message) 91317d80d56SOliver Neukum { 914b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 91517d80d56SOliver Neukum int rv = 0; 91617d80d56SOliver Neukum 91717d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); 91817d80d56SOliver Neukum 919d93d16e9SOliver Neukum /* if this is an autosuspend the caller does the locking */ 920e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 921e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 922e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 923e8537bd2SBjørn Mork } 92462e66854SOliver Neukum spin_lock_irq(&desc->iuspin); 925d93d16e9SOliver Neukum 9265b1b0b81SAlan Stern if (PMSG_IS_AUTO(message) && 927922a5eadSOliver Neukum (test_bit(WDM_IN_USE, &desc->flags) 928922a5eadSOliver Neukum || test_bit(WDM_RESPONDING, &desc->flags))) { 92962e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 93017d80d56SOliver Neukum rv = -EBUSY; 93117d80d56SOliver Neukum } else { 932d93d16e9SOliver Neukum 933beb1d35fSOliver Neukum set_bit(WDM_SUSPENDING, &desc->flags); 93462e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 935d93d16e9SOliver Neukum /* callback submits work - order is essential */ 93617d80d56SOliver Neukum kill_urbs(desc); 937d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 93817d80d56SOliver Neukum } 939e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 940e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 941e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 942e8537bd2SBjørn Mork } 94317d80d56SOliver Neukum 94417d80d56SOliver Neukum return rv; 94517d80d56SOliver Neukum } 946d93d16e9SOliver Neukum #endif 94717d80d56SOliver Neukum 94817d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc) 94917d80d56SOliver Neukum { 95017d80d56SOliver Neukum int rv = 0; 95117d80d56SOliver Neukum 95217d80d56SOliver Neukum if (desc->count) { 95317d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_NOIO); 95417d80d56SOliver Neukum if (rv < 0) 9559908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 9569908a32eSGreg Kroah-Hartman "Error resume submitting int urb - %d\n", rv); 95717d80d56SOliver Neukum } 95817d80d56SOliver Neukum return rv; 95917d80d56SOliver Neukum } 960d93d16e9SOliver Neukum 961d93d16e9SOliver Neukum #ifdef CONFIG_PM 96217d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf) 96317d80d56SOliver Neukum { 964b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 96517d80d56SOliver Neukum int rv; 96617d80d56SOliver Neukum 96717d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); 968338124c1SOliver Neukum 969beb1d35fSOliver Neukum clear_bit(WDM_SUSPENDING, &desc->flags); 97062e66854SOliver Neukum rv = recover_from_urb_loss(desc); 971338124c1SOliver Neukum 97217d80d56SOliver Neukum return rv; 97317d80d56SOliver Neukum } 974d93d16e9SOliver Neukum #endif 97517d80d56SOliver Neukum 97617d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf) 97717d80d56SOliver Neukum { 978b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 97917d80d56SOliver Neukum 980d771d8aaSOliver Neukum /* 981d771d8aaSOliver Neukum * we notify everybody using poll of 982d771d8aaSOliver Neukum * an exceptional situation 983d771d8aaSOliver Neukum * must be done before recovery lest a spontaneous 984d771d8aaSOliver Neukum * message from the device is lost 985d771d8aaSOliver Neukum */ 986d771d8aaSOliver Neukum spin_lock_irq(&desc->iuspin); 98788044202SBjørn Mork set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */ 98888044202SBjørn Mork set_bit(WDM_READ, &desc->flags); /* unblock read */ 98988044202SBjørn Mork clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */ 990d771d8aaSOliver Neukum desc->rerr = -EINTR; 991d771d8aaSOliver Neukum spin_unlock_irq(&desc->iuspin); 992d771d8aaSOliver Neukum wake_up_all(&desc->wait); 99388044202SBjørn Mork mutex_lock(&desc->rlock); 99488044202SBjørn Mork mutex_lock(&desc->wlock); 99588044202SBjørn Mork kill_urbs(desc); 99688044202SBjørn Mork cancel_work_sync(&desc->rxwork); 99717d80d56SOliver Neukum return 0; 99817d80d56SOliver Neukum } 99917d80d56SOliver Neukum 100017d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf) 100117d80d56SOliver Neukum { 1002b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 100317d80d56SOliver Neukum int rv; 100417d80d56SOliver Neukum 100588044202SBjørn Mork clear_bit(WDM_RESETTING, &desc->flags); 100617d80d56SOliver Neukum rv = recover_from_urb_loss(desc); 1007e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 1008e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 100917d80d56SOliver Neukum return 0; 101017d80d56SOliver Neukum } 101117d80d56SOliver Neukum 1012afba937eSOliver Neukum static struct usb_driver wdm_driver = { 1013afba937eSOliver Neukum .name = "cdc_wdm", 1014afba937eSOliver Neukum .probe = wdm_probe, 1015afba937eSOliver Neukum .disconnect = wdm_disconnect, 1016d93d16e9SOliver Neukum #ifdef CONFIG_PM 101717d80d56SOliver Neukum .suspend = wdm_suspend, 101817d80d56SOliver Neukum .resume = wdm_resume, 101917d80d56SOliver Neukum .reset_resume = wdm_resume, 1020d93d16e9SOliver Neukum #endif 102117d80d56SOliver Neukum .pre_reset = wdm_pre_reset, 102217d80d56SOliver Neukum .post_reset = wdm_post_reset, 1023afba937eSOliver Neukum .id_table = wdm_ids, 102417d80d56SOliver Neukum .supports_autosuspend = 1, 1025afba937eSOliver Neukum }; 1026afba937eSOliver Neukum 102765db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver); 1028afba937eSOliver Neukum 1029afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR); 103087d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC); 1031afba937eSOliver Neukum MODULE_LICENSE("GPL"); 1032