1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * cdc-wdm.c |
4 | * |
5 | * This driver supports USB CDC WCM Device Management. |
6 | * |
7 | * Copyright (c) 2007-2009 Oliver Neukum |
8 | * |
9 | * Some code taken from cdc-acm.c |
10 | * |
11 | * Released under the GPLv2. |
12 | * |
13 | * Many thanks to Carl Nordbeck |
14 | */ |
15 | #include <linux/kernel.h> |
16 | #include <linux/errno.h> |
17 | #include <linux/ioctl.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/module.h> |
20 | #include <linux/mutex.h> |
21 | #include <linux/uaccess.h> |
22 | #include <linux/bitops.h> |
23 | #include <linux/poll.h> |
24 | #include <linux/skbuff.h> |
25 | #include <linux/usb.h> |
26 | #include <linux/usb/cdc.h> |
27 | #include <linux/wwan.h> |
28 | #include <asm/byteorder.h> |
29 | #include <asm/unaligned.h> |
30 | #include <linux/usb/cdc-wdm.h> |
31 | |
32 | #define DRIVER_AUTHOR "Oliver Neukum" |
33 | #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" |
34 | |
35 | static const struct usb_device_id wdm_ids[] = { |
36 | { |
37 | .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | |
38 | USB_DEVICE_ID_MATCH_INT_SUBCLASS, |
39 | .bInterfaceClass = USB_CLASS_COMM, |
40 | .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM |
41 | }, |
42 | { } |
43 | }; |
44 | |
45 | MODULE_DEVICE_TABLE (usb, wdm_ids); |
46 | |
47 | #define WDM_MINOR_BASE 176 |
48 | |
49 | |
50 | #define WDM_IN_USE 1 |
51 | #define WDM_DISCONNECTING 2 |
52 | #define WDM_RESULT 3 |
53 | #define WDM_READ 4 |
54 | #define WDM_INT_STALL 5 |
55 | #define WDM_POLL_RUNNING 6 |
56 | #define WDM_RESPONDING 7 |
57 | #define WDM_SUSPENDING 8 |
58 | #define WDM_RESETTING 9 |
59 | #define WDM_OVERFLOW 10 |
60 | #define WDM_WWAN_IN_USE 11 |
61 | |
62 | #define WDM_MAX 16 |
63 | |
64 | /* we cannot wait forever at flush() */ |
65 | #define WDM_FLUSH_TIMEOUT (30 * HZ) |
66 | |
67 | /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ |
68 | #define WDM_DEFAULT_BUFSIZE 256 |
69 | |
70 | static DEFINE_MUTEX(wdm_mutex); |
71 | static DEFINE_SPINLOCK(wdm_device_list_lock); |
72 | static LIST_HEAD(wdm_device_list); |
73 | |
74 | /* --- method tables --- */ |
75 | |
76 | struct wdm_device { |
77 | u8 *inbuf; /* buffer for response */ |
78 | u8 *outbuf; /* buffer for command */ |
79 | u8 *sbuf; /* buffer for status */ |
80 | u8 *ubuf; /* buffer for copy to user space */ |
81 | |
82 | struct urb *command; |
83 | struct urb *response; |
84 | struct urb *validity; |
85 | struct usb_interface *intf; |
86 | struct usb_ctrlrequest *orq; |
87 | struct usb_ctrlrequest *irq; |
88 | spinlock_t iuspin; |
89 | |
90 | unsigned long flags; |
91 | u16 bufsize; |
92 | u16 wMaxCommand; |
93 | u16 wMaxPacketSize; |
94 | __le16 inum; |
95 | int reslength; |
96 | int length; |
97 | int read; |
98 | int count; |
99 | dma_addr_t shandle; |
100 | dma_addr_t ihandle; |
101 | struct mutex wlock; |
102 | struct mutex rlock; |
103 | wait_queue_head_t wait; |
104 | struct work_struct rxwork; |
105 | struct work_struct service_outs_intr; |
106 | int werr; |
107 | int rerr; |
108 | int resp_count; |
109 | |
110 | struct list_head device_list; |
111 | int (*manage_power)(struct usb_interface *, int); |
112 | |
113 | enum wwan_port_type wwanp_type; |
114 | struct wwan_port *wwanp; |
115 | }; |
116 | |
117 | static struct usb_driver wdm_driver; |
118 | |
119 | /* return intfdata if we own the interface, else look up intf in the list */ |
120 | static struct wdm_device *wdm_find_device(struct usb_interface *intf) |
121 | { |
122 | struct wdm_device *desc; |
123 | |
124 | spin_lock(lock: &wdm_device_list_lock); |
125 | list_for_each_entry(desc, &wdm_device_list, device_list) |
126 | if (desc->intf == intf) |
127 | goto found; |
128 | desc = NULL; |
129 | found: |
130 | spin_unlock(lock: &wdm_device_list_lock); |
131 | |
132 | return desc; |
133 | } |
134 | |
135 | static struct wdm_device *wdm_find_device_by_minor(int minor) |
136 | { |
137 | struct wdm_device *desc; |
138 | |
139 | spin_lock(lock: &wdm_device_list_lock); |
140 | list_for_each_entry(desc, &wdm_device_list, device_list) |
141 | if (desc->intf->minor == minor) |
142 | goto found; |
143 | desc = NULL; |
144 | found: |
145 | spin_unlock(lock: &wdm_device_list_lock); |
146 | |
147 | return desc; |
148 | } |
149 | |
150 | /* --- callbacks --- */ |
151 | static void wdm_out_callback(struct urb *urb) |
152 | { |
153 | struct wdm_device *desc; |
154 | unsigned long flags; |
155 | |
156 | desc = urb->context; |
157 | spin_lock_irqsave(&desc->iuspin, flags); |
158 | desc->werr = urb->status; |
159 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
160 | kfree(objp: desc->outbuf); |
161 | desc->outbuf = NULL; |
162 | clear_bit(WDM_IN_USE, addr: &desc->flags); |
163 | wake_up_all(&desc->wait); |
164 | } |
165 | |
166 | static void wdm_wwan_rx(struct wdm_device *desc, int length); |
167 | |
168 | static void wdm_in_callback(struct urb *urb) |
169 | { |
170 | unsigned long flags; |
171 | struct wdm_device *desc = urb->context; |
172 | int status = urb->status; |
173 | int length = urb->actual_length; |
174 | |
175 | spin_lock_irqsave(&desc->iuspin, flags); |
176 | clear_bit(WDM_RESPONDING, addr: &desc->flags); |
177 | |
178 | if (status) { |
179 | switch (status) { |
180 | case -ENOENT: |
181 | dev_dbg(&desc->intf->dev, |
182 | "nonzero urb status received: -ENOENT\n"); |
183 | goto skip_error; |
184 | case -ECONNRESET: |
185 | dev_dbg(&desc->intf->dev, |
186 | "nonzero urb status received: -ECONNRESET\n"); |
187 | goto skip_error; |
188 | case -ESHUTDOWN: |
189 | dev_dbg(&desc->intf->dev, |
190 | "nonzero urb status received: -ESHUTDOWN\n"); |
191 | goto skip_error; |
192 | case -EPIPE: |
193 | dev_err(&desc->intf->dev, |
194 | "nonzero urb status received: -EPIPE\n"); |
195 | break; |
196 | default: |
197 | dev_err(&desc->intf->dev, |
198 | "Unexpected error %d\n", status); |
199 | break; |
200 | } |
201 | } |
202 | |
203 | if (test_bit(WDM_WWAN_IN_USE, &desc->flags)) { |
204 | wdm_wwan_rx(desc, length); |
205 | goto out; |
206 | } |
207 | |
208 | /* |
209 | * only set a new error if there is no previous error. |
210 | * Errors are only cleared during read/open |
211 | * Avoid propagating -EPIPE (stall) to userspace since it is |
212 | * better handled as an empty read |
213 | */ |
214 | if (desc->rerr == 0 && status != -EPIPE) |
215 | desc->rerr = status; |
216 | |
217 | if (length + desc->length > desc->wMaxCommand) { |
218 | /* The buffer would overflow */ |
219 | set_bit(WDM_OVERFLOW, addr: &desc->flags); |
220 | } else { |
221 | /* we may already be in overflow */ |
222 | if (!test_bit(WDM_OVERFLOW, &desc->flags)) { |
223 | memmove(desc->ubuf + desc->length, desc->inbuf, length); |
224 | desc->length += length; |
225 | desc->reslength = length; |
226 | } |
227 | } |
228 | skip_error: |
229 | |
230 | if (desc->rerr) { |
231 | /* |
232 | * Since there was an error, userspace may decide to not read |
233 | * any data after poll'ing. |
234 | * We should respond to further attempts from the device to send |
235 | * data, so that we can get unstuck. |
236 | */ |
237 | schedule_work(work: &desc->service_outs_intr); |
238 | } else { |
239 | set_bit(WDM_READ, addr: &desc->flags); |
240 | wake_up(&desc->wait); |
241 | } |
242 | out: |
243 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
244 | } |
245 | |
246 | static void wdm_int_callback(struct urb *urb) |
247 | { |
248 | unsigned long flags; |
249 | int rv = 0; |
250 | int responding; |
251 | int status = urb->status; |
252 | struct wdm_device *desc; |
253 | struct usb_cdc_notification *dr; |
254 | |
255 | desc = urb->context; |
256 | dr = (struct usb_cdc_notification *)desc->sbuf; |
257 | |
258 | if (status) { |
259 | switch (status) { |
260 | case -ESHUTDOWN: |
261 | case -ENOENT: |
262 | case -ECONNRESET: |
263 | return; /* unplug */ |
264 | case -EPIPE: |
265 | set_bit(WDM_INT_STALL, addr: &desc->flags); |
266 | dev_err(&desc->intf->dev, "Stall on int endpoint\n"); |
267 | goto sw; /* halt is cleared in work */ |
268 | default: |
269 | dev_err(&desc->intf->dev, |
270 | "nonzero urb status received: %d\n", status); |
271 | break; |
272 | } |
273 | } |
274 | |
275 | if (urb->actual_length < sizeof(struct usb_cdc_notification)) { |
276 | dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n", |
277 | urb->actual_length); |
278 | goto exit; |
279 | } |
280 | |
281 | switch (dr->bNotificationType) { |
282 | case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: |
283 | dev_dbg(&desc->intf->dev, |
284 | "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d\n", |
285 | le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength)); |
286 | break; |
287 | |
288 | case USB_CDC_NOTIFY_NETWORK_CONNECTION: |
289 | |
290 | dev_dbg(&desc->intf->dev, |
291 | "NOTIFY_NETWORK_CONNECTION %s network\n", |
292 | dr->wValue ? "connected to": "disconnected from"); |
293 | goto exit; |
294 | case USB_CDC_NOTIFY_SPEED_CHANGE: |
295 | dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)\n", |
296 | urb->actual_length); |
297 | goto exit; |
298 | default: |
299 | clear_bit(WDM_POLL_RUNNING, addr: &desc->flags); |
300 | dev_err(&desc->intf->dev, |
301 | "unknown notification %d received: index %d len %d\n", |
302 | dr->bNotificationType, |
303 | le16_to_cpu(dr->wIndex), |
304 | le16_to_cpu(dr->wLength)); |
305 | goto exit; |
306 | } |
307 | |
308 | spin_lock_irqsave(&desc->iuspin, flags); |
309 | responding = test_and_set_bit(WDM_RESPONDING, addr: &desc->flags); |
310 | if (!desc->resp_count++ && !responding |
311 | && !test_bit(WDM_DISCONNECTING, &desc->flags) |
312 | && !test_bit(WDM_SUSPENDING, &desc->flags)) { |
313 | rv = usb_submit_urb(urb: desc->response, GFP_ATOMIC); |
314 | dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv); |
315 | } |
316 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
317 | if (rv < 0) { |
318 | clear_bit(WDM_RESPONDING, addr: &desc->flags); |
319 | if (rv == -EPERM) |
320 | return; |
321 | if (rv == -ENOMEM) { |
322 | sw: |
323 | rv = schedule_work(work: &desc->rxwork); |
324 | if (rv) |
325 | dev_err(&desc->intf->dev, |
326 | "Cannot schedule work\n"); |
327 | } |
328 | } |
329 | exit: |
330 | rv = usb_submit_urb(urb, GFP_ATOMIC); |
331 | if (rv) |
332 | dev_err(&desc->intf->dev, |
333 | "%s - usb_submit_urb failed with result %d\n", |
334 | __func__, rv); |
335 | |
336 | } |
337 | |
338 | static void poison_urbs(struct wdm_device *desc) |
339 | { |
340 | /* the order here is essential */ |
341 | usb_poison_urb(urb: desc->command); |
342 | usb_poison_urb(urb: desc->validity); |
343 | usb_poison_urb(urb: desc->response); |
344 | } |
345 | |
346 | static void unpoison_urbs(struct wdm_device *desc) |
347 | { |
348 | /* |
349 | * the order here is not essential |
350 | * it is symmetrical just to be nice |
351 | */ |
352 | usb_unpoison_urb(urb: desc->response); |
353 | usb_unpoison_urb(urb: desc->validity); |
354 | usb_unpoison_urb(urb: desc->command); |
355 | } |
356 | |
357 | static void free_urbs(struct wdm_device *desc) |
358 | { |
359 | usb_free_urb(urb: desc->validity); |
360 | usb_free_urb(urb: desc->response); |
361 | usb_free_urb(urb: desc->command); |
362 | } |
363 | |
364 | static void cleanup(struct wdm_device *desc) |
365 | { |
366 | kfree(objp: desc->sbuf); |
367 | kfree(objp: desc->inbuf); |
368 | kfree(objp: desc->orq); |
369 | kfree(objp: desc->irq); |
370 | kfree(objp: desc->ubuf); |
371 | free_urbs(desc); |
372 | kfree(objp: desc); |
373 | } |
374 | |
375 | static ssize_t wdm_write |
376 | (struct file *file, const char __user *buffer, size_t count, loff_t *ppos) |
377 | { |
378 | u8 *buf; |
379 | int rv = -EMSGSIZE, r, we; |
380 | struct wdm_device *desc = file->private_data; |
381 | struct usb_ctrlrequest *req; |
382 | |
383 | if (count > desc->wMaxCommand) |
384 | count = desc->wMaxCommand; |
385 | |
386 | spin_lock_irq(lock: &desc->iuspin); |
387 | we = desc->werr; |
388 | desc->werr = 0; |
389 | spin_unlock_irq(lock: &desc->iuspin); |
390 | if (we < 0) |
391 | return usb_translate_errors(error_code: we); |
392 | |
393 | buf = memdup_user(buffer, count); |
394 | if (IS_ERR(ptr: buf)) |
395 | return PTR_ERR(ptr: buf); |
396 | |
397 | /* concurrent writes and disconnect */ |
398 | r = mutex_lock_interruptible(&desc->wlock); |
399 | rv = -ERESTARTSYS; |
400 | if (r) |
401 | goto out_free_mem; |
402 | |
403 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { |
404 | rv = -ENODEV; |
405 | goto out_free_mem_lock; |
406 | } |
407 | |
408 | r = usb_autopm_get_interface(intf: desc->intf); |
409 | if (r < 0) { |
410 | rv = usb_translate_errors(error_code: r); |
411 | goto out_free_mem_lock; |
412 | } |
413 | |
414 | if (!(file->f_flags & O_NONBLOCK)) |
415 | r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, |
416 | &desc->flags)); |
417 | else |
418 | if (test_bit(WDM_IN_USE, &desc->flags)) |
419 | r = -EAGAIN; |
420 | |
421 | if (test_bit(WDM_RESETTING, &desc->flags)) |
422 | r = -EIO; |
423 | |
424 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) |
425 | r = -ENODEV; |
426 | |
427 | if (r < 0) { |
428 | rv = r; |
429 | goto out_free_mem_pm; |
430 | } |
431 | |
432 | req = desc->orq; |
433 | usb_fill_control_urb( |
434 | urb: desc->command, |
435 | interface_to_usbdev(desc->intf), |
436 | /* using common endpoint 0 */ |
437 | usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0), |
438 | setup_packet: (unsigned char *)req, |
439 | transfer_buffer: buf, |
440 | buffer_length: count, |
441 | complete_fn: wdm_out_callback, |
442 | context: desc |
443 | ); |
444 | |
445 | req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | |
446 | USB_RECIP_INTERFACE); |
447 | req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; |
448 | req->wValue = 0; |
449 | req->wIndex = desc->inum; /* already converted */ |
450 | req->wLength = cpu_to_le16(count); |
451 | set_bit(WDM_IN_USE, addr: &desc->flags); |
452 | desc->outbuf = buf; |
453 | |
454 | rv = usb_submit_urb(urb: desc->command, GFP_KERNEL); |
455 | if (rv < 0) { |
456 | desc->outbuf = NULL; |
457 | clear_bit(WDM_IN_USE, addr: &desc->flags); |
458 | wake_up_all(&desc->wait); /* for wdm_wait_for_response() */ |
459 | dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); |
460 | rv = usb_translate_errors(error_code: rv); |
461 | goto out_free_mem_pm; |
462 | } else { |
463 | dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d\n", |
464 | le16_to_cpu(req->wIndex)); |
465 | } |
466 | |
467 | usb_autopm_put_interface(intf: desc->intf); |
468 | mutex_unlock(lock: &desc->wlock); |
469 | return count; |
470 | |
471 | out_free_mem_pm: |
472 | usb_autopm_put_interface(intf: desc->intf); |
473 | out_free_mem_lock: |
474 | mutex_unlock(lock: &desc->wlock); |
475 | out_free_mem: |
476 | kfree(objp: buf); |
477 | return rv; |
478 | } |
479 | |
480 | /* |
481 | * Submit the read urb if resp_count is non-zero. |
482 | * |
483 | * Called with desc->iuspin locked |
484 | */ |
485 | static int service_outstanding_interrupt(struct wdm_device *desc) |
486 | { |
487 | int rv = 0; |
488 | |
489 | /* submit read urb only if the device is waiting for it */ |
490 | if (!desc->resp_count || !--desc->resp_count) |
491 | goto out; |
492 | |
493 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { |
494 | rv = -ENODEV; |
495 | goto out; |
496 | } |
497 | if (test_bit(WDM_RESETTING, &desc->flags)) { |
498 | rv = -EIO; |
499 | goto out; |
500 | } |
501 | |
502 | set_bit(WDM_RESPONDING, addr: &desc->flags); |
503 | spin_unlock_irq(lock: &desc->iuspin); |
504 | rv = usb_submit_urb(urb: desc->response, GFP_KERNEL); |
505 | spin_lock_irq(lock: &desc->iuspin); |
506 | if (rv) { |
507 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) |
508 | dev_err(&desc->intf->dev, |
509 | "usb_submit_urb failed with result %d\n", rv); |
510 | |
511 | /* make sure the next notification trigger a submit */ |
512 | clear_bit(WDM_RESPONDING, addr: &desc->flags); |
513 | desc->resp_count = 0; |
514 | } |
515 | out: |
516 | return rv; |
517 | } |
518 | |
519 | static ssize_t wdm_read |
520 | (struct file *file, char __user *buffer, size_t count, loff_t *ppos) |
521 | { |
522 | int rv, cntr; |
523 | int i = 0; |
524 | struct wdm_device *desc = file->private_data; |
525 | |
526 | |
527 | rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ |
528 | if (rv < 0) |
529 | return -ERESTARTSYS; |
530 | |
531 | cntr = READ_ONCE(desc->length); |
532 | if (cntr == 0) { |
533 | desc->read = 0; |
534 | retry: |
535 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { |
536 | rv = -ENODEV; |
537 | goto err; |
538 | } |
539 | if (test_bit(WDM_OVERFLOW, &desc->flags)) { |
540 | clear_bit(WDM_OVERFLOW, addr: &desc->flags); |
541 | rv = -ENOBUFS; |
542 | goto err; |
543 | } |
544 | i++; |
545 | if (file->f_flags & O_NONBLOCK) { |
546 | if (!test_bit(WDM_READ, &desc->flags)) { |
547 | rv = -EAGAIN; |
548 | goto err; |
549 | } |
550 | rv = 0; |
551 | } else { |
552 | rv = wait_event_interruptible(desc->wait, |
553 | test_bit(WDM_READ, &desc->flags)); |
554 | } |
555 | |
556 | /* may have happened while we slept */ |
557 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { |
558 | rv = -ENODEV; |
559 | goto err; |
560 | } |
561 | if (test_bit(WDM_RESETTING, &desc->flags)) { |
562 | rv = -EIO; |
563 | goto err; |
564 | } |
565 | usb_mark_last_busy(interface_to_usbdev(desc->intf)); |
566 | if (rv < 0) { |
567 | rv = -ERESTARTSYS; |
568 | goto err; |
569 | } |
570 | |
571 | spin_lock_irq(lock: &desc->iuspin); |
572 | |
573 | if (desc->rerr) { /* read completed, error happened */ |
574 | rv = usb_translate_errors(error_code: desc->rerr); |
575 | desc->rerr = 0; |
576 | spin_unlock_irq(lock: &desc->iuspin); |
577 | goto err; |
578 | } |
579 | /* |
580 | * recheck whether we've lost the race |
581 | * against the completion handler |
582 | */ |
583 | if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */ |
584 | spin_unlock_irq(lock: &desc->iuspin); |
585 | goto retry; |
586 | } |
587 | |
588 | if (!desc->reslength) { /* zero length read */ |
589 | dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n"); |
590 | clear_bit(WDM_READ, addr: &desc->flags); |
591 | rv = service_outstanding_interrupt(desc); |
592 | spin_unlock_irq(lock: &desc->iuspin); |
593 | if (rv < 0) |
594 | goto err; |
595 | goto retry; |
596 | } |
597 | cntr = desc->length; |
598 | spin_unlock_irq(lock: &desc->iuspin); |
599 | } |
600 | |
601 | if (cntr > count) |
602 | cntr = count; |
603 | rv = copy_to_user(to: buffer, from: desc->ubuf, n: cntr); |
604 | if (rv > 0) { |
605 | rv = -EFAULT; |
606 | goto err; |
607 | } |
608 | |
609 | spin_lock_irq(lock: &desc->iuspin); |
610 | |
611 | for (i = 0; i < desc->length - cntr; i++) |
612 | desc->ubuf[i] = desc->ubuf[i + cntr]; |
613 | |
614 | desc->length -= cntr; |
615 | /* in case we had outstanding data */ |
616 | if (!desc->length) { |
617 | clear_bit(WDM_READ, addr: &desc->flags); |
618 | service_outstanding_interrupt(desc); |
619 | } |
620 | spin_unlock_irq(lock: &desc->iuspin); |
621 | rv = cntr; |
622 | |
623 | err: |
624 | mutex_unlock(lock: &desc->rlock); |
625 | return rv; |
626 | } |
627 | |
628 | static int wdm_wait_for_response(struct file *file, long timeout) |
629 | { |
630 | struct wdm_device *desc = file->private_data; |
631 | long rv; /* Use long here because (int) MAX_SCHEDULE_TIMEOUT < 0. */ |
632 | |
633 | /* |
634 | * Needs both flags. We cannot do with one because resetting it would |
635 | * cause a race with write() yet we need to signal a disconnect. |
636 | */ |
637 | rv = wait_event_interruptible_timeout(desc->wait, |
638 | !test_bit(WDM_IN_USE, &desc->flags) || |
639 | test_bit(WDM_DISCONNECTING, &desc->flags), |
640 | timeout); |
641 | |
642 | /* |
643 | * To report the correct error. This is best effort. |
644 | * We are inevitably racing with the hardware. |
645 | */ |
646 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) |
647 | return -ENODEV; |
648 | if (!rv) |
649 | return -EIO; |
650 | if (rv < 0) |
651 | return -EINTR; |
652 | |
653 | spin_lock_irq(lock: &desc->iuspin); |
654 | rv = desc->werr; |
655 | desc->werr = 0; |
656 | spin_unlock_irq(lock: &desc->iuspin); |
657 | |
658 | return usb_translate_errors(error_code: rv); |
659 | |
660 | } |
661 | |
662 | /* |
663 | * You need to send a signal when you react to malicious or defective hardware. |
664 | * Also, don't abort when fsync() returned -EINVAL, for older kernels which do |
665 | * not implement wdm_flush() will return -EINVAL. |
666 | */ |
667 | static int wdm_fsync(struct file *file, loff_t start, loff_t end, int datasync) |
668 | { |
669 | return wdm_wait_for_response(file, MAX_SCHEDULE_TIMEOUT); |
670 | } |
671 | |
672 | /* |
673 | * Same with wdm_fsync(), except it uses finite timeout in order to react to |
674 | * malicious or defective hardware which ceased communication after close() was |
675 | * implicitly called due to process termination. |
676 | */ |
677 | static int wdm_flush(struct file *file, fl_owner_t id) |
678 | { |
679 | return wdm_wait_for_response(file, WDM_FLUSH_TIMEOUT); |
680 | } |
681 | |
682 | static __poll_t wdm_poll(struct file *file, struct poll_table_struct *wait) |
683 | { |
684 | struct wdm_device *desc = file->private_data; |
685 | unsigned long flags; |
686 | __poll_t mask = 0; |
687 | |
688 | spin_lock_irqsave(&desc->iuspin, flags); |
689 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { |
690 | mask = EPOLLHUP | EPOLLERR; |
691 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
692 | goto desc_out; |
693 | } |
694 | if (test_bit(WDM_READ, &desc->flags)) |
695 | mask = EPOLLIN | EPOLLRDNORM; |
696 | if (desc->rerr || desc->werr) |
697 | mask |= EPOLLERR; |
698 | if (!test_bit(WDM_IN_USE, &desc->flags)) |
699 | mask |= EPOLLOUT | EPOLLWRNORM; |
700 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
701 | |
702 | poll_wait(filp: file, wait_address: &desc->wait, p: wait); |
703 | |
704 | desc_out: |
705 | return mask; |
706 | } |
707 | |
708 | static int wdm_open(struct inode *inode, struct file *file) |
709 | { |
710 | int minor = iminor(inode); |
711 | int rv = -ENODEV; |
712 | struct usb_interface *intf; |
713 | struct wdm_device *desc; |
714 | |
715 | mutex_lock(&wdm_mutex); |
716 | desc = wdm_find_device_by_minor(minor); |
717 | if (!desc) |
718 | goto out; |
719 | |
720 | intf = desc->intf; |
721 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) |
722 | goto out; |
723 | file->private_data = desc; |
724 | |
725 | if (test_bit(WDM_WWAN_IN_USE, &desc->flags)) { |
726 | rv = -EBUSY; |
727 | goto out; |
728 | } |
729 | |
730 | rv = usb_autopm_get_interface(intf: desc->intf); |
731 | if (rv < 0) { |
732 | dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); |
733 | goto out; |
734 | } |
735 | |
736 | /* using write lock to protect desc->count */ |
737 | mutex_lock(&desc->wlock); |
738 | if (!desc->count++) { |
739 | desc->werr = 0; |
740 | desc->rerr = 0; |
741 | rv = usb_submit_urb(urb: desc->validity, GFP_KERNEL); |
742 | if (rv < 0) { |
743 | desc->count--; |
744 | dev_err(&desc->intf->dev, |
745 | "Error submitting int urb - %d\n", rv); |
746 | rv = usb_translate_errors(error_code: rv); |
747 | } |
748 | } else { |
749 | rv = 0; |
750 | } |
751 | mutex_unlock(lock: &desc->wlock); |
752 | if (desc->count == 1) |
753 | desc->manage_power(intf, 1); |
754 | usb_autopm_put_interface(intf: desc->intf); |
755 | out: |
756 | mutex_unlock(lock: &wdm_mutex); |
757 | return rv; |
758 | } |
759 | |
760 | static int wdm_release(struct inode *inode, struct file *file) |
761 | { |
762 | struct wdm_device *desc = file->private_data; |
763 | |
764 | mutex_lock(&wdm_mutex); |
765 | |
766 | /* using write lock to protect desc->count */ |
767 | mutex_lock(&desc->wlock); |
768 | desc->count--; |
769 | mutex_unlock(lock: &desc->wlock); |
770 | |
771 | if (!desc->count) { |
772 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { |
773 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n"); |
774 | poison_urbs(desc); |
775 | spin_lock_irq(lock: &desc->iuspin); |
776 | desc->resp_count = 0; |
777 | clear_bit(WDM_RESPONDING, addr: &desc->flags); |
778 | spin_unlock_irq(lock: &desc->iuspin); |
779 | desc->manage_power(desc->intf, 0); |
780 | unpoison_urbs(desc); |
781 | } else { |
782 | /* must avoid dev_printk here as desc->intf is invalid */ |
783 | pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__); |
784 | cleanup(desc); |
785 | } |
786 | } |
787 | mutex_unlock(lock: &wdm_mutex); |
788 | return 0; |
789 | } |
790 | |
791 | static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
792 | { |
793 | struct wdm_device *desc = file->private_data; |
794 | int rv = 0; |
795 | |
796 | switch (cmd) { |
797 | case IOCTL_WDM_MAX_COMMAND: |
798 | if (copy_to_user(to: (void __user *)arg, from: &desc->wMaxCommand, n: sizeof(desc->wMaxCommand))) |
799 | rv = -EFAULT; |
800 | break; |
801 | default: |
802 | rv = -ENOTTY; |
803 | } |
804 | return rv; |
805 | } |
806 | |
807 | static const struct file_operations wdm_fops = { |
808 | .owner = THIS_MODULE, |
809 | .read = wdm_read, |
810 | .write = wdm_write, |
811 | .fsync = wdm_fsync, |
812 | .open = wdm_open, |
813 | .flush = wdm_flush, |
814 | .release = wdm_release, |
815 | .poll = wdm_poll, |
816 | .unlocked_ioctl = wdm_ioctl, |
817 | .compat_ioctl = compat_ptr_ioctl, |
818 | .llseek = noop_llseek, |
819 | }; |
820 | |
821 | static struct usb_class_driver wdm_class = { |
822 | .name = "cdc-wdm%d", |
823 | .fops = &wdm_fops, |
824 | .minor_base = WDM_MINOR_BASE, |
825 | }; |
826 | |
827 | /* --- WWAN framework integration --- */ |
828 | #ifdef CONFIG_WWAN |
829 | static int wdm_wwan_port_start(struct wwan_port *port) |
830 | { |
831 | struct wdm_device *desc = wwan_port_get_drvdata(port); |
832 | |
833 | /* The interface is both exposed via the WWAN framework and as a |
834 | * legacy usbmisc chardev. If chardev is already open, just fail |
835 | * to prevent concurrent usage. Otherwise, switch to WWAN mode. |
836 | */ |
837 | mutex_lock(&wdm_mutex); |
838 | if (desc->count) { |
839 | mutex_unlock(lock: &wdm_mutex); |
840 | return -EBUSY; |
841 | } |
842 | set_bit(WDM_WWAN_IN_USE, addr: &desc->flags); |
843 | mutex_unlock(lock: &wdm_mutex); |
844 | |
845 | desc->manage_power(desc->intf, 1); |
846 | |
847 | /* tx is allowed */ |
848 | wwan_port_txon(port); |
849 | |
850 | /* Start getting events */ |
851 | return usb_submit_urb(urb: desc->validity, GFP_KERNEL); |
852 | } |
853 | |
854 | static void wdm_wwan_port_stop(struct wwan_port *port) |
855 | { |
856 | struct wdm_device *desc = wwan_port_get_drvdata(port); |
857 | |
858 | /* Stop all transfers and disable WWAN mode */ |
859 | poison_urbs(desc); |
860 | desc->manage_power(desc->intf, 0); |
861 | clear_bit(WDM_READ, addr: &desc->flags); |
862 | clear_bit(WDM_WWAN_IN_USE, addr: &desc->flags); |
863 | unpoison_urbs(desc); |
864 | } |
865 | |
866 | static void wdm_wwan_port_tx_complete(struct urb *urb) |
867 | { |
868 | struct sk_buff *skb = urb->context; |
869 | struct wdm_device *desc = skb_shinfo(skb)->destructor_arg; |
870 | |
871 | usb_autopm_put_interface(intf: desc->intf); |
872 | wwan_port_txon(port: desc->wwanp); |
873 | kfree_skb(skb); |
874 | } |
875 | |
876 | static int wdm_wwan_port_tx(struct wwan_port *port, struct sk_buff *skb) |
877 | { |
878 | struct wdm_device *desc = wwan_port_get_drvdata(port); |
879 | struct usb_interface *intf = desc->intf; |
880 | struct usb_ctrlrequest *req = desc->orq; |
881 | int rv; |
882 | |
883 | rv = usb_autopm_get_interface(intf); |
884 | if (rv) |
885 | return rv; |
886 | |
887 | usb_fill_control_urb( |
888 | urb: desc->command, |
889 | interface_to_usbdev(intf), |
890 | usb_sndctrlpipe(interface_to_usbdev(intf), 0), |
891 | setup_packet: (unsigned char *)req, |
892 | transfer_buffer: skb->data, |
893 | buffer_length: skb->len, |
894 | complete_fn: wdm_wwan_port_tx_complete, |
895 | context: skb |
896 | ); |
897 | |
898 | req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); |
899 | req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; |
900 | req->wValue = 0; |
901 | req->wIndex = desc->inum; |
902 | req->wLength = cpu_to_le16(skb->len); |
903 | |
904 | skb_shinfo(skb)->destructor_arg = desc; |
905 | |
906 | rv = usb_submit_urb(urb: desc->command, GFP_KERNEL); |
907 | if (rv) |
908 | usb_autopm_put_interface(intf); |
909 | else /* One transfer at a time, stop TX until URB completion */ |
910 | wwan_port_txoff(port); |
911 | |
912 | return rv; |
913 | } |
914 | |
915 | static const struct wwan_port_ops wdm_wwan_port_ops = { |
916 | .start = wdm_wwan_port_start, |
917 | .stop = wdm_wwan_port_stop, |
918 | .tx = wdm_wwan_port_tx, |
919 | }; |
920 | |
921 | static void wdm_wwan_init(struct wdm_device *desc) |
922 | { |
923 | struct usb_interface *intf = desc->intf; |
924 | struct wwan_port *port; |
925 | |
926 | /* Only register to WWAN core if protocol/type is known */ |
927 | if (desc->wwanp_type == WWAN_PORT_UNKNOWN) { |
928 | dev_info(&intf->dev, "Unknown control protocol\n"); |
929 | return; |
930 | } |
931 | |
932 | port = wwan_create_port(parent: &intf->dev, type: desc->wwanp_type, ops: &wdm_wwan_port_ops, |
933 | NULL, drvdata: desc); |
934 | if (IS_ERR(ptr: port)) { |
935 | dev_err(&intf->dev, "%s: Unable to create WWAN port\n", |
936 | dev_name(intf->usb_dev)); |
937 | return; |
938 | } |
939 | |
940 | desc->wwanp = port; |
941 | } |
942 | |
943 | static void wdm_wwan_deinit(struct wdm_device *desc) |
944 | { |
945 | if (!desc->wwanp) |
946 | return; |
947 | |
948 | wwan_remove_port(port: desc->wwanp); |
949 | desc->wwanp = NULL; |
950 | } |
951 | |
952 | static void wdm_wwan_rx(struct wdm_device *desc, int length) |
953 | { |
954 | struct wwan_port *port = desc->wwanp; |
955 | struct sk_buff *skb; |
956 | |
957 | /* Forward data to WWAN port */ |
958 | skb = alloc_skb(size: length, GFP_ATOMIC); |
959 | if (!skb) |
960 | return; |
961 | |
962 | skb_put_data(skb, data: desc->inbuf, len: length); |
963 | wwan_port_rx(port, skb); |
964 | |
965 | /* inbuf has been copied, it is safe to check for outstanding data */ |
966 | schedule_work(work: &desc->service_outs_intr); |
967 | } |
968 | #else /* CONFIG_WWAN */ |
969 | static void wdm_wwan_init(struct wdm_device *desc) {} |
970 | static void wdm_wwan_deinit(struct wdm_device *desc) {} |
971 | static void wdm_wwan_rx(struct wdm_device *desc, int length) {} |
972 | #endif /* CONFIG_WWAN */ |
973 | |
974 | /* --- error handling --- */ |
975 | static void wdm_rxwork(struct work_struct *work) |
976 | { |
977 | struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); |
978 | unsigned long flags; |
979 | int rv = 0; |
980 | int responding; |
981 | |
982 | spin_lock_irqsave(&desc->iuspin, flags); |
983 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { |
984 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
985 | } else { |
986 | responding = test_and_set_bit(WDM_RESPONDING, addr: &desc->flags); |
987 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
988 | if (!responding) |
989 | rv = usb_submit_urb(urb: desc->response, GFP_KERNEL); |
990 | if (rv < 0 && rv != -EPERM) { |
991 | spin_lock_irqsave(&desc->iuspin, flags); |
992 | clear_bit(WDM_RESPONDING, addr: &desc->flags); |
993 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) |
994 | schedule_work(work: &desc->rxwork); |
995 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
996 | } |
997 | } |
998 | } |
999 | |
1000 | static void service_interrupt_work(struct work_struct *work) |
1001 | { |
1002 | struct wdm_device *desc; |
1003 | |
1004 | desc = container_of(work, struct wdm_device, service_outs_intr); |
1005 | |
1006 | spin_lock_irq(lock: &desc->iuspin); |
1007 | service_outstanding_interrupt(desc); |
1008 | if (!desc->resp_count) { |
1009 | set_bit(WDM_READ, addr: &desc->flags); |
1010 | wake_up(&desc->wait); |
1011 | } |
1012 | spin_unlock_irq(lock: &desc->iuspin); |
1013 | } |
1014 | |
1015 | /* --- hotplug --- */ |
1016 | |
1017 | static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, |
1018 | u16 bufsize, enum wwan_port_type type, |
1019 | int (*manage_power)(struct usb_interface *, int)) |
1020 | { |
1021 | int rv = -ENOMEM; |
1022 | struct wdm_device *desc; |
1023 | |
1024 | desc = kzalloc(size: sizeof(struct wdm_device), GFP_KERNEL); |
1025 | if (!desc) |
1026 | goto out; |
1027 | INIT_LIST_HEAD(list: &desc->device_list); |
1028 | mutex_init(&desc->rlock); |
1029 | mutex_init(&desc->wlock); |
1030 | spin_lock_init(&desc->iuspin); |
1031 | init_waitqueue_head(&desc->wait); |
1032 | desc->wMaxCommand = bufsize; |
1033 | /* this will be expanded and needed in hardware endianness */ |
1034 | desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); |
1035 | desc->intf = intf; |
1036 | desc->wwanp_type = type; |
1037 | INIT_WORK(&desc->rxwork, wdm_rxwork); |
1038 | INIT_WORK(&desc->service_outs_intr, service_interrupt_work); |
1039 | |
1040 | if (!usb_endpoint_is_int_in(epd: ep)) { |
1041 | rv = -EINVAL; |
1042 | goto err; |
1043 | } |
1044 | |
1045 | desc->wMaxPacketSize = usb_endpoint_maxp(epd: ep); |
1046 | |
1047 | desc->orq = kmalloc(size: sizeof(struct usb_ctrlrequest), GFP_KERNEL); |
1048 | if (!desc->orq) |
1049 | goto err; |
1050 | desc->irq = kmalloc(size: sizeof(struct usb_ctrlrequest), GFP_KERNEL); |
1051 | if (!desc->irq) |
1052 | goto err; |
1053 | |
1054 | desc->validity = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
1055 | if (!desc->validity) |
1056 | goto err; |
1057 | |
1058 | desc->response = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
1059 | if (!desc->response) |
1060 | goto err; |
1061 | |
1062 | desc->command = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
1063 | if (!desc->command) |
1064 | goto err; |
1065 | |
1066 | desc->ubuf = kmalloc(size: desc->wMaxCommand, GFP_KERNEL); |
1067 | if (!desc->ubuf) |
1068 | goto err; |
1069 | |
1070 | desc->sbuf = kmalloc(size: desc->wMaxPacketSize, GFP_KERNEL); |
1071 | if (!desc->sbuf) |
1072 | goto err; |
1073 | |
1074 | desc->inbuf = kmalloc(size: desc->wMaxCommand, GFP_KERNEL); |
1075 | if (!desc->inbuf) |
1076 | goto err; |
1077 | |
1078 | usb_fill_int_urb( |
1079 | urb: desc->validity, |
1080 | interface_to_usbdev(intf), |
1081 | usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress), |
1082 | transfer_buffer: desc->sbuf, |
1083 | buffer_length: desc->wMaxPacketSize, |
1084 | complete_fn: wdm_int_callback, |
1085 | context: desc, |
1086 | interval: ep->bInterval |
1087 | ); |
1088 | |
1089 | desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); |
1090 | desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; |
1091 | desc->irq->wValue = 0; |
1092 | desc->irq->wIndex = desc->inum; /* already converted */ |
1093 | desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); |
1094 | |
1095 | usb_fill_control_urb( |
1096 | urb: desc->response, |
1097 | interface_to_usbdev(intf), |
1098 | /* using common endpoint 0 */ |
1099 | usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), |
1100 | setup_packet: (unsigned char *)desc->irq, |
1101 | transfer_buffer: desc->inbuf, |
1102 | buffer_length: desc->wMaxCommand, |
1103 | complete_fn: wdm_in_callback, |
1104 | context: desc |
1105 | ); |
1106 | |
1107 | desc->manage_power = manage_power; |
1108 | |
1109 | spin_lock(lock: &wdm_device_list_lock); |
1110 | list_add(new: &desc->device_list, head: &wdm_device_list); |
1111 | spin_unlock(lock: &wdm_device_list_lock); |
1112 | |
1113 | rv = usb_register_dev(intf, class_driver: &wdm_class); |
1114 | if (rv < 0) |
1115 | goto err; |
1116 | else |
1117 | dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev)); |
1118 | |
1119 | wdm_wwan_init(desc); |
1120 | |
1121 | out: |
1122 | return rv; |
1123 | err: |
1124 | spin_lock(lock: &wdm_device_list_lock); |
1125 | list_del(entry: &desc->device_list); |
1126 | spin_unlock(lock: &wdm_device_list_lock); |
1127 | cleanup(desc); |
1128 | return rv; |
1129 | } |
1130 | |
1131 | static int wdm_manage_power(struct usb_interface *intf, int on) |
1132 | { |
1133 | /* need autopm_get/put here to ensure the usbcore sees the new value */ |
1134 | int rv = usb_autopm_get_interface(intf); |
1135 | |
1136 | intf->needs_remote_wakeup = on; |
1137 | if (!rv) |
1138 | usb_autopm_put_interface(intf); |
1139 | return 0; |
1140 | } |
1141 | |
1142 | static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) |
1143 | { |
1144 | int rv = -EINVAL; |
1145 | struct usb_host_interface *iface; |
1146 | struct usb_endpoint_descriptor *ep; |
1147 | struct usb_cdc_parsed_header hdr; |
1148 | u8 *buffer = intf->altsetting->extra; |
1149 | int buflen = intf->altsetting->extralen; |
1150 | u16 maxcom = WDM_DEFAULT_BUFSIZE; |
1151 | |
1152 | if (!buffer) |
1153 | goto err; |
1154 | |
1155 | cdc_parse_cdc_header(hdr: &hdr, intf, buffer, buflen); |
1156 | |
1157 | if (hdr.usb_cdc_dmm_desc) |
1158 | maxcom = le16_to_cpu(hdr.usb_cdc_dmm_desc->wMaxCommand); |
1159 | |
1160 | iface = intf->cur_altsetting; |
1161 | if (iface->desc.bNumEndpoints != 1) |
1162 | goto err; |
1163 | ep = &iface->endpoint[0].desc; |
1164 | |
1165 | rv = wdm_create(intf, ep, bufsize: maxcom, type: WWAN_PORT_UNKNOWN, manage_power: &wdm_manage_power); |
1166 | |
1167 | err: |
1168 | return rv; |
1169 | } |
1170 | |
1171 | /** |
1172 | * usb_cdc_wdm_register - register a WDM subdriver |
1173 | * @intf: usb interface the subdriver will associate with |
1174 | * @ep: interrupt endpoint to monitor for notifications |
1175 | * @bufsize: maximum message size to support for read/write |
1176 | * @type: Type/protocol of the transported data (MBIM, QMI...) |
1177 | * @manage_power: call-back invoked during open and release to |
1178 | * manage the device's power |
1179 | * Create WDM usb class character device and associate it with intf |
1180 | * without binding, allowing another driver to manage the interface. |
1181 | * |
1182 | * The subdriver will manage the given interrupt endpoint exclusively |
1183 | * and will issue control requests referring to the given intf. It |
1184 | * will otherwise avoid interferring, and in particular not do |
1185 | * usb_set_intfdata/usb_get_intfdata on intf. |
1186 | * |
1187 | * The return value is a pointer to the subdriver's struct usb_driver. |
1188 | * The registering driver is responsible for calling this subdriver's |
1189 | * disconnect, suspend, resume, pre_reset and post_reset methods from |
1190 | * its own. |
1191 | */ |
1192 | struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, |
1193 | struct usb_endpoint_descriptor *ep, |
1194 | int bufsize, enum wwan_port_type type, |
1195 | int (*manage_power)(struct usb_interface *, int)) |
1196 | { |
1197 | int rv; |
1198 | |
1199 | rv = wdm_create(intf, ep, bufsize, type, manage_power); |
1200 | if (rv < 0) |
1201 | goto err; |
1202 | |
1203 | return &wdm_driver; |
1204 | err: |
1205 | return ERR_PTR(error: rv); |
1206 | } |
1207 | EXPORT_SYMBOL(usb_cdc_wdm_register); |
1208 | |
1209 | static void wdm_disconnect(struct usb_interface *intf) |
1210 | { |
1211 | struct wdm_device *desc; |
1212 | unsigned long flags; |
1213 | |
1214 | usb_deregister_dev(intf, class_driver: &wdm_class); |
1215 | desc = wdm_find_device(intf); |
1216 | mutex_lock(&wdm_mutex); |
1217 | |
1218 | wdm_wwan_deinit(desc); |
1219 | |
1220 | /* the spinlock makes sure no new urbs are generated in the callbacks */ |
1221 | spin_lock_irqsave(&desc->iuspin, flags); |
1222 | set_bit(WDM_DISCONNECTING, addr: &desc->flags); |
1223 | set_bit(WDM_READ, addr: &desc->flags); |
1224 | spin_unlock_irqrestore(lock: &desc->iuspin, flags); |
1225 | wake_up_all(&desc->wait); |
1226 | mutex_lock(&desc->rlock); |
1227 | mutex_lock(&desc->wlock); |
1228 | poison_urbs(desc); |
1229 | cancel_work_sync(work: &desc->rxwork); |
1230 | cancel_work_sync(work: &desc->service_outs_intr); |
1231 | mutex_unlock(lock: &desc->wlock); |
1232 | mutex_unlock(lock: &desc->rlock); |
1233 | |
1234 | /* the desc->intf pointer used as list key is now invalid */ |
1235 | spin_lock(lock: &wdm_device_list_lock); |
1236 | list_del(entry: &desc->device_list); |
1237 | spin_unlock(lock: &wdm_device_list_lock); |
1238 | |
1239 | if (!desc->count) |
1240 | cleanup(desc); |
1241 | else |
1242 | dev_dbg(&intf->dev, "%d open files - postponing cleanup\n", desc->count); |
1243 | mutex_unlock(lock: &wdm_mutex); |
1244 | } |
1245 | |
1246 | #ifdef CONFIG_PM |
1247 | static int wdm_suspend(struct usb_interface *intf, pm_message_t message) |
1248 | { |
1249 | struct wdm_device *desc = wdm_find_device(intf); |
1250 | int rv = 0; |
1251 | |
1252 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); |
1253 | |
1254 | /* if this is an autosuspend the caller does the locking */ |
1255 | if (!PMSG_IS_AUTO(message)) { |
1256 | mutex_lock(&desc->rlock); |
1257 | mutex_lock(&desc->wlock); |
1258 | } |
1259 | spin_lock_irq(lock: &desc->iuspin); |
1260 | |
1261 | if (PMSG_IS_AUTO(message) && |
1262 | (test_bit(WDM_IN_USE, &desc->flags) |
1263 | || test_bit(WDM_RESPONDING, &desc->flags))) { |
1264 | spin_unlock_irq(lock: &desc->iuspin); |
1265 | rv = -EBUSY; |
1266 | } else { |
1267 | |
1268 | set_bit(WDM_SUSPENDING, addr: &desc->flags); |
1269 | spin_unlock_irq(lock: &desc->iuspin); |
1270 | /* callback submits work - order is essential */ |
1271 | poison_urbs(desc); |
1272 | cancel_work_sync(work: &desc->rxwork); |
1273 | cancel_work_sync(work: &desc->service_outs_intr); |
1274 | unpoison_urbs(desc); |
1275 | } |
1276 | if (!PMSG_IS_AUTO(message)) { |
1277 | mutex_unlock(lock: &desc->wlock); |
1278 | mutex_unlock(lock: &desc->rlock); |
1279 | } |
1280 | |
1281 | return rv; |
1282 | } |
1283 | #endif |
1284 | |
1285 | static int recover_from_urb_loss(struct wdm_device *desc) |
1286 | { |
1287 | int rv = 0; |
1288 | |
1289 | if (desc->count) { |
1290 | rv = usb_submit_urb(urb: desc->validity, GFP_NOIO); |
1291 | if (rv < 0) |
1292 | dev_err(&desc->intf->dev, |
1293 | "Error resume submitting int urb - %d\n", rv); |
1294 | } |
1295 | return rv; |
1296 | } |
1297 | |
1298 | #ifdef CONFIG_PM |
1299 | static int wdm_resume(struct usb_interface *intf) |
1300 | { |
1301 | struct wdm_device *desc = wdm_find_device(intf); |
1302 | int rv; |
1303 | |
1304 | dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); |
1305 | |
1306 | clear_bit(WDM_SUSPENDING, addr: &desc->flags); |
1307 | rv = recover_from_urb_loss(desc); |
1308 | |
1309 | return rv; |
1310 | } |
1311 | #endif |
1312 | |
1313 | static int wdm_pre_reset(struct usb_interface *intf) |
1314 | { |
1315 | struct wdm_device *desc = wdm_find_device(intf); |
1316 | |
1317 | /* |
1318 | * we notify everybody using poll of |
1319 | * an exceptional situation |
1320 | * must be done before recovery lest a spontaneous |
1321 | * message from the device is lost |
1322 | */ |
1323 | spin_lock_irq(lock: &desc->iuspin); |
1324 | set_bit(WDM_RESETTING, addr: &desc->flags); /* inform read/write */ |
1325 | set_bit(WDM_READ, addr: &desc->flags); /* unblock read */ |
1326 | clear_bit(WDM_IN_USE, addr: &desc->flags); /* unblock write */ |
1327 | desc->rerr = -EINTR; |
1328 | spin_unlock_irq(lock: &desc->iuspin); |
1329 | wake_up_all(&desc->wait); |
1330 | mutex_lock(&desc->rlock); |
1331 | mutex_lock(&desc->wlock); |
1332 | poison_urbs(desc); |
1333 | cancel_work_sync(work: &desc->rxwork); |
1334 | cancel_work_sync(work: &desc->service_outs_intr); |
1335 | return 0; |
1336 | } |
1337 | |
1338 | static int wdm_post_reset(struct usb_interface *intf) |
1339 | { |
1340 | struct wdm_device *desc = wdm_find_device(intf); |
1341 | int rv; |
1342 | |
1343 | unpoison_urbs(desc); |
1344 | clear_bit(WDM_OVERFLOW, addr: &desc->flags); |
1345 | clear_bit(WDM_RESETTING, addr: &desc->flags); |
1346 | rv = recover_from_urb_loss(desc); |
1347 | mutex_unlock(lock: &desc->wlock); |
1348 | mutex_unlock(lock: &desc->rlock); |
1349 | return rv; |
1350 | } |
1351 | |
1352 | static struct usb_driver wdm_driver = { |
1353 | .name = "cdc_wdm", |
1354 | .probe = wdm_probe, |
1355 | .disconnect = wdm_disconnect, |
1356 | #ifdef CONFIG_PM |
1357 | .suspend = wdm_suspend, |
1358 | .resume = wdm_resume, |
1359 | .reset_resume = wdm_resume, |
1360 | #endif |
1361 | .pre_reset = wdm_pre_reset, |
1362 | .post_reset = wdm_post_reset, |
1363 | .id_table = wdm_ids, |
1364 | .supports_autosuspend = 1, |
1365 | .disable_hub_initiated_lpm = 1, |
1366 | }; |
1367 | |
1368 | module_usb_driver(wdm_driver); |
1369 | |
1370 | MODULE_AUTHOR(DRIVER_AUTHOR); |
1371 | MODULE_DESCRIPTION(DRIVER_DESC); |
1372 | MODULE_LICENSE("GPL"); |
1373 |
Definitions
- wdm_ids
- wdm_mutex
- wdm_device_list_lock
- wdm_device_list
- wdm_device
- wdm_driver
- wdm_find_device
- wdm_find_device_by_minor
- wdm_out_callback
- wdm_in_callback
- wdm_int_callback
- poison_urbs
- unpoison_urbs
- free_urbs
- cleanup
- wdm_write
- service_outstanding_interrupt
- wdm_read
- wdm_wait_for_response
- wdm_fsync
- wdm_flush
- wdm_poll
- wdm_open
- wdm_release
- wdm_ioctl
- wdm_fops
- wdm_class
- wdm_wwan_port_start
- wdm_wwan_port_stop
- wdm_wwan_port_tx_complete
- wdm_wwan_port_tx
- wdm_wwan_port_ops
- wdm_wwan_init
- wdm_wwan_deinit
- wdm_wwan_rx
- wdm_rxwork
- service_interrupt_work
- wdm_create
- wdm_manage_power
- wdm_probe
- usb_cdc_wdm_register
- wdm_disconnect
- wdm_suspend
- recover_from_urb_loss
- wdm_resume
- wdm_pre_reset
- wdm_post_reset
Improve your Profiling and Debugging skills
Find out more