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); 37117d80d56SOliver Neukum goto outnp; 372860e41a7SOliver Neukum } 3737f1dc313SOliver Neukum 3740cdfb819SDavid Sterba if (!(file->f_flags & O_NONBLOCK)) 375afba937eSOliver Neukum r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, 376afba937eSOliver Neukum &desc->flags)); 3777f1dc313SOliver Neukum else 3787f1dc313SOliver Neukum if (test_bit(WDM_IN_USE, &desc->flags)) 3797f1dc313SOliver Neukum r = -EAGAIN; 38088044202SBjørn Mork 38188044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) 38288044202SBjørn Mork r = -EIO; 38388044202SBjørn Mork 384860e41a7SOliver Neukum if (r < 0) { 385afba937eSOliver Neukum kfree(buf); 386afba937eSOliver Neukum goto out; 387afba937eSOliver Neukum } 388afba937eSOliver Neukum 389afba937eSOliver Neukum req = desc->orq; 390afba937eSOliver Neukum usb_fill_control_urb( 391afba937eSOliver Neukum desc->command, 392afba937eSOliver Neukum interface_to_usbdev(desc->intf), 393afba937eSOliver Neukum /* using common endpoint 0 */ 394afba937eSOliver Neukum usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0), 395afba937eSOliver Neukum (unsigned char *)req, 396afba937eSOliver Neukum buf, 397afba937eSOliver Neukum count, 398afba937eSOliver Neukum wdm_out_callback, 399afba937eSOliver Neukum desc 400afba937eSOliver Neukum ); 401afba937eSOliver Neukum 402afba937eSOliver Neukum req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | 403afba937eSOliver Neukum USB_RECIP_INTERFACE); 404afba937eSOliver Neukum req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; 405afba937eSOliver Neukum req->wValue = 0; 406afba937eSOliver Neukum req->wIndex = desc->inum; 407afba937eSOliver Neukum req->wLength = cpu_to_le16(count); 408afba937eSOliver Neukum set_bit(WDM_IN_USE, &desc->flags); 409afba937eSOliver Neukum 410afba937eSOliver Neukum rv = usb_submit_urb(desc->command, GFP_KERNEL); 411afba937eSOliver Neukum if (rv < 0) { 412afba937eSOliver Neukum kfree(buf); 413afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 4149908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); 415afba937eSOliver Neukum } else { 416afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", 417afba937eSOliver Neukum req->wIndex); 418afba937eSOliver Neukum } 419afba937eSOliver Neukum out: 42017d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 42117d80d56SOliver Neukum outnp: 422e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 423afba937eSOliver Neukum outnl: 424afba937eSOliver Neukum return rv < 0 ? rv : count; 425afba937eSOliver Neukum } 426afba937eSOliver Neukum 427afba937eSOliver Neukum static ssize_t wdm_read 428afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos) 429afba937eSOliver Neukum { 430711c68b3SBen Hutchings int rv, cntr; 431afba937eSOliver Neukum int i = 0; 432afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 433afba937eSOliver Neukum 434afba937eSOliver Neukum 435e8537bd2SBjørn Mork rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ 436afba937eSOliver Neukum if (rv < 0) 437afba937eSOliver Neukum return -ERESTARTSYS; 438afba937eSOliver Neukum 439711c68b3SBen Hutchings cntr = ACCESS_ONCE(desc->length); 440711c68b3SBen Hutchings if (cntr == 0) { 441afba937eSOliver Neukum desc->read = 0; 442afba937eSOliver Neukum retry: 4437f1dc313SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 4447f1dc313SOliver Neukum rv = -ENODEV; 4457f1dc313SOliver Neukum goto err; 4467f1dc313SOliver Neukum } 447afba937eSOliver Neukum i++; 4487f1dc313SOliver Neukum if (file->f_flags & O_NONBLOCK) { 4497f1dc313SOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { 4507f1dc313SOliver Neukum rv = cntr ? cntr : -EAGAIN; 4517f1dc313SOliver Neukum goto err; 4527f1dc313SOliver Neukum } 4537f1dc313SOliver Neukum rv = 0; 4547f1dc313SOliver Neukum } else { 455afba937eSOliver Neukum rv = wait_event_interruptible(desc->wait, 456afba937eSOliver Neukum test_bit(WDM_READ, &desc->flags)); 4577f1dc313SOliver Neukum } 458afba937eSOliver Neukum 4597f1dc313SOliver Neukum /* may have happened while we slept */ 46017d80d56SOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 46117d80d56SOliver Neukum rv = -ENODEV; 46217d80d56SOliver Neukum goto err; 46317d80d56SOliver Neukum } 46488044202SBjørn Mork if (test_bit(WDM_RESETTING, &desc->flags)) { 46588044202SBjørn Mork rv = -EIO; 46688044202SBjørn Mork goto err; 46788044202SBjørn Mork } 46817d80d56SOliver Neukum usb_mark_last_busy(interface_to_usbdev(desc->intf)); 469afba937eSOliver Neukum if (rv < 0) { 470afba937eSOliver Neukum rv = -ERESTARTSYS; 471afba937eSOliver Neukum goto err; 472afba937eSOliver Neukum } 473afba937eSOliver Neukum 474afba937eSOliver Neukum spin_lock_irq(&desc->iuspin); 475afba937eSOliver Neukum 476afba937eSOliver Neukum if (desc->rerr) { /* read completed, error happened */ 477afba937eSOliver Neukum desc->rerr = 0; 478afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 479afba937eSOliver Neukum rv = -EIO; 480afba937eSOliver Neukum goto err; 481afba937eSOliver Neukum } 482afba937eSOliver Neukum /* 483afba937eSOliver Neukum * recheck whether we've lost the race 484afba937eSOliver Neukum * against the completion handler 485afba937eSOliver Neukum */ 486afba937eSOliver Neukum if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */ 487afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 488afba937eSOliver Neukum goto retry; 489afba937eSOliver Neukum } 490afba937eSOliver Neukum if (!desc->reslength) { /* zero length read */ 491afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 492afba937eSOliver Neukum goto retry; 493afba937eSOliver Neukum } 494711c68b3SBen Hutchings cntr = desc->length; 495afba937eSOliver Neukum spin_unlock_irq(&desc->iuspin); 496afba937eSOliver Neukum } 497afba937eSOliver Neukum 498711c68b3SBen Hutchings if (cntr > count) 499711c68b3SBen Hutchings cntr = count; 500afba937eSOliver Neukum rv = copy_to_user(buffer, desc->ubuf, cntr); 501afba937eSOliver Neukum if (rv > 0) { 502afba937eSOliver Neukum rv = -EFAULT; 503afba937eSOliver Neukum goto err; 504afba937eSOliver Neukum } 505afba937eSOliver Neukum 506711c68b3SBen Hutchings spin_lock_irq(&desc->iuspin); 507711c68b3SBen Hutchings 508afba937eSOliver Neukum for (i = 0; i < desc->length - cntr; i++) 509afba937eSOliver Neukum desc->ubuf[i] = desc->ubuf[i + cntr]; 510afba937eSOliver Neukum 511afba937eSOliver Neukum desc->length -= cntr; 51287d65e54SOliver Neukum /* in case we had outstanding data */ 51387d65e54SOliver Neukum if (!desc->length) 51487d65e54SOliver Neukum clear_bit(WDM_READ, &desc->flags); 515711c68b3SBen Hutchings 516711c68b3SBen Hutchings spin_unlock_irq(&desc->iuspin); 517711c68b3SBen Hutchings 518afba937eSOliver Neukum rv = cntr; 519afba937eSOliver Neukum 520afba937eSOliver Neukum err: 521e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 522afba937eSOliver Neukum return rv; 523afba937eSOliver Neukum } 524afba937eSOliver Neukum 525afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id) 526afba937eSOliver Neukum { 527afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 528afba937eSOliver Neukum 529afba937eSOliver Neukum wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); 530afba937eSOliver Neukum if (desc->werr < 0) 5319908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error in flush path: %d\n", 5329908a32eSGreg Kroah-Hartman desc->werr); 533afba937eSOliver Neukum 53424a85baeSOliver Neukum return usb_translate_errors(desc->werr); 535afba937eSOliver Neukum } 536afba937eSOliver Neukum 537afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) 538afba937eSOliver Neukum { 539afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 540afba937eSOliver Neukum unsigned long flags; 541afba937eSOliver Neukum unsigned int mask = 0; 542afba937eSOliver Neukum 543afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 544afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 545afba937eSOliver Neukum mask = POLLERR; 546afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 547afba937eSOliver Neukum goto desc_out; 548afba937eSOliver Neukum } 549afba937eSOliver Neukum if (test_bit(WDM_READ, &desc->flags)) 550afba937eSOliver Neukum mask = POLLIN | POLLRDNORM; 551afba937eSOliver Neukum if (desc->rerr || desc->werr) 552afba937eSOliver Neukum mask |= POLLERR; 553afba937eSOliver Neukum if (!test_bit(WDM_IN_USE, &desc->flags)) 554afba937eSOliver Neukum mask |= POLLOUT | POLLWRNORM; 555afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 556afba937eSOliver Neukum 557afba937eSOliver Neukum poll_wait(file, &desc->wait, wait); 558afba937eSOliver Neukum 559afba937eSOliver Neukum desc_out: 560afba937eSOliver Neukum return mask; 561afba937eSOliver Neukum } 562afba937eSOliver Neukum 563afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file) 564afba937eSOliver Neukum { 565afba937eSOliver Neukum int minor = iminor(inode); 566afba937eSOliver Neukum int rv = -ENODEV; 567afba937eSOliver Neukum struct usb_interface *intf; 568afba937eSOliver Neukum struct wdm_device *desc; 569afba937eSOliver Neukum 570afba937eSOliver Neukum mutex_lock(&wdm_mutex); 571b0c13860SBjørn Mork desc = wdm_find_device_by_minor(minor); 572b0c13860SBjørn Mork if (!desc) 573afba937eSOliver Neukum goto out; 574afba937eSOliver Neukum 575b0c13860SBjørn Mork intf = desc->intf; 576afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) 577afba937eSOliver Neukum goto out; 578afba937eSOliver Neukum file->private_data = desc; 579afba937eSOliver Neukum 58017d80d56SOliver Neukum rv = usb_autopm_get_interface(desc->intf); 58117d80d56SOliver Neukum if (rv < 0) { 5829908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); 58317d80d56SOliver Neukum goto out; 58417d80d56SOliver Neukum } 585afba937eSOliver Neukum 586e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 587e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 58817d80d56SOliver Neukum if (!desc->count++) { 589d771d8aaSOliver Neukum desc->werr = 0; 590d771d8aaSOliver Neukum desc->rerr = 0; 59117d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_KERNEL); 592afba937eSOliver Neukum if (rv < 0) { 593afba937eSOliver Neukum desc->count--; 5949908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 5959908a32eSGreg Kroah-Hartman "Error submitting int urb - %d\n", rv); 596afba937eSOliver Neukum } 59717d80d56SOliver Neukum } else { 598afba937eSOliver Neukum rv = 0; 59917d80d56SOliver Neukum } 600e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 6013cc36157SBjørn Mork if (desc->count == 1) 6023cc36157SBjørn Mork desc->manage_power(intf, 1); 60317d80d56SOliver Neukum usb_autopm_put_interface(desc->intf); 604afba937eSOliver Neukum out: 605afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 606afba937eSOliver Neukum return rv; 607afba937eSOliver Neukum } 608afba937eSOliver Neukum 609afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file) 610afba937eSOliver Neukum { 611afba937eSOliver Neukum struct wdm_device *desc = file->private_data; 612afba937eSOliver Neukum 613afba937eSOliver Neukum mutex_lock(&wdm_mutex); 614e8537bd2SBjørn Mork 615e8537bd2SBjørn Mork /* using write lock to protect desc->count */ 616e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 617afba937eSOliver Neukum desc->count--; 618e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 61917d80d56SOliver Neukum 620afba937eSOliver Neukum if (!desc->count) { 621afba937eSOliver Neukum dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); 622afba937eSOliver Neukum kill_urbs(desc); 62317d80d56SOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 6243cc36157SBjørn Mork desc->manage_power(desc->intf, 0); 625afba937eSOliver Neukum } 626afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 627afba937eSOliver Neukum return 0; 628afba937eSOliver Neukum } 629afba937eSOliver Neukum 630afba937eSOliver Neukum static const struct file_operations wdm_fops = { 631afba937eSOliver Neukum .owner = THIS_MODULE, 632afba937eSOliver Neukum .read = wdm_read, 633afba937eSOliver Neukum .write = wdm_write, 634afba937eSOliver Neukum .open = wdm_open, 635afba937eSOliver Neukum .flush = wdm_flush, 636afba937eSOliver Neukum .release = wdm_release, 6376038f373SArnd Bergmann .poll = wdm_poll, 6386038f373SArnd Bergmann .llseek = noop_llseek, 639afba937eSOliver Neukum }; 640afba937eSOliver Neukum 641afba937eSOliver Neukum static struct usb_class_driver wdm_class = { 642afba937eSOliver Neukum .name = "cdc-wdm%d", 643afba937eSOliver Neukum .fops = &wdm_fops, 644afba937eSOliver Neukum .minor_base = WDM_MINOR_BASE, 645afba937eSOliver Neukum }; 646afba937eSOliver Neukum 647afba937eSOliver Neukum /* --- error handling --- */ 648afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work) 649afba937eSOliver Neukum { 650afba937eSOliver Neukum struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); 651afba937eSOliver Neukum unsigned long flags; 652afba937eSOliver Neukum int rv; 653afba937eSOliver Neukum 654afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 655afba937eSOliver Neukum if (test_bit(WDM_DISCONNECTING, &desc->flags)) { 656afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 657afba937eSOliver Neukum } else { 658afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 659afba937eSOliver Neukum rv = usb_submit_urb(desc->response, GFP_KERNEL); 660afba937eSOliver Neukum if (rv < 0 && rv != -EPERM) { 661afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 662afba937eSOliver Neukum if (!test_bit(WDM_DISCONNECTING, &desc->flags)) 663afba937eSOliver Neukum schedule_work(&desc->rxwork); 664afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 665afba937eSOliver Neukum } 666afba937eSOliver Neukum } 667afba937eSOliver Neukum } 668afba937eSOliver Neukum 669afba937eSOliver Neukum /* --- hotplug --- */ 670afba937eSOliver Neukum 6713cc36157SBjørn Mork static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, 6723cc36157SBjørn Mork u16 bufsize, int (*manage_power)(struct usb_interface *, int)) 673afba937eSOliver Neukum { 6740dffb486SBjørn Mork int rv = -ENOMEM; 675afba937eSOliver Neukum struct wdm_device *desc; 676afba937eSOliver Neukum 677afba937eSOliver Neukum desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); 678afba937eSOliver Neukum if (!desc) 679afba937eSOliver Neukum goto out; 680b0c13860SBjørn Mork INIT_LIST_HEAD(&desc->device_list); 681e8537bd2SBjørn Mork mutex_init(&desc->rlock); 682e8537bd2SBjørn Mork mutex_init(&desc->wlock); 683afba937eSOliver Neukum spin_lock_init(&desc->iuspin); 684afba937eSOliver Neukum init_waitqueue_head(&desc->wait); 6850dffb486SBjørn Mork desc->wMaxCommand = bufsize; 686052fbc0dSOliver Neukum /* this will be expanded and needed in hardware endianness */ 687afba937eSOliver Neukum desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); 688afba937eSOliver Neukum desc->intf = intf; 689afba937eSOliver Neukum INIT_WORK(&desc->rxwork, wdm_rxwork); 690afba937eSOliver Neukum 691afba937eSOliver Neukum rv = -EINVAL; 6920dffb486SBjørn Mork if (!usb_endpoint_is_int_in(ep)) 693052fbc0dSOliver Neukum goto err; 694afba937eSOliver Neukum 69529cc8897SKuninori Morimoto desc->wMaxPacketSize = usb_endpoint_maxp(ep); 696afba937eSOliver Neukum 697afba937eSOliver Neukum desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 698afba937eSOliver Neukum if (!desc->orq) 699afba937eSOliver Neukum goto err; 700afba937eSOliver Neukum desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); 701afba937eSOliver Neukum if (!desc->irq) 702afba937eSOliver Neukum goto err; 703afba937eSOliver Neukum 704afba937eSOliver Neukum desc->validity = usb_alloc_urb(0, GFP_KERNEL); 705afba937eSOliver Neukum if (!desc->validity) 706afba937eSOliver Neukum goto err; 707afba937eSOliver Neukum 708afba937eSOliver Neukum desc->response = usb_alloc_urb(0, GFP_KERNEL); 709afba937eSOliver Neukum if (!desc->response) 710afba937eSOliver Neukum goto err; 711afba937eSOliver Neukum 712afba937eSOliver Neukum desc->command = usb_alloc_urb(0, GFP_KERNEL); 713afba937eSOliver Neukum if (!desc->command) 714afba937eSOliver Neukum goto err; 715afba937eSOliver Neukum 716afba937eSOliver Neukum desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 717afba937eSOliver Neukum if (!desc->ubuf) 718afba937eSOliver Neukum goto err; 719afba937eSOliver Neukum 7208457d99cSBjørn Mork desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL); 721afba937eSOliver Neukum if (!desc->sbuf) 722afba937eSOliver Neukum goto err; 723afba937eSOliver Neukum 7248457d99cSBjørn Mork desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); 725afba937eSOliver Neukum if (!desc->inbuf) 7268457d99cSBjørn Mork goto err; 727afba937eSOliver Neukum 728afba937eSOliver Neukum usb_fill_int_urb( 729afba937eSOliver Neukum desc->validity, 730afba937eSOliver Neukum interface_to_usbdev(intf), 731afba937eSOliver Neukum usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress), 732afba937eSOliver Neukum desc->sbuf, 733afba937eSOliver Neukum desc->wMaxPacketSize, 734afba937eSOliver Neukum wdm_int_callback, 735afba937eSOliver Neukum desc, 736afba937eSOliver Neukum ep->bInterval 737afba937eSOliver Neukum ); 738afba937eSOliver Neukum 73919b85b3bSBjørn Mork desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); 74019b85b3bSBjørn Mork desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; 74119b85b3bSBjørn Mork desc->irq->wValue = 0; 74219b85b3bSBjørn Mork desc->irq->wIndex = desc->inum; 74319b85b3bSBjørn Mork desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); 74419b85b3bSBjørn Mork 74519b85b3bSBjørn Mork usb_fill_control_urb( 74619b85b3bSBjørn Mork desc->response, 7478143a896SBjørn Mork interface_to_usbdev(intf), 74819b85b3bSBjørn Mork /* using common endpoint 0 */ 74919b85b3bSBjørn Mork usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), 75019b85b3bSBjørn Mork (unsigned char *)desc->irq, 75119b85b3bSBjørn Mork desc->inbuf, 75219b85b3bSBjørn Mork desc->wMaxCommand, 75319b85b3bSBjørn Mork wdm_in_callback, 75419b85b3bSBjørn Mork desc 75519b85b3bSBjørn Mork ); 75619b85b3bSBjørn Mork 7573cc36157SBjørn Mork desc->manage_power = manage_power; 7583cc36157SBjørn Mork 759b0c13860SBjørn Mork spin_lock(&wdm_device_list_lock); 760b0c13860SBjørn Mork list_add(&desc->device_list, &wdm_device_list); 761b0c13860SBjørn Mork spin_unlock(&wdm_device_list_lock); 762b0c13860SBjørn Mork 763afba937eSOliver Neukum rv = usb_register_dev(intf, &wdm_class); 764052fbc0dSOliver Neukum if (rv < 0) 765b0c13860SBjørn Mork goto err; 766052fbc0dSOliver Neukum else 767820c629aSBjørn Mork dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev)); 768afba937eSOliver Neukum out: 769afba937eSOliver Neukum return rv; 770afba937eSOliver Neukum err: 7710dffb486SBjørn Mork cleanup(desc); 7720dffb486SBjørn Mork return rv; 7730dffb486SBjørn Mork } 7740dffb486SBjørn Mork 7753cc36157SBjørn Mork static int wdm_manage_power(struct usb_interface *intf, int on) 7763cc36157SBjørn Mork { 7773cc36157SBjørn Mork /* need autopm_get/put here to ensure the usbcore sees the new value */ 7783cc36157SBjørn Mork int rv = usb_autopm_get_interface(intf); 7793cc36157SBjørn Mork if (rv < 0) 7803cc36157SBjørn Mork goto err; 7813cc36157SBjørn Mork 7823cc36157SBjørn Mork intf->needs_remote_wakeup = on; 7833cc36157SBjørn Mork usb_autopm_put_interface(intf); 7843cc36157SBjørn Mork err: 7853cc36157SBjørn Mork return rv; 7863cc36157SBjørn Mork } 7873cc36157SBjørn Mork 7880dffb486SBjørn Mork static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) 7890dffb486SBjørn Mork { 7900dffb486SBjørn Mork int rv = -EINVAL; 7910dffb486SBjørn Mork struct usb_host_interface *iface; 7920dffb486SBjørn Mork struct usb_endpoint_descriptor *ep; 7930dffb486SBjørn Mork struct usb_cdc_dmm_desc *dmhd; 7940dffb486SBjørn Mork u8 *buffer = intf->altsetting->extra; 7950dffb486SBjørn Mork int buflen = intf->altsetting->extralen; 7960dffb486SBjørn Mork u16 maxcom = WDM_DEFAULT_BUFSIZE; 7970dffb486SBjørn Mork 7980dffb486SBjørn Mork if (!buffer) 7990dffb486SBjørn Mork goto err; 8000dffb486SBjørn Mork while (buflen > 2) { 8010dffb486SBjørn Mork if (buffer[1] != USB_DT_CS_INTERFACE) { 8020dffb486SBjørn Mork dev_err(&intf->dev, "skipping garbage\n"); 8030dffb486SBjørn Mork goto next_desc; 8040dffb486SBjørn Mork } 8050dffb486SBjørn Mork 8060dffb486SBjørn Mork switch (buffer[2]) { 8070dffb486SBjørn Mork case USB_CDC_HEADER_TYPE: 8080dffb486SBjørn Mork break; 8090dffb486SBjørn Mork case USB_CDC_DMM_TYPE: 8100dffb486SBjørn Mork dmhd = (struct usb_cdc_dmm_desc *)buffer; 8110dffb486SBjørn Mork maxcom = le16_to_cpu(dmhd->wMaxCommand); 8120dffb486SBjørn Mork dev_dbg(&intf->dev, 8130dffb486SBjørn Mork "Finding maximum buffer length: %d", maxcom); 8140dffb486SBjørn Mork break; 8150dffb486SBjørn Mork default: 8160dffb486SBjørn Mork dev_err(&intf->dev, 8170dffb486SBjørn Mork "Ignoring extra header, type %d, length %d\n", 8180dffb486SBjørn Mork buffer[2], buffer[0]); 8190dffb486SBjørn Mork break; 8200dffb486SBjørn Mork } 8210dffb486SBjørn Mork next_desc: 8220dffb486SBjørn Mork buflen -= buffer[0]; 8230dffb486SBjørn Mork buffer += buffer[0]; 8240dffb486SBjørn Mork } 8250dffb486SBjørn Mork 8260dffb486SBjørn Mork iface = intf->cur_altsetting; 8270dffb486SBjørn Mork if (iface->desc.bNumEndpoints != 1) 8280dffb486SBjørn Mork goto err; 8290dffb486SBjørn Mork ep = &iface->endpoint[0].desc; 8300dffb486SBjørn Mork 8313cc36157SBjørn Mork rv = wdm_create(intf, ep, maxcom, &wdm_manage_power); 8320dffb486SBjørn Mork 8330dffb486SBjørn Mork err: 834afba937eSOliver Neukum return rv; 835afba937eSOliver Neukum } 836afba937eSOliver Neukum 8373cc36157SBjørn Mork /** 8383cc36157SBjørn Mork * usb_cdc_wdm_register - register a WDM subdriver 8393cc36157SBjørn Mork * @intf: usb interface the subdriver will associate with 8403cc36157SBjørn Mork * @ep: interrupt endpoint to monitor for notifications 8413cc36157SBjørn Mork * @bufsize: maximum message size to support for read/write 8423cc36157SBjørn Mork * 8433cc36157SBjørn Mork * Create WDM usb class character device and associate it with intf 8443cc36157SBjørn Mork * without binding, allowing another driver to manage the interface. 8453cc36157SBjørn Mork * 8463cc36157SBjørn Mork * The subdriver will manage the given interrupt endpoint exclusively 8473cc36157SBjørn Mork * and will issue control requests referring to the given intf. It 8483cc36157SBjørn Mork * will otherwise avoid interferring, and in particular not do 8493cc36157SBjørn Mork * usb_set_intfdata/usb_get_intfdata on intf. 8503cc36157SBjørn Mork * 8513cc36157SBjørn Mork * The return value is a pointer to the subdriver's struct usb_driver. 8523cc36157SBjørn Mork * The registering driver is responsible for calling this subdriver's 8533cc36157SBjørn Mork * disconnect, suspend, resume, pre_reset and post_reset methods from 8543cc36157SBjørn Mork * its own. 8553cc36157SBjørn Mork */ 8563cc36157SBjørn Mork struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, 8573cc36157SBjørn Mork struct usb_endpoint_descriptor *ep, 8583cc36157SBjørn Mork int bufsize, 8593cc36157SBjørn Mork int (*manage_power)(struct usb_interface *, int)) 8603cc36157SBjørn Mork { 8613cc36157SBjørn Mork int rv = -EINVAL; 8623cc36157SBjørn Mork 8633cc36157SBjørn Mork rv = wdm_create(intf, ep, bufsize, manage_power); 8643cc36157SBjørn Mork if (rv < 0) 8653cc36157SBjørn Mork goto err; 8663cc36157SBjørn Mork 8673cc36157SBjørn Mork return &wdm_driver; 8683cc36157SBjørn Mork err: 8693cc36157SBjørn Mork return ERR_PTR(rv); 8703cc36157SBjørn Mork } 8713cc36157SBjørn Mork EXPORT_SYMBOL(usb_cdc_wdm_register); 8723cc36157SBjørn Mork 873afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf) 874afba937eSOliver Neukum { 875afba937eSOliver Neukum struct wdm_device *desc; 876afba937eSOliver Neukum unsigned long flags; 877afba937eSOliver Neukum 878afba937eSOliver Neukum usb_deregister_dev(intf, &wdm_class); 879b0c13860SBjørn Mork desc = wdm_find_device(intf); 880afba937eSOliver Neukum mutex_lock(&wdm_mutex); 881afba937eSOliver Neukum 882afba937eSOliver Neukum /* the spinlock makes sure no new urbs are generated in the callbacks */ 883afba937eSOliver Neukum spin_lock_irqsave(&desc->iuspin, flags); 884afba937eSOliver Neukum set_bit(WDM_DISCONNECTING, &desc->flags); 885afba937eSOliver Neukum set_bit(WDM_READ, &desc->flags); 88617d80d56SOliver Neukum /* to terminate pending flushes */ 887afba937eSOliver Neukum clear_bit(WDM_IN_USE, &desc->flags); 888afba937eSOliver Neukum spin_unlock_irqrestore(&desc->iuspin, flags); 88962aaf24dSBjørn Mork wake_up_all(&desc->wait); 890e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 891e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 892afba937eSOliver Neukum kill_urbs(desc); 893d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 894e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 895e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 896afba937eSOliver Neukum if (!desc->count) 897afba937eSOliver Neukum cleanup(desc); 898afba937eSOliver Neukum mutex_unlock(&wdm_mutex); 899afba937eSOliver Neukum } 900afba937eSOliver Neukum 901d93d16e9SOliver Neukum #ifdef CONFIG_PM 90217d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message) 90317d80d56SOliver Neukum { 904b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 90517d80d56SOliver Neukum int rv = 0; 90617d80d56SOliver Neukum 90717d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); 90817d80d56SOliver Neukum 909d93d16e9SOliver Neukum /* if this is an autosuspend the caller does the locking */ 910e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 911e8537bd2SBjørn Mork mutex_lock(&desc->rlock); 912e8537bd2SBjørn Mork mutex_lock(&desc->wlock); 913e8537bd2SBjørn Mork } 91462e66854SOliver Neukum spin_lock_irq(&desc->iuspin); 915d93d16e9SOliver Neukum 9165b1b0b81SAlan Stern if (PMSG_IS_AUTO(message) && 917922a5eadSOliver Neukum (test_bit(WDM_IN_USE, &desc->flags) 918922a5eadSOliver Neukum || test_bit(WDM_RESPONDING, &desc->flags))) { 91962e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 92017d80d56SOliver Neukum rv = -EBUSY; 92117d80d56SOliver Neukum } else { 922d93d16e9SOliver Neukum 923beb1d35fSOliver Neukum set_bit(WDM_SUSPENDING, &desc->flags); 92462e66854SOliver Neukum spin_unlock_irq(&desc->iuspin); 925d93d16e9SOliver Neukum /* callback submits work - order is essential */ 92617d80d56SOliver Neukum kill_urbs(desc); 927d93d16e9SOliver Neukum cancel_work_sync(&desc->rxwork); 92817d80d56SOliver Neukum } 929e8537bd2SBjørn Mork if (!PMSG_IS_AUTO(message)) { 930e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 931e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 932e8537bd2SBjørn Mork } 93317d80d56SOliver Neukum 93417d80d56SOliver Neukum return rv; 93517d80d56SOliver Neukum } 936d93d16e9SOliver Neukum #endif 93717d80d56SOliver Neukum 93817d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc) 93917d80d56SOliver Neukum { 94017d80d56SOliver Neukum int rv = 0; 94117d80d56SOliver Neukum 94217d80d56SOliver Neukum if (desc->count) { 94317d80d56SOliver Neukum rv = usb_submit_urb(desc->validity, GFP_NOIO); 94417d80d56SOliver Neukum if (rv < 0) 9459908a32eSGreg Kroah-Hartman dev_err(&desc->intf->dev, 9469908a32eSGreg Kroah-Hartman "Error resume submitting int urb - %d\n", rv); 94717d80d56SOliver Neukum } 94817d80d56SOliver Neukum return rv; 94917d80d56SOliver Neukum } 950d93d16e9SOliver Neukum 951d93d16e9SOliver Neukum #ifdef CONFIG_PM 95217d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf) 95317d80d56SOliver Neukum { 954b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 95517d80d56SOliver Neukum int rv; 95617d80d56SOliver Neukum 95717d80d56SOliver Neukum dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); 958338124c1SOliver Neukum 959beb1d35fSOliver Neukum clear_bit(WDM_SUSPENDING, &desc->flags); 96062e66854SOliver Neukum rv = recover_from_urb_loss(desc); 961338124c1SOliver Neukum 96217d80d56SOliver Neukum return rv; 96317d80d56SOliver Neukum } 964d93d16e9SOliver Neukum #endif 96517d80d56SOliver Neukum 96617d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf) 96717d80d56SOliver Neukum { 968b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 96917d80d56SOliver Neukum 970d771d8aaSOliver Neukum /* 971d771d8aaSOliver Neukum * we notify everybody using poll of 972d771d8aaSOliver Neukum * an exceptional situation 973d771d8aaSOliver Neukum * must be done before recovery lest a spontaneous 974d771d8aaSOliver Neukum * message from the device is lost 975d771d8aaSOliver Neukum */ 976d771d8aaSOliver Neukum spin_lock_irq(&desc->iuspin); 97788044202SBjørn Mork set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */ 97888044202SBjørn Mork set_bit(WDM_READ, &desc->flags); /* unblock read */ 97988044202SBjørn Mork clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */ 980d771d8aaSOliver Neukum desc->rerr = -EINTR; 981d771d8aaSOliver Neukum spin_unlock_irq(&desc->iuspin); 982d771d8aaSOliver Neukum wake_up_all(&desc->wait); 98388044202SBjørn Mork mutex_lock(&desc->rlock); 98488044202SBjørn Mork mutex_lock(&desc->wlock); 98588044202SBjørn Mork kill_urbs(desc); 98688044202SBjørn Mork cancel_work_sync(&desc->rxwork); 98717d80d56SOliver Neukum return 0; 98817d80d56SOliver Neukum } 98917d80d56SOliver Neukum 99017d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf) 99117d80d56SOliver Neukum { 992b0c13860SBjørn Mork struct wdm_device *desc = wdm_find_device(intf); 99317d80d56SOliver Neukum int rv; 99417d80d56SOliver Neukum 99588044202SBjørn Mork clear_bit(WDM_RESETTING, &desc->flags); 99617d80d56SOliver Neukum rv = recover_from_urb_loss(desc); 997e8537bd2SBjørn Mork mutex_unlock(&desc->wlock); 998e8537bd2SBjørn Mork mutex_unlock(&desc->rlock); 99917d80d56SOliver Neukum return 0; 100017d80d56SOliver Neukum } 100117d80d56SOliver Neukum 1002afba937eSOliver Neukum static struct usb_driver wdm_driver = { 1003afba937eSOliver Neukum .name = "cdc_wdm", 1004afba937eSOliver Neukum .probe = wdm_probe, 1005afba937eSOliver Neukum .disconnect = wdm_disconnect, 1006d93d16e9SOliver Neukum #ifdef CONFIG_PM 100717d80d56SOliver Neukum .suspend = wdm_suspend, 100817d80d56SOliver Neukum .resume = wdm_resume, 100917d80d56SOliver Neukum .reset_resume = wdm_resume, 1010d93d16e9SOliver Neukum #endif 101117d80d56SOliver Neukum .pre_reset = wdm_pre_reset, 101217d80d56SOliver Neukum .post_reset = wdm_post_reset, 1013afba937eSOliver Neukum .id_table = wdm_ids, 101417d80d56SOliver Neukum .supports_autosuspend = 1, 1015afba937eSOliver Neukum }; 1016afba937eSOliver Neukum 101765db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver); 1018afba937eSOliver Neukum 1019afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR); 102087d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC); 1021afba937eSOliver Neukum MODULE_LICENSE("GPL"); 1022