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