blob: c154d4f1d67465c0e0e41964109ed77eac565517 [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
Németh Márton6ef48522010-01-10 15:33:45 +010034static const struct usb_device_id wdm_ids[] = {
Oliver Neukumafba9372008-05-13 17:01:25 +020035 {
36 .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
37 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
38 .bInterfaceClass = USB_CLASS_COMM,
39 .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
40 },
41 { }
42};
43
Oliver Neukumaa5380b2008-10-13 14:05:20 +020044MODULE_DEVICE_TABLE (usb, wdm_ids);
45
Oliver Neukumafba9372008-05-13 17:01:25 +020046#define WDM_MINOR_BASE 176
47
48
49#define WDM_IN_USE 1
50#define WDM_DISCONNECTING 2
51#define WDM_RESULT 3
52#define WDM_READ 4
53#define WDM_INT_STALL 5
54#define WDM_POLL_RUNNING 6
Oliver Neukum922a5ea2010-02-27 20:54:59 +010055#define WDM_RESPONDING 7
Oliver Neukumbeb1d352010-02-27 20:55:52 +010056#define WDM_SUSPENDING 8
Oliver Neukumafba9372008-05-13 17:01:25 +020057
58#define WDM_MAX 16
59
Bjørn Mork7e3054a2012-01-20 01:49:57 +010060/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */
61#define WDM_DEFAULT_BUFSIZE 256
Oliver Neukumafba9372008-05-13 17:01:25 +020062
63static DEFINE_MUTEX(wdm_mutex);
64
65/* --- method tables --- */
66
67struct wdm_device {
68 u8 *inbuf; /* buffer for response */
69 u8 *outbuf; /* buffer for command */
70 u8 *sbuf; /* buffer for status */
71 u8 *ubuf; /* buffer for copy to user space */
72
73 struct urb *command;
74 struct urb *response;
75 struct urb *validity;
76 struct usb_interface *intf;
77 struct usb_ctrlrequest *orq;
78 struct usb_ctrlrequest *irq;
79 spinlock_t iuspin;
80
81 unsigned long flags;
82 u16 bufsize;
83 u16 wMaxCommand;
84 u16 wMaxPacketSize;
Oliver Neukumafba9372008-05-13 17:01:25 +020085 __le16 inum;
86 int reslength;
87 int length;
88 int read;
89 int count;
90 dma_addr_t shandle;
91 dma_addr_t ihandle;
Oliver Neukum860e41a2010-02-27 20:54:24 +010092 struct mutex lock;
Oliver Neukumafba9372008-05-13 17:01:25 +020093 wait_queue_head_t wait;
94 struct work_struct rxwork;
95 int werr;
96 int rerr;
97};
98
99static struct usb_driver wdm_driver;
100
101/* --- callbacks --- */
102static void wdm_out_callback(struct urb *urb)
103{
104 struct wdm_device *desc;
105 desc = urb->context;
106 spin_lock(&desc->iuspin);
107 desc->werr = urb->status;
108 spin_unlock(&desc->iuspin);
109 clear_bit(WDM_IN_USE, &desc->flags);
110 kfree(desc->outbuf);
111 wake_up(&desc->wait);
112}
113
114static void wdm_in_callback(struct urb *urb)
115{
116 struct wdm_device *desc = urb->context;
117 int status = urb->status;
118
119 spin_lock(&desc->iuspin);
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100120 clear_bit(WDM_RESPONDING, &desc->flags);
Oliver Neukumafba9372008-05-13 17:01:25 +0200121
122 if (status) {
123 switch (status) {
124 case -ENOENT:
125 dev_dbg(&desc->intf->dev,
126 "nonzero urb status received: -ENOENT");
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100127 goto skip_error;
Oliver Neukumafba9372008-05-13 17:01:25 +0200128 case -ECONNRESET:
129 dev_dbg(&desc->intf->dev,
130 "nonzero urb status received: -ECONNRESET");
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100131 goto skip_error;
Oliver Neukumafba9372008-05-13 17:01:25 +0200132 case -ESHUTDOWN:
133 dev_dbg(&desc->intf->dev,
134 "nonzero urb status received: -ESHUTDOWN");
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100135 goto skip_error;
Oliver Neukumafba9372008-05-13 17:01:25 +0200136 case -EPIPE:
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700137 dev_err(&desc->intf->dev,
138 "nonzero urb status received: -EPIPE\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200139 break;
140 default:
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700141 dev_err(&desc->intf->dev,
142 "Unexpected error %d\n", status);
Oliver Neukumafba9372008-05-13 17:01:25 +0200143 break;
144 }
145 }
146
147 desc->rerr = status;
148 desc->reslength = urb->actual_length;
149 memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
150 desc->length += desc->reslength;
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100151skip_error:
Oliver Neukumafba9372008-05-13 17:01:25 +0200152 wake_up(&desc->wait);
153
154 set_bit(WDM_READ, &desc->flags);
155 spin_unlock(&desc->iuspin);
156}
157
158static void wdm_int_callback(struct urb *urb)
159{
160 int rv = 0;
161 int status = urb->status;
162 struct wdm_device *desc;
Oliver Neukumafba9372008-05-13 17:01:25 +0200163 struct usb_cdc_notification *dr;
164
165 desc = urb->context;
Oliver Neukumafba9372008-05-13 17:01:25 +0200166 dr = (struct usb_cdc_notification *)desc->sbuf;
167
168 if (status) {
169 switch (status) {
170 case -ESHUTDOWN:
171 case -ENOENT:
172 case -ECONNRESET:
173 return; /* unplug */
174 case -EPIPE:
175 set_bit(WDM_INT_STALL, &desc->flags);
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700176 dev_err(&desc->intf->dev, "Stall on int endpoint\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200177 goto sw; /* halt is cleared in work */
178 default:
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700179 dev_err(&desc->intf->dev,
180 "nonzero urb status received: %d\n", status);
Oliver Neukumafba9372008-05-13 17:01:25 +0200181 break;
182 }
183 }
184
185 if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700186 dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
187 urb->actual_length);
Oliver Neukumafba9372008-05-13 17:01:25 +0200188 goto exit;
189 }
190
191 switch (dr->bNotificationType) {
192 case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
193 dev_dbg(&desc->intf->dev,
194 "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
195 dr->wIndex, dr->wLength);
196 break;
197
198 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
199
200 dev_dbg(&desc->intf->dev,
201 "NOTIFY_NETWORK_CONNECTION %s network",
202 dr->wValue ? "connected to" : "disconnected from");
203 goto exit;
204 default:
205 clear_bit(WDM_POLL_RUNNING, &desc->flags);
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700206 dev_err(&desc->intf->dev,
207 "unknown notification %d received: index %d len %d\n",
Oliver Neukumafba9372008-05-13 17:01:25 +0200208 dr->bNotificationType, dr->wIndex, dr->wLength);
209 goto exit;
210 }
211
Oliver Neukumafba9372008-05-13 17:01:25 +0200212 spin_lock(&desc->iuspin);
213 clear_bit(WDM_READ, &desc->flags);
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100214 set_bit(WDM_RESPONDING, &desc->flags);
Oliver Neukumbeb1d352010-02-27 20:55:52 +0100215 if (!test_bit(WDM_DISCONNECTING, &desc->flags)
216 && !test_bit(WDM_SUSPENDING, &desc->flags)) {
Oliver Neukumafba9372008-05-13 17:01:25 +0200217 rv = usb_submit_urb(desc->response, GFP_ATOMIC);
218 dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
219 __func__, rv);
220 }
221 spin_unlock(&desc->iuspin);
222 if (rv < 0) {
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100223 clear_bit(WDM_RESPONDING, &desc->flags);
Oliver Neukumafba9372008-05-13 17:01:25 +0200224 if (rv == -EPERM)
225 return;
226 if (rv == -ENOMEM) {
227sw:
228 rv = schedule_work(&desc->rxwork);
229 if (rv)
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700230 dev_err(&desc->intf->dev,
231 "Cannot schedule work\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200232 }
233 }
234exit:
235 rv = usb_submit_urb(urb, GFP_ATOMIC);
236 if (rv)
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700237 dev_err(&desc->intf->dev,
238 "%s - usb_submit_urb failed with result %d\n",
239 __func__, rv);
Oliver Neukumafba9372008-05-13 17:01:25 +0200240
241}
242
243static void kill_urbs(struct wdm_device *desc)
244{
Oliver Neukum17d80d52008-06-24 15:56:10 +0200245 /* the order here is essential */
Oliver Neukumafba9372008-05-13 17:01:25 +0200246 usb_kill_urb(desc->command);
247 usb_kill_urb(desc->validity);
248 usb_kill_urb(desc->response);
249}
250
251static void free_urbs(struct wdm_device *desc)
252{
253 usb_free_urb(desc->validity);
254 usb_free_urb(desc->response);
255 usb_free_urb(desc->command);
256}
257
258static void cleanup(struct wdm_device *desc)
259{
Bjørn Mork8457d992012-01-16 15:12:00 +0100260 kfree(desc->sbuf);
261 kfree(desc->inbuf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200262 kfree(desc->orq);
263 kfree(desc->irq);
264 kfree(desc->ubuf);
265 free_urbs(desc);
266 kfree(desc);
267}
268
269static ssize_t wdm_write
270(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
271{
272 u8 *buf;
273 int rv = -EMSGSIZE, r, we;
274 struct wdm_device *desc = file->private_data;
275 struct usb_ctrlrequest *req;
276
277 if (count > desc->wMaxCommand)
278 count = desc->wMaxCommand;
279
280 spin_lock_irq(&desc->iuspin);
281 we = desc->werr;
282 desc->werr = 0;
283 spin_unlock_irq(&desc->iuspin);
284 if (we < 0)
285 return -EIO;
286
Oliver Neukum860e41a2010-02-27 20:54:24 +0100287 desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
288 if (!buf) {
289 rv = -ENOMEM;
Oliver Neukumafba9372008-05-13 17:01:25 +0200290 goto outnl;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100291 }
292
293 r = copy_from_user(buf, buffer, count);
294 if (r > 0) {
295 kfree(buf);
296 rv = -EFAULT;
297 goto outnl;
298 }
299
300 /* concurrent writes and disconnect */
301 r = mutex_lock_interruptible(&desc->lock);
302 rv = -ERESTARTSYS;
303 if (r) {
304 kfree(buf);
305 goto outnl;
306 }
307
308 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
309 kfree(buf);
310 rv = -ENODEV;
311 goto outnp;
312 }
Oliver Neukumafba9372008-05-13 17:01:25 +0200313
Oliver Neukum17d80d52008-06-24 15:56:10 +0200314 r = usb_autopm_get_interface(desc->intf);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100315 if (r < 0) {
316 kfree(buf);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200317 goto outnp;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100318 }
Oliver Neukum7f1dc313d2009-09-09 10:12:48 +0200319
David Sterba0cdfb812010-12-27 18:49:58 +0100320 if (!(file->f_flags & O_NONBLOCK))
Oliver Neukum7f1dc313d2009-09-09 10:12:48 +0200321 r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
322 &desc->flags));
323 else
324 if (test_bit(WDM_IN_USE, &desc->flags))
325 r = -EAGAIN;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100326 if (r < 0) {
Oliver Neukumafba9372008-05-13 17:01:25 +0200327 kfree(buf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200328 goto out;
329 }
330
331 req = desc->orq;
332 usb_fill_control_urb(
333 desc->command,
334 interface_to_usbdev(desc->intf),
335 /* using common endpoint 0 */
336 usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
337 (unsigned char *)req,
338 buf,
339 count,
340 wdm_out_callback,
341 desc
342 );
343
344 req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
345 USB_RECIP_INTERFACE);
346 req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
347 req->wValue = 0;
348 req->wIndex = desc->inum;
349 req->wLength = cpu_to_le16(count);
350 set_bit(WDM_IN_USE, &desc->flags);
351
352 rv = usb_submit_urb(desc->command, GFP_KERNEL);
353 if (rv < 0) {
354 kfree(buf);
355 clear_bit(WDM_IN_USE, &desc->flags);
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700356 dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
Oliver Neukumafba9372008-05-13 17:01:25 +0200357 } else {
358 dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
359 req->wIndex);
360 }
361out:
Oliver Neukum17d80d52008-06-24 15:56:10 +0200362 usb_autopm_put_interface(desc->intf);
363outnp:
Oliver Neukum860e41a2010-02-27 20:54:24 +0100364 mutex_unlock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200365outnl:
366 return rv < 0 ? rv : count;
367}
368
369static ssize_t wdm_read
370(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
371{
Oliver Neukum7f1dc313d2009-09-09 10:12:48 +0200372 int rv, cntr = 0;
Oliver Neukumafba9372008-05-13 17:01:25 +0200373 int i = 0;
374 struct wdm_device *desc = file->private_data;
375
376
Oliver Neukum860e41a2010-02-27 20:54:24 +0100377 rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */
Oliver Neukumafba9372008-05-13 17:01:25 +0200378 if (rv < 0)
379 return -ERESTARTSYS;
380
381 if (desc->length == 0) {
382 desc->read = 0;
383retry:
Oliver Neukum7f1dc313d2009-09-09 10:12:48 +0200384 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
385 rv = -ENODEV;
386 goto err;
387 }
Oliver Neukumafba9372008-05-13 17:01:25 +0200388 i++;
Oliver Neukum7f1dc313d2009-09-09 10:12:48 +0200389 if (file->f_flags & O_NONBLOCK) {
390 if (!test_bit(WDM_READ, &desc->flags)) {
391 rv = cntr ? cntr : -EAGAIN;
392 goto err;
393 }
394 rv = 0;
395 } else {
396 rv = wait_event_interruptible(desc->wait,
397 test_bit(WDM_READ, &desc->flags));
398 }
Oliver Neukumafba9372008-05-13 17:01:25 +0200399
Oliver Neukum7f1dc313d2009-09-09 10:12:48 +0200400 /* may have happened while we slept */
Oliver Neukum17d80d52008-06-24 15:56:10 +0200401 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
402 rv = -ENODEV;
403 goto err;
404 }
405 usb_mark_last_busy(interface_to_usbdev(desc->intf));
Oliver Neukumafba9372008-05-13 17:01:25 +0200406 if (rv < 0) {
407 rv = -ERESTARTSYS;
408 goto err;
409 }
410
411 spin_lock_irq(&desc->iuspin);
412
413 if (desc->rerr) { /* read completed, error happened */
Oliver Neukumafba9372008-05-13 17:01:25 +0200414 desc->rerr = 0;
415 spin_unlock_irq(&desc->iuspin);
Oliver Neukumafba9372008-05-13 17:01:25 +0200416 rv = -EIO;
417 goto err;
418 }
419 /*
420 * recheck whether we've lost the race
421 * against the completion handler
422 */
423 if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
424 spin_unlock_irq(&desc->iuspin);
425 goto retry;
426 }
427 if (!desc->reslength) { /* zero length read */
428 spin_unlock_irq(&desc->iuspin);
429 goto retry;
430 }
431 clear_bit(WDM_READ, &desc->flags);
432 spin_unlock_irq(&desc->iuspin);
433 }
434
435 cntr = count > desc->length ? desc->length : count;
436 rv = copy_to_user(buffer, desc->ubuf, cntr);
437 if (rv > 0) {
438 rv = -EFAULT;
439 goto err;
440 }
441
442 for (i = 0; i < desc->length - cntr; i++)
443 desc->ubuf[i] = desc->ubuf[i + cntr];
444
445 desc->length -= cntr;
Oliver Neukum87d65e52008-06-19 14:20:18 +0200446 /* in case we had outstanding data */
447 if (!desc->length)
448 clear_bit(WDM_READ, &desc->flags);
Oliver Neukumafba9372008-05-13 17:01:25 +0200449 rv = cntr;
450
451err:
Oliver Neukum860e41a2010-02-27 20:54:24 +0100452 mutex_unlock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200453 return rv;
454}
455
456static int wdm_flush(struct file *file, fl_owner_t id)
457{
458 struct wdm_device *desc = file->private_data;
459
460 wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
461 if (desc->werr < 0)
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700462 dev_err(&desc->intf->dev, "Error in flush path: %d\n",
463 desc->werr);
Oliver Neukumafba9372008-05-13 17:01:25 +0200464
465 return desc->werr;
466}
467
468static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
469{
470 struct wdm_device *desc = file->private_data;
471 unsigned long flags;
472 unsigned int mask = 0;
473
474 spin_lock_irqsave(&desc->iuspin, flags);
475 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
476 mask = POLLERR;
477 spin_unlock_irqrestore(&desc->iuspin, flags);
478 goto desc_out;
479 }
480 if (test_bit(WDM_READ, &desc->flags))
481 mask = POLLIN | POLLRDNORM;
482 if (desc->rerr || desc->werr)
483 mask |= POLLERR;
484 if (!test_bit(WDM_IN_USE, &desc->flags))
485 mask |= POLLOUT | POLLWRNORM;
486 spin_unlock_irqrestore(&desc->iuspin, flags);
487
488 poll_wait(file, &desc->wait, wait);
489
490desc_out:
491 return mask;
492}
493
494static int wdm_open(struct inode *inode, struct file *file)
495{
496 int minor = iminor(inode);
497 int rv = -ENODEV;
498 struct usb_interface *intf;
499 struct wdm_device *desc;
500
501 mutex_lock(&wdm_mutex);
502 intf = usb_find_interface(&wdm_driver, minor);
503 if (!intf)
504 goto out;
505
506 desc = usb_get_intfdata(intf);
507 if (test_bit(WDM_DISCONNECTING, &desc->flags))
508 goto out;
Oliver Neukumafba9372008-05-13 17:01:25 +0200509 file->private_data = desc;
510
Oliver Neukum17d80d52008-06-24 15:56:10 +0200511 rv = usb_autopm_get_interface(desc->intf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200512 if (rv < 0) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700513 dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
Oliver Neukumafba9372008-05-13 17:01:25 +0200514 goto out;
515 }
Oliver Neukum17d80d52008-06-24 15:56:10 +0200516 intf->needs_remote_wakeup = 1;
Oliver Neukumafba9372008-05-13 17:01:25 +0200517
Oliver Neukum860e41a2010-02-27 20:54:24 +0100518 mutex_lock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200519 if (!desc->count++) {
Oliver Neukumd771d8a2011-04-29 14:12:21 +0200520 desc->werr = 0;
521 desc->rerr = 0;
Oliver Neukum17d80d52008-06-24 15:56:10 +0200522 rv = usb_submit_urb(desc->validity, GFP_KERNEL);
523 if (rv < 0) {
524 desc->count--;
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700525 dev_err(&desc->intf->dev,
526 "Error submitting int urb - %d\n", rv);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200527 }
528 } else {
529 rv = 0;
530 }
Oliver Neukum860e41a2010-02-27 20:54:24 +0100531 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200532 usb_autopm_put_interface(desc->intf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200533out:
534 mutex_unlock(&wdm_mutex);
535 return rv;
536}
537
538static int wdm_release(struct inode *inode, struct file *file)
539{
540 struct wdm_device *desc = file->private_data;
541
542 mutex_lock(&wdm_mutex);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100543 mutex_lock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200544 desc->count--;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100545 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200546
Oliver Neukumafba9372008-05-13 17:01:25 +0200547 if (!desc->count) {
548 dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
549 kill_urbs(desc);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200550 if (!test_bit(WDM_DISCONNECTING, &desc->flags))
551 desc->intf->needs_remote_wakeup = 0;
Oliver Neukumafba9372008-05-13 17:01:25 +0200552 }
553 mutex_unlock(&wdm_mutex);
554 return 0;
555}
556
557static const struct file_operations wdm_fops = {
558 .owner = THIS_MODULE,
559 .read = wdm_read,
560 .write = wdm_write,
561 .open = wdm_open,
562 .flush = wdm_flush,
563 .release = wdm_release,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200564 .poll = wdm_poll,
565 .llseek = noop_llseek,
Oliver Neukumafba9372008-05-13 17:01:25 +0200566};
567
568static struct usb_class_driver wdm_class = {
569 .name = "cdc-wdm%d",
570 .fops = &wdm_fops,
571 .minor_base = WDM_MINOR_BASE,
572};
573
574/* --- error handling --- */
575static void wdm_rxwork(struct work_struct *work)
576{
577 struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
578 unsigned long flags;
579 int rv;
580
581 spin_lock_irqsave(&desc->iuspin, flags);
582 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
583 spin_unlock_irqrestore(&desc->iuspin, flags);
584 } else {
585 spin_unlock_irqrestore(&desc->iuspin, flags);
586 rv = usb_submit_urb(desc->response, GFP_KERNEL);
587 if (rv < 0 && rv != -EPERM) {
588 spin_lock_irqsave(&desc->iuspin, flags);
589 if (!test_bit(WDM_DISCONNECTING, &desc->flags))
590 schedule_work(&desc->rxwork);
591 spin_unlock_irqrestore(&desc->iuspin, flags);
592 }
593 }
594}
595
596/* --- hotplug --- */
597
598static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
599{
600 int rv = -EINVAL;
Oliver Neukumafba9372008-05-13 17:01:25 +0200601 struct wdm_device *desc;
602 struct usb_host_interface *iface;
603 struct usb_endpoint_descriptor *ep;
604 struct usb_cdc_dmm_desc *dmhd;
605 u8 *buffer = intf->altsetting->extra;
606 int buflen = intf->altsetting->extralen;
Bjørn Mork7e3054a2012-01-20 01:49:57 +0100607 u16 maxcom = WDM_DEFAULT_BUFSIZE;
Oliver Neukumafba9372008-05-13 17:01:25 +0200608
609 if (!buffer)
610 goto out;
611
Oliver Neukum052fbc02009-04-20 17:24:49 +0200612 while (buflen > 2) {
Oliver Neukumafba9372008-05-13 17:01:25 +0200613 if (buffer [1] != USB_DT_CS_INTERFACE) {
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700614 dev_err(&intf->dev, "skipping garbage\n");
Oliver Neukumafba9372008-05-13 17:01:25 +0200615 goto next_desc;
616 }
617
618 switch (buffer [2]) {
619 case USB_CDC_HEADER_TYPE:
620 break;
621 case USB_CDC_DMM_TYPE:
622 dmhd = (struct usb_cdc_dmm_desc *)buffer;
623 maxcom = le16_to_cpu(dmhd->wMaxCommand);
624 dev_dbg(&intf->dev,
625 "Finding maximum buffer length: %d", maxcom);
626 break;
627 default:
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700628 dev_err(&intf->dev,
629 "Ignoring extra header, type %d, length %d\n",
Oliver Neukumafba9372008-05-13 17:01:25 +0200630 buffer[2], buffer[0]);
631 break;
632 }
633next_desc:
634 buflen -= buffer[0];
635 buffer += buffer[0];
636 }
637
638 rv = -ENOMEM;
639 desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
640 if (!desc)
641 goto out;
Oliver Neukum860e41a2010-02-27 20:54:24 +0100642 mutex_init(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200643 spin_lock_init(&desc->iuspin);
644 init_waitqueue_head(&desc->wait);
645 desc->wMaxCommand = maxcom;
Oliver Neukum052fbc02009-04-20 17:24:49 +0200646 /* this will be expanded and needed in hardware endianness */
Oliver Neukumafba9372008-05-13 17:01:25 +0200647 desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
648 desc->intf = intf;
649 INIT_WORK(&desc->rxwork, wdm_rxwork);
650
Oliver Neukum052fbc02009-04-20 17:24:49 +0200651 rv = -EINVAL;
652 iface = intf->cur_altsetting;
653 if (iface->desc.bNumEndpoints != 1)
Oliver Neukumafba9372008-05-13 17:01:25 +0200654 goto err;
Oliver Neukum052fbc02009-04-20 17:24:49 +0200655 ep = &iface->endpoint[0].desc;
656 if (!ep || !usb_endpoint_is_int_in(ep))
657 goto err;
Oliver Neukumafba9372008-05-13 17:01:25 +0200658
Kuninori Morimoto29cc8892011-08-23 03:12:03 -0700659 desc->wMaxPacketSize = usb_endpoint_maxp(ep);
Oliver Neukumafba9372008-05-13 17:01:25 +0200660
661 desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
662 if (!desc->orq)
663 goto err;
664 desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
665 if (!desc->irq)
666 goto err;
667
668 desc->validity = usb_alloc_urb(0, GFP_KERNEL);
669 if (!desc->validity)
670 goto err;
671
672 desc->response = usb_alloc_urb(0, GFP_KERNEL);
673 if (!desc->response)
674 goto err;
675
676 desc->command = usb_alloc_urb(0, GFP_KERNEL);
677 if (!desc->command)
678 goto err;
679
680 desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
681 if (!desc->ubuf)
682 goto err;
683
Bjørn Mork8457d992012-01-16 15:12:00 +0100684 desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
Oliver Neukumafba9372008-05-13 17:01:25 +0200685 if (!desc->sbuf)
686 goto err;
687
Bjørn Mork8457d992012-01-16 15:12:00 +0100688 desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
Oliver Neukumafba9372008-05-13 17:01:25 +0200689 if (!desc->inbuf)
Bjørn Mork8457d992012-01-16 15:12:00 +0100690 goto err;
Oliver Neukumafba9372008-05-13 17:01:25 +0200691
692 usb_fill_int_urb(
693 desc->validity,
694 interface_to_usbdev(intf),
695 usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
696 desc->sbuf,
697 desc->wMaxPacketSize,
698 wdm_int_callback,
699 desc,
700 ep->bInterval
701 );
Oliver Neukumafba9372008-05-13 17:01:25 +0200702
Bjørn Mork19b85b32012-01-16 15:11:58 +0100703 desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
704 desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
705 desc->irq->wValue = 0;
706 desc->irq->wIndex = desc->inum;
707 desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
708
709 usb_fill_control_urb(
710 desc->response,
Bjørn Mork8143a892012-01-16 15:12:01 +0100711 interface_to_usbdev(intf),
Bjørn Mork19b85b32012-01-16 15:11:58 +0100712 /* using common endpoint 0 */
713 usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
714 (unsigned char *)desc->irq,
715 desc->inbuf,
716 desc->wMaxCommand,
717 wdm_in_callback,
718 desc
719 );
Bjørn Mork19b85b32012-01-16 15:11:58 +0100720
Oliver Neukumafba9372008-05-13 17:01:25 +0200721 usb_set_intfdata(intf, desc);
722 rv = usb_register_dev(intf, &wdm_class);
Oliver Neukumafba9372008-05-13 17:01:25 +0200723 if (rv < 0)
Bjørn Mork8457d992012-01-16 15:12:00 +0100724 goto err2;
Oliver Neukum052fbc02009-04-20 17:24:49 +0200725 else
Bjørn Mork820c6292012-01-20 04:17:25 +0100726 dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
Oliver Neukumafba9372008-05-13 17:01:25 +0200727out:
728 return rv;
729err2:
Bjørn Mork8457d992012-01-16 15:12:00 +0100730 usb_set_intfdata(intf, NULL);
Oliver Neukumafba9372008-05-13 17:01:25 +0200731err:
732 free_urbs(desc);
Bjørn Mork8457d992012-01-16 15:12:00 +0100733 kfree(desc->inbuf);
734 kfree(desc->sbuf);
Oliver Neukumafba9372008-05-13 17:01:25 +0200735 kfree(desc->ubuf);
736 kfree(desc->orq);
737 kfree(desc->irq);
738 kfree(desc);
739 return rv;
740}
741
742static void wdm_disconnect(struct usb_interface *intf)
743{
744 struct wdm_device *desc;
745 unsigned long flags;
746
747 usb_deregister_dev(intf, &wdm_class);
748 mutex_lock(&wdm_mutex);
749 desc = usb_get_intfdata(intf);
750
751 /* the spinlock makes sure no new urbs are generated in the callbacks */
752 spin_lock_irqsave(&desc->iuspin, flags);
753 set_bit(WDM_DISCONNECTING, &desc->flags);
754 set_bit(WDM_READ, &desc->flags);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200755 /* to terminate pending flushes */
Oliver Neukumafba9372008-05-13 17:01:25 +0200756 clear_bit(WDM_IN_USE, &desc->flags);
757 spin_unlock_irqrestore(&desc->iuspin, flags);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100758 mutex_lock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200759 kill_urbs(desc);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100760 cancel_work_sync(&desc->rxwork);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100761 mutex_unlock(&desc->lock);
Oliver Neukumafba9372008-05-13 17:01:25 +0200762 wake_up_all(&desc->wait);
763 if (!desc->count)
764 cleanup(desc);
765 mutex_unlock(&wdm_mutex);
766}
767
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100768#ifdef CONFIG_PM
Oliver Neukum17d80d52008-06-24 15:56:10 +0200769static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
770{
771 struct wdm_device *desc = usb_get_intfdata(intf);
772 int rv = 0;
773
774 dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
775
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100776 /* if this is an autosuspend the caller does the locking */
Alan Stern5b1b0b82011-08-19 23:49:48 +0200777 if (!PMSG_IS_AUTO(message))
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100778 mutex_lock(&desc->lock);
Oliver Neukum62e66852010-02-27 20:56:22 +0100779 spin_lock_irq(&desc->iuspin);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100780
Alan Stern5b1b0b82011-08-19 23:49:48 +0200781 if (PMSG_IS_AUTO(message) &&
Oliver Neukum922a5ea2010-02-27 20:54:59 +0100782 (test_bit(WDM_IN_USE, &desc->flags)
783 || test_bit(WDM_RESPONDING, &desc->flags))) {
Oliver Neukum62e66852010-02-27 20:56:22 +0100784 spin_unlock_irq(&desc->iuspin);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200785 rv = -EBUSY;
786 } else {
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100787
Oliver Neukumbeb1d352010-02-27 20:55:52 +0100788 set_bit(WDM_SUSPENDING, &desc->flags);
Oliver Neukum62e66852010-02-27 20:56:22 +0100789 spin_unlock_irq(&desc->iuspin);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100790 /* callback submits work - order is essential */
Oliver Neukum17d80d52008-06-24 15:56:10 +0200791 kill_urbs(desc);
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100792 cancel_work_sync(&desc->rxwork);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200793 }
Alan Stern5b1b0b82011-08-19 23:49:48 +0200794 if (!PMSG_IS_AUTO(message))
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100795 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200796
797 return rv;
798}
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100799#endif
Oliver Neukum17d80d52008-06-24 15:56:10 +0200800
801static int recover_from_urb_loss(struct wdm_device *desc)
802{
803 int rv = 0;
804
805 if (desc->count) {
806 rv = usb_submit_urb(desc->validity, GFP_NOIO);
807 if (rv < 0)
Greg Kroah-Hartman9908a322008-08-14 09:37:34 -0700808 dev_err(&desc->intf->dev,
809 "Error resume submitting int urb - %d\n", rv);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200810 }
811 return rv;
812}
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100813
814#ifdef CONFIG_PM
Oliver Neukum17d80d52008-06-24 15:56:10 +0200815static int wdm_resume(struct usb_interface *intf)
816{
817 struct wdm_device *desc = usb_get_intfdata(intf);
818 int rv;
819
820 dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
Oliver Neukum338124c2010-02-27 20:57:12 +0100821
Oliver Neukumbeb1d352010-02-27 20:55:52 +0100822 clear_bit(WDM_SUSPENDING, &desc->flags);
Oliver Neukum62e66852010-02-27 20:56:22 +0100823 rv = recover_from_urb_loss(desc);
Oliver Neukum338124c2010-02-27 20:57:12 +0100824
Oliver Neukum17d80d52008-06-24 15:56:10 +0200825 return rv;
826}
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100827#endif
Oliver Neukum17d80d52008-06-24 15:56:10 +0200828
829static int wdm_pre_reset(struct usb_interface *intf)
830{
831 struct wdm_device *desc = usb_get_intfdata(intf);
832
Oliver Neukum860e41a2010-02-27 20:54:24 +0100833 mutex_lock(&desc->lock);
Oliver Neukumd771d8a2011-04-29 14:12:21 +0200834 kill_urbs(desc);
835
836 /*
837 * we notify everybody using poll of
838 * an exceptional situation
839 * must be done before recovery lest a spontaneous
840 * message from the device is lost
841 */
842 spin_lock_irq(&desc->iuspin);
843 desc->rerr = -EINTR;
844 spin_unlock_irq(&desc->iuspin);
845 wake_up_all(&desc->wait);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200846 return 0;
847}
848
849static int wdm_post_reset(struct usb_interface *intf)
850{
851 struct wdm_device *desc = usb_get_intfdata(intf);
852 int rv;
853
854 rv = recover_from_urb_loss(desc);
Oliver Neukum860e41a2010-02-27 20:54:24 +0100855 mutex_unlock(&desc->lock);
Oliver Neukum17d80d52008-06-24 15:56:10 +0200856 return 0;
857}
858
Oliver Neukumafba9372008-05-13 17:01:25 +0200859static struct usb_driver wdm_driver = {
860 .name = "cdc_wdm",
861 .probe = wdm_probe,
862 .disconnect = wdm_disconnect,
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100863#ifdef CONFIG_PM
Oliver Neukum17d80d52008-06-24 15:56:10 +0200864 .suspend = wdm_suspend,
865 .resume = wdm_resume,
866 .reset_resume = wdm_resume,
Oliver Neukumd93d16e2010-02-27 20:56:47 +0100867#endif
Oliver Neukum17d80d52008-06-24 15:56:10 +0200868 .pre_reset = wdm_pre_reset,
869 .post_reset = wdm_post_reset,
Oliver Neukumafba9372008-05-13 17:01:25 +0200870 .id_table = wdm_ids,
Oliver Neukum17d80d52008-06-24 15:56:10 +0200871 .supports_autosuspend = 1,
Oliver Neukumafba9372008-05-13 17:01:25 +0200872};
873
Greg Kroah-Hartman65db4302011-11-18 09:34:02 -0800874module_usb_driver(wdm_driver);
Oliver Neukumafba9372008-05-13 17:01:25 +0200875
876MODULE_AUTHOR(DRIVER_AUTHOR);
Oliver Neukum87d65e52008-06-19 14:20:18 +0200877MODULE_DESCRIPTION(DRIVER_DESC);
Oliver Neukumafba9372008-05-13 17:01:25 +0200878MODULE_LICENSE("GPL");