1 | // SPDX-License-Identifier: ISC |
2 | /* |
3 | * Copyright (c) 2007-2011 Atheros Communications Inc. |
4 | * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc. |
5 | * Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/usb.h> |
10 | |
11 | #include "debug.h" |
12 | #include "core.h" |
13 | #include "bmi.h" |
14 | #include "hif.h" |
15 | #include "htc.h" |
16 | #include "usb.h" |
17 | |
18 | static void ath10k_usb_post_recv_transfers(struct ath10k *ar, |
19 | struct ath10k_usb_pipe *recv_pipe); |
20 | |
21 | /* inlined helper functions */ |
22 | |
23 | static inline enum ath10k_htc_ep_id |
24 | eid_from_htc_hdr(struct ath10k_htc_hdr *htc_hdr) |
25 | { |
26 | return (enum ath10k_htc_ep_id)htc_hdr->eid; |
27 | } |
28 | |
29 | static inline bool is_trailer_only_msg(struct ath10k_htc_hdr *htc_hdr) |
30 | { |
31 | return __le16_to_cpu(htc_hdr->len) == htc_hdr->trailer_len; |
32 | } |
33 | |
34 | /* pipe/urb operations */ |
35 | static struct ath10k_urb_context * |
36 | ath10k_usb_alloc_urb_from_pipe(struct ath10k_usb_pipe *pipe) |
37 | { |
38 | struct ath10k_urb_context *urb_context = NULL; |
39 | unsigned long flags; |
40 | |
41 | /* bail if this pipe is not initialized */ |
42 | if (!pipe->ar_usb) |
43 | return NULL; |
44 | |
45 | spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); |
46 | if (!list_empty(head: &pipe->urb_list_head)) { |
47 | urb_context = list_first_entry(&pipe->urb_list_head, |
48 | struct ath10k_urb_context, link); |
49 | list_del(entry: &urb_context->link); |
50 | pipe->urb_cnt--; |
51 | } |
52 | spin_unlock_irqrestore(lock: &pipe->ar_usb->cs_lock, flags); |
53 | |
54 | return urb_context; |
55 | } |
56 | |
57 | static void ath10k_usb_free_urb_to_pipe(struct ath10k_usb_pipe *pipe, |
58 | struct ath10k_urb_context *urb_context) |
59 | { |
60 | unsigned long flags; |
61 | |
62 | /* bail if this pipe is not initialized */ |
63 | if (!pipe->ar_usb) |
64 | return; |
65 | |
66 | spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); |
67 | |
68 | pipe->urb_cnt++; |
69 | list_add(new: &urb_context->link, head: &pipe->urb_list_head); |
70 | |
71 | spin_unlock_irqrestore(lock: &pipe->ar_usb->cs_lock, flags); |
72 | } |
73 | |
74 | static void ath10k_usb_cleanup_recv_urb(struct ath10k_urb_context *urb_context) |
75 | { |
76 | dev_kfree_skb(urb_context->skb); |
77 | urb_context->skb = NULL; |
78 | |
79 | ath10k_usb_free_urb_to_pipe(pipe: urb_context->pipe, urb_context); |
80 | } |
81 | |
82 | static void ath10k_usb_free_pipe_resources(struct ath10k *ar, |
83 | struct ath10k_usb_pipe *pipe) |
84 | { |
85 | struct ath10k_urb_context *urb_context; |
86 | |
87 | if (!pipe->ar_usb) { |
88 | /* nothing allocated for this pipe */ |
89 | return; |
90 | } |
91 | |
92 | ath10k_dbg(ar, ATH10K_DBG_USB, |
93 | "usb free resources lpipe %d hpipe 0x%x urbs %d avail %d\n" , |
94 | pipe->logical_pipe_num, pipe->usb_pipe_handle, |
95 | pipe->urb_alloc, pipe->urb_cnt); |
96 | |
97 | if (pipe->urb_alloc != pipe->urb_cnt) { |
98 | ath10k_dbg(ar, ATH10K_DBG_USB, |
99 | "usb urb leak lpipe %d hpipe 0x%x urbs %d avail %d\n" , |
100 | pipe->logical_pipe_num, pipe->usb_pipe_handle, |
101 | pipe->urb_alloc, pipe->urb_cnt); |
102 | } |
103 | |
104 | for (;;) { |
105 | urb_context = ath10k_usb_alloc_urb_from_pipe(pipe); |
106 | |
107 | if (!urb_context) |
108 | break; |
109 | |
110 | kfree(objp: urb_context); |
111 | } |
112 | } |
113 | |
114 | static void ath10k_usb_cleanup_pipe_resources(struct ath10k *ar) |
115 | { |
116 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
117 | int i; |
118 | |
119 | for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) |
120 | ath10k_usb_free_pipe_resources(ar, pipe: &ar_usb->pipes[i]); |
121 | } |
122 | |
123 | /* hif usb rx/tx completion functions */ |
124 | |
125 | static void ath10k_usb_recv_complete(struct urb *urb) |
126 | { |
127 | struct ath10k_urb_context *urb_context = urb->context; |
128 | struct ath10k_usb_pipe *pipe = urb_context->pipe; |
129 | struct ath10k *ar = pipe->ar_usb->ar; |
130 | struct sk_buff *skb; |
131 | int status = 0; |
132 | |
133 | ath10k_dbg(ar, ATH10K_DBG_USB_BULK, |
134 | "usb recv pipe %d stat %d len %d urb 0x%pK\n" , |
135 | pipe->logical_pipe_num, urb->status, urb->actual_length, |
136 | urb); |
137 | |
138 | if (urb->status != 0) { |
139 | status = -EIO; |
140 | switch (urb->status) { |
141 | case -ECONNRESET: |
142 | case -ENOENT: |
143 | case -ESHUTDOWN: |
144 | /* no need to spew these errors when device |
145 | * removed or urb killed due to driver shutdown |
146 | */ |
147 | status = -ECANCELED; |
148 | break; |
149 | default: |
150 | ath10k_dbg(ar, ATH10K_DBG_USB_BULK, |
151 | "usb recv pipe %d ep 0x%2.2x failed: %d\n" , |
152 | pipe->logical_pipe_num, |
153 | pipe->ep_address, urb->status); |
154 | break; |
155 | } |
156 | goto cleanup_recv_urb; |
157 | } |
158 | |
159 | if (urb->actual_length == 0) |
160 | goto cleanup_recv_urb; |
161 | |
162 | skb = urb_context->skb; |
163 | |
164 | /* we are going to pass it up */ |
165 | urb_context->skb = NULL; |
166 | skb_put(skb, len: urb->actual_length); |
167 | |
168 | /* note: queue implements a lock */ |
169 | skb_queue_tail(list: &pipe->io_comp_queue, newsk: skb); |
170 | schedule_work(work: &pipe->io_complete_work); |
171 | |
172 | cleanup_recv_urb: |
173 | ath10k_usb_cleanup_recv_urb(urb_context); |
174 | |
175 | if (status == 0 && |
176 | pipe->urb_cnt >= pipe->urb_cnt_thresh) { |
177 | /* our free urbs are piling up, post more transfers */ |
178 | ath10k_usb_post_recv_transfers(ar, recv_pipe: pipe); |
179 | } |
180 | } |
181 | |
182 | static void ath10k_usb_transmit_complete(struct urb *urb) |
183 | { |
184 | struct ath10k_urb_context *urb_context = urb->context; |
185 | struct ath10k_usb_pipe *pipe = urb_context->pipe; |
186 | struct ath10k *ar = pipe->ar_usb->ar; |
187 | struct sk_buff *skb; |
188 | |
189 | if (urb->status != 0) { |
190 | ath10k_dbg(ar, ATH10K_DBG_USB_BULK, |
191 | "pipe: %d, failed:%d\n" , |
192 | pipe->logical_pipe_num, urb->status); |
193 | } |
194 | |
195 | skb = urb_context->skb; |
196 | urb_context->skb = NULL; |
197 | ath10k_usb_free_urb_to_pipe(pipe: urb_context->pipe, urb_context); |
198 | |
199 | /* note: queue implements a lock */ |
200 | skb_queue_tail(list: &pipe->io_comp_queue, newsk: skb); |
201 | schedule_work(work: &pipe->io_complete_work); |
202 | } |
203 | |
204 | /* pipe operations */ |
205 | static void ath10k_usb_post_recv_transfers(struct ath10k *ar, |
206 | struct ath10k_usb_pipe *recv_pipe) |
207 | { |
208 | struct ath10k_urb_context *urb_context; |
209 | struct urb *urb; |
210 | int usb_status; |
211 | |
212 | for (;;) { |
213 | urb_context = ath10k_usb_alloc_urb_from_pipe(pipe: recv_pipe); |
214 | if (!urb_context) |
215 | break; |
216 | |
217 | urb_context->skb = dev_alloc_skb(ATH10K_USB_RX_BUFFER_SIZE); |
218 | if (!urb_context->skb) |
219 | goto err; |
220 | |
221 | urb = usb_alloc_urb(iso_packets: 0, GFP_ATOMIC); |
222 | if (!urb) |
223 | goto err; |
224 | |
225 | usb_fill_bulk_urb(urb, |
226 | dev: recv_pipe->ar_usb->udev, |
227 | pipe: recv_pipe->usb_pipe_handle, |
228 | transfer_buffer: urb_context->skb->data, |
229 | ATH10K_USB_RX_BUFFER_SIZE, |
230 | complete_fn: ath10k_usb_recv_complete, context: urb_context); |
231 | |
232 | ath10k_dbg(ar, ATH10K_DBG_USB_BULK, |
233 | "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%pK\n" , |
234 | recv_pipe->logical_pipe_num, |
235 | recv_pipe->usb_pipe_handle, recv_pipe->ep_address, |
236 | ATH10K_USB_RX_BUFFER_SIZE, urb_context->skb); |
237 | |
238 | usb_anchor_urb(urb, anchor: &recv_pipe->urb_submitted); |
239 | usb_status = usb_submit_urb(urb, GFP_ATOMIC); |
240 | |
241 | if (usb_status) { |
242 | ath10k_dbg(ar, ATH10K_DBG_USB_BULK, |
243 | "usb bulk recv failed: %d\n" , |
244 | usb_status); |
245 | usb_unanchor_urb(urb); |
246 | usb_free_urb(urb); |
247 | goto err; |
248 | } |
249 | usb_free_urb(urb); |
250 | } |
251 | |
252 | return; |
253 | |
254 | err: |
255 | ath10k_usb_cleanup_recv_urb(urb_context); |
256 | } |
257 | |
258 | static void ath10k_usb_flush_all(struct ath10k *ar) |
259 | { |
260 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
261 | int i; |
262 | |
263 | for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) { |
264 | if (ar_usb->pipes[i].ar_usb) { |
265 | usb_kill_anchored_urbs(anchor: &ar_usb->pipes[i].urb_submitted); |
266 | cancel_work_sync(work: &ar_usb->pipes[i].io_complete_work); |
267 | } |
268 | } |
269 | } |
270 | |
271 | static void ath10k_usb_start_recv_pipes(struct ath10k *ar) |
272 | { |
273 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
274 | |
275 | ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA].urb_cnt_thresh = 1; |
276 | |
277 | ath10k_usb_post_recv_transfers(ar, |
278 | recv_pipe: &ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA]); |
279 | } |
280 | |
281 | static void ath10k_usb_tx_complete(struct ath10k *ar, struct sk_buff *skb) |
282 | { |
283 | struct ath10k_htc_hdr *htc_hdr; |
284 | struct ath10k_htc_ep *ep; |
285 | |
286 | htc_hdr = (struct ath10k_htc_hdr *)skb->data; |
287 | ep = &ar->htc.endpoint[htc_hdr->eid]; |
288 | ath10k_htc_notify_tx_completion(ep, skb); |
289 | /* The TX complete handler now owns the skb... */ |
290 | } |
291 | |
292 | static void ath10k_usb_rx_complete(struct ath10k *ar, struct sk_buff *skb) |
293 | { |
294 | struct ath10k_htc *htc = &ar->htc; |
295 | struct ath10k_htc_hdr *htc_hdr; |
296 | enum ath10k_htc_ep_id eid; |
297 | struct ath10k_htc_ep *ep; |
298 | u16 payload_len; |
299 | u8 *trailer; |
300 | int ret; |
301 | |
302 | htc_hdr = (struct ath10k_htc_hdr *)skb->data; |
303 | eid = eid_from_htc_hdr(htc_hdr); |
304 | ep = &ar->htc.endpoint[eid]; |
305 | |
306 | if (ep->service_id == 0) { |
307 | ath10k_warn(ar, fmt: "ep %d is not connected\n" , eid); |
308 | goto out_free_skb; |
309 | } |
310 | |
311 | payload_len = le16_to_cpu(htc_hdr->len); |
312 | if (!payload_len) { |
313 | ath10k_warn(ar, fmt: "zero length frame received, firmware crashed?\n" ); |
314 | goto out_free_skb; |
315 | } |
316 | |
317 | if (payload_len < htc_hdr->trailer_len) { |
318 | ath10k_warn(ar, fmt: "malformed frame received, firmware crashed?\n" ); |
319 | goto out_free_skb; |
320 | } |
321 | |
322 | if (htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT) { |
323 | trailer = skb->data + sizeof(*htc_hdr) + payload_len - |
324 | htc_hdr->trailer_len; |
325 | |
326 | ret = ath10k_htc_process_trailer(htc, |
327 | buffer: trailer, |
328 | length: htc_hdr->trailer_len, |
329 | src_eid: eid, |
330 | NULL, |
331 | NULL); |
332 | if (ret) |
333 | goto out_free_skb; |
334 | |
335 | if (is_trailer_only_msg(htc_hdr)) |
336 | goto out_free_skb; |
337 | |
338 | /* strip off the trailer from the skb since it should not |
339 | * be passed on to upper layers |
340 | */ |
341 | skb_trim(skb, len: skb->len - htc_hdr->trailer_len); |
342 | } |
343 | |
344 | skb_pull(skb, len: sizeof(*htc_hdr)); |
345 | ep->ep_ops.ep_rx_complete(ar, skb); |
346 | /* The RX complete handler now owns the skb... */ |
347 | |
348 | if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) { |
349 | local_bh_disable(); |
350 | napi_schedule(n: &ar->napi); |
351 | local_bh_enable(); |
352 | } |
353 | |
354 | return; |
355 | |
356 | out_free_skb: |
357 | dev_kfree_skb(skb); |
358 | } |
359 | |
360 | static void ath10k_usb_io_comp_work(struct work_struct *work) |
361 | { |
362 | struct ath10k_usb_pipe *pipe = container_of(work, |
363 | struct ath10k_usb_pipe, |
364 | io_complete_work); |
365 | struct ath10k *ar = pipe->ar_usb->ar; |
366 | struct sk_buff *skb; |
367 | |
368 | while ((skb = skb_dequeue(list: &pipe->io_comp_queue))) { |
369 | if (pipe->flags & ATH10K_USB_PIPE_FLAG_TX) |
370 | ath10k_usb_tx_complete(ar, skb); |
371 | else |
372 | ath10k_usb_rx_complete(ar, skb); |
373 | } |
374 | } |
375 | |
376 | #define ATH10K_USB_MAX_DIAG_CMD (sizeof(struct ath10k_usb_ctrl_diag_cmd_write)) |
377 | #define ATH10K_USB_MAX_DIAG_RESP (sizeof(struct ath10k_usb_ctrl_diag_resp_read)) |
378 | |
379 | static void ath10k_usb_destroy(struct ath10k *ar) |
380 | { |
381 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
382 | |
383 | ath10k_usb_flush_all(ar); |
384 | ath10k_usb_cleanup_pipe_resources(ar); |
385 | usb_set_intfdata(intf: ar_usb->interface, NULL); |
386 | |
387 | kfree(objp: ar_usb->diag_cmd_buffer); |
388 | kfree(objp: ar_usb->diag_resp_buffer); |
389 | } |
390 | |
391 | static int ath10k_usb_hif_start(struct ath10k *ar) |
392 | { |
393 | int i; |
394 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
395 | |
396 | ath10k_core_napi_enable(ar); |
397 | ath10k_usb_start_recv_pipes(ar); |
398 | |
399 | /* set the TX resource avail threshold for each TX pipe */ |
400 | for (i = ATH10K_USB_PIPE_TX_CTRL; |
401 | i <= ATH10K_USB_PIPE_TX_DATA_HP; i++) { |
402 | ar_usb->pipes[i].urb_cnt_thresh = |
403 | ar_usb->pipes[i].urb_alloc / 2; |
404 | } |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | static int ath10k_usb_hif_tx_sg(struct ath10k *ar, u8 pipe_id, |
410 | struct ath10k_hif_sg_item *items, int n_items) |
411 | { |
412 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
413 | struct ath10k_usb_pipe *pipe = &ar_usb->pipes[pipe_id]; |
414 | struct ath10k_urb_context *urb_context; |
415 | struct sk_buff *skb; |
416 | struct urb *urb; |
417 | int ret, i; |
418 | |
419 | for (i = 0; i < n_items; i++) { |
420 | urb_context = ath10k_usb_alloc_urb_from_pipe(pipe); |
421 | if (!urb_context) { |
422 | ret = -ENOMEM; |
423 | goto err; |
424 | } |
425 | |
426 | skb = items[i].transfer_context; |
427 | urb_context->skb = skb; |
428 | |
429 | urb = usb_alloc_urb(iso_packets: 0, GFP_ATOMIC); |
430 | if (!urb) { |
431 | ret = -ENOMEM; |
432 | goto err_free_urb_to_pipe; |
433 | } |
434 | |
435 | usb_fill_bulk_urb(urb, |
436 | dev: ar_usb->udev, |
437 | pipe: pipe->usb_pipe_handle, |
438 | transfer_buffer: skb->data, |
439 | buffer_length: skb->len, |
440 | complete_fn: ath10k_usb_transmit_complete, context: urb_context); |
441 | |
442 | if (!(skb->len % pipe->max_packet_size)) { |
443 | /* hit a max packet boundary on this pipe */ |
444 | urb->transfer_flags |= URB_ZERO_PACKET; |
445 | } |
446 | |
447 | usb_anchor_urb(urb, anchor: &pipe->urb_submitted); |
448 | ret = usb_submit_urb(urb, GFP_ATOMIC); |
449 | if (ret) { |
450 | ath10k_dbg(ar, ATH10K_DBG_USB_BULK, |
451 | "usb bulk transmit failed: %d\n" , ret); |
452 | usb_unanchor_urb(urb); |
453 | usb_free_urb(urb); |
454 | ret = -EINVAL; |
455 | goto err_free_urb_to_pipe; |
456 | } |
457 | |
458 | usb_free_urb(urb); |
459 | } |
460 | |
461 | return 0; |
462 | |
463 | err_free_urb_to_pipe: |
464 | ath10k_usb_free_urb_to_pipe(pipe: urb_context->pipe, urb_context); |
465 | err: |
466 | return ret; |
467 | } |
468 | |
469 | static void ath10k_usb_hif_stop(struct ath10k *ar) |
470 | { |
471 | ath10k_usb_flush_all(ar); |
472 | ath10k_core_napi_sync_disable(ar); |
473 | } |
474 | |
475 | static u16 ath10k_usb_hif_get_free_queue_number(struct ath10k *ar, u8 pipe_id) |
476 | { |
477 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
478 | |
479 | return ar_usb->pipes[pipe_id].urb_cnt; |
480 | } |
481 | |
482 | static int ath10k_usb_submit_ctrl_out(struct ath10k *ar, |
483 | u8 req, u16 value, u16 index, void *data, |
484 | u32 size) |
485 | { |
486 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
487 | u8 *buf = NULL; |
488 | int ret; |
489 | |
490 | if (size > 0) { |
491 | buf = kmemdup(p: data, size, GFP_KERNEL); |
492 | if (!buf) |
493 | return -ENOMEM; |
494 | } |
495 | |
496 | /* note: if successful returns number of bytes transferred */ |
497 | ret = usb_control_msg(dev: ar_usb->udev, |
498 | usb_sndctrlpipe(ar_usb->udev, 0), |
499 | request: req, |
500 | USB_DIR_OUT | USB_TYPE_VENDOR | |
501 | USB_RECIP_DEVICE, value, index, data: buf, |
502 | size, timeout: 1000); |
503 | |
504 | if (ret < 0) { |
505 | ath10k_warn(ar, fmt: "Failed to submit usb control message: %d\n" , |
506 | ret); |
507 | kfree(objp: buf); |
508 | return ret; |
509 | } |
510 | |
511 | kfree(objp: buf); |
512 | |
513 | return 0; |
514 | } |
515 | |
516 | static int ath10k_usb_submit_ctrl_in(struct ath10k *ar, |
517 | u8 req, u16 value, u16 index, void *data, |
518 | u32 size) |
519 | { |
520 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
521 | u8 *buf = NULL; |
522 | int ret; |
523 | |
524 | if (size > 0) { |
525 | buf = kmalloc(size, GFP_KERNEL); |
526 | if (!buf) |
527 | return -ENOMEM; |
528 | } |
529 | |
530 | /* note: if successful returns number of bytes transferred */ |
531 | ret = usb_control_msg(dev: ar_usb->udev, |
532 | usb_rcvctrlpipe(ar_usb->udev, 0), |
533 | request: req, |
534 | USB_DIR_IN | USB_TYPE_VENDOR | |
535 | USB_RECIP_DEVICE, value, index, data: buf, |
536 | size, timeout: 2000); |
537 | |
538 | if (ret < 0) { |
539 | ath10k_warn(ar, fmt: "Failed to read usb control message: %d\n" , |
540 | ret); |
541 | kfree(objp: buf); |
542 | return ret; |
543 | } |
544 | |
545 | memcpy((u8 *)data, buf, size); |
546 | |
547 | kfree(objp: buf); |
548 | |
549 | return 0; |
550 | } |
551 | |
552 | static int ath10k_usb_ctrl_msg_exchange(struct ath10k *ar, |
553 | u8 req_val, u8 *req_buf, u32 req_len, |
554 | u8 resp_val, u8 *resp_buf, |
555 | u32 *resp_len) |
556 | { |
557 | int ret; |
558 | |
559 | /* send command */ |
560 | ret = ath10k_usb_submit_ctrl_out(ar, req: req_val, value: 0, index: 0, |
561 | data: req_buf, size: req_len); |
562 | if (ret) |
563 | goto err; |
564 | |
565 | /* get response */ |
566 | if (resp_buf) { |
567 | ret = ath10k_usb_submit_ctrl_in(ar, req: resp_val, value: 0, index: 0, |
568 | data: resp_buf, size: *resp_len); |
569 | if (ret) |
570 | goto err; |
571 | } |
572 | |
573 | return 0; |
574 | err: |
575 | return ret; |
576 | } |
577 | |
578 | static int ath10k_usb_hif_diag_read(struct ath10k *ar, u32 address, void *buf, |
579 | size_t buf_len) |
580 | { |
581 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
582 | struct ath10k_usb_ctrl_diag_cmd_read *cmd; |
583 | u32 resp_len; |
584 | int ret; |
585 | |
586 | if (buf_len < sizeof(struct ath10k_usb_ctrl_diag_resp_read)) |
587 | return -EINVAL; |
588 | |
589 | cmd = (struct ath10k_usb_ctrl_diag_cmd_read *)ar_usb->diag_cmd_buffer; |
590 | memset(cmd, 0, sizeof(*cmd)); |
591 | cmd->cmd = ATH10K_USB_CTRL_DIAG_CC_READ; |
592 | cmd->address = cpu_to_le32(address); |
593 | resp_len = sizeof(struct ath10k_usb_ctrl_diag_resp_read); |
594 | |
595 | ret = ath10k_usb_ctrl_msg_exchange(ar, |
596 | ATH10K_USB_CONTROL_REQ_DIAG_CMD, |
597 | req_buf: (u8 *)cmd, |
598 | req_len: sizeof(*cmd), |
599 | ATH10K_USB_CONTROL_REQ_DIAG_RESP, |
600 | resp_buf: ar_usb->diag_resp_buffer, resp_len: &resp_len); |
601 | if (ret) |
602 | return ret; |
603 | |
604 | if (resp_len != sizeof(struct ath10k_usb_ctrl_diag_resp_read)) |
605 | return -EMSGSIZE; |
606 | |
607 | memcpy(buf, ar_usb->diag_resp_buffer, |
608 | sizeof(struct ath10k_usb_ctrl_diag_resp_read)); |
609 | |
610 | return 0; |
611 | } |
612 | |
613 | static int ath10k_usb_hif_diag_write(struct ath10k *ar, u32 address, |
614 | const void *data, int nbytes) |
615 | { |
616 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
617 | struct ath10k_usb_ctrl_diag_cmd_write *cmd; |
618 | int ret; |
619 | |
620 | if (nbytes != sizeof(cmd->value)) |
621 | return -EINVAL; |
622 | |
623 | cmd = (struct ath10k_usb_ctrl_diag_cmd_write *)ar_usb->diag_cmd_buffer; |
624 | memset(cmd, 0, sizeof(*cmd)); |
625 | cmd->cmd = cpu_to_le32(ATH10K_USB_CTRL_DIAG_CC_WRITE); |
626 | cmd->address = cpu_to_le32(address); |
627 | memcpy(&cmd->value, data, nbytes); |
628 | |
629 | ret = ath10k_usb_ctrl_msg_exchange(ar, |
630 | ATH10K_USB_CONTROL_REQ_DIAG_CMD, |
631 | req_buf: (u8 *)cmd, |
632 | req_len: sizeof(*cmd), |
633 | resp_val: 0, NULL, NULL); |
634 | if (ret) |
635 | return ret; |
636 | |
637 | return 0; |
638 | } |
639 | |
640 | static int ath10k_usb_bmi_exchange_msg(struct ath10k *ar, |
641 | void *req, u32 req_len, |
642 | void *resp, u32 *resp_len) |
643 | { |
644 | int ret; |
645 | |
646 | if (req) { |
647 | ret = ath10k_usb_submit_ctrl_out(ar, |
648 | ATH10K_USB_CONTROL_REQ_SEND_BMI_CMD, |
649 | value: 0, index: 0, data: req, size: req_len); |
650 | if (ret) { |
651 | ath10k_warn(ar, |
652 | fmt: "unable to send the bmi data to the device: %d\n" , |
653 | ret); |
654 | return ret; |
655 | } |
656 | } |
657 | |
658 | if (resp) { |
659 | ret = ath10k_usb_submit_ctrl_in(ar, |
660 | ATH10K_USB_CONTROL_REQ_RECV_BMI_RESP, |
661 | value: 0, index: 0, data: resp, size: *resp_len); |
662 | if (ret) { |
663 | ath10k_warn(ar, |
664 | fmt: "Unable to read the bmi data from the device: %d\n" , |
665 | ret); |
666 | return ret; |
667 | } |
668 | } |
669 | |
670 | return 0; |
671 | } |
672 | |
673 | static void ath10k_usb_hif_get_default_pipe(struct ath10k *ar, |
674 | u8 *ul_pipe, u8 *dl_pipe) |
675 | { |
676 | *ul_pipe = ATH10K_USB_PIPE_TX_CTRL; |
677 | *dl_pipe = ATH10K_USB_PIPE_RX_CTRL; |
678 | } |
679 | |
680 | static int ath10k_usb_hif_map_service_to_pipe(struct ath10k *ar, u16 svc_id, |
681 | u8 *ul_pipe, u8 *dl_pipe) |
682 | { |
683 | switch (svc_id) { |
684 | case ATH10K_HTC_SVC_ID_RSVD_CTRL: |
685 | case ATH10K_HTC_SVC_ID_WMI_CONTROL: |
686 | *ul_pipe = ATH10K_USB_PIPE_TX_CTRL; |
687 | /* due to large control packets, shift to data pipe */ |
688 | *dl_pipe = ATH10K_USB_PIPE_RX_DATA; |
689 | break; |
690 | case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: |
691 | *ul_pipe = ATH10K_USB_PIPE_TX_DATA_LP; |
692 | /* Disable rxdata2 directly, it will be enabled |
693 | * if FW enable rxdata2 |
694 | */ |
695 | *dl_pipe = ATH10K_USB_PIPE_RX_DATA; |
696 | break; |
697 | default: |
698 | return -EPERM; |
699 | } |
700 | |
701 | return 0; |
702 | } |
703 | |
704 | static int ath10k_usb_hif_power_up(struct ath10k *ar, |
705 | enum ath10k_firmware_mode fw_mode) |
706 | { |
707 | return 0; |
708 | } |
709 | |
710 | static void ath10k_usb_hif_power_down(struct ath10k *ar) |
711 | { |
712 | ath10k_usb_flush_all(ar); |
713 | } |
714 | |
715 | #ifdef CONFIG_PM |
716 | |
717 | static int ath10k_usb_hif_suspend(struct ath10k *ar) |
718 | { |
719 | return -EOPNOTSUPP; |
720 | } |
721 | |
722 | static int ath10k_usb_hif_resume(struct ath10k *ar) |
723 | { |
724 | return -EOPNOTSUPP; |
725 | } |
726 | #endif |
727 | |
728 | static const struct ath10k_hif_ops ath10k_usb_hif_ops = { |
729 | .tx_sg = ath10k_usb_hif_tx_sg, |
730 | .diag_read = ath10k_usb_hif_diag_read, |
731 | .diag_write = ath10k_usb_hif_diag_write, |
732 | .exchange_bmi_msg = ath10k_usb_bmi_exchange_msg, |
733 | .start = ath10k_usb_hif_start, |
734 | .stop = ath10k_usb_hif_stop, |
735 | .map_service_to_pipe = ath10k_usb_hif_map_service_to_pipe, |
736 | .get_default_pipe = ath10k_usb_hif_get_default_pipe, |
737 | .get_free_queue_number = ath10k_usb_hif_get_free_queue_number, |
738 | .power_up = ath10k_usb_hif_power_up, |
739 | .power_down = ath10k_usb_hif_power_down, |
740 | #ifdef CONFIG_PM |
741 | .suspend = ath10k_usb_hif_suspend, |
742 | .resume = ath10k_usb_hif_resume, |
743 | #endif |
744 | }; |
745 | |
746 | static u8 ath10k_usb_get_logical_pipe_num(u8 ep_address, int *urb_count) |
747 | { |
748 | u8 pipe_num = ATH10K_USB_PIPE_INVALID; |
749 | |
750 | switch (ep_address) { |
751 | case ATH10K_USB_EP_ADDR_APP_CTRL_IN: |
752 | pipe_num = ATH10K_USB_PIPE_RX_CTRL; |
753 | *urb_count = RX_URB_COUNT; |
754 | break; |
755 | case ATH10K_USB_EP_ADDR_APP_DATA_IN: |
756 | pipe_num = ATH10K_USB_PIPE_RX_DATA; |
757 | *urb_count = RX_URB_COUNT; |
758 | break; |
759 | case ATH10K_USB_EP_ADDR_APP_INT_IN: |
760 | pipe_num = ATH10K_USB_PIPE_RX_INT; |
761 | *urb_count = RX_URB_COUNT; |
762 | break; |
763 | case ATH10K_USB_EP_ADDR_APP_DATA2_IN: |
764 | pipe_num = ATH10K_USB_PIPE_RX_DATA2; |
765 | *urb_count = RX_URB_COUNT; |
766 | break; |
767 | case ATH10K_USB_EP_ADDR_APP_CTRL_OUT: |
768 | pipe_num = ATH10K_USB_PIPE_TX_CTRL; |
769 | *urb_count = TX_URB_COUNT; |
770 | break; |
771 | case ATH10K_USB_EP_ADDR_APP_DATA_LP_OUT: |
772 | pipe_num = ATH10K_USB_PIPE_TX_DATA_LP; |
773 | *urb_count = TX_URB_COUNT; |
774 | break; |
775 | case ATH10K_USB_EP_ADDR_APP_DATA_MP_OUT: |
776 | pipe_num = ATH10K_USB_PIPE_TX_DATA_MP; |
777 | *urb_count = TX_URB_COUNT; |
778 | break; |
779 | case ATH10K_USB_EP_ADDR_APP_DATA_HP_OUT: |
780 | pipe_num = ATH10K_USB_PIPE_TX_DATA_HP; |
781 | *urb_count = TX_URB_COUNT; |
782 | break; |
783 | default: |
784 | /* note: there may be endpoints not currently used */ |
785 | break; |
786 | } |
787 | |
788 | return pipe_num; |
789 | } |
790 | |
791 | static int ath10k_usb_alloc_pipe_resources(struct ath10k *ar, |
792 | struct ath10k_usb_pipe *pipe, |
793 | int urb_cnt) |
794 | { |
795 | struct ath10k_urb_context *urb_context; |
796 | int i; |
797 | |
798 | INIT_LIST_HEAD(list: &pipe->urb_list_head); |
799 | init_usb_anchor(anchor: &pipe->urb_submitted); |
800 | |
801 | for (i = 0; i < urb_cnt; i++) { |
802 | urb_context = kzalloc(size: sizeof(*urb_context), GFP_KERNEL); |
803 | if (!urb_context) |
804 | return -ENOMEM; |
805 | |
806 | urb_context->pipe = pipe; |
807 | |
808 | /* we are only allocate the urb contexts here, the actual URB |
809 | * is allocated from the kernel as needed to do a transaction |
810 | */ |
811 | pipe->urb_alloc++; |
812 | ath10k_usb_free_urb_to_pipe(pipe, urb_context); |
813 | } |
814 | |
815 | ath10k_dbg(ar, ATH10K_DBG_USB, |
816 | "usb alloc resources lpipe %d hpipe 0x%x urbs %d\n" , |
817 | pipe->logical_pipe_num, pipe->usb_pipe_handle, |
818 | pipe->urb_alloc); |
819 | |
820 | return 0; |
821 | } |
822 | |
823 | static int ath10k_usb_setup_pipe_resources(struct ath10k *ar, |
824 | struct usb_interface *interface) |
825 | { |
826 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
827 | struct usb_host_interface *iface_desc = interface->cur_altsetting; |
828 | struct usb_endpoint_descriptor *endpoint; |
829 | struct ath10k_usb_pipe *pipe; |
830 | int ret, i, urbcount; |
831 | u8 pipe_num; |
832 | |
833 | ath10k_dbg(ar, ATH10K_DBG_USB, "usb setting up pipes using interface\n" ); |
834 | |
835 | /* walk descriptors and setup pipes */ |
836 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
837 | endpoint = &iface_desc->endpoint[i].desc; |
838 | |
839 | if (ATH10K_USB_IS_BULK_EP(endpoint->bmAttributes)) { |
840 | ath10k_dbg(ar, ATH10K_DBG_USB, |
841 | "usb %s bulk ep 0x%2.2x maxpktsz %d\n" , |
842 | ATH10K_USB_IS_DIR_IN |
843 | (endpoint->bEndpointAddress) ? |
844 | "rx" : "tx" , endpoint->bEndpointAddress, |
845 | le16_to_cpu(endpoint->wMaxPacketSize)); |
846 | } else if (ATH10K_USB_IS_INT_EP(endpoint->bmAttributes)) { |
847 | ath10k_dbg(ar, ATH10K_DBG_USB, |
848 | "usb %s int ep 0x%2.2x maxpktsz %d interval %d\n" , |
849 | ATH10K_USB_IS_DIR_IN |
850 | (endpoint->bEndpointAddress) ? |
851 | "rx" : "tx" , endpoint->bEndpointAddress, |
852 | le16_to_cpu(endpoint->wMaxPacketSize), |
853 | endpoint->bInterval); |
854 | } else if (ATH10K_USB_IS_ISOC_EP(endpoint->bmAttributes)) { |
855 | /* TODO for ISO */ |
856 | ath10k_dbg(ar, ATH10K_DBG_USB, |
857 | "usb %s isoc ep 0x%2.2x maxpktsz %d interval %d\n" , |
858 | ATH10K_USB_IS_DIR_IN |
859 | (endpoint->bEndpointAddress) ? |
860 | "rx" : "tx" , endpoint->bEndpointAddress, |
861 | le16_to_cpu(endpoint->wMaxPacketSize), |
862 | endpoint->bInterval); |
863 | } |
864 | |
865 | /* Ignore broken descriptors. */ |
866 | if (usb_endpoint_maxp(epd: endpoint) == 0) |
867 | continue; |
868 | |
869 | urbcount = 0; |
870 | |
871 | pipe_num = |
872 | ath10k_usb_get_logical_pipe_num(ep_address: endpoint->bEndpointAddress, |
873 | urb_count: &urbcount); |
874 | if (pipe_num == ATH10K_USB_PIPE_INVALID) |
875 | continue; |
876 | |
877 | pipe = &ar_usb->pipes[pipe_num]; |
878 | if (pipe->ar_usb) |
879 | /* hmmm..pipe was already setup */ |
880 | continue; |
881 | |
882 | pipe->ar_usb = ar_usb; |
883 | pipe->logical_pipe_num = pipe_num; |
884 | pipe->ep_address = endpoint->bEndpointAddress; |
885 | pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); |
886 | |
887 | if (ATH10K_USB_IS_BULK_EP(endpoint->bmAttributes)) { |
888 | if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { |
889 | pipe->usb_pipe_handle = |
890 | usb_rcvbulkpipe(ar_usb->udev, |
891 | pipe->ep_address); |
892 | } else { |
893 | pipe->usb_pipe_handle = |
894 | usb_sndbulkpipe(ar_usb->udev, |
895 | pipe->ep_address); |
896 | } |
897 | } else if (ATH10K_USB_IS_INT_EP(endpoint->bmAttributes)) { |
898 | if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { |
899 | pipe->usb_pipe_handle = |
900 | usb_rcvintpipe(ar_usb->udev, |
901 | pipe->ep_address); |
902 | } else { |
903 | pipe->usb_pipe_handle = |
904 | usb_sndintpipe(ar_usb->udev, |
905 | pipe->ep_address); |
906 | } |
907 | } else if (ATH10K_USB_IS_ISOC_EP(endpoint->bmAttributes)) { |
908 | /* TODO for ISO */ |
909 | if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { |
910 | pipe->usb_pipe_handle = |
911 | usb_rcvisocpipe(ar_usb->udev, |
912 | pipe->ep_address); |
913 | } else { |
914 | pipe->usb_pipe_handle = |
915 | usb_sndisocpipe(ar_usb->udev, |
916 | pipe->ep_address); |
917 | } |
918 | } |
919 | |
920 | pipe->ep_desc = endpoint; |
921 | |
922 | if (!ATH10K_USB_IS_DIR_IN(pipe->ep_address)) |
923 | pipe->flags |= ATH10K_USB_PIPE_FLAG_TX; |
924 | |
925 | ret = ath10k_usb_alloc_pipe_resources(ar, pipe, urb_cnt: urbcount); |
926 | if (ret) |
927 | return ret; |
928 | } |
929 | |
930 | return 0; |
931 | } |
932 | |
933 | static int ath10k_usb_create(struct ath10k *ar, |
934 | struct usb_interface *interface) |
935 | { |
936 | struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); |
937 | struct usb_device *dev = interface_to_usbdev(interface); |
938 | struct ath10k_usb_pipe *pipe; |
939 | int ret, i; |
940 | |
941 | usb_set_intfdata(intf: interface, data: ar_usb); |
942 | spin_lock_init(&ar_usb->cs_lock); |
943 | ar_usb->udev = dev; |
944 | ar_usb->interface = interface; |
945 | |
946 | for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) { |
947 | pipe = &ar_usb->pipes[i]; |
948 | INIT_WORK(&pipe->io_complete_work, |
949 | ath10k_usb_io_comp_work); |
950 | skb_queue_head_init(list: &pipe->io_comp_queue); |
951 | } |
952 | |
953 | ar_usb->diag_cmd_buffer = kzalloc(ATH10K_USB_MAX_DIAG_CMD, GFP_KERNEL); |
954 | if (!ar_usb->diag_cmd_buffer) { |
955 | ret = -ENOMEM; |
956 | goto err; |
957 | } |
958 | |
959 | ar_usb->diag_resp_buffer = kzalloc(ATH10K_USB_MAX_DIAG_RESP, |
960 | GFP_KERNEL); |
961 | if (!ar_usb->diag_resp_buffer) { |
962 | ret = -ENOMEM; |
963 | goto err; |
964 | } |
965 | |
966 | ret = ath10k_usb_setup_pipe_resources(ar, interface); |
967 | if (ret) |
968 | goto err; |
969 | |
970 | return 0; |
971 | |
972 | err: |
973 | ath10k_usb_destroy(ar); |
974 | return ret; |
975 | } |
976 | |
977 | static int ath10k_usb_napi_poll(struct napi_struct *ctx, int budget) |
978 | { |
979 | struct ath10k *ar = container_of(ctx, struct ath10k, napi); |
980 | int done; |
981 | |
982 | done = ath10k_htt_rx_hl_indication(ar, budget); |
983 | ath10k_dbg(ar, ATH10K_DBG_USB, "napi poll: done: %d, budget:%d\n" , done, budget); |
984 | |
985 | if (done < budget) |
986 | napi_complete_done(n: ctx, work_done: done); |
987 | |
988 | return done; |
989 | } |
990 | |
991 | /* ath10k usb driver registered functions */ |
992 | static int ath10k_usb_probe(struct usb_interface *interface, |
993 | const struct usb_device_id *id) |
994 | { |
995 | struct ath10k *ar; |
996 | struct ath10k_usb *ar_usb; |
997 | struct usb_device *dev = interface_to_usbdev(interface); |
998 | int ret, vendor_id, product_id; |
999 | enum ath10k_hw_rev hw_rev; |
1000 | struct ath10k_bus_params bus_params = {}; |
1001 | |
1002 | /* Assumption: All USB based chipsets (so far) are QCA9377 based. |
1003 | * If there will be newer chipsets that does not use the hw reg |
1004 | * setup as defined in qca6174_regs and qca6174_values, this |
1005 | * assumption is no longer valid and hw_rev must be setup differently |
1006 | * depending on chipset. |
1007 | */ |
1008 | hw_rev = ATH10K_HW_QCA9377; |
1009 | |
1010 | ar = ath10k_core_create(priv_size: sizeof(*ar_usb), dev: &dev->dev, bus: ATH10K_BUS_USB, |
1011 | hw_rev, hif_ops: &ath10k_usb_hif_ops); |
1012 | if (!ar) { |
1013 | dev_err(&dev->dev, "failed to allocate core\n" ); |
1014 | return -ENOMEM; |
1015 | } |
1016 | |
1017 | netif_napi_add(dev: &ar->napi_dev, napi: &ar->napi, poll: ath10k_usb_napi_poll); |
1018 | |
1019 | usb_get_dev(dev); |
1020 | vendor_id = le16_to_cpu(dev->descriptor.idVendor); |
1021 | product_id = le16_to_cpu(dev->descriptor.idProduct); |
1022 | |
1023 | ath10k_dbg(ar, ATH10K_DBG_BOOT, |
1024 | "usb new func vendor 0x%04x product 0x%04x\n" , |
1025 | vendor_id, product_id); |
1026 | |
1027 | ar_usb = ath10k_usb_priv(ar); |
1028 | ret = ath10k_usb_create(ar, interface); |
1029 | if (ret) |
1030 | goto err; |
1031 | ar_usb->ar = ar; |
1032 | |
1033 | ar->dev_id = product_id; |
1034 | ar->id.vendor = vendor_id; |
1035 | ar->id.device = product_id; |
1036 | |
1037 | bus_params.dev_type = ATH10K_DEV_TYPE_HL; |
1038 | /* TODO: don't know yet how to get chip_id with USB */ |
1039 | bus_params.chip_id = 0; |
1040 | bus_params.hl_msdu_ids = true; |
1041 | ret = ath10k_core_register(ar, bus_params: &bus_params); |
1042 | if (ret) { |
1043 | ath10k_warn(ar, fmt: "failed to register driver core: %d\n" , ret); |
1044 | goto err_usb_destroy; |
1045 | } |
1046 | |
1047 | /* TODO: remove this once USB support is fully implemented */ |
1048 | ath10k_warn(ar, fmt: "Warning: ath10k USB support is incomplete, don't expect anything to work!\n" ); |
1049 | |
1050 | return 0; |
1051 | |
1052 | err_usb_destroy: |
1053 | ath10k_usb_destroy(ar); |
1054 | |
1055 | err: |
1056 | ath10k_core_destroy(ar); |
1057 | |
1058 | usb_put_dev(dev); |
1059 | |
1060 | return ret; |
1061 | } |
1062 | |
1063 | static void ath10k_usb_remove(struct usb_interface *interface) |
1064 | { |
1065 | struct ath10k_usb *ar_usb; |
1066 | |
1067 | ar_usb = usb_get_intfdata(intf: interface); |
1068 | if (!ar_usb) |
1069 | return; |
1070 | |
1071 | ath10k_core_unregister(ar: ar_usb->ar); |
1072 | netif_napi_del(napi: &ar_usb->ar->napi); |
1073 | ath10k_usb_destroy(ar: ar_usb->ar); |
1074 | usb_put_dev(interface_to_usbdev(interface)); |
1075 | ath10k_core_destroy(ar: ar_usb->ar); |
1076 | } |
1077 | |
1078 | #ifdef CONFIG_PM |
1079 | |
1080 | static int ath10k_usb_pm_suspend(struct usb_interface *interface, |
1081 | pm_message_t message) |
1082 | { |
1083 | struct ath10k_usb *ar_usb = usb_get_intfdata(intf: interface); |
1084 | |
1085 | ath10k_usb_flush_all(ar: ar_usb->ar); |
1086 | return 0; |
1087 | } |
1088 | |
1089 | static int ath10k_usb_pm_resume(struct usb_interface *interface) |
1090 | { |
1091 | struct ath10k_usb *ar_usb = usb_get_intfdata(intf: interface); |
1092 | struct ath10k *ar = ar_usb->ar; |
1093 | |
1094 | ath10k_usb_post_recv_transfers(ar, |
1095 | recv_pipe: &ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA]); |
1096 | |
1097 | return 0; |
1098 | } |
1099 | |
1100 | #else |
1101 | |
1102 | #define ath10k_usb_pm_suspend NULL |
1103 | #define ath10k_usb_pm_resume NULL |
1104 | |
1105 | #endif |
1106 | |
1107 | /* table of devices that work with this driver */ |
1108 | static struct usb_device_id ath10k_usb_ids[] = { |
1109 | {USB_DEVICE(0x13b1, 0x0042)}, /* Linksys WUSB6100M */ |
1110 | { /* Terminating entry */ }, |
1111 | }; |
1112 | |
1113 | MODULE_DEVICE_TABLE(usb, ath10k_usb_ids); |
1114 | |
1115 | static struct usb_driver ath10k_usb_driver = { |
1116 | .name = "ath10k_usb" , |
1117 | .probe = ath10k_usb_probe, |
1118 | .suspend = ath10k_usb_pm_suspend, |
1119 | .resume = ath10k_usb_pm_resume, |
1120 | .disconnect = ath10k_usb_remove, |
1121 | .id_table = ath10k_usb_ids, |
1122 | .supports_autosuspend = true, |
1123 | .disable_hub_initiated_lpm = 1, |
1124 | }; |
1125 | |
1126 | module_usb_driver(ath10k_usb_driver); |
1127 | |
1128 | MODULE_AUTHOR("Atheros Communications, Inc." ); |
1129 | MODULE_DESCRIPTION("Driver support for Qualcomm Atheros USB 802.11ac WLAN devices" ); |
1130 | MODULE_LICENSE("Dual BSD/GPL" ); |
1131 | |