162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2007-2011 Atheros Communications Inc. 462306a36Sopenharmony_ci * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc. 562306a36Sopenharmony_ci * Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/usb.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "debug.h" 1262306a36Sopenharmony_ci#include "core.h" 1362306a36Sopenharmony_ci#include "bmi.h" 1462306a36Sopenharmony_ci#include "hif.h" 1562306a36Sopenharmony_ci#include "htc.h" 1662306a36Sopenharmony_ci#include "usb.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic void ath10k_usb_post_recv_transfers(struct ath10k *ar, 1962306a36Sopenharmony_ci struct ath10k_usb_pipe *recv_pipe); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* inlined helper functions */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic inline enum ath10k_htc_ep_id 2462306a36Sopenharmony_cieid_from_htc_hdr(struct ath10k_htc_hdr *htc_hdr) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci return (enum ath10k_htc_ep_id)htc_hdr->eid; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic inline bool is_trailer_only_msg(struct ath10k_htc_hdr *htc_hdr) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci return __le16_to_cpu(htc_hdr->len) == htc_hdr->trailer_len; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* pipe/urb operations */ 3562306a36Sopenharmony_cistatic struct ath10k_urb_context * 3662306a36Sopenharmony_ciath10k_usb_alloc_urb_from_pipe(struct ath10k_usb_pipe *pipe) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct ath10k_urb_context *urb_context = NULL; 3962306a36Sopenharmony_ci unsigned long flags; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* bail if this pipe is not initialized */ 4262306a36Sopenharmony_ci if (!pipe->ar_usb) 4362306a36Sopenharmony_ci return NULL; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); 4662306a36Sopenharmony_ci if (!list_empty(&pipe->urb_list_head)) { 4762306a36Sopenharmony_ci urb_context = list_first_entry(&pipe->urb_list_head, 4862306a36Sopenharmony_ci struct ath10k_urb_context, link); 4962306a36Sopenharmony_ci list_del(&urb_context->link); 5062306a36Sopenharmony_ci pipe->urb_cnt--; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return urb_context; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void ath10k_usb_free_urb_to_pipe(struct ath10k_usb_pipe *pipe, 5862306a36Sopenharmony_ci struct ath10k_urb_context *urb_context) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci unsigned long flags; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* bail if this pipe is not initialized */ 6362306a36Sopenharmony_ci if (!pipe->ar_usb) 6462306a36Sopenharmony_ci return; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci pipe->urb_cnt++; 6962306a36Sopenharmony_ci list_add(&urb_context->link, &pipe->urb_list_head); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void ath10k_usb_cleanup_recv_urb(struct ath10k_urb_context *urb_context) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci dev_kfree_skb(urb_context->skb); 7762306a36Sopenharmony_ci urb_context->skb = NULL; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ath10k_usb_free_urb_to_pipe(urb_context->pipe, urb_context); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void ath10k_usb_free_pipe_resources(struct ath10k *ar, 8362306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct ath10k_urb_context *urb_context; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!pipe->ar_usb) { 8862306a36Sopenharmony_ci /* nothing allocated for this pipe */ 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, 9362306a36Sopenharmony_ci "usb free resources lpipe %d hpipe 0x%x urbs %d avail %d\n", 9462306a36Sopenharmony_ci pipe->logical_pipe_num, pipe->usb_pipe_handle, 9562306a36Sopenharmony_ci pipe->urb_alloc, pipe->urb_cnt); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (pipe->urb_alloc != pipe->urb_cnt) { 9862306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, 9962306a36Sopenharmony_ci "usb urb leak lpipe %d hpipe 0x%x urbs %d avail %d\n", 10062306a36Sopenharmony_ci pipe->logical_pipe_num, pipe->usb_pipe_handle, 10162306a36Sopenharmony_ci pipe->urb_alloc, pipe->urb_cnt); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (;;) { 10562306a36Sopenharmony_ci urb_context = ath10k_usb_alloc_urb_from_pipe(pipe); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!urb_context) 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci kfree(urb_context); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void ath10k_usb_cleanup_pipe_resources(struct ath10k *ar) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) 12062306a36Sopenharmony_ci ath10k_usb_free_pipe_resources(ar, &ar_usb->pipes[i]); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* hif usb rx/tx completion functions */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void ath10k_usb_recv_complete(struct urb *urb) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct ath10k_urb_context *urb_context = urb->context; 12862306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe = urb_context->pipe; 12962306a36Sopenharmony_ci struct ath10k *ar = pipe->ar_usb->ar; 13062306a36Sopenharmony_ci struct sk_buff *skb; 13162306a36Sopenharmony_ci int status = 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB_BULK, 13462306a36Sopenharmony_ci "usb recv pipe %d stat %d len %d urb 0x%pK\n", 13562306a36Sopenharmony_ci pipe->logical_pipe_num, urb->status, urb->actual_length, 13662306a36Sopenharmony_ci urb); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (urb->status != 0) { 13962306a36Sopenharmony_ci status = -EIO; 14062306a36Sopenharmony_ci switch (urb->status) { 14162306a36Sopenharmony_ci case -ECONNRESET: 14262306a36Sopenharmony_ci case -ENOENT: 14362306a36Sopenharmony_ci case -ESHUTDOWN: 14462306a36Sopenharmony_ci /* no need to spew these errors when device 14562306a36Sopenharmony_ci * removed or urb killed due to driver shutdown 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci status = -ECANCELED; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci default: 15062306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB_BULK, 15162306a36Sopenharmony_ci "usb recv pipe %d ep 0x%2.2x failed: %d\n", 15262306a36Sopenharmony_ci pipe->logical_pipe_num, 15362306a36Sopenharmony_ci pipe->ep_address, urb->status); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci goto cleanup_recv_urb; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (urb->actual_length == 0) 16062306a36Sopenharmony_ci goto cleanup_recv_urb; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci skb = urb_context->skb; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* we are going to pass it up */ 16562306a36Sopenharmony_ci urb_context->skb = NULL; 16662306a36Sopenharmony_ci skb_put(skb, urb->actual_length); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* note: queue implements a lock */ 16962306a36Sopenharmony_ci skb_queue_tail(&pipe->io_comp_queue, skb); 17062306a36Sopenharmony_ci schedule_work(&pipe->io_complete_work); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cicleanup_recv_urb: 17362306a36Sopenharmony_ci ath10k_usb_cleanup_recv_urb(urb_context); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (status == 0 && 17662306a36Sopenharmony_ci pipe->urb_cnt >= pipe->urb_cnt_thresh) { 17762306a36Sopenharmony_ci /* our free urbs are piling up, post more transfers */ 17862306a36Sopenharmony_ci ath10k_usb_post_recv_transfers(ar, pipe); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void ath10k_usb_transmit_complete(struct urb *urb) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct ath10k_urb_context *urb_context = urb->context; 18562306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe = urb_context->pipe; 18662306a36Sopenharmony_ci struct ath10k *ar = pipe->ar_usb->ar; 18762306a36Sopenharmony_ci struct sk_buff *skb; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (urb->status != 0) { 19062306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB_BULK, 19162306a36Sopenharmony_ci "pipe: %d, failed:%d\n", 19262306a36Sopenharmony_ci pipe->logical_pipe_num, urb->status); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci skb = urb_context->skb; 19662306a36Sopenharmony_ci urb_context->skb = NULL; 19762306a36Sopenharmony_ci ath10k_usb_free_urb_to_pipe(urb_context->pipe, urb_context); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* note: queue implements a lock */ 20062306a36Sopenharmony_ci skb_queue_tail(&pipe->io_comp_queue, skb); 20162306a36Sopenharmony_ci schedule_work(&pipe->io_complete_work); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* pipe operations */ 20562306a36Sopenharmony_cistatic void ath10k_usb_post_recv_transfers(struct ath10k *ar, 20662306a36Sopenharmony_ci struct ath10k_usb_pipe *recv_pipe) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct ath10k_urb_context *urb_context; 20962306a36Sopenharmony_ci struct urb *urb; 21062306a36Sopenharmony_ci int usb_status; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (;;) { 21362306a36Sopenharmony_ci urb_context = ath10k_usb_alloc_urb_from_pipe(recv_pipe); 21462306a36Sopenharmony_ci if (!urb_context) 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci urb_context->skb = dev_alloc_skb(ATH10K_USB_RX_BUFFER_SIZE); 21862306a36Sopenharmony_ci if (!urb_context->skb) 21962306a36Sopenharmony_ci goto err; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 22262306a36Sopenharmony_ci if (!urb) 22362306a36Sopenharmony_ci goto err; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci usb_fill_bulk_urb(urb, 22662306a36Sopenharmony_ci recv_pipe->ar_usb->udev, 22762306a36Sopenharmony_ci recv_pipe->usb_pipe_handle, 22862306a36Sopenharmony_ci urb_context->skb->data, 22962306a36Sopenharmony_ci ATH10K_USB_RX_BUFFER_SIZE, 23062306a36Sopenharmony_ci ath10k_usb_recv_complete, urb_context); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB_BULK, 23362306a36Sopenharmony_ci "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%pK\n", 23462306a36Sopenharmony_ci recv_pipe->logical_pipe_num, 23562306a36Sopenharmony_ci recv_pipe->usb_pipe_handle, recv_pipe->ep_address, 23662306a36Sopenharmony_ci ATH10K_USB_RX_BUFFER_SIZE, urb_context->skb); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci usb_anchor_urb(urb, &recv_pipe->urb_submitted); 23962306a36Sopenharmony_ci usb_status = usb_submit_urb(urb, GFP_ATOMIC); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (usb_status) { 24262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB_BULK, 24362306a36Sopenharmony_ci "usb bulk recv failed: %d\n", 24462306a36Sopenharmony_ci usb_status); 24562306a36Sopenharmony_ci usb_unanchor_urb(urb); 24662306a36Sopenharmony_ci usb_free_urb(urb); 24762306a36Sopenharmony_ci goto err; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci usb_free_urb(urb); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cierr: 25562306a36Sopenharmony_ci ath10k_usb_cleanup_recv_urb(urb_context); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void ath10k_usb_flush_all(struct ath10k *ar) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 26162306a36Sopenharmony_ci int i; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) { 26462306a36Sopenharmony_ci if (ar_usb->pipes[i].ar_usb) { 26562306a36Sopenharmony_ci usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted); 26662306a36Sopenharmony_ci cancel_work_sync(&ar_usb->pipes[i].io_complete_work); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void ath10k_usb_start_recv_pipes(struct ath10k *ar) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA].urb_cnt_thresh = 1; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ath10k_usb_post_recv_transfers(ar, 27862306a36Sopenharmony_ci &ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA]); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void ath10k_usb_tx_complete(struct ath10k *ar, struct sk_buff *skb) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct ath10k_htc_hdr *htc_hdr; 28462306a36Sopenharmony_ci struct ath10k_htc_ep *ep; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci htc_hdr = (struct ath10k_htc_hdr *)skb->data; 28762306a36Sopenharmony_ci ep = &ar->htc.endpoint[htc_hdr->eid]; 28862306a36Sopenharmony_ci ath10k_htc_notify_tx_completion(ep, skb); 28962306a36Sopenharmony_ci /* The TX complete handler now owns the skb... */ 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void ath10k_usb_rx_complete(struct ath10k *ar, struct sk_buff *skb) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct ath10k_htc *htc = &ar->htc; 29562306a36Sopenharmony_ci struct ath10k_htc_hdr *htc_hdr; 29662306a36Sopenharmony_ci enum ath10k_htc_ep_id eid; 29762306a36Sopenharmony_ci struct ath10k_htc_ep *ep; 29862306a36Sopenharmony_ci u16 payload_len; 29962306a36Sopenharmony_ci u8 *trailer; 30062306a36Sopenharmony_ci int ret; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci htc_hdr = (struct ath10k_htc_hdr *)skb->data; 30362306a36Sopenharmony_ci eid = eid_from_htc_hdr(htc_hdr); 30462306a36Sopenharmony_ci ep = &ar->htc.endpoint[eid]; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (ep->service_id == 0) { 30762306a36Sopenharmony_ci ath10k_warn(ar, "ep %d is not connected\n", eid); 30862306a36Sopenharmony_ci goto out_free_skb; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci payload_len = le16_to_cpu(htc_hdr->len); 31262306a36Sopenharmony_ci if (!payload_len) { 31362306a36Sopenharmony_ci ath10k_warn(ar, "zero length frame received, firmware crashed?\n"); 31462306a36Sopenharmony_ci goto out_free_skb; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (payload_len < htc_hdr->trailer_len) { 31862306a36Sopenharmony_ci ath10k_warn(ar, "malformed frame received, firmware crashed?\n"); 31962306a36Sopenharmony_ci goto out_free_skb; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT) { 32362306a36Sopenharmony_ci trailer = skb->data + sizeof(*htc_hdr) + payload_len - 32462306a36Sopenharmony_ci htc_hdr->trailer_len; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ret = ath10k_htc_process_trailer(htc, 32762306a36Sopenharmony_ci trailer, 32862306a36Sopenharmony_ci htc_hdr->trailer_len, 32962306a36Sopenharmony_ci eid, 33062306a36Sopenharmony_ci NULL, 33162306a36Sopenharmony_ci NULL); 33262306a36Sopenharmony_ci if (ret) 33362306a36Sopenharmony_ci goto out_free_skb; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (is_trailer_only_msg(htc_hdr)) 33662306a36Sopenharmony_ci goto out_free_skb; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* strip off the trailer from the skb since it should not 33962306a36Sopenharmony_ci * be passed on to upper layers 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci skb_trim(skb, skb->len - htc_hdr->trailer_len); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci skb_pull(skb, sizeof(*htc_hdr)); 34562306a36Sopenharmony_ci ep->ep_ops.ep_rx_complete(ar, skb); 34662306a36Sopenharmony_ci /* The RX complete handler now owns the skb... */ 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) { 34962306a36Sopenharmony_ci local_bh_disable(); 35062306a36Sopenharmony_ci napi_schedule(&ar->napi); 35162306a36Sopenharmony_ci local_bh_enable(); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciout_free_skb: 35762306a36Sopenharmony_ci dev_kfree_skb(skb); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void ath10k_usb_io_comp_work(struct work_struct *work) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe = container_of(work, 36362306a36Sopenharmony_ci struct ath10k_usb_pipe, 36462306a36Sopenharmony_ci io_complete_work); 36562306a36Sopenharmony_ci struct ath10k *ar = pipe->ar_usb->ar; 36662306a36Sopenharmony_ci struct sk_buff *skb; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci while ((skb = skb_dequeue(&pipe->io_comp_queue))) { 36962306a36Sopenharmony_ci if (pipe->flags & ATH10K_USB_PIPE_FLAG_TX) 37062306a36Sopenharmony_ci ath10k_usb_tx_complete(ar, skb); 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci ath10k_usb_rx_complete(ar, skb); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci#define ATH10K_USB_MAX_DIAG_CMD (sizeof(struct ath10k_usb_ctrl_diag_cmd_write)) 37762306a36Sopenharmony_ci#define ATH10K_USB_MAX_DIAG_RESP (sizeof(struct ath10k_usb_ctrl_diag_resp_read)) 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void ath10k_usb_destroy(struct ath10k *ar) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ath10k_usb_flush_all(ar); 38462306a36Sopenharmony_ci ath10k_usb_cleanup_pipe_resources(ar); 38562306a36Sopenharmony_ci usb_set_intfdata(ar_usb->interface, NULL); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci kfree(ar_usb->diag_cmd_buffer); 38862306a36Sopenharmony_ci kfree(ar_usb->diag_resp_buffer); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int ath10k_usb_hif_start(struct ath10k *ar) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci int i; 39462306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ath10k_core_napi_enable(ar); 39762306a36Sopenharmony_ci ath10k_usb_start_recv_pipes(ar); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* set the TX resource avail threshold for each TX pipe */ 40062306a36Sopenharmony_ci for (i = ATH10K_USB_PIPE_TX_CTRL; 40162306a36Sopenharmony_ci i <= ATH10K_USB_PIPE_TX_DATA_HP; i++) { 40262306a36Sopenharmony_ci ar_usb->pipes[i].urb_cnt_thresh = 40362306a36Sopenharmony_ci ar_usb->pipes[i].urb_alloc / 2; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int ath10k_usb_hif_tx_sg(struct ath10k *ar, u8 pipe_id, 41062306a36Sopenharmony_ci struct ath10k_hif_sg_item *items, int n_items) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 41362306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe = &ar_usb->pipes[pipe_id]; 41462306a36Sopenharmony_ci struct ath10k_urb_context *urb_context; 41562306a36Sopenharmony_ci struct sk_buff *skb; 41662306a36Sopenharmony_ci struct urb *urb; 41762306a36Sopenharmony_ci int ret, i; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci for (i = 0; i < n_items; i++) { 42062306a36Sopenharmony_ci urb_context = ath10k_usb_alloc_urb_from_pipe(pipe); 42162306a36Sopenharmony_ci if (!urb_context) { 42262306a36Sopenharmony_ci ret = -ENOMEM; 42362306a36Sopenharmony_ci goto err; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci skb = items[i].transfer_context; 42762306a36Sopenharmony_ci urb_context->skb = skb; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 43062306a36Sopenharmony_ci if (!urb) { 43162306a36Sopenharmony_ci ret = -ENOMEM; 43262306a36Sopenharmony_ci goto err_free_urb_to_pipe; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci usb_fill_bulk_urb(urb, 43662306a36Sopenharmony_ci ar_usb->udev, 43762306a36Sopenharmony_ci pipe->usb_pipe_handle, 43862306a36Sopenharmony_ci skb->data, 43962306a36Sopenharmony_ci skb->len, 44062306a36Sopenharmony_ci ath10k_usb_transmit_complete, urb_context); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (!(skb->len % pipe->max_packet_size)) { 44362306a36Sopenharmony_ci /* hit a max packet boundary on this pipe */ 44462306a36Sopenharmony_ci urb->transfer_flags |= URB_ZERO_PACKET; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci usb_anchor_urb(urb, &pipe->urb_submitted); 44862306a36Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 44962306a36Sopenharmony_ci if (ret) { 45062306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB_BULK, 45162306a36Sopenharmony_ci "usb bulk transmit failed: %d\n", ret); 45262306a36Sopenharmony_ci usb_unanchor_urb(urb); 45362306a36Sopenharmony_ci usb_free_urb(urb); 45462306a36Sopenharmony_ci ret = -EINVAL; 45562306a36Sopenharmony_ci goto err_free_urb_to_pipe; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci usb_free_urb(urb); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cierr_free_urb_to_pipe: 46462306a36Sopenharmony_ci ath10k_usb_free_urb_to_pipe(urb_context->pipe, urb_context); 46562306a36Sopenharmony_cierr: 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic void ath10k_usb_hif_stop(struct ath10k *ar) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci ath10k_usb_flush_all(ar); 47262306a36Sopenharmony_ci ath10k_core_napi_sync_disable(ar); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic u16 ath10k_usb_hif_get_free_queue_number(struct ath10k *ar, u8 pipe_id) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ar_usb->pipes[pipe_id].urb_cnt; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int ath10k_usb_submit_ctrl_out(struct ath10k *ar, 48362306a36Sopenharmony_ci u8 req, u16 value, u16 index, void *data, 48462306a36Sopenharmony_ci u32 size) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 48762306a36Sopenharmony_ci u8 *buf = NULL; 48862306a36Sopenharmony_ci int ret; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (size > 0) { 49162306a36Sopenharmony_ci buf = kmemdup(data, size, GFP_KERNEL); 49262306a36Sopenharmony_ci if (!buf) 49362306a36Sopenharmony_ci return -ENOMEM; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* note: if successful returns number of bytes transferred */ 49762306a36Sopenharmony_ci ret = usb_control_msg(ar_usb->udev, 49862306a36Sopenharmony_ci usb_sndctrlpipe(ar_usb->udev, 0), 49962306a36Sopenharmony_ci req, 50062306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | 50162306a36Sopenharmony_ci USB_RECIP_DEVICE, value, index, buf, 50262306a36Sopenharmony_ci size, 1000); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (ret < 0) { 50562306a36Sopenharmony_ci ath10k_warn(ar, "Failed to submit usb control message: %d\n", 50662306a36Sopenharmony_ci ret); 50762306a36Sopenharmony_ci kfree(buf); 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci kfree(buf); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int ath10k_usb_submit_ctrl_in(struct ath10k *ar, 51762306a36Sopenharmony_ci u8 req, u16 value, u16 index, void *data, 51862306a36Sopenharmony_ci u32 size) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 52162306a36Sopenharmony_ci u8 *buf = NULL; 52262306a36Sopenharmony_ci int ret; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (size > 0) { 52562306a36Sopenharmony_ci buf = kmalloc(size, GFP_KERNEL); 52662306a36Sopenharmony_ci if (!buf) 52762306a36Sopenharmony_ci return -ENOMEM; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* note: if successful returns number of bytes transferred */ 53162306a36Sopenharmony_ci ret = usb_control_msg(ar_usb->udev, 53262306a36Sopenharmony_ci usb_rcvctrlpipe(ar_usb->udev, 0), 53362306a36Sopenharmony_ci req, 53462306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | 53562306a36Sopenharmony_ci USB_RECIP_DEVICE, value, index, buf, 53662306a36Sopenharmony_ci size, 2000); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (ret < 0) { 53962306a36Sopenharmony_ci ath10k_warn(ar, "Failed to read usb control message: %d\n", 54062306a36Sopenharmony_ci ret); 54162306a36Sopenharmony_ci kfree(buf); 54262306a36Sopenharmony_ci return ret; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci memcpy((u8 *)data, buf, size); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci kfree(buf); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int ath10k_usb_ctrl_msg_exchange(struct ath10k *ar, 55362306a36Sopenharmony_ci u8 req_val, u8 *req_buf, u32 req_len, 55462306a36Sopenharmony_ci u8 resp_val, u8 *resp_buf, 55562306a36Sopenharmony_ci u32 *resp_len) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* send command */ 56062306a36Sopenharmony_ci ret = ath10k_usb_submit_ctrl_out(ar, req_val, 0, 0, 56162306a36Sopenharmony_ci req_buf, req_len); 56262306a36Sopenharmony_ci if (ret) 56362306a36Sopenharmony_ci goto err; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* get response */ 56662306a36Sopenharmony_ci if (resp_buf) { 56762306a36Sopenharmony_ci ret = ath10k_usb_submit_ctrl_in(ar, resp_val, 0, 0, 56862306a36Sopenharmony_ci resp_buf, *resp_len); 56962306a36Sopenharmony_ci if (ret) 57062306a36Sopenharmony_ci goto err; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_cierr: 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int ath10k_usb_hif_diag_read(struct ath10k *ar, u32 address, void *buf, 57962306a36Sopenharmony_ci size_t buf_len) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 58262306a36Sopenharmony_ci struct ath10k_usb_ctrl_diag_cmd_read *cmd; 58362306a36Sopenharmony_ci u32 resp_len; 58462306a36Sopenharmony_ci int ret; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (buf_len < sizeof(struct ath10k_usb_ctrl_diag_resp_read)) 58762306a36Sopenharmony_ci return -EINVAL; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci cmd = (struct ath10k_usb_ctrl_diag_cmd_read *)ar_usb->diag_cmd_buffer; 59062306a36Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 59162306a36Sopenharmony_ci cmd->cmd = ATH10K_USB_CTRL_DIAG_CC_READ; 59262306a36Sopenharmony_ci cmd->address = cpu_to_le32(address); 59362306a36Sopenharmony_ci resp_len = sizeof(struct ath10k_usb_ctrl_diag_resp_read); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ret = ath10k_usb_ctrl_msg_exchange(ar, 59662306a36Sopenharmony_ci ATH10K_USB_CONTROL_REQ_DIAG_CMD, 59762306a36Sopenharmony_ci (u8 *)cmd, 59862306a36Sopenharmony_ci sizeof(*cmd), 59962306a36Sopenharmony_ci ATH10K_USB_CONTROL_REQ_DIAG_RESP, 60062306a36Sopenharmony_ci ar_usb->diag_resp_buffer, &resp_len); 60162306a36Sopenharmony_ci if (ret) 60262306a36Sopenharmony_ci return ret; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (resp_len != sizeof(struct ath10k_usb_ctrl_diag_resp_read)) 60562306a36Sopenharmony_ci return -EMSGSIZE; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci memcpy(buf, ar_usb->diag_resp_buffer, 60862306a36Sopenharmony_ci sizeof(struct ath10k_usb_ctrl_diag_resp_read)); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int ath10k_usb_hif_diag_write(struct ath10k *ar, u32 address, 61462306a36Sopenharmony_ci const void *data, int nbytes) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 61762306a36Sopenharmony_ci struct ath10k_usb_ctrl_diag_cmd_write *cmd; 61862306a36Sopenharmony_ci int ret; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (nbytes != sizeof(cmd->value)) 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci cmd = (struct ath10k_usb_ctrl_diag_cmd_write *)ar_usb->diag_cmd_buffer; 62462306a36Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 62562306a36Sopenharmony_ci cmd->cmd = cpu_to_le32(ATH10K_USB_CTRL_DIAG_CC_WRITE); 62662306a36Sopenharmony_ci cmd->address = cpu_to_le32(address); 62762306a36Sopenharmony_ci memcpy(&cmd->value, data, nbytes); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci ret = ath10k_usb_ctrl_msg_exchange(ar, 63062306a36Sopenharmony_ci ATH10K_USB_CONTROL_REQ_DIAG_CMD, 63162306a36Sopenharmony_ci (u8 *)cmd, 63262306a36Sopenharmony_ci sizeof(*cmd), 63362306a36Sopenharmony_ci 0, NULL, NULL); 63462306a36Sopenharmony_ci if (ret) 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int ath10k_usb_bmi_exchange_msg(struct ath10k *ar, 64162306a36Sopenharmony_ci void *req, u32 req_len, 64262306a36Sopenharmony_ci void *resp, u32 *resp_len) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci int ret; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (req) { 64762306a36Sopenharmony_ci ret = ath10k_usb_submit_ctrl_out(ar, 64862306a36Sopenharmony_ci ATH10K_USB_CONTROL_REQ_SEND_BMI_CMD, 64962306a36Sopenharmony_ci 0, 0, req, req_len); 65062306a36Sopenharmony_ci if (ret) { 65162306a36Sopenharmony_ci ath10k_warn(ar, 65262306a36Sopenharmony_ci "unable to send the bmi data to the device: %d\n", 65362306a36Sopenharmony_ci ret); 65462306a36Sopenharmony_ci return ret; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (resp) { 65962306a36Sopenharmony_ci ret = ath10k_usb_submit_ctrl_in(ar, 66062306a36Sopenharmony_ci ATH10K_USB_CONTROL_REQ_RECV_BMI_RESP, 66162306a36Sopenharmony_ci 0, 0, resp, *resp_len); 66262306a36Sopenharmony_ci if (ret) { 66362306a36Sopenharmony_ci ath10k_warn(ar, 66462306a36Sopenharmony_ci "Unable to read the bmi data from the device: %d\n", 66562306a36Sopenharmony_ci ret); 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void ath10k_usb_hif_get_default_pipe(struct ath10k *ar, 67462306a36Sopenharmony_ci u8 *ul_pipe, u8 *dl_pipe) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci *ul_pipe = ATH10K_USB_PIPE_TX_CTRL; 67762306a36Sopenharmony_ci *dl_pipe = ATH10K_USB_PIPE_RX_CTRL; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int ath10k_usb_hif_map_service_to_pipe(struct ath10k *ar, u16 svc_id, 68162306a36Sopenharmony_ci u8 *ul_pipe, u8 *dl_pipe) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci switch (svc_id) { 68462306a36Sopenharmony_ci case ATH10K_HTC_SVC_ID_RSVD_CTRL: 68562306a36Sopenharmony_ci case ATH10K_HTC_SVC_ID_WMI_CONTROL: 68662306a36Sopenharmony_ci *ul_pipe = ATH10K_USB_PIPE_TX_CTRL; 68762306a36Sopenharmony_ci /* due to large control packets, shift to data pipe */ 68862306a36Sopenharmony_ci *dl_pipe = ATH10K_USB_PIPE_RX_DATA; 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: 69162306a36Sopenharmony_ci *ul_pipe = ATH10K_USB_PIPE_TX_DATA_LP; 69262306a36Sopenharmony_ci /* Disable rxdata2 directly, it will be enabled 69362306a36Sopenharmony_ci * if FW enable rxdata2 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ci *dl_pipe = ATH10K_USB_PIPE_RX_DATA; 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci default: 69862306a36Sopenharmony_ci return -EPERM; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int ath10k_usb_hif_power_up(struct ath10k *ar, 70562306a36Sopenharmony_ci enum ath10k_firmware_mode fw_mode) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic void ath10k_usb_hif_power_down(struct ath10k *ar) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci ath10k_usb_flush_all(ar); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci#ifdef CONFIG_PM 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int ath10k_usb_hif_suspend(struct ath10k *ar) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci return -EOPNOTSUPP; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic int ath10k_usb_hif_resume(struct ath10k *ar) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci return -EOPNOTSUPP; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci#endif 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic const struct ath10k_hif_ops ath10k_usb_hif_ops = { 72962306a36Sopenharmony_ci .tx_sg = ath10k_usb_hif_tx_sg, 73062306a36Sopenharmony_ci .diag_read = ath10k_usb_hif_diag_read, 73162306a36Sopenharmony_ci .diag_write = ath10k_usb_hif_diag_write, 73262306a36Sopenharmony_ci .exchange_bmi_msg = ath10k_usb_bmi_exchange_msg, 73362306a36Sopenharmony_ci .start = ath10k_usb_hif_start, 73462306a36Sopenharmony_ci .stop = ath10k_usb_hif_stop, 73562306a36Sopenharmony_ci .map_service_to_pipe = ath10k_usb_hif_map_service_to_pipe, 73662306a36Sopenharmony_ci .get_default_pipe = ath10k_usb_hif_get_default_pipe, 73762306a36Sopenharmony_ci .get_free_queue_number = ath10k_usb_hif_get_free_queue_number, 73862306a36Sopenharmony_ci .power_up = ath10k_usb_hif_power_up, 73962306a36Sopenharmony_ci .power_down = ath10k_usb_hif_power_down, 74062306a36Sopenharmony_ci#ifdef CONFIG_PM 74162306a36Sopenharmony_ci .suspend = ath10k_usb_hif_suspend, 74262306a36Sopenharmony_ci .resume = ath10k_usb_hif_resume, 74362306a36Sopenharmony_ci#endif 74462306a36Sopenharmony_ci}; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic u8 ath10k_usb_get_logical_pipe_num(u8 ep_address, int *urb_count) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci u8 pipe_num = ATH10K_USB_PIPE_INVALID; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci switch (ep_address) { 75162306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_CTRL_IN: 75262306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_RX_CTRL; 75362306a36Sopenharmony_ci *urb_count = RX_URB_COUNT; 75462306a36Sopenharmony_ci break; 75562306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_DATA_IN: 75662306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_RX_DATA; 75762306a36Sopenharmony_ci *urb_count = RX_URB_COUNT; 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_INT_IN: 76062306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_RX_INT; 76162306a36Sopenharmony_ci *urb_count = RX_URB_COUNT; 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_DATA2_IN: 76462306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_RX_DATA2; 76562306a36Sopenharmony_ci *urb_count = RX_URB_COUNT; 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_CTRL_OUT: 76862306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_TX_CTRL; 76962306a36Sopenharmony_ci *urb_count = TX_URB_COUNT; 77062306a36Sopenharmony_ci break; 77162306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_DATA_LP_OUT: 77262306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_TX_DATA_LP; 77362306a36Sopenharmony_ci *urb_count = TX_URB_COUNT; 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_DATA_MP_OUT: 77662306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_TX_DATA_MP; 77762306a36Sopenharmony_ci *urb_count = TX_URB_COUNT; 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci case ATH10K_USB_EP_ADDR_APP_DATA_HP_OUT: 78062306a36Sopenharmony_ci pipe_num = ATH10K_USB_PIPE_TX_DATA_HP; 78162306a36Sopenharmony_ci *urb_count = TX_URB_COUNT; 78262306a36Sopenharmony_ci break; 78362306a36Sopenharmony_ci default: 78462306a36Sopenharmony_ci /* note: there may be endpoints not currently used */ 78562306a36Sopenharmony_ci break; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return pipe_num; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int ath10k_usb_alloc_pipe_resources(struct ath10k *ar, 79262306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe, 79362306a36Sopenharmony_ci int urb_cnt) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct ath10k_urb_context *urb_context; 79662306a36Sopenharmony_ci int i; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci INIT_LIST_HEAD(&pipe->urb_list_head); 79962306a36Sopenharmony_ci init_usb_anchor(&pipe->urb_submitted); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci for (i = 0; i < urb_cnt; i++) { 80262306a36Sopenharmony_ci urb_context = kzalloc(sizeof(*urb_context), GFP_KERNEL); 80362306a36Sopenharmony_ci if (!urb_context) 80462306a36Sopenharmony_ci return -ENOMEM; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci urb_context->pipe = pipe; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* we are only allocate the urb contexts here, the actual URB 80962306a36Sopenharmony_ci * is allocated from the kernel as needed to do a transaction 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_ci pipe->urb_alloc++; 81262306a36Sopenharmony_ci ath10k_usb_free_urb_to_pipe(pipe, urb_context); 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, 81662306a36Sopenharmony_ci "usb alloc resources lpipe %d hpipe 0x%x urbs %d\n", 81762306a36Sopenharmony_ci pipe->logical_pipe_num, pipe->usb_pipe_handle, 81862306a36Sopenharmony_ci pipe->urb_alloc); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int ath10k_usb_setup_pipe_resources(struct ath10k *ar, 82462306a36Sopenharmony_ci struct usb_interface *interface) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 82762306a36Sopenharmony_ci struct usb_host_interface *iface_desc = interface->cur_altsetting; 82862306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 82962306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe; 83062306a36Sopenharmony_ci int ret, i, urbcount; 83162306a36Sopenharmony_ci u8 pipe_num; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, "usb setting up pipes using interface\n"); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* walk descriptors and setup pipes */ 83662306a36Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 83762306a36Sopenharmony_ci endpoint = &iface_desc->endpoint[i].desc; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (ATH10K_USB_IS_BULK_EP(endpoint->bmAttributes)) { 84062306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, 84162306a36Sopenharmony_ci "usb %s bulk ep 0x%2.2x maxpktsz %d\n", 84262306a36Sopenharmony_ci ATH10K_USB_IS_DIR_IN 84362306a36Sopenharmony_ci (endpoint->bEndpointAddress) ? 84462306a36Sopenharmony_ci "rx" : "tx", endpoint->bEndpointAddress, 84562306a36Sopenharmony_ci le16_to_cpu(endpoint->wMaxPacketSize)); 84662306a36Sopenharmony_ci } else if (ATH10K_USB_IS_INT_EP(endpoint->bmAttributes)) { 84762306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, 84862306a36Sopenharmony_ci "usb %s int ep 0x%2.2x maxpktsz %d interval %d\n", 84962306a36Sopenharmony_ci ATH10K_USB_IS_DIR_IN 85062306a36Sopenharmony_ci (endpoint->bEndpointAddress) ? 85162306a36Sopenharmony_ci "rx" : "tx", endpoint->bEndpointAddress, 85262306a36Sopenharmony_ci le16_to_cpu(endpoint->wMaxPacketSize), 85362306a36Sopenharmony_ci endpoint->bInterval); 85462306a36Sopenharmony_ci } else if (ATH10K_USB_IS_ISOC_EP(endpoint->bmAttributes)) { 85562306a36Sopenharmony_ci /* TODO for ISO */ 85662306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, 85762306a36Sopenharmony_ci "usb %s isoc ep 0x%2.2x maxpktsz %d interval %d\n", 85862306a36Sopenharmony_ci ATH10K_USB_IS_DIR_IN 85962306a36Sopenharmony_ci (endpoint->bEndpointAddress) ? 86062306a36Sopenharmony_ci "rx" : "tx", endpoint->bEndpointAddress, 86162306a36Sopenharmony_ci le16_to_cpu(endpoint->wMaxPacketSize), 86262306a36Sopenharmony_ci endpoint->bInterval); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* Ignore broken descriptors. */ 86662306a36Sopenharmony_ci if (usb_endpoint_maxp(endpoint) == 0) 86762306a36Sopenharmony_ci continue; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci urbcount = 0; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci pipe_num = 87262306a36Sopenharmony_ci ath10k_usb_get_logical_pipe_num(endpoint->bEndpointAddress, 87362306a36Sopenharmony_ci &urbcount); 87462306a36Sopenharmony_ci if (pipe_num == ATH10K_USB_PIPE_INVALID) 87562306a36Sopenharmony_ci continue; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci pipe = &ar_usb->pipes[pipe_num]; 87862306a36Sopenharmony_ci if (pipe->ar_usb) 87962306a36Sopenharmony_ci /* hmmm..pipe was already setup */ 88062306a36Sopenharmony_ci continue; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci pipe->ar_usb = ar_usb; 88362306a36Sopenharmony_ci pipe->logical_pipe_num = pipe_num; 88462306a36Sopenharmony_ci pipe->ep_address = endpoint->bEndpointAddress; 88562306a36Sopenharmony_ci pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (ATH10K_USB_IS_BULK_EP(endpoint->bmAttributes)) { 88862306a36Sopenharmony_ci if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { 88962306a36Sopenharmony_ci pipe->usb_pipe_handle = 89062306a36Sopenharmony_ci usb_rcvbulkpipe(ar_usb->udev, 89162306a36Sopenharmony_ci pipe->ep_address); 89262306a36Sopenharmony_ci } else { 89362306a36Sopenharmony_ci pipe->usb_pipe_handle = 89462306a36Sopenharmony_ci usb_sndbulkpipe(ar_usb->udev, 89562306a36Sopenharmony_ci pipe->ep_address); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci } else if (ATH10K_USB_IS_INT_EP(endpoint->bmAttributes)) { 89862306a36Sopenharmony_ci if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { 89962306a36Sopenharmony_ci pipe->usb_pipe_handle = 90062306a36Sopenharmony_ci usb_rcvintpipe(ar_usb->udev, 90162306a36Sopenharmony_ci pipe->ep_address); 90262306a36Sopenharmony_ci } else { 90362306a36Sopenharmony_ci pipe->usb_pipe_handle = 90462306a36Sopenharmony_ci usb_sndintpipe(ar_usb->udev, 90562306a36Sopenharmony_ci pipe->ep_address); 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci } else if (ATH10K_USB_IS_ISOC_EP(endpoint->bmAttributes)) { 90862306a36Sopenharmony_ci /* TODO for ISO */ 90962306a36Sopenharmony_ci if (ATH10K_USB_IS_DIR_IN(pipe->ep_address)) { 91062306a36Sopenharmony_ci pipe->usb_pipe_handle = 91162306a36Sopenharmony_ci usb_rcvisocpipe(ar_usb->udev, 91262306a36Sopenharmony_ci pipe->ep_address); 91362306a36Sopenharmony_ci } else { 91462306a36Sopenharmony_ci pipe->usb_pipe_handle = 91562306a36Sopenharmony_ci usb_sndisocpipe(ar_usb->udev, 91662306a36Sopenharmony_ci pipe->ep_address); 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci pipe->ep_desc = endpoint; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (!ATH10K_USB_IS_DIR_IN(pipe->ep_address)) 92362306a36Sopenharmony_ci pipe->flags |= ATH10K_USB_PIPE_FLAG_TX; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci ret = ath10k_usb_alloc_pipe_resources(ar, pipe, urbcount); 92662306a36Sopenharmony_ci if (ret) 92762306a36Sopenharmony_ci return ret; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return 0; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic int ath10k_usb_create(struct ath10k *ar, 93462306a36Sopenharmony_ci struct usb_interface *interface) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); 93762306a36Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(interface); 93862306a36Sopenharmony_ci struct ath10k_usb_pipe *pipe; 93962306a36Sopenharmony_ci int ret, i; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci usb_set_intfdata(interface, ar_usb); 94262306a36Sopenharmony_ci spin_lock_init(&ar_usb->cs_lock); 94362306a36Sopenharmony_ci ar_usb->udev = dev; 94462306a36Sopenharmony_ci ar_usb->interface = interface; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci for (i = 0; i < ATH10K_USB_PIPE_MAX; i++) { 94762306a36Sopenharmony_ci pipe = &ar_usb->pipes[i]; 94862306a36Sopenharmony_ci INIT_WORK(&pipe->io_complete_work, 94962306a36Sopenharmony_ci ath10k_usb_io_comp_work); 95062306a36Sopenharmony_ci skb_queue_head_init(&pipe->io_comp_queue); 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ar_usb->diag_cmd_buffer = kzalloc(ATH10K_USB_MAX_DIAG_CMD, GFP_KERNEL); 95462306a36Sopenharmony_ci if (!ar_usb->diag_cmd_buffer) { 95562306a36Sopenharmony_ci ret = -ENOMEM; 95662306a36Sopenharmony_ci goto err; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci ar_usb->diag_resp_buffer = kzalloc(ATH10K_USB_MAX_DIAG_RESP, 96062306a36Sopenharmony_ci GFP_KERNEL); 96162306a36Sopenharmony_ci if (!ar_usb->diag_resp_buffer) { 96262306a36Sopenharmony_ci ret = -ENOMEM; 96362306a36Sopenharmony_ci goto err; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci ret = ath10k_usb_setup_pipe_resources(ar, interface); 96762306a36Sopenharmony_ci if (ret) 96862306a36Sopenharmony_ci goto err; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cierr: 97362306a36Sopenharmony_ci ath10k_usb_destroy(ar); 97462306a36Sopenharmony_ci return ret; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic int ath10k_usb_napi_poll(struct napi_struct *ctx, int budget) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct ath10k *ar = container_of(ctx, struct ath10k, napi); 98062306a36Sopenharmony_ci int done; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci done = ath10k_htt_rx_hl_indication(ar, budget); 98362306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_USB, "napi poll: done: %d, budget:%d\n", done, budget); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (done < budget) 98662306a36Sopenharmony_ci napi_complete_done(ctx, done); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return done; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* ath10k usb driver registered functions */ 99262306a36Sopenharmony_cistatic int ath10k_usb_probe(struct usb_interface *interface, 99362306a36Sopenharmony_ci const struct usb_device_id *id) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct ath10k *ar; 99662306a36Sopenharmony_ci struct ath10k_usb *ar_usb; 99762306a36Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(interface); 99862306a36Sopenharmony_ci int ret, vendor_id, product_id; 99962306a36Sopenharmony_ci enum ath10k_hw_rev hw_rev; 100062306a36Sopenharmony_ci struct ath10k_bus_params bus_params = {}; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* Assumption: All USB based chipsets (so far) are QCA9377 based. 100362306a36Sopenharmony_ci * If there will be newer chipsets that does not use the hw reg 100462306a36Sopenharmony_ci * setup as defined in qca6174_regs and qca6174_values, this 100562306a36Sopenharmony_ci * assumption is no longer valid and hw_rev must be setup differently 100662306a36Sopenharmony_ci * depending on chipset. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci hw_rev = ATH10K_HW_QCA9377; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci ar = ath10k_core_create(sizeof(*ar_usb), &dev->dev, ATH10K_BUS_USB, 101162306a36Sopenharmony_ci hw_rev, &ath10k_usb_hif_ops); 101262306a36Sopenharmony_ci if (!ar) { 101362306a36Sopenharmony_ci dev_err(&dev->dev, "failed to allocate core\n"); 101462306a36Sopenharmony_ci return -ENOMEM; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_usb_napi_poll); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci usb_get_dev(dev); 102062306a36Sopenharmony_ci vendor_id = le16_to_cpu(dev->descriptor.idVendor); 102162306a36Sopenharmony_ci product_id = le16_to_cpu(dev->descriptor.idProduct); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BOOT, 102462306a36Sopenharmony_ci "usb new func vendor 0x%04x product 0x%04x\n", 102562306a36Sopenharmony_ci vendor_id, product_id); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci ar_usb = ath10k_usb_priv(ar); 102862306a36Sopenharmony_ci ret = ath10k_usb_create(ar, interface); 102962306a36Sopenharmony_ci if (ret) 103062306a36Sopenharmony_ci goto err; 103162306a36Sopenharmony_ci ar_usb->ar = ar; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci ar->dev_id = product_id; 103462306a36Sopenharmony_ci ar->id.vendor = vendor_id; 103562306a36Sopenharmony_ci ar->id.device = product_id; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci bus_params.dev_type = ATH10K_DEV_TYPE_HL; 103862306a36Sopenharmony_ci /* TODO: don't know yet how to get chip_id with USB */ 103962306a36Sopenharmony_ci bus_params.chip_id = 0; 104062306a36Sopenharmony_ci bus_params.hl_msdu_ids = true; 104162306a36Sopenharmony_ci ret = ath10k_core_register(ar, &bus_params); 104262306a36Sopenharmony_ci if (ret) { 104362306a36Sopenharmony_ci ath10k_warn(ar, "failed to register driver core: %d\n", ret); 104462306a36Sopenharmony_ci goto err_usb_destroy; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci /* TODO: remove this once USB support is fully implemented */ 104862306a36Sopenharmony_ci ath10k_warn(ar, "Warning: ath10k USB support is incomplete, don't expect anything to work!\n"); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return 0; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cierr_usb_destroy: 105362306a36Sopenharmony_ci ath10k_usb_destroy(ar); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cierr: 105662306a36Sopenharmony_ci ath10k_core_destroy(ar); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci usb_put_dev(dev); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci return ret; 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic void ath10k_usb_remove(struct usb_interface *interface) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci struct ath10k_usb *ar_usb; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci ar_usb = usb_get_intfdata(interface); 106862306a36Sopenharmony_ci if (!ar_usb) 106962306a36Sopenharmony_ci return; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci ath10k_core_unregister(ar_usb->ar); 107262306a36Sopenharmony_ci netif_napi_del(&ar_usb->ar->napi); 107362306a36Sopenharmony_ci ath10k_usb_destroy(ar_usb->ar); 107462306a36Sopenharmony_ci usb_put_dev(interface_to_usbdev(interface)); 107562306a36Sopenharmony_ci ath10k_core_destroy(ar_usb->ar); 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci#ifdef CONFIG_PM 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int ath10k_usb_pm_suspend(struct usb_interface *interface, 108162306a36Sopenharmony_ci pm_message_t message) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct ath10k_usb *ar_usb = usb_get_intfdata(interface); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ath10k_usb_flush_all(ar_usb->ar); 108662306a36Sopenharmony_ci return 0; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic int ath10k_usb_pm_resume(struct usb_interface *interface) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci struct ath10k_usb *ar_usb = usb_get_intfdata(interface); 109262306a36Sopenharmony_ci struct ath10k *ar = ar_usb->ar; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci ath10k_usb_post_recv_transfers(ar, 109562306a36Sopenharmony_ci &ar_usb->pipes[ATH10K_USB_PIPE_RX_DATA]); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return 0; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci#else 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci#define ath10k_usb_pm_suspend NULL 110362306a36Sopenharmony_ci#define ath10k_usb_pm_resume NULL 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci#endif 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci/* table of devices that work with this driver */ 110862306a36Sopenharmony_cistatic struct usb_device_id ath10k_usb_ids[] = { 110962306a36Sopenharmony_ci {USB_DEVICE(0x13b1, 0x0042)}, /* Linksys WUSB6100M */ 111062306a36Sopenharmony_ci { /* Terminating entry */ }, 111162306a36Sopenharmony_ci}; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ath10k_usb_ids); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic struct usb_driver ath10k_usb_driver = { 111662306a36Sopenharmony_ci .name = "ath10k_usb", 111762306a36Sopenharmony_ci .probe = ath10k_usb_probe, 111862306a36Sopenharmony_ci .suspend = ath10k_usb_pm_suspend, 111962306a36Sopenharmony_ci .resume = ath10k_usb_pm_resume, 112062306a36Sopenharmony_ci .disconnect = ath10k_usb_remove, 112162306a36Sopenharmony_ci .id_table = ath10k_usb_ids, 112262306a36Sopenharmony_ci .supports_autosuspend = true, 112362306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 112462306a36Sopenharmony_ci}; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cimodule_usb_driver(ath10k_usb_driver); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ciMODULE_AUTHOR("Atheros Communications, Inc."); 112962306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver support for Qualcomm Atheros USB 802.11ac WLAN devices"); 113062306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 1131