blob: 23cf9d38eb54eab0b22cbd4e0c1ecbf3a017af16 [file] [log] [blame]
Oliver Neukumafba9372008-05-13 17:01:25 +02001/*
2 * cdc-wdm.c
3 *
4 * This driver supports USB CDC WCM Device Management.
5 *
Oliver Neukum052fbc02009-04-20 17:24:49 +02006 * Copyright (c) 2007-2009 Oliver Neukum
Oliver Neukumafba9372008-05-13 17:01:25 +02007 *
8 * Some code taken from cdc-acm.c
9 *
10 * Released under the GPLv2.
11 *
12 * Many thanks to Carl Nordbeck
13 */
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/slab.h>
17#include <linux/module.h>
Oliver Neukumafba9372008-05-13 17:01:25 +020018#include <linux/mutex.h>
19#include <linux/uaccess.h>
20#include <linux/bitops.h>
21#include <linux/poll.h>
22#include <linux/usb.h>
23#include <linux/usb/cdc.h>
24#include <asm/byteorder.h>
25#include <asm/unaligned.h>
26
27/*
28 * Version Information
29 */
Oliver Neukum87d65e52008-06-19 14:20:18 +020030#define DRIVER_VERSION "v0.03"
Oliver Neukumafba9372008-05-13 17:01:25 +020031#define DRIVER_AUTHOR "Oliver Neukum"
Oliver Neukum87d65e52008-06-19 14:20:18 +020032#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
Oliver Neukumafba9372008-05-13 17:01:25 +020033
Bjørn Morkfec67b42012-01-25 13:03:29 +010034#define HUAWEI_VENDOR_ID 0x12D1
35
Németh Márton6ef48522010-01-10 15:33:45 +010036static const struct usb_device_id wdm_ids[] = {
Oliver Neukumafba9372008-05-13 17:01:25 +020037 {
38 .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
39 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
40 .bInterfaceClass = USB_CLASS_COMM,
41 .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
42 },
Bjørn Morkfec67b42012-01-25 13:03:29 +010043 {
44 /*
45 * Huawei E392, E398 and possibly other Qualcomm based modems
46 * embed the Qualcomm QMI protocol inside CDC on CDC ECM like
47 * control interfaces. Userspace access to this is required
48 * to configure the accompanying data interface
49 */
50 .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
51 USB_DEVICE_ID_MATCH_INT_INFO,
52 .idVendor = HUAWEI_VENDOR_ID,
53 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
54 .bInterfaceSubClass = 1,
55 .bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
56 },
Oliver Neukumafba9372008-05-13 17:01:25 +020057 { }
58};
59
Oliver Neukumaa5380b2008-10-13 14:05:20 +020060MODULE_DEVICE_TABLE (usb, wdm_ids);
61
Oliver Neukumafba9372008-05-13 17:01:25 +020062#define WDM_MINOR_BASE 176
63
64
65#define WDM_IN_USE 1
66#define WDM_DISCONNECTING 2
67#define WDM_RESULT 3
68#define WDM_READ 4
69#define WDM_INT_STALL 5
70#define WDM_POLL_RUNNING 6
Oliver Neukum922a5ea2010-02-27 20:54:59 +010071#define WDM_RESPONDING 7
Oliver Neukumbeb1d352010-02-27 20:55:52 +010072#define WDM_SUSPENDING 8
Oliver Neukumafba9372008-05-13 17:01:25 +020073
74#define WDM_MAX 16
75
Bjørn Mork7e3054a2012-01-20 01:49:57 +010076/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */
77#define WDM_DEFAULT_BUFSIZE 256
Oliver Neukumafba9372008-05-13 17:01:25 +020078
79static DEFINE_MUTEX(wdm_mutex);
80
81/* --- method tables --- */
82
83struct wdm_device {
84 u8 *inbuf; /* buffer for response */
85 u8 *outbuf; /* buffer for command */
86 u8 *sbuf; /* buffer for status */
87 u8 *ubuf; /* buffer for copy to user space */
88
89 struct urb *command;
90 struct urb *response;
91 struct urb *validity;
92 struct usb_interface *intf;
93 struct usb_ctrlrequest *orq;
94 struct usb_ctrlrequest *irq;
95 spinlock_t iuspin;
96
97 unsigned long flags;
98 u16 bufsize;
99 u16 wMaxCommand;
100 u16 wMaxPacketSize;
Oliver Neukumafba9372008-05-13 17:01:25 +0200101 __le16 inum;
102 int reslength;
103 int length;
104 int read;
105 int count;
106 dma_addr_t shandle;
107 dma_addr_t ihandle;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100108 struct mutex lock;
Oliver Neukumafba9372008-05-13 17:01:25 +0200109 wait_queue_head_t wait;
110 struct work_struct rxwork;
111 int werr;
112 int rerr;
113};
114
115static struct usb_driver wdm_driver;
116
117/* --- callbacks --- */
118static void wdm_out_callback(struct urb *urb)
119{
120 struct wdm_device *desc;
121 desc = urb->context;
122 spin_lock(&desc->iuspin);
123 desc->werr = urb->status;
124 spin_unlock(&desc->iuspin);
125 clear_bit(WDM_IN_USE, &desc->flags);
126 kfree(desc->outbuf);
127 wake_up(&desc->wait);
128}
129
130static void wdm_in_callback(struct urb *urb)
131{
132 struct wdm_device *desc = urb->context;
133 int status = urb->status;
134
135 spin_lock(&desc->iuspin);
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100136 clear_bit(WDM_RESPONDING, &desc->flags);
Oliver Neukumafba9372008-05-13 17:01:25 +0200137
138 if (status) {
139 switch (status) {
140 case -ENOENT:
141 dev_dbg(&desc->intf->dev,
142 "nonzero urb status received: -ENOENT");
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100143 goto skip_error;
Oliver Neukumafba9372008-05-13 17:01:25 +0200144 case -ECONNRESET:
145 dev_dbg(&desc->intf->dev,
146 "nonzero urb status received: -ECONNRESET");
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100147 goto skip_error;
Oliver Neukumafba9372008-05-13 17:01:25 +0200148 case -ESHUTDOWN:
149 dev_dbg(&desc->intf->dev,
150 "nonzero urb status received: -ESHUTDOWN");
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100151 goto skip_error;
Oliver Neukumafba9372008-05-13 17:01:25 +0200152 case -EPIPE:
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700153 dev_err(&desc->intf->dev,
154 "nonzero urb status received: -EPIPE\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200155 break;
156 default:
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700157 dev_err(&desc->intf->dev,
158 "Unexpected error %d\n", status);
Oliver Neukumafba9372008-05-13 17:01:25 +0200159 break;
160 }
161 }
162
163 desc->rerr = status;
164 desc->reslength = urb->actual_length;
165 memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
166 desc->length += desc->reslength;
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100167skip_error:
Oliver Neukumafba9372008-05-13 17:01:25 +0200168 wake_up(&desc->wait);
169
170 set_bit(WDM_READ, &desc->flags);
171 spin_unlock(&desc->iuspin);
172}
173
174static void wdm_int_callback(struct urb *urb)
175{
176 int rv = 0;
177 int status = urb->status;
178 struct wdm_device *desc;
Oliver Neukumafba9372008-05-13 17:01:25 +0200179 struct usb_cdc_notification *dr;
180
181 desc = urb->context;
Oliver Neukumafba9372008-05-13 17:01:25 +0200182 dr = (struct usb_cdc_notification *)desc->sbuf;
183
184 if (status) {
185 switch (status) {
186 case -ESHUTDOWN:
187 case -ENOENT:
188 case -ECONNRESET:
189 return; /* unplug */
190 case -EPIPE:
191 set_bit(WDM_INT_STALL, &desc->flags);
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700192 dev_err(&desc->intf->dev, "Stall on int endpoint\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200193 goto sw; /* halt is cleared in work */
194 default:
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700195 dev_err(&desc->intf->dev,
196 "nonzero urb status received: %d\n", status);
Oliver Neukumafba9372008-05-13 17:01:25 +0200197 break;
198 }
199 }
200
201 if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700202 dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
203 urb->actual_length);
Oliver Neukumafba9372008-05-13 17:01:25 +0200204 goto exit;
205 }
206
207 switch (dr->bNotificationType) {
208 case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
209 dev_dbg(&desc->intf->dev,
210 "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
211 dr->wIndex, dr->wLength);
212 break;
213
214 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
215
216 dev_dbg(&desc->intf->dev,
217 "NOTIFY_NETWORK_CONNECTION %s network",
218 dr->wValue ? "connected to" : "disconnected from");
219 goto exit;
220 default:
221 clear_bit(WDM_POLL_RUNNING, &desc->flags);
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700222 dev_err(&desc->intf->dev,
223 "unknown notification %d received: index %d len %d\n",
Oliver Neukumafba9372008-05-13 17:01:25 +0200224 dr->bNotificationType, dr->wIndex, dr->wLength);
225 goto exit;
226 }
227
Oliver Neukumafba9372008-05-13 17:01:25 +0200228 spin_lock(&desc->iuspin);
229 clear_bit(WDM_READ, &desc->flags);
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100230 set_bit(WDM_RESPONDING, &desc->flags);
Oliver Neukumbeb1d352010-02-27 20:55:52 +0100231 if (!test_bit(WDM_DISCONNECTING, &desc->flags)
232 && !test_bit(WDM_SUSPENDING, &desc->flags)) {
Oliver Neukumafba9372008-05-13 17:01:25 +0200233 rv = usb_submit_urb(desc->response, GFP_ATOMIC);
234 dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
235 __func__, rv);
236 }
237 spin_unlock(&desc->iuspin);
238 if (rv < 0) {
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100239 clear_bit(WDM_RESPONDING, &desc->flags);
Oliver Neukumafba9372008-05-13 17:01:25 +0200240 if (rv == -EPERM)
241 return;
242 if (rv == -ENOMEM) {
243sw:
244 rv = schedule_work(&desc->rxwork);
245 if (rv)
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700246 dev_err(&desc->intf->dev,
247 "Cannot schedule work\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200248 }
249 }
250exit:
251 rv = usb_submit_urb(urb, GFP_ATOMIC);
252 if (rv)
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700253 dev_err(&desc->intf->dev,
254 "%s - usb_submit_urb failed with result %d\n",
255 __func__, rv);
Oliver Neukumafba9372008-05-13 17:01:25 +0200256
257}
258
259static void kill_urbs(struct wdm_device *desc)
260{
Oliver Neukum17d80d52008-06-24 15:56:10 +0200261 /* the order here is essential */
Oliver Neukumafba9372008-05-13 17:01:25 +0200262 usb_kill_urb(desc->command);
263 usb_kill_urb(desc->validity);
264 usb_kill_urb(desc->response);
265}
266
267static void free_urbs(struct wdm_device *desc)
268{
269 usb_free_urb(desc->validity);
270 usb_free_urb(desc->response);
271 usb_free_urb(desc->command);
272}
273
274static void cleanup(struct wdm_device *desc)
275{
Bjørn Mork8457d992012-01-16 15:12:00 +0100276 kfree(desc->sbuf);
277 kfree(desc->inbuf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200278 kfree(desc->orq);
279 kfree(desc->irq);
280 kfree(desc->ubuf);
281 free_urbs(desc);
282 kfree(desc);
283}
284
285static ssize_t wdm_write
286(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
287{
288 u8 *buf;
289 int rv = -EMSGSIZE, r, we;
290 struct wdm_device *desc = file->private_data;
291 struct usb_ctrlrequest *req;
292
293 if (count > desc->wMaxCommand)
294 count = desc->wMaxCommand;
295
296 spin_lock_irq(&desc->iuspin);
297 we = desc->werr;
298 desc->werr = 0;
299 spin_unlock_irq(&desc->iuspin);
300 if (we < 0)
301 return -EIO;
302
Oliver Neukum860e41a2010-02-27 20:54:24 +0100303 desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
304 if (!buf) {
305 rv = -ENOMEM;
Oliver Neukumafba9372008-05-13 17:01:25 +0200306 goto outnl;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100307 }
308
309 r = copy_from_user(buf, buffer, count);
310 if (r > 0) {
311 kfree(buf);
312 rv = -EFAULT;
313 goto outnl;
314 }
315
316 /* concurrent writes and disconnect */
317 r = mutex_lock_interruptible(&desc->lock);
318 rv = -ERESTARTSYS;
319 if (r) {
320 kfree(buf);
321 goto outnl;
322 }
323
324 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
325 kfree(buf);
326 rv = -ENODEV;
327 goto outnp;
328 }
Oliver Neukumafba9372008-05-13 17:01:25 +0200329
Oliver Neukum17d80d52008-06-24 15:56:10 +0200330 r = usb_autopm_get_interface(desc->intf);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100331 if (r < 0) {
332 kfree(buf);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200333 goto outnp;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100334 }
Oliver Neukum7f1dc312009-09-09 10:12:48 +0200335
David Sterba0cdfb812010-12-27 18:49:58 +0100336 if (!(file->f_flags & O_NONBLOCK))
Oliver Neukum7f1dc312009-09-09 10:12:48 +0200337 r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
338 &desc->flags));
339 else
340 if (test_bit(WDM_IN_USE, &desc->flags))
341 r = -EAGAIN;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100342 if (r < 0) {
Oliver Neukumafba9372008-05-13 17:01:25 +0200343 kfree(buf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200344 goto out;
345 }
346
347 req = desc->orq;
348 usb_fill_control_urb(
349 desc->command,
350 interface_to_usbdev(desc->intf),
351 /* using common endpoint 0 */
352 usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
353 (unsigned char *)req,
354 buf,
355 count,
356 wdm_out_callback,
357 desc
358 );
359
360 req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
361 USB_RECIP_INTERFACE);
362 req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
363 req->wValue = 0;
364 req->wIndex = desc->inum;
365 req->wLength = cpu_to_le16(count);
366 set_bit(WDM_IN_USE, &desc->flags);
367
368 rv = usb_submit_urb(desc->command, GFP_KERNEL);
369 if (rv < 0) {
370 kfree(buf);
371 clear_bit(WDM_IN_USE, &desc->flags);
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700372 dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
Oliver Neukumafba9372008-05-13 17:01:25 +0200373 } else {
374 dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
375 req->wIndex);
376 }
377out:
Oliver Neukum17d80d52008-06-24 15:56:10 +0200378 usb_autopm_put_interface(desc->intf);
379outnp:
Oliver Neukum860e41a2010-02-27 20:54:24 +0100380 mutex_unlock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200381outnl:
382 return rv < 0 ? rv : count;
383}
384
385static ssize_t wdm_read
386(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
387{
Oliver Neukum7f1dc312009-09-09 10:12:48 +0200388 int rv, cntr = 0;
Oliver Neukumafba9372008-05-13 17:01:25 +0200389 int i = 0;
390 struct wdm_device *desc = file->private_data;
391
392
Oliver Neukum860e41a2010-02-27 20:54:24 +0100393 rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */
Oliver Neukumafba9372008-05-13 17:01:25 +0200394 if (rv < 0)
395 return -ERESTARTSYS;
396
397 if (desc->length == 0) {
398 desc->read = 0;
399retry:
Oliver Neukum7f1dc312009-09-09 10:12:48 +0200400 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
401 rv = -ENODEV;
402 goto err;
403 }
Oliver Neukumafba9372008-05-13 17:01:25 +0200404 i++;
Oliver Neukum7f1dc312009-09-09 10:12:48 +0200405 if (file->f_flags & O_NONBLOCK) {
406 if (!test_bit(WDM_READ, &desc->flags)) {
407 rv = cntr ? cntr : -EAGAIN;
408 goto err;
409 }
410 rv = 0;
411 } else {
412 rv = wait_event_interruptible(desc->wait,
413 test_bit(WDM_READ, &desc->flags));
414 }
Oliver Neukumafba9372008-05-13 17:01:25 +0200415
Oliver Neukum7f1dc312009-09-09 10:12:48 +0200416 /* may have happened while we slept */
Oliver Neukum17d80d52008-06-24 15:56:10 +0200417 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
418 rv = -ENODEV;
419 goto err;
420 }
421 usb_mark_last_busy(interface_to_usbdev(desc->intf));
Oliver Neukumafba9372008-05-13 17:01:25 +0200422 if (rv < 0) {
423 rv = -ERESTARTSYS;
424 goto err;
425 }
426
427 spin_lock_irq(&desc->iuspin);
428
429 if (desc->rerr) { /* read completed, error happened */
Oliver Neukumafba9372008-05-13 17:01:25 +0200430 desc->rerr = 0;
431 spin_unlock_irq(&desc->iuspin);
Oliver Neukumafba9372008-05-13 17:01:25 +0200432 rv = -EIO;
433 goto err;
434 }
435 /*
436 * recheck whether we've lost the race
437 * against the completion handler
438 */
439 if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
440 spin_unlock_irq(&desc->iuspin);
441 goto retry;
442 }
443 if (!desc->reslength) { /* zero length read */
444 spin_unlock_irq(&desc->iuspin);
445 goto retry;
446 }
447 clear_bit(WDM_READ, &desc->flags);
448 spin_unlock_irq(&desc->iuspin);
449 }
450
451 cntr = count > desc->length ? desc->length : count;
452 rv = copy_to_user(buffer, desc->ubuf, cntr);
453 if (rv > 0) {
454 rv = -EFAULT;
455 goto err;
456 }
457
458 for (i = 0; i < desc->length - cntr; i++)
459 desc->ubuf[i] = desc->ubuf[i + cntr];
460
461 desc->length -= cntr;
Oliver Neukum87d65e52008-06-19 14:20:18 +0200462 /* in case we had outstanding data */
463 if (!desc->length)
464 clear_bit(WDM_READ, &desc->flags);
Oliver Neukumafba9372008-05-13 17:01:25 +0200465 rv = cntr;
466
467err:
Oliver Neukum860e41a2010-02-27 20:54:24 +0100468 mutex_unlock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200469 return rv;
470}
471
472static int wdm_flush(struct file *file, fl_owner_t id)
473{
474 struct wdm_device *desc = file->private_data;
475
476 wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
477 if (desc->werr < 0)
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700478 dev_err(&desc->intf->dev, "Error in flush path: %d\n",
479 desc->werr);
Oliver Neukumafba9372008-05-13 17:01:25 +0200480
481 return desc->werr;
482}
483
484static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
485{
486 struct wdm_device *desc = file->private_data;
487 unsigned long flags;
488 unsigned int mask = 0;
489
490 spin_lock_irqsave(&desc->iuspin, flags);
491 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
492 mask = POLLERR;
493 spin_unlock_irqrestore(&desc->iuspin, flags);
494 goto desc_out;
495 }
496 if (test_bit(WDM_READ, &desc->flags))
497 mask = POLLIN | POLLRDNORM;
498 if (desc->rerr || desc->werr)
499 mask |= POLLERR;
500 if (!test_bit(WDM_IN_USE, &desc->flags))
501 mask |= POLLOUT | POLLWRNORM;
502 spin_unlock_irqrestore(&desc->iuspin, flags);
503
504 poll_wait(file, &desc->wait, wait);
505
506desc_out:
507 return mask;
508}
509
510static int wdm_open(struct inode *inode, struct file *file)
511{
512 int minor = iminor(inode);
513 int rv = -ENODEV;
514 struct usb_interface *intf;
515 struct wdm_device *desc;
516
517 mutex_lock(&wdm_mutex);
518 intf = usb_find_interface(&wdm_driver, minor);
519 if (!intf)
520 goto out;
521
522 desc = usb_get_intfdata(intf);
523 if (test_bit(WDM_DISCONNECTING, &desc->flags))
524 goto out;
Oliver Neukumafba9372008-05-13 17:01:25 +0200525 file->private_data = desc;
526
Oliver Neukum17d80d52008-06-24 15:56:10 +0200527 rv = usb_autopm_get_interface(desc->intf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200528 if (rv < 0) {
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700529 dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
Oliver Neukumafba9372008-05-13 17:01:25 +0200530 goto out;
531 }
Oliver Neukum17d80d52008-06-24 15:56:10 +0200532 intf->needs_remote_wakeup = 1;
Oliver Neukumafba9372008-05-13 17:01:25 +0200533
Oliver Neukum860e41a2010-02-27 20:54:24 +0100534 mutex_lock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200535 if (!desc->count++) {
Oliver Neukumd771d8a2011-04-29 14:12:21 +0200536 desc->werr = 0;
537 desc->rerr = 0;
Oliver Neukum17d80d52008-06-24 15:56:10 +0200538 rv = usb_submit_urb(desc->validity, GFP_KERNEL);
539 if (rv < 0) {
540 desc->count--;
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700541 dev_err(&desc->intf->dev,
542 "Error submitting int urb - %d\n", rv);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200543 }
544 } else {
545 rv = 0;
546 }
Oliver Neukum860e41a2010-02-27 20:54:24 +0100547 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200548 usb_autopm_put_interface(desc->intf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200549out:
550 mutex_unlock(&wdm_mutex);
551 return rv;
552}
553
554static int wdm_release(struct inode *inode, struct file *file)
555{
556 struct wdm_device *desc = file->private_data;
557
558 mutex_lock(&wdm_mutex);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100559 mutex_lock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200560 desc->count--;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100561 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200562
Oliver Neukumafba9372008-05-13 17:01:25 +0200563 if (!desc->count) {
564 dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
565 kill_urbs(desc);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200566 if (!test_bit(WDM_DISCONNECTING, &desc->flags))
567 desc->intf->needs_remote_wakeup = 0;
Oliver Neukumafba9372008-05-13 17:01:25 +0200568 }
569 mutex_unlock(&wdm_mutex);
570 return 0;
571}
572
573static const struct file_operations wdm_fops = {
574 .owner = THIS_MODULE,
575 .read = wdm_read,
576 .write = wdm_write,
577 .open = wdm_open,
578 .flush = wdm_flush,
579 .release = wdm_release,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200580 .poll = wdm_poll,
581 .llseek = noop_llseek,
Oliver Neukumafba9372008-05-13 17:01:25 +0200582};
583
584static struct usb_class_driver wdm_class = {
585 .name = "cdc-wdm%d",
586 .fops = &wdm_fops,
587 .minor_base = WDM_MINOR_BASE,
588};
589
590/* --- error handling --- */
591static void wdm_rxwork(struct work_struct *work)
592{
593 struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
594 unsigned long flags;
595 int rv;
596
597 spin_lock_irqsave(&desc->iuspin, flags);
598 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
599 spin_unlock_irqrestore(&desc->iuspin, flags);
600 } else {
601 spin_unlock_irqrestore(&desc->iuspin, flags);
602 rv = usb_submit_urb(desc->response, GFP_KERNEL);
603 if (rv < 0 && rv != -EPERM) {
604 spin_lock_irqsave(&desc->iuspin, flags);
605 if (!test_bit(WDM_DISCONNECTING, &desc->flags))
606 schedule_work(&desc->rxwork);
607 spin_unlock_irqrestore(&desc->iuspin, flags);
608 }
609 }
610}
611
612/* --- hotplug --- */
613
614static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
615{
616 int rv = -EINVAL;
Oliver Neukumafba9372008-05-13 17:01:25 +0200617 struct wdm_device *desc;
618 struct usb_host_interface *iface;
619 struct usb_endpoint_descriptor *ep;
620 struct usb_cdc_dmm_desc *dmhd;
621 u8 *buffer = intf->altsetting->extra;
622 int buflen = intf->altsetting->extralen;
Bjørn Mork7e3054a2012-01-20 01:49:57 +0100623 u16 maxcom = WDM_DEFAULT_BUFSIZE;
Oliver Neukumafba9372008-05-13 17:01:25 +0200624
625 if (!buffer)
626 goto out;
627
Oliver Neukum052fbc02009-04-20 17:24:49 +0200628 while (buflen > 2) {
Oliver Neukumafba9372008-05-13 17:01:25 +0200629 if (buffer [1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700630 dev_err(&intf->dev, "skipping garbage\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200631 goto next_desc;
632 }
633
634 switch (buffer [2]) {
635 case USB_CDC_HEADER_TYPE:
636 break;
637 case USB_CDC_DMM_TYPE:
638 dmhd = (struct usb_cdc_dmm_desc *)buffer;
639 maxcom = le16_to_cpu(dmhd->wMaxCommand);
640 dev_dbg(&intf->dev,
641 "Finding maximum buffer length: %d", maxcom);
642 break;
643 default:
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700644 dev_err(&intf->dev,
645 "Ignoring extra header, type %d, length %d\n",
Oliver Neukumafba9372008-05-13 17:01:25 +0200646 buffer[2], buffer[0]);
647 break;
648 }
649next_desc:
650 buflen -= buffer[0];
651 buffer += buffer[0];
652 }
653
654 rv = -ENOMEM;
655 desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
656 if (!desc)
657 goto out;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100658 mutex_init(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200659 spin_lock_init(&desc->iuspin);
660 init_waitqueue_head(&desc->wait);
661 desc->wMaxCommand = maxcom;
Oliver Neukum052fbc02009-04-20 17:24:49 +0200662 /* this will be expanded and needed in hardware endianness */
Oliver Neukumafba9372008-05-13 17:01:25 +0200663 desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
664 desc->intf = intf;
665 INIT_WORK(&desc->rxwork, wdm_rxwork);
666
Oliver Neukum052fbc02009-04-20 17:24:49 +0200667 rv = -EINVAL;
668 iface = intf->cur_altsetting;
669 if (iface->desc.bNumEndpoints != 1)
Oliver Neukumafba9372008-05-13 17:01:25 +0200670 goto err;
Oliver Neukum052fbc02009-04-20 17:24:49 +0200671 ep = &iface->endpoint[0].desc;
672 if (!ep || !usb_endpoint_is_int_in(ep))
673 goto err;
Oliver Neukumafba9372008-05-13 17:01:25 +0200674
Kuninori Morimoto29cc8892011-08-23 03:12:03 -0700675 desc->wMaxPacketSize = usb_endpoint_maxp(ep);
Oliver Neukumafba9372008-05-13 17:01:25 +0200676
677 desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
678 if (!desc->orq)
679 goto err;
680 desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
681 if (!desc->irq)
682 goto err;
683
684 desc->validity = usb_alloc_urb(0, GFP_KERNEL);
685 if (!desc->validity)
686 goto err;
687
688 desc->response = usb_alloc_urb(0, GFP_KERNEL);
689 if (!desc->response)
690 goto err;
691
692 desc->command = usb_alloc_urb(0, GFP_KERNEL);
693 if (!desc->command)
694 goto err;
695
696 desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
697 if (!desc->ubuf)
698 goto err;
699
Bjørn Mork8457d992012-01-16 15:12:00 +0100700 desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
Oliver Neukumafba9372008-05-13 17:01:25 +0200701 if (!desc->sbuf)
702 goto err;
703
Bjørn Mork8457d992012-01-16 15:12:00 +0100704 desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
Oliver Neukumafba9372008-05-13 17:01:25 +0200705 if (!desc->inbuf)
Bjørn Mork8457d992012-01-16 15:12:00 +0100706 goto err;
Oliver Neukumafba9372008-05-13 17:01:25 +0200707
708 usb_fill_int_urb(
709 desc->validity,
710 interface_to_usbdev(intf),
711 usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
712 desc->sbuf,
713 desc->wMaxPacketSize,
714 wdm_int_callback,
715 desc,
716 ep->bInterval
717 );
Oliver Neukumafba9372008-05-13 17:01:25 +0200718
Bjørn Mork19b85b32012-01-16 15:11:58 +0100719 desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
720 desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
721 desc->irq->wValue = 0;
722 desc->irq->wIndex = desc->inum;
723 desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
724
725 usb_fill_control_urb(
726 desc->response,
Bjørn Mork8143a892012-01-16 15:12:01 +0100727 interface_to_usbdev(intf),
Bjørn Mork19b85b32012-01-16 15:11:58 +0100728 /* using common endpoint 0 */
729 usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
730 (unsigned char *)desc->irq,
731 desc->inbuf,
732 desc->wMaxCommand,
733 wdm_in_callback,
734 desc
735 );
Bjørn Mork19b85b32012-01-16 15:11:58 +0100736
Oliver Neukumafba9372008-05-13 17:01:25 +0200737 usb_set_intfdata(intf, desc);
738 rv = usb_register_dev(intf, &wdm_class);
Oliver Neukumafba9372008-05-13 17:01:25 +0200739 if (rv < 0)
Bjørn Mork8457d992012-01-16 15:12:00 +0100740 goto err2;
Oliver Neukum052fbc02009-04-20 17:24:49 +0200741 else
Bjørn Mork820c6292012-01-20 04:17:25 +0100742 dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
Oliver Neukumafba9372008-05-13 17:01:25 +0200743out:
744 return rv;
745err2:
Bjørn Mork8457d992012-01-16 15:12:00 +0100746 usb_set_intfdata(intf, NULL);
Oliver Neukumafba9372008-05-13 17:01:25 +0200747err:
748 free_urbs(desc);
Bjørn Mork8457d992012-01-16 15:12:00 +0100749 kfree(desc->inbuf);
750 kfree(desc->sbuf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200751 kfree(desc->ubuf);
752 kfree(desc->orq);
753 kfree(desc->irq);
754 kfree(desc);
755 return rv;
756}
757
758static void wdm_disconnect(struct usb_interface *intf)
759{
760 struct wdm_device *desc;
761 unsigned long flags;
762
763 usb_deregister_dev(intf, &wdm_class);
764 mutex_lock(&wdm_mutex);
765 desc = usb_get_intfdata(intf);
766
767 /* the spinlock makes sure no new urbs are generated in the callbacks */
768 spin_lock_irqsave(&desc->iuspin, flags);
769 set_bit(WDM_DISCONNECTING, &desc->flags);
770 set_bit(WDM_READ, &desc->flags);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200771 /* to terminate pending flushes */
Oliver Neukumafba9372008-05-13 17:01:25 +0200772 clear_bit(WDM_IN_USE, &desc->flags);
773 spin_unlock_irqrestore(&desc->iuspin, flags);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100774 mutex_lock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200775 kill_urbs(desc);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100776 cancel_work_sync(&desc->rxwork);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100777 mutex_unlock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200778 wake_up_all(&desc->wait);
779 if (!desc->count)
780 cleanup(desc);
781 mutex_unlock(&wdm_mutex);
782}
783
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100784#ifdef CONFIG_PM
Oliver Neukum17d80d52008-06-24 15:56:10 +0200785static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
786{
787 struct wdm_device *desc = usb_get_intfdata(intf);
788 int rv = 0;
789
790 dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
791
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100792 /* if this is an autosuspend the caller does the locking */
Alan Stern5b1b0b82011-08-19 23:49:48 +0200793 if (!PMSG_IS_AUTO(message))
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100794 mutex_lock(&desc->lock);
Oliver Neukum62e66852010-02-27 20:56:22 +0100795 spin_lock_irq(&desc->iuspin);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100796
Alan Stern5b1b0b82011-08-19 23:49:48 +0200797 if (PMSG_IS_AUTO(message) &&
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100798 (test_bit(WDM_IN_USE, &desc->flags)
799 || test_bit(WDM_RESPONDING, &desc->flags))) {
Oliver Neukum62e66852010-02-27 20:56:22 +0100800 spin_unlock_irq(&desc->iuspin);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200801 rv = -EBUSY;
802 } else {
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100803
Oliver Neukumbeb1d352010-02-27 20:55:52 +0100804 set_bit(WDM_SUSPENDING, &desc->flags);
Oliver Neukum62e66852010-02-27 20:56:22 +0100805 spin_unlock_irq(&desc->iuspin);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100806 /* callback submits work - order is essential */
Oliver Neukum17d80d52008-06-24 15:56:10 +0200807 kill_urbs(desc);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100808 cancel_work_sync(&desc->rxwork);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200809 }
Alan Stern5b1b0b82011-08-19 23:49:48 +0200810 if (!PMSG_IS_AUTO(message))
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100811 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200812
813 return rv;
814}
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100815#endif
Oliver Neukum17d80d52008-06-24 15:56:10 +0200816
817static int recover_from_urb_loss(struct wdm_device *desc)
818{
819 int rv = 0;
820
821 if (desc->count) {
822 rv = usb_submit_urb(desc->validity, GFP_NOIO);
823 if (rv < 0)
Greg Kroah-Hartman9908a32e92008-08-14 09:37:34 -0700824 dev_err(&desc->intf->dev,
825 "Error resume submitting int urb - %d\n", rv);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200826 }
827 return rv;
828}
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100829
830#ifdef CONFIG_PM
Oliver Neukum17d80d52008-06-24 15:56:10 +0200831static int wdm_resume(struct usb_interface *intf)
832{
833 struct wdm_device *desc = usb_get_intfdata(intf);
834 int rv;
835
836 dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
Oliver Neukum338124c2010-02-27 20:57:12 +0100837
Oliver Neukumbeb1d352010-02-27 20:55:52 +0100838 clear_bit(WDM_SUSPENDING, &desc->flags);
Oliver Neukum62e66852010-02-27 20:56:22 +0100839 rv = recover_from_urb_loss(desc);
Oliver Neukum338124c2010-02-27 20:57:12 +0100840
Oliver Neukum17d80d52008-06-24 15:56:10 +0200841 return rv;
842}
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100843#endif
Oliver Neukum17d80d52008-06-24 15:56:10 +0200844
845static int wdm_pre_reset(struct usb_interface *intf)
846{
847 struct wdm_device *desc = usb_get_intfdata(intf);
848
Oliver Neukum860e41a2010-02-27 20:54:24 +0100849 mutex_lock(&desc->lock);
Oliver Neukumd771d8a2011-04-29 14:12:21 +0200850 kill_urbs(desc);
851
852 /*
853 * we notify everybody using poll of
854 * an exceptional situation
855 * must be done before recovery lest a spontaneous
856 * message from the device is lost
857 */
858 spin_lock_irq(&desc->iuspin);
859 desc->rerr = -EINTR;
860 spin_unlock_irq(&desc->iuspin);
861 wake_up_all(&desc->wait);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200862 return 0;
863}
864
865static int wdm_post_reset(struct usb_interface *intf)
866{
867 struct wdm_device *desc = usb_get_intfdata(intf);
868 int rv;
869
870 rv = recover_from_urb_loss(desc);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100871 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200872 return 0;
873}
874
Oliver Neukumafba9372008-05-13 17:01:25 +0200875static struct usb_driver wdm_driver = {
876 .name = "cdc_wdm",
877 .probe = wdm_probe,
878 .disconnect = wdm_disconnect,
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100879#ifdef CONFIG_PM
Oliver Neukum17d80d52008-06-24 15:56:10 +0200880 .suspend = wdm_suspend,
881 .resume = wdm_resume,
882 .reset_resume = wdm_resume,
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100883#endif
Oliver Neukum17d80d52008-06-24 15:56:10 +0200884 .pre_reset = wdm_pre_reset,
885 .post_reset = wdm_post_reset,
Oliver Neukumafba9372008-05-13 17:01:25 +0200886 .id_table = wdm_ids,
Oliver Neukum17d80d52008-06-24 15:56:10 +0200887 .supports_autosuspend = 1,
Oliver Neukumafba9372008-05-13 17:01:25 +0200888};
889
Greg Kroah-Hartman65db4302011-11-18 09:34:02 -0800890module_usb_driver(wdm_driver);
Oliver Neukumafba9372008-05-13 17:01:25 +0200891
892MODULE_AUTHOR(DRIVER_AUTHOR);
Oliver Neukum87d65e52008-06-19 14:20:18 +0200893MODULE_DESCRIPTION(DRIVER_DESC);
Oliver Neukumafba9372008-05-13 17:01:25 +0200894MODULE_LICENSE("GPL");