Merge tag 'for-linville-20131001' of git://github.com/kvalo/ath

This commit is contained in:
John W. Linville 2013-10-03 16:16:34 -04:00
commit 75ae83d686
21 changed files with 1007 additions and 1038 deletions

View File

@ -22,7 +22,8 @@
void ath10k_bmi_start(struct ath10k *ar)
{
ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n");
ar->bmi.done_sent = false;
}
@ -32,8 +33,10 @@ int ath10k_bmi_done(struct ath10k *ar)
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
int ret;
ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n");
if (ar->bmi.done_sent) {
ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n");
return 0;
}
@ -46,7 +49,6 @@ int ath10k_bmi_done(struct ath10k *ar)
return ret;
}
ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
return 0;
}
@ -59,6 +61,8 @@ int ath10k_bmi_get_target_info(struct ath10k *ar,
u32 resplen = sizeof(resp.get_target_info);
int ret;
ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n");
if (ar->bmi.done_sent) {
ath10k_warn("BMI Get Target Info Command disallowed\n");
return -EBUSY;
@ -80,6 +84,7 @@ int ath10k_bmi_get_target_info(struct ath10k *ar,
target_info->version = __le32_to_cpu(resp.get_target_info.version);
target_info->type = __le32_to_cpu(resp.get_target_info.type);
return 0;
}
@ -92,15 +97,14 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
u32 rxlen;
int ret;
ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
address, length);
if (ar->bmi.done_sent) {
ath10k_warn("command disallowed\n");
return -EBUSY;
}
ath10k_dbg(ATH10K_DBG_CORE,
"%s: (device: 0x%p, address: 0x%x, length: %d)\n",
__func__, ar, address, length);
while (length) {
rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
@ -133,15 +137,14 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
u32 txlen;
int ret;
ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
address, length);
if (ar->bmi.done_sent) {
ath10k_warn("command disallowed\n");
return -EBUSY;
}
ath10k_dbg(ATH10K_DBG_CORE,
"%s: (device: 0x%p, address: 0x%x, length: %d)\n",
__func__, ar, address, length);
while (length) {
txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
@ -180,15 +183,14 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
u32 resplen = sizeof(resp.execute);
int ret;
ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
address, *param);
if (ar->bmi.done_sent) {
ath10k_warn("command disallowed\n");
return -EBUSY;
}
ath10k_dbg(ATH10K_DBG_CORE,
"%s: (device: 0x%p, address: 0x%x, param: %d)\n",
__func__, ar, address, *param);
cmd.id = __cpu_to_le32(BMI_EXECUTE);
cmd.execute.addr = __cpu_to_le32(address);
cmd.execute.param = __cpu_to_le32(*param);
@ -216,6 +218,9 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
u32 txlen;
int ret;
ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",
buffer, length);
if (ar->bmi.done_sent) {
ath10k_warn("command disallowed\n");
return -EBUSY;
@ -250,6 +255,9 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
int ret;
ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
address);
if (ar->bmi.done_sent) {
ath10k_warn("command disallowed\n");
return -EBUSY;
@ -275,6 +283,10 @@ int ath10k_bmi_fast_download(struct ath10k *ar,
u32 trailer_len = length - head_len;
int ret;
ath10k_dbg(ATH10K_DBG_BMI,
"bmi fast download address 0x%x buffer 0x%p length %d\n",
address, buffer, length);
ret = ath10k_bmi_lz_stream_start(ar, address);
if (ret)
return ret;

View File

@ -338,33 +338,19 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
return ret;
}
void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer,
unsigned int nbytes, u32 flags)
{
unsigned int num_items = sendlist->num_items;
struct ce_sendlist_item *item;
item = &sendlist->item[num_items];
item->data = buffer;
item->u.nbytes = nbytes;
item->flags = flags;
sendlist->num_items++;
}
int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state,
void *per_transfer_context,
struct ce_sendlist *sendlist,
unsigned int transfer_id)
unsigned int transfer_id,
u32 paddr, unsigned int nbytes,
u32 flags)
{
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
struct ce_sendlist_item *item;
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
unsigned int nentries_mask = src_ring->nentries_mask;
unsigned int num_items = sendlist->num_items;
unsigned int sw_index;
unsigned int write_index;
int i, delta, ret = -ENOMEM;
int delta, ret = -ENOMEM;
spin_lock_bh(&ar_pci->ce_lock);
@ -373,30 +359,12 @@ int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state,
delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
if (delta >= num_items) {
/*
* Handle all but the last item uniformly.
*/
for (i = 0; i < num_items - 1; i++) {
item = &sendlist->item[i];
ret = ath10k_ce_send_nolock(ce_state,
CE_SENDLIST_ITEM_CTXT,
(u32) item->data,
item->u.nbytes, transfer_id,
item->flags |
CE_SEND_FLAG_GATHER);
if (ret)
ath10k_warn("CE send failed for item: %d\n", i);
}
/*
* Provide valid context pointer for final item.
*/
item = &sendlist->item[i];
if (delta >= 1) {
ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
(u32) item->data, item->u.nbytes,
transfer_id, item->flags);
paddr, nbytes,
transfer_id, flags);
if (ret)
ath10k_warn("CE send failed for last item: %d\n", i);
ath10k_warn("CE send failed: %d\n", ret);
}
spin_unlock_bh(&ar_pci->ce_lock);
@ -742,11 +710,6 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
u32 ctrl_addr = ce_state->ctrl_addr;
void *transfer_context;
u32 buf;
unsigned int nbytes;
unsigned int id;
unsigned int flags;
int ret;
ret = ath10k_pci_wake(ar);
@ -759,38 +722,15 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
HOST_IS_COPY_COMPLETE_MASK);
if (ce_state->recv_cb) {
/*
* Pop completed recv buffers and call the registered
* recv callback for each
*/
while (ath10k_ce_completed_recv_next_nolock(ce_state,
&transfer_context,
&buf, &nbytes,
&id, &flags) == 0) {
spin_unlock_bh(&ar_pci->ce_lock);
ce_state->recv_cb(ce_state, transfer_context, buf,
nbytes, id, flags);
spin_lock_bh(&ar_pci->ce_lock);
}
}
spin_unlock_bh(&ar_pci->ce_lock);
if (ce_state->send_cb) {
/*
* Pop completed send buffers and call the registered
* send callback for each
*/
while (ath10k_ce_completed_send_next_nolock(ce_state,
&transfer_context,
&buf,
&nbytes,
&id) == 0) {
spin_unlock_bh(&ar_pci->ce_lock);
ce_state->send_cb(ce_state, transfer_context,
buf, nbytes, id);
spin_lock_bh(&ar_pci->ce_lock);
}
}
if (ce_state->recv_cb)
ce_state->recv_cb(ce_state);
if (ce_state->send_cb)
ce_state->send_cb(ce_state);
spin_lock_bh(&ar_pci->ce_lock);
/*
* Misc CE interrupts are not being handled, but still need
@ -881,11 +821,7 @@ void ath10k_ce_disable_interrupts(struct ath10k *ar)
}
void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
void (*send_cb)(struct ath10k_ce_pipe *ce_state,
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id),
void (*send_cb)(struct ath10k_ce_pipe *),
int disable_interrupts)
{
struct ath10k *ar = ce_state->ar;
@ -898,12 +834,7 @@ void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
}
void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
void (*recv_cb)(struct ath10k_ce_pipe *ce_state,
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags))
void (*recv_cb)(struct ath10k_ce_pipe *))
{
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@ -1010,6 +941,10 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
ath10k_dbg(ATH10K_DBG_BOOT,
"boot ce src ring id %d entries %d base_addr %p\n",
ce_id, nentries, src_ring->base_addr_owner_space);
return 0;
}
@ -1091,6 +1026,10 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
ath10k_dbg(ATH10K_DBG_BOOT,
"boot ce dest ring id %d entries %d base_addr %p\n",
ce_id, nentries, dest_ring->base_addr_owner_space);
return 0;
}

View File

@ -27,7 +27,6 @@
/* Descriptor rings must be aligned to this boundary */
#define CE_DESC_RING_ALIGN 8
#define CE_SENDLIST_ITEMS_MAX 12
#define CE_SEND_FLAG_GATHER 0x00010000
/*
@ -116,41 +115,14 @@ struct ath10k_ce_pipe {
u32 ctrl_addr;
void (*send_cb) (struct ath10k_ce_pipe *ce_state,
void *per_transfer_send_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id);
void (*recv_cb) (struct ath10k_ce_pipe *ce_state,
void *per_transfer_recv_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags);
void (*send_cb)(struct ath10k_ce_pipe *);
void (*recv_cb)(struct ath10k_ce_pipe *);
unsigned int src_sz_max;
struct ath10k_ce_ring *src_ring;
struct ath10k_ce_ring *dest_ring;
};
struct ce_sendlist_item {
/* e.g. buffer or desc list */
dma_addr_t data;
union {
/* simple buffer */
unsigned int nbytes;
/* Rx descriptor list */
unsigned int ndesc;
} u;
/* externally-specified flags; OR-ed with internal flags */
u32 flags;
};
struct ce_sendlist {
unsigned int num_items;
struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX];
};
/* Copy Engine settable attributes */
struct ce_attr;
@ -181,20 +153,9 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
unsigned int flags);
void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
void (*send_cb)(struct ath10k_ce_pipe *ce_state,
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id),
void (*send_cb)(struct ath10k_ce_pipe *),
int disable_interrupts);
/* Append a simple buffer (address/length) to a sendlist. */
void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
u32 buffer,
unsigned int nbytes,
/* OR-ed with internal flags */
u32 flags);
/*
* Queue a "sendlist" of buffers to be sent using gather to a single
* anonymous destination buffer
@ -206,10 +167,10 @@ void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
* Implemenation note: Pushes multiple buffers with Gather to Source ring.
*/
int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state,
void *per_transfer_send_context,
struct ce_sendlist *sendlist,
/* 14 bits */
unsigned int transfer_id);
void *per_transfer_context,
unsigned int transfer_id,
u32 paddr, unsigned int nbytes,
u32 flags);
/*==================Recv=======================*/
@ -228,12 +189,7 @@ int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
u32 buffer);
void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
void (*recv_cb)(struct ath10k_ce_pipe *ce_state,
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags));
void (*recv_cb)(struct ath10k_ce_pipe *));
/* recv flags */
/* Data is byte-swapped */
@ -325,16 +281,6 @@ struct ce_attr {
unsigned int dest_nentries;
};
/*
* When using sendlist_send to transfer multiple buffer fragments, the
* transfer context of each fragment, except last one, will be filled
* with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
* each fragment done with send and the transfer context would be
* CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
* status of a send completion.
*/
#define CE_SENDLIST_ITEM_CTXT ((void *)0xcecebeef)
#define SR_BA_ADDRESS 0x0000
#define SR_SIZE_ADDRESS 0x0004
#define DR_BA_ADDRESS 0x0008

View File

@ -53,7 +53,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
static void ath10k_send_suspend_complete(struct ath10k *ar)
{
ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__);
ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n");
ar->is_target_paused = true;
wake_up(&ar->event_queue);
@ -101,7 +101,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
goto timeout;
}
ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n");
ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n");
return 0;
timeout:
@ -203,8 +203,8 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
return ret;
}
ath10k_dbg(ATH10K_DBG_CORE,
"ath10k: Board extended Data download addr: 0x%x\n",
ath10k_dbg(ATH10K_DBG_BOOT,
"boot push board extended data addr 0x%x\n",
board_ext_data_addr);
if (board_ext_data_addr == 0)
@ -435,6 +435,13 @@ static int ath10k_init_uart(struct ath10k *ar)
return ret;
}
/* Set the UART baud rate to 19200. */
ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200);
if (ret) {
ath10k_warn("could not set the baud rate (%d)\n", ret);
return ret;
}
ath10k_info("UART prints enabled\n");
return 0;
}
@ -630,6 +637,10 @@ int ath10k_core_start(struct ath10k *ar)
if (status)
goto err_disconnect_htc;
status = ath10k_debug_start(ar);
if (status)
goto err_disconnect_htc;
ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
return 0;
@ -647,6 +658,7 @@ EXPORT_SYMBOL(ath10k_core_start);
void ath10k_core_stop(struct ath10k *ar)
{
ath10k_debug_stop(ar);
ath10k_htc_stop(&ar->htc);
ath10k_htt_detach(&ar->htt);
ath10k_wmi_detach(ar);
@ -710,6 +722,9 @@ static int ath10k_core_check_chip_id(struct ath10k *ar)
{
u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV);
ath10k_dbg(ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n",
ar->chip_id, hw_revision);
/* Check that we are not using hw1.0 (some of them have same pci id
* as hw2.0) before doing anything else as ath10k crashes horribly
* due to missing hw1.0 workarounds. */
@ -777,6 +792,7 @@ void ath10k_core_unregister(struct ath10k *ar)
* Otherwise we will fail to submit commands to FW and mac80211 will be
* unhappy about callback failures. */
ath10k_mac_unregister(ar);
ath10k_core_free_firmware_files(ar);
}
EXPORT_SYMBOL(ath10k_core_unregister);

View File

@ -52,18 +52,12 @@ struct ath10k_skb_cb {
struct {
u8 vdev_id;
u16 msdu_id;
u8 tid;
bool is_offchan;
bool is_conf;
bool discard;
bool no_ack;
u8 refcount;
struct sk_buff *txfrag;
struct sk_buff *msdu;
} __packed htt;
/* 4 bytes left on 64bit arch */
u8 frag_len;
u8 pad_len;
} __packed htt;
} __packed;
static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
@ -112,11 +106,7 @@ struct ath10k_wmi {
enum ath10k_htc_ep_id eid;
struct completion service_ready;
struct completion unified_ready;
atomic_t pending_tx_count;
wait_queue_head_t wq;
struct sk_buff_head wmi_event_list;
struct work_struct wmi_event_work;
wait_queue_head_t tx_credits_wq;
};
struct ath10k_peer_stat {
@ -203,6 +193,7 @@ struct ath10k_vif {
enum wmi_vdev_subtype vdev_subtype;
u32 beacon_interval;
u32 dtim_period;
struct sk_buff *beacon;
struct ath10k *ar;
struct ieee80211_vif *vif;
@ -246,6 +237,9 @@ struct ath10k_debug {
u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
struct completion event_stats_compl;
unsigned long htt_stats_mask;
struct delayed_work htt_stats_dwork;
};
enum ath10k_state {

View File

@ -21,6 +21,9 @@
#include "core.h"
#include "debug.h"
/* ms */
#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
static int ath10k_printk(const char *level, const char *fmt, ...)
{
struct va_format vaf;
@ -517,6 +520,117 @@ static const struct file_operations fops_chip_id = {
.llseek = default_llseek,
};
static int ath10k_debug_htt_stats_req(struct ath10k *ar)
{
u64 cookie;
int ret;
lockdep_assert_held(&ar->conf_mutex);
if (ar->debug.htt_stats_mask == 0)
/* htt stats are disabled */
return 0;
if (ar->state != ATH10K_STATE_ON)
return 0;
cookie = get_jiffies_64();
ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,
cookie);
if (ret) {
ath10k_warn("failed to send htt stats request: %d\n", ret);
return ret;
}
queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork,
msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL));
return 0;
}
static void ath10k_debug_htt_stats_dwork(struct work_struct *work)
{
struct ath10k *ar = container_of(work, struct ath10k,
debug.htt_stats_dwork.work);
mutex_lock(&ar->conf_mutex);
ath10k_debug_htt_stats_req(ar);
mutex_unlock(&ar->conf_mutex);
}
static ssize_t ath10k_read_htt_stats_mask(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
char buf[32];
unsigned int len;
len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t ath10k_write_htt_stats_mask(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
unsigned long mask;
int ret;
ret = kstrtoul_from_user(user_buf, count, 0, &mask);
if (ret)
return ret;
/* max 8 bit masks (for now) */
if (mask > 0xff)
return -E2BIG;
mutex_lock(&ar->conf_mutex);
ar->debug.htt_stats_mask = mask;
ret = ath10k_debug_htt_stats_req(ar);
if (ret)
goto out;
ret = count;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_htt_stats_mask = {
.read = ath10k_read_htt_stats_mask,
.write = ath10k_write_htt_stats_mask,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
int ath10k_debug_start(struct ath10k *ar)
{
int ret;
ret = ath10k_debug_htt_stats_req(ar);
if (ret)
/* continue normally anyway, this isn't serious */
ath10k_warn("failed to start htt stats workqueue: %d\n", ret);
return 0;
}
void ath10k_debug_stop(struct ath10k *ar)
{
cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);
}
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@ -525,6 +639,9 @@ int ath10k_debug_create(struct ath10k *ar)
if (!ar->debug.debugfs_phy)
return -ENOMEM;
INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
ath10k_debug_htt_stats_dwork);
init_completion(&ar->debug.event_stats_compl);
debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
@ -539,8 +656,12 @@ int ath10k_debug_create(struct ath10k *ar)
debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_chip_id);
debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_htt_stats_mask);
return 0;
}
#endif /* CONFIG_ATH10K_DEBUGFS */
#ifdef CONFIG_ATH10K_DEBUG

View File

@ -27,11 +27,12 @@ enum ath10k_debug_mask {
ATH10K_DBG_HTC = 0x00000004,
ATH10K_DBG_HTT = 0x00000008,
ATH10K_DBG_MAC = 0x00000010,
ATH10K_DBG_CORE = 0x00000020,
ATH10K_DBG_BOOT = 0x00000020,
ATH10K_DBG_PCI_DUMP = 0x00000040,
ATH10K_DBG_HTT_DUMP = 0x00000080,
ATH10K_DBG_MGMT = 0x00000100,
ATH10K_DBG_DATA = 0x00000200,
ATH10K_DBG_BMI = 0x00000400,
ATH10K_DBG_ANY = 0xffffffff,
};
@ -42,6 +43,8 @@ extern __printf(1, 2) int ath10k_err(const char *fmt, ...);
extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
#ifdef CONFIG_ATH10K_DEBUGFS
int ath10k_debug_start(struct ath10k *ar);
void ath10k_debug_stop(struct ath10k *ar);
int ath10k_debug_create(struct ath10k *ar);
void ath10k_debug_read_service_map(struct ath10k *ar,
void *service_map,
@ -50,6 +53,15 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
struct wmi_stats_event *ev);
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
return 0;
}
static inline void ath10k_debug_stop(struct ath10k *ar)
{
}
static inline int ath10k_debug_create(struct ath10k *ar)
{
return 0;

View File

@ -103,10 +103,10 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
struct ath10k_htc_hdr *hdr;
hdr = (struct ath10k_htc_hdr *)skb->data;
memset(hdr, 0, sizeof(*hdr));
hdr->eid = ep->eid;
hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
hdr->flags = 0;
spin_lock_bh(&ep->htc->tx_lock);
hdr->seq_no = ep->seq_no++;
@ -117,134 +117,13 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
spin_unlock_bh(&ep->htc->tx_lock);
}
static int ath10k_htc_issue_skb(struct ath10k_htc *htc,
struct ath10k_htc_ep *ep,
struct sk_buff *skb,
u8 credits)
{
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
int ret;
ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
ep->eid, skb);
ath10k_htc_prepare_tx_skb(ep, skb);
ret = ath10k_skb_map(htc->ar->dev, skb);
if (ret)
goto err;
ret = ath10k_hif_send_head(htc->ar,
ep->ul_pipe_id,
ep->eid,
skb->len,
skb);
if (unlikely(ret))
goto err;
return 0;
err:
ath10k_warn("HTC issue failed: %d\n", ret);
spin_lock_bh(&htc->tx_lock);
ep->tx_credits += credits;
spin_unlock_bh(&htc->tx_lock);
/* this is the simplest way to handle out-of-resources for non-credit
* based endpoints. credit based endpoints can still get -ENOSR, but
* this is highly unlikely as credit reservation should prevent that */
if (ret == -ENOSR) {
spin_lock_bh(&htc->tx_lock);
__skb_queue_head(&ep->tx_queue, skb);
spin_unlock_bh(&htc->tx_lock);
return ret;
}
skb_cb->is_aborted = true;
ath10k_htc_notify_tx_completion(ep, skb);
return ret;
}
static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
struct ath10k_htc_ep *ep,
u8 *credits)
{
struct sk_buff *skb;
struct ath10k_skb_cb *skb_cb;
int credits_required;
int remainder;
unsigned int transfer_len;
lockdep_assert_held(&htc->tx_lock);
skb = __skb_dequeue(&ep->tx_queue);
if (!skb)
return NULL;
skb_cb = ATH10K_SKB_CB(skb);
transfer_len = skb->len;
if (likely(transfer_len <= htc->target_credit_size)) {
credits_required = 1;
} else {
/* figure out how many credits this message requires */
credits_required = transfer_len / htc->target_credit_size;
remainder = transfer_len % htc->target_credit_size;
if (remainder)
credits_required++;
}
ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
credits_required, ep->tx_credits);
if (ep->tx_credits < credits_required) {
__skb_queue_head(&ep->tx_queue, skb);
return NULL;
}
ep->tx_credits -= credits_required;
*credits = credits_required;
return skb;
}
static void ath10k_htc_send_work(struct work_struct *work)
{
struct ath10k_htc_ep *ep = container_of(work,
struct ath10k_htc_ep, send_work);
struct ath10k_htc *htc = ep->htc;
struct sk_buff *skb;
u8 credits = 0;
int ret;
while (true) {
if (ep->ul_is_polled)
ath10k_htc_send_complete_check(ep, 0);
spin_lock_bh(&htc->tx_lock);
if (ep->tx_credit_flow_enabled)
skb = ath10k_htc_get_skb_credit_based(htc, ep,
&credits);
else
skb = __skb_dequeue(&ep->tx_queue);
spin_unlock_bh(&htc->tx_lock);
if (!skb)
break;
ret = ath10k_htc_issue_skb(htc, ep, skb, credits);
if (ret == -ENOSR)
break;
}
}
int ath10k_htc_send(struct ath10k_htc *htc,
enum ath10k_htc_ep_id eid,
struct sk_buff *skb)
{
struct ath10k_htc_ep *ep = &htc->endpoint[eid];
int credits = 0;
int ret;
if (htc->ar->state == ATH10K_STATE_WEDGED)
return -ECOMM;
@ -254,18 +133,55 @@ int ath10k_htc_send(struct ath10k_htc *htc,
return -ENOENT;
}
/* FIXME: This looks ugly, can we fix it? */
spin_lock_bh(&htc->tx_lock);
if (htc->stopped) {
spin_unlock_bh(&htc->tx_lock);
return -ESHUTDOWN;
}
__skb_queue_tail(&ep->tx_queue, skb);
skb_push(skb, sizeof(struct ath10k_htc_hdr));
spin_unlock_bh(&htc->tx_lock);
queue_work(htc->ar->workqueue, &ep->send_work);
skb_push(skb, sizeof(struct ath10k_htc_hdr));
if (ep->tx_credit_flow_enabled) {
credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
spin_lock_bh(&htc->tx_lock);
if (ep->tx_credits < credits) {
spin_unlock_bh(&htc->tx_lock);
ret = -EAGAIN;
goto err_pull;
}
ep->tx_credits -= credits;
spin_unlock_bh(&htc->tx_lock);
}
ath10k_htc_prepare_tx_skb(ep, skb);
ret = ath10k_skb_map(htc->ar->dev, skb);
if (ret)
goto err_credits;
ret = ath10k_hif_send_head(htc->ar, ep->ul_pipe_id, ep->eid,
skb->len, skb);
if (ret)
goto err_unmap;
return 0;
err_unmap:
ath10k_skb_unmap(htc->ar->dev, skb);
err_credits:
if (ep->tx_credit_flow_enabled) {
spin_lock_bh(&htc->tx_lock);
ep->tx_credits += credits;
spin_unlock_bh(&htc->tx_lock);
if (ep->ep_ops.ep_tx_credits)
ep->ep_ops.ep_tx_credits(htc->ar);
}
err_pull:
skb_pull(skb, sizeof(struct ath10k_htc_hdr));
return ret;
}
static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
@ -278,39 +194,9 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
ath10k_htc_notify_tx_completion(ep, skb);
/* the skb now belongs to the completion handler */
/* note: when using TX credit flow, the re-checking of queues happens
* when credits flow back from the target. in the non-TX credit case,
* we recheck after the packet completes */
spin_lock_bh(&htc->tx_lock);
if (!ep->tx_credit_flow_enabled && !htc->stopped)
queue_work(ar->workqueue, &ep->send_work);
spin_unlock_bh(&htc->tx_lock);
return 0;
}
/* flush endpoint TX queue */
static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc,
struct ath10k_htc_ep *ep)
{
struct sk_buff *skb;
struct ath10k_skb_cb *skb_cb;
spin_lock_bh(&htc->tx_lock);
for (;;) {
skb = __skb_dequeue(&ep->tx_queue);
if (!skb)
break;
skb_cb = ATH10K_SKB_CB(skb);
skb_cb->is_aborted = true;
ath10k_htc_notify_tx_completion(ep, skb);
}
spin_unlock_bh(&htc->tx_lock);
cancel_work_sync(&ep->send_work);
}
/***********/
/* Receive */
/***********/
@ -340,8 +226,11 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc,
ep = &htc->endpoint[report->eid];
ep->tx_credits += report->credits;
if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
queue_work(htc->ar->workqueue, &ep->send_work);
if (ep->ep_ops.ep_tx_credits) {
spin_unlock_bh(&htc->tx_lock);
ep->ep_ops.ep_tx_credits(htc->ar);
spin_lock_bh(&htc->tx_lock);
}
}
spin_unlock_bh(&htc->tx_lock);
}
@ -599,10 +488,8 @@ static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
ep->max_ep_message_len = 0;
ep->max_tx_queue_depth = 0;
ep->eid = i;
skb_queue_head_init(&ep->tx_queue);
ep->htc = htc;
ep->tx_credit_flow_enabled = true;
INIT_WORK(&ep->send_work, ath10k_htc_send_work);
}
}
@ -752,8 +639,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
tx_alloc = ath10k_htc_get_credit_allocation(htc,
conn_req->service_id);
if (!tx_alloc)
ath10k_dbg(ATH10K_DBG_HTC,
"HTC Service %s does not allocate target credits\n",
ath10k_dbg(ATH10K_DBG_BOOT,
"boot htc service %s does not allocate target credits\n",
htc_service_name(conn_req->service_id));
skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
@ -873,19 +760,19 @@ setup:
if (status)
return status;
ath10k_dbg(ATH10K_DBG_HTC,
"HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
ath10k_dbg(ATH10K_DBG_BOOT,
"boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
htc_service_name(ep->service_id), ep->ul_pipe_id,
ep->dl_pipe_id, ep->eid);
ath10k_dbg(ATH10K_DBG_HTC,
"EP %d UL polled: %d, DL polled: %d\n",
ath10k_dbg(ATH10K_DBG_BOOT,
"boot htc ep %d ul polled %d dl polled %d\n",
ep->eid, ep->ul_is_polled, ep->dl_is_polled);
if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
ep->tx_credit_flow_enabled = false;
ath10k_dbg(ATH10K_DBG_HTC,
"HTC service: %s eid: %d TX flow control disabled\n",
ath10k_dbg(ATH10K_DBG_BOOT,
"boot htc service '%s' eid %d TX flow control disabled\n",
htc_service_name(ep->service_id), assigned_eid);
}
@ -945,18 +832,10 @@ int ath10k_htc_start(struct ath10k_htc *htc)
*/
void ath10k_htc_stop(struct ath10k_htc *htc)
{
int i;
struct ath10k_htc_ep *ep;
spin_lock_bh(&htc->tx_lock);
htc->stopped = true;
spin_unlock_bh(&htc->tx_lock);
for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
ep = &htc->endpoint[i];
ath10k_htc_flush_endpoint_tx(htc, ep);
}
ath10k_hif_stop(htc->ar);
}

View File

@ -276,6 +276,7 @@ struct ath10k_htc_ops {
struct ath10k_htc_ep_ops {
void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
void (*ep_tx_credits)(struct ath10k *);
};
/* service connection information */
@ -315,15 +316,11 @@ struct ath10k_htc_ep {
int ul_is_polled; /* call HIF to get tx completions */
int dl_is_polled; /* call HIF to fetch rx (not implemented) */
struct sk_buff_head tx_queue;
u8 seq_no; /* for debugging */
int tx_credits;
int tx_credit_size;
int tx_credits_per_max_message;
bool tx_credit_flow_enabled;
struct work_struct send_work;
};
struct ath10k_htc_svc_tx_credits {

View File

@ -19,6 +19,7 @@
#define _HTT_H_
#include <linux/bug.h>
#include <linux/interrupt.h>
#include "htc.h"
#include "rx_desc.h"
@ -1268,6 +1269,7 @@ struct ath10k_htt {
/* set if host-fw communication goes haywire
* used to avoid further failures */
bool rx_confused;
struct tasklet_struct rx_replenish_task;
};
#define RX_HTT_HDR_STATUS_LEN 64
@ -1308,6 +1310,10 @@ struct htt_rx_desc {
#define HTT_RX_BUF_SIZE 1920
#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
/* Refill a bunch of RX buffers for each refill round so that FW/HW can handle
* aggregated traffic more nicely. */
#define ATH10K_HTT_MAX_NUM_REFILL 16
/*
* DMA_MAP expects the buffer to be an integral number of cache lines.
* Rather than checking the actual cache line size, this code makes a
@ -1327,6 +1333,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt);
void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie);
int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);

View File

@ -20,6 +20,7 @@
#include "htt.h"
#include "txrx.h"
#include "debug.h"
#include "trace.h"
#include <linux/log2.h>
@ -40,6 +41,10 @@
/* when under memory pressure rx ring refill may fail and needs a retry */
#define HTT_RX_RING_REFILL_RETRY_MS 50
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
{
int size;
@ -177,10 +182,27 @@ static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
{
int ret, num_to_fill;
int ret, num_deficit, num_to_fill;
/* Refilling the whole RX ring buffer proves to be a bad idea. The
* reason is RX may take up significant amount of CPU cycles and starve
* other tasks, e.g. TX on an ethernet device while acting as a bridge
* with ath10k wlan interface. This ended up with very poor performance
* once CPU the host system was overwhelmed with RX on ath10k.
*
* By limiting the number of refills the replenishing occurs
* progressively. This in turns makes use of the fact tasklets are
* processed in FIFO order. This means actual RX processing can starve
* out refilling. If there's not enough buffers on RX ring FW will not
* report RX until it is refilled with enough buffers. This
* automatically balances load wrt to CPU power.
*
* This probably comes at a cost of lower maximum throughput but
* improves the avarage and stability. */
spin_lock_bh(&htt->rx_ring.lock);
num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
num_deficit -= num_to_fill;
ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
if (ret == -ENOMEM) {
/*
@ -191,6 +213,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
*/
mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
} else if (num_deficit > 0) {
tasklet_schedule(&htt->rx_replenish_task);
}
spin_unlock_bh(&htt->rx_ring.lock);
}
@ -212,6 +236,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt)
int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
del_timer_sync(&htt->rx_ring.refill_retry_timer);
tasklet_kill(&htt->rx_replenish_task);
while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
struct sk_buff *skb =
@ -441,6 +466,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
return msdu_chaining;
}
static void ath10k_htt_rx_replenish_task(unsigned long ptr)
{
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
ath10k_htt_rx_msdu_buff_replenish(htt);
}
int ath10k_htt_rx_attach(struct ath10k_htt *htt)
{
dma_addr_t paddr;
@ -501,7 +532,10 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
goto err_fill_ring;
ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
(unsigned long)htt);
ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
htt->rx_ring.size, htt->rx_ring.fill_level);
return 0;
@ -590,142 +624,144 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
return false;
}
static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
struct htt_rx_info *info)
struct rfc1042_hdr {
u8 llc_dsap;
u8 llc_ssap;
u8 llc_ctrl;
u8 snap_oui[3];
__be16 snap_type;
} __packed;
struct amsdu_subframe_hdr {
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
__be16 len;
} __packed;
static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
struct htt_rx_info *info)
{
struct htt_rx_desc *rxd;
struct sk_buff *amsdu;
struct sk_buff *first;
struct ieee80211_hdr *hdr;
struct sk_buff *skb = info->skb;
enum rx_msdu_decap_format fmt;
enum htt_rx_mpdu_encrypt_type enctype;
struct ieee80211_hdr *hdr;
u8 hdr_buf[64], addr[ETH_ALEN], *qos;
unsigned int hdr_len;
int crypto_len;
rxd = (void *)skb->data - sizeof(*rxd);
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
RX_MPDU_START_INFO0_ENCRYPT_TYPE);
/* FIXME: No idea what assumptions are safe here. Need logs */
if ((fmt == RX_MSDU_DECAP_RAW && skb->next)) {
ath10k_htt_rx_free_msdu_chain(skb->next);
skb->next = NULL;
return -ENOTSUPP;
}
hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(hdr_buf, hdr, hdr_len);
hdr = (struct ieee80211_hdr *)hdr_buf;
/* A-MSDU max is a little less than 8K */
amsdu = dev_alloc_skb(8*1024);
if (!amsdu) {
ath10k_warn("A-MSDU allocation failed\n");
ath10k_htt_rx_free_msdu_chain(skb->next);
skb->next = NULL;
return -ENOMEM;
}
if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
int hdrlen;
hdr = (void *)rxd->rx_hdr_status;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
}
/* FIXME: Hopefully this is a temporary measure.
*
* Reporting individual A-MSDU subframes means each reported frame
* shares the same sequence number.
*
* mac80211 drops frames it recognizes as duplicates, i.e.
* retransmission flag is set and sequence number matches sequence
* number from a previous frame (as per IEEE 802.11-2012: 9.3.2.10
* "Duplicate detection and recovery")
*
* To avoid frames being dropped clear retransmission flag for all
* received A-MSDUs.
*
* Worst case: actual duplicate frames will be reported but this should
* still be handled gracefully by other OSI/ISO layers. */
hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_RETRY);
first = skb;
while (skb) {
void *decap_hdr;
int decap_len = 0;
int len;
rxd = (void *)skb->data - sizeof(*rxd);
fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
RX_MSDU_START_INFO1_DECAP_FORMAT);
RX_MSDU_START_INFO1_DECAP_FORMAT);
decap_hdr = (void *)rxd->rx_hdr_status;
skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
/* First frame in an A-MSDU chain has more decapped data. */
if (skb == first) {
/* We receive linked A-MSDU subframe skbuffs. The
* first one contains the original 802.11 header (and
* possible crypto param) in the RX descriptor. The
* A-MSDU subframe header follows that. Each part is
* aligned to 4 byte boundary. */
hdr = (void *)amsdu->data;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
decap_hdr += roundup(hdr_len, 4);
decap_hdr += roundup(crypto_len, 4);
len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
4);
decap_hdr += len;
}
/* When fmt == RX_MSDU_DECAP_8023_SNAP_LLC:
*
* SNAP 802.3 consists of:
* [dst:6][src:6][len:2][dsap:1][ssap:1][ctl:1][snap:5]
* [data][fcs:4].
*
* Since this overlaps with A-MSDU header (da, sa, len)
* there's nothing extra to do. */
if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
/* Ethernet2 decap inserts ethernet header in place of
* A-MSDU subframe header. */
skb_pull(skb, 6 + 6 + 2);
/* A-MSDU subframe header length */
decap_len += 6 + 6 + 2;
/* Ethernet2 decap also strips the LLC/SNAP so we need
* to re-insert it. The LLC/SNAP follows A-MSDU
* subframe header. */
/* FIXME: Not all LLCs are 8 bytes long */
decap_len += 8;
memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
}
if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
/* Native Wifi decap inserts regular 802.11 header
* in place of A-MSDU subframe header. */
switch (fmt) {
case RX_MSDU_DECAP_RAW:
/* remove trailing FCS */
skb_trim(skb, skb->len - FCS_LEN);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
/* pull decapped header and copy DA */
hdr = (struct ieee80211_hdr *)skb->data;
skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN);
skb_pull(skb, hdr_len);
/* A-MSDU subframe header length */
decap_len += 6 + 6 + 2;
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)hdr_buf;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
}
if (fmt == RX_MSDU_DECAP_RAW)
skb_trim(skb, skb->len - 4); /* remove FCS */
memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
/* A-MSDU subframes are padded to 4bytes
* but relative to first subframe, not the whole MPDU */
if (skb->next && ((decap_len + skb->len) & 3)) {
int padlen = 4 - ((decap_len + skb->len) & 3);
memset(skb_put(amsdu, padlen), 0, padlen);
/* original A-MSDU header has the bit set but we're
* not including A-MSDU subframe header */
hdr = (struct ieee80211_hdr *)skb->data;
qos = ieee80211_get_qos_ctl(hdr);
qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
/* original 802.11 header has a different DA */
memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
/* strip ethernet header and insert decapped 802.11
* header, amsdu subframe header and rfc1042 header */
len = 0;
len += sizeof(struct rfc1042_hdr);
len += sizeof(struct amsdu_subframe_hdr);
skb_pull(skb, sizeof(struct ethhdr));
memcpy(skb_push(skb, len), decap_hdr, len);
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
/* insert decapped 802.11 header making a singly
* A-MSDU */
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
}
info->skb = skb;
info->encrypt_type = enctype;
skb = skb->next;
info->skb->next = NULL;
ath10k_process_rx(htt->ar, info);
}
info->skb = amsdu;
info->encrypt_type = enctype;
ath10k_htt_rx_free_msdu_chain(first);
return 0;
/* FIXME: It might be nice to re-assemble the A-MSDU when there's a
* monitor interface active for sniffing purposes. */
}
static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
{
struct sk_buff *skb = info->skb;
struct htt_rx_desc *rxd;
struct ieee80211_hdr *hdr;
enum rx_msdu_decap_format fmt;
enum htt_rx_mpdu_encrypt_type enctype;
int hdr_len;
void *rfc1042;
/* This shouldn't happen. If it does than it may be a FW bug. */
if (skb->next) {
@ -739,49 +775,53 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
RX_MSDU_START_INFO1_DECAP_FORMAT);
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
RX_MPDU_START_INFO0_ENCRYPT_TYPE);
hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
switch (fmt) {
case RX_MSDU_DECAP_RAW:
/* remove trailing FCS */
skb_trim(skb, skb->len - 4);
skb_trim(skb, skb->len - FCS_LEN);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
/* nothing to do here */
/* Pull decapped header */
hdr = (struct ieee80211_hdr *)skb->data;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
skb_pull(skb, hdr_len);
/* Push original header */
hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
/* macaddr[6] + macaddr[6] + ethertype[2] */
skb_pull(skb, 6 + 6 + 2);
/* strip ethernet header and insert decapped 802.11 header and
* rfc1042 header */
rfc1042 = hdr;
rfc1042 += roundup(hdr_len, 4);
rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
skb_pull(skb, sizeof(struct ethhdr));
memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
rfc1042, sizeof(struct rfc1042_hdr));
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
/* macaddr[6] + macaddr[6] + len[2] */
/* we don't need this for non-A-MSDU */
skb_pull(skb, 6 + 6 + 2);
/* remove A-MSDU subframe header and insert
* decapped 802.11 header. rfc1042 header is already there */
skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
break;
}
if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
void *llc;
int llclen;
llclen = 8;
llc = hdr;
llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
skb_push(skb, llclen);
memcpy(skb->data, llc, llclen);
}
if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
int len = ieee80211_hdrlen(hdr->frame_control);
skb_push(skb, len);
memcpy(skb->data, hdr, len);
}
info->skb = skb;
info->encrypt_type = enctype;
return 0;
ath10k_process_rx(htt->ar, info);
}
static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
@ -853,8 +893,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
int fw_desc_len;
u8 *fw_desc;
int i, j;
int ret;
int ip_summed;
memset(&info, 0, sizeof(info));
@ -929,11 +967,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
continue;
}
/* The skb is not yet processed and it may be
* reallocated. Since the offload is in the original
* skb extract the checksum now and assign it later */
ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
info.skb = msdu_head;
info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
info.signal = ATH10K_DEFAULT_NOISE_FLOOR;
@ -946,28 +979,13 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
if (ath10k_htt_rx_hdr_is_amsdu(hdr))
ret = ath10k_htt_rx_amsdu(htt, &info);
ath10k_htt_rx_amsdu(htt, &info);
else
ret = ath10k_htt_rx_msdu(htt, &info);
if (ret && !info.fcs_err) {
ath10k_warn("error processing msdus %d\n", ret);
dev_kfree_skb_any(info.skb);
continue;
}
if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
info.skb->ip_summed = ip_summed;
ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
info.skb->data, info.skb->len);
ath10k_process_rx(htt->ar, &info);
ath10k_htt_rx_msdu(htt, &info);
}
}
ath10k_htt_rx_msdu_buff_replenish(htt);
tasklet_schedule(&htt->rx_replenish_task);
}
static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
@ -1139,7 +1157,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
break;
}
ath10k_txrx_tx_completed(htt, &tx_done);
ath10k_txrx_tx_unref(htt, &tx_done);
break;
}
case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
@ -1173,7 +1191,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
msdu_id = resp->data_tx_completion.msdus[i];
tx_done.msdu_id = __le16_to_cpu(msdu_id);
ath10k_txrx_tx_completed(htt, &tx_done);
ath10k_txrx_tx_unref(htt, &tx_done);
}
break;
}
@ -1198,8 +1216,10 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_TEST:
/* FIX THIS */
break;
case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
case HTT_T2H_MSG_TYPE_STATS_CONF:
trace_ath10k_htt_stats(skb->data, skb->len);
break;
case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
case HTT_T2H_MSG_TYPE_RX_ADDBA:
case HTT_T2H_MSG_TYPE_RX_DELBA:
case HTT_T2H_MSG_TYPE_RX_FLUSH:

View File

@ -96,7 +96,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
pipe);
ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n",
ath10k_dbg(ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
htt->max_num_pending_tx);
htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
@ -117,7 +117,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
{
struct sk_buff *txdesc;
struct htt_tx_done tx_done = {0};
int msdu_id;
/* No locks needed. Called after communication with the device has
@ -127,18 +127,13 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
if (!test_bit(msdu_id, htt->used_msdu_ids))
continue;
txdesc = htt->pending_tx[msdu_id];
if (!txdesc)
continue;
ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
msdu_id);
if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
ATH10K_SKB_CB(txdesc)->htt.refcount = 1;
tx_done.discard = 1;
tx_done.msdu_id = msdu_id;
ATH10K_SKB_CB(txdesc)->htt.discard = true;
ath10k_txrx_tx_unref(htt, txdesc);
ath10k_txrx_tx_unref(htt, &tx_done);
}
}
@ -152,26 +147,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt)
void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct ath10k_htt *htt = &ar->htt;
if (skb_cb->htt.is_conf) {
dev_kfree_skb_any(skb);
return;
}
if (skb_cb->is_aborted) {
skb_cb->htt.discard = true;
/* if the skbuff is aborted we need to make sure we'll free up
* the tx resources, we can't simply run tx_unref() 2 times
* because if htt tx completion came in earlier we'd access
* unallocated memory */
if (skb_cb->htt.refcount > 1)
skb_cb->htt.refcount = 1;
}
ath10k_txrx_tx_unref(htt, skb);
dev_kfree_skb_any(skb);
}
int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
@ -192,10 +168,48 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
cmd = (struct htt_cmd *)skb->data;
cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ;
ATH10K_SKB_CB(skb)->htt.is_conf = true;
ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
if (ret) {
dev_kfree_skb_any(skb);
return ret;
}
return 0;
}
int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie)
{
struct htt_stats_req *req;
struct sk_buff *skb;
struct htt_cmd *cmd;
int len = 0, ret;
len += sizeof(cmd->hdr);
len += sizeof(cmd->stats_req);
skb = ath10k_htc_alloc_skb(len);
if (!skb)
return -ENOMEM;
skb_put(skb, len);
cmd = (struct htt_cmd *)skb->data;
cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_STATS_REQ;
req = &cmd->stats_req;
memset(req, 0, sizeof(*req));
/* currently we support only max 8 bit masks so no need to worry
* about endian support */
req->upload_types[0] = mask;
req->reset_types[0] = mask;
req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID;
req->cookie_lsb = cpu_to_le32(cookie & 0xffffffff);
req->cookie_msb = cpu_to_le32((cookie & 0xffffffff00000000ULL) >> 32);
ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
if (ret) {
ath10k_warn("failed to send htt type stats request: %d", ret);
dev_kfree_skb_any(skb);
return ret;
}
@ -279,8 +293,6 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
#undef desc_offset
ATH10K_SKB_CB(skb)->htt.is_conf = true;
ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
if (ret) {
dev_kfree_skb_any(skb);
@ -293,10 +305,10 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
{
struct device *dev = htt->ar->dev;
struct ath10k_skb_cb *skb_cb;
struct sk_buff *txdesc = NULL;
struct htt_cmd *cmd;
u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
u8 vdev_id = skb_cb->htt.vdev_id;
int len = 0;
int msdu_id = -1;
int res;
@ -304,30 +316,30 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res = ath10k_htt_tx_inc_pending(htt);
if (res)
return res;
goto err;
len += sizeof(cmd->hdr);
len += sizeof(cmd->mgmt_tx);
spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt);
if (res < 0) {
spin_unlock_bh(&htt->tx_lock);
goto err_tx_dec;
}
msdu_id = res;
htt->pending_tx[msdu_id] = msdu;
spin_unlock_bh(&htt->tx_lock);
txdesc = ath10k_htc_alloc_skb(len);
if (!txdesc) {
res = -ENOMEM;
goto err;
goto err_free_msdu_id;
}
spin_lock_bh(&htt->tx_lock);
msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
if (msdu_id < 0) {
spin_unlock_bh(&htt->tx_lock);
res = msdu_id;
goto err;
}
htt->pending_tx[msdu_id] = txdesc;
spin_unlock_bh(&htt->tx_lock);
res = ath10k_skb_map(dev, msdu);
if (res)
goto err;
goto err_free_txdesc;
skb_put(txdesc, len);
cmd = (struct htt_cmd *)txdesc->data;
@ -339,31 +351,27 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
memcpy(cmd->mgmt_tx.hdr, msdu->data,
min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
/* refcount is decremented by HTC and HTT completions until it reaches
* zero and is freed */
skb_cb = ATH10K_SKB_CB(txdesc);
skb_cb->htt.msdu_id = msdu_id;
skb_cb->htt.refcount = 2;
skb_cb->htt.msdu = msdu;
skb_cb->htt.frag_len = 0;
skb_cb->htt.pad_len = 0;
res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
if (res)
goto err;
goto err_unmap_msdu;
return 0;
err:
err_unmap_msdu:
ath10k_skb_unmap(dev, msdu);
if (txdesc)
dev_kfree_skb_any(txdesc);
if (msdu_id >= 0) {
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
}
err_free_txdesc:
dev_kfree_skb_any(txdesc);
err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
err_tx_dec:
ath10k_htt_tx_dec_pending(htt);
err:
return res;
}
@ -373,13 +381,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
struct htt_cmd *cmd;
struct htt_data_tx_desc_frag *tx_frags;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
struct ath10k_skb_cb *skb_cb;
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
struct sk_buff *txdesc = NULL;
struct sk_buff *txfrag = NULL;
bool use_frags;
u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
u8 tid;
int prefetch_len, desc_len, frag_len;
dma_addr_t frags_paddr;
int prefetch_len, desc_len;
int msdu_id = -1;
int res;
u8 flags0;
@ -387,73 +394,73 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res = ath10k_htt_tx_inc_pending(htt);
if (res)
return res;
goto err;
spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt);
if (res < 0) {
spin_unlock_bh(&htt->tx_lock);
goto err_tx_dec;
}
msdu_id = res;
htt->pending_tx[msdu_id] = msdu;
spin_unlock_bh(&htt->tx_lock);
prefetch_len = min(htt->prefetch_len, msdu->len);
prefetch_len = roundup(prefetch_len, 4);
desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
frag_len = sizeof(*tx_frags) * 2;
txdesc = ath10k_htc_alloc_skb(desc_len);
if (!txdesc) {
res = -ENOMEM;
goto err;
goto err_free_msdu_id;
}
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if (htt->target_version_major < 3 ||
!ieee80211_is_mgmt(hdr->frame_control)) {
txfrag = dev_alloc_skb(frag_len);
if (!txfrag) {
res = -ENOMEM;
goto err;
}
}
use_frags = htt->target_version_major < 3 ||
!ieee80211_is_mgmt(hdr->frame_control);
if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
ath10k_warn("htt alignment check failed. dropping packet.\n");
res = -EIO;
goto err;
goto err_free_txdesc;
}
spin_lock_bh(&htt->tx_lock);
msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
if (msdu_id < 0) {
spin_unlock_bh(&htt->tx_lock);
res = msdu_id;
goto err;
if (use_frags) {
skb_cb->htt.frag_len = sizeof(*tx_frags) * 2;
skb_cb->htt.pad_len = (unsigned long)msdu->data -
round_down((unsigned long)msdu->data, 4);
skb_push(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
} else {
skb_cb->htt.frag_len = 0;
skb_cb->htt.pad_len = 0;
}
htt->pending_tx[msdu_id] = txdesc;
spin_unlock_bh(&htt->tx_lock);
res = ath10k_skb_map(dev, msdu);
if (res)
goto err;
goto err_pull_txfrag;
if (use_frags) {
dma_sync_single_for_cpu(dev, skb_cb->paddr, msdu->len,
DMA_TO_DEVICE);
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if (htt->target_version_major < 3 ||
!ieee80211_is_mgmt(hdr->frame_control)) {
/* tx fragment list must be terminated with zero-entry */
skb_put(txfrag, frag_len);
tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data;
tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
tx_frags[0].len = __cpu_to_le32(msdu->len);
tx_frags = (struct htt_data_tx_desc_frag *)msdu->data;
tx_frags[0].paddr = __cpu_to_le32(skb_cb->paddr +
skb_cb->htt.frag_len +
skb_cb->htt.pad_len);
tx_frags[0].len = __cpu_to_le32(msdu->len -
skb_cb->htt.frag_len -
skb_cb->htt.pad_len);
tx_frags[1].paddr = __cpu_to_le32(0);
tx_frags[1].len = __cpu_to_le32(0);
res = ath10k_skb_map(dev, txfrag);
if (res)
goto err;
ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx\n",
(unsigned long long) ATH10K_SKB_CB(txfrag)->paddr);
ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ",
txfrag->data, frag_len);
dma_sync_single_for_device(dev, skb_cb->paddr, msdu->len,
DMA_TO_DEVICE);
}
ath10k_dbg(ATH10K_DBG_HTT, "msdu 0x%llx\n",
@ -463,7 +470,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
skb_put(txdesc, desc_len);
cmd = (struct htt_cmd *)txdesc->data;
memset(cmd, 0, desc_len);
tid = ATH10K_SKB_CB(msdu)->htt.tid;
@ -474,15 +480,11 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if (htt->target_version_major >= 3 &&
ieee80211_is_mgmt(hdr->frame_control))
flags0 |= SM(ATH10K_HW_TXRX_MGMT,
if (use_frags)
flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
else
flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
flags0 |= SM(ATH10K_HW_TXRX_MGMT,
HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
flags1 = 0;
@ -491,52 +493,37 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if (htt->target_version_major >= 3 &&
ieee80211_is_mgmt(hdr->frame_control))
frags_paddr = ATH10K_SKB_CB(msdu)->paddr;
else
frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
cmd->data_tx.flags0 = flags0;
cmd->data_tx.flags1 = __cpu_to_le16(flags1);
cmd->data_tx.len = __cpu_to_le16(msdu->len);
cmd->data_tx.len = __cpu_to_le16(msdu->len -
skb_cb->htt.frag_len -
skb_cb->htt.pad_len);
cmd->data_tx.id = __cpu_to_le16(msdu_id);
cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr);
cmd->data_tx.frags_paddr = __cpu_to_le32(skb_cb->paddr);
cmd->data_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len);
/* refcount is decremented by HTC and HTT completions until it reaches
* zero and is freed */
skb_cb = ATH10K_SKB_CB(txdesc);
skb_cb->htt.msdu_id = msdu_id;
skb_cb->htt.refcount = 2;
skb_cb->htt.txfrag = txfrag;
skb_cb->htt.msdu = msdu;
memcpy(cmd->data_tx.prefetch, hdr, prefetch_len);
res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
if (res)
goto err;
goto err_unmap_msdu;
return 0;
err:
if (txfrag)
ath10k_skb_unmap(dev, txfrag);
if (txdesc)
dev_kfree_skb_any(txdesc);
if (txfrag)
dev_kfree_skb_any(txfrag);
if (msdu_id >= 0) {
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
}
ath10k_htt_tx_dec_pending(htt);
err_unmap_msdu:
ath10k_skb_unmap(dev, msdu);
err_pull_txfrag:
skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
err_free_txdesc:
dev_kfree_skb_any(txdesc);
err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
err_tx_dec:
ath10k_htt_tx_dec_pending(htt);
err:
return res;
}

View File

@ -74,7 +74,11 @@ enum ath10k_mcast2ucast_mode {
#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_RX_TIMEOUT_LO_PRI 100
#define TARGET_RX_TIMEOUT_HI_PRI 40
#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET
/* Native Wifi decap mode is used to align IP frames to 4-byte boundaries and
* avoid a very expensive re-alignment in mac80211. */
#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_NATIVE_WIFI
#define TARGET_SCAN_MAX_PENDING_REQS 4
#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3
#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3

View File

@ -460,6 +460,11 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
arg.ssid_len = arvif->vif->bss_conf.ssid_len;
}
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d start center_freq %d phymode %s\n",
arg.vdev_id, arg.channel.freq,
ath10k_wmi_phymode_str(arg.channel.mode));
ret = ath10k_wmi_vdev_start(ar, &arg);
if (ret) {
ath10k_warn("WMI vdev start failed: ret %d\n", ret);
@ -604,7 +609,7 @@ static int ath10k_monitor_create(struct ath10k *ar)
goto vdev_fail;
}
ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
ar->monitor_vdev_id);
ar->monitor_present = true;
@ -636,7 +641,7 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
ar->monitor_present = false;
ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
ar->monitor_vdev_id);
return ret;
}
@ -665,7 +670,7 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
arvif->vdev_id);
return;
}
ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
}
static void ath10k_control_ibss(struct ath10k_vif *arvif,
@ -749,14 +754,14 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
psmode = WMI_STA_PS_MODE_DISABLED;
}
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
arvif->vdev_id, psmode ? "enable" : "disable");
ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
psmode);
if (ar_iter->ret)
ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
psmode, arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
psmode, arvif->vdev_id);
}
/**********************/
@ -946,7 +951,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
arg->peer_ht_rates.num_rates = n;
arg->peer_num_spatial_streams = max((n+7) / 8, 1);
ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
arg->addr,
arg->peer_ht_rates.num_rates,
arg->peer_num_spatial_streams);
}
@ -966,7 +972,7 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
arg->peer_flags |= WMI_PEER_QOS;
if (sta->wme && sta->uapsd_queues) {
ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
sta->uapsd_queues, sta->max_sp);
arg->peer_flags |= WMI_PEER_APSD;
@ -1045,7 +1051,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
arg->peer_vht_rates.tx_mcs_set =
__le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n",
sta->addr, arg->peer_max_mpdu, arg->peer_flags);
}
static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
@ -1073,8 +1080,6 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
{
enum wmi_phy_mode phymode = MODE_UNKNOWN;
/* FIXME: add VHT */
switch (ar->hw->conf.chandef.chan->band) {
case IEEE80211_BAND_2GHZ:
if (sta->ht_cap.ht_supported) {
@ -1088,7 +1093,17 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
break;
case IEEE80211_BAND_5GHZ:
if (sta->ht_cap.ht_supported) {
/*
* Check VHT first.
*/
if (sta->vht_cap.vht_supported) {
if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
phymode = MODE_11AC_VHT80;
else if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
phymode = MODE_11AC_VHT40;
else if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
phymode = MODE_11AC_VHT20;
} else if (sta->ht_cap.ht_supported) {
if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
phymode = MODE_11NA_HT40;
else
@ -1102,6 +1117,9 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
break;
}
ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n",
sta->addr, ath10k_wmi_phymode_str(phymode));
arg->peer_phymode = phymode;
WARN_ON(phymode == MODE_UNKNOWN);
}
@ -1159,15 +1177,15 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
rcu_read_unlock();
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d up (associated) bssid %pM aid %d\n",
arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
bss_conf->bssid);
if (ret)
ath10k_warn("VDEV: %d up failed: ret %d\n",
arvif->vdev_id, ret);
else
ath10k_dbg(ATH10K_DBG_MAC,
"VDEV: %d associated, BSSID: %pM, AID: %d\n",
arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
}
/*
@ -1188,10 +1206,11 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
* No idea why this happens, even though VDEV-DOWN is supposed
* to be analogous to link down, so just stop the VDEV.
*/
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n",
arvif->vdev_id);
/* FIXME: check return value */
ret = ath10k_vdev_stop(arvif);
if (!ret)
ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
arvif->vdev_id);
/*
* If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
@ -1200,12 +1219,10 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
* interfaces as it expects there is no rx when no interface is
* running.
*/
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
if (ret)
ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
arvif->vdev_id, ret);
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id);
ath10k_wmi_flush_tx(ar);
/* FIXME: why don't we print error if wmi call fails? */
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
arvif->def_wep_key_index = 0;
}
@ -1330,8 +1347,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
continue;
ath10k_dbg(ATH10K_DBG_WMI,
"%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
__func__, ch - arg.channels, arg.n_channels,
"mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
ch - arg.channels, arg.n_channels,
ch->freq, ch->max_power, ch->max_reg_power,
ch->max_antenna_gain, ch->mode);
@ -1431,7 +1448,8 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
if (key->keyidx == arvif->def_wep_key_index)
return;
ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d keyidx %d\n",
arvif->vdev_id, key->keyidx);
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
WMI_VDEV_PARAM_DEF_KEYID,
@ -1534,7 +1552,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
mutex_lock(&ar->conf_mutex);
ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n",
skb);
hdr = (struct ieee80211_hdr *)skb->data;
@ -1546,6 +1564,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
spin_unlock_bh(&ar->data_lock);
if (peer)
/* FIXME: should this use ath10k_warn()? */
ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
peer_addr, vdev_id);
@ -1643,8 +1662,6 @@ static int ath10k_abort_scan(struct ath10k *ar)
return -EIO;
}
ath10k_wmi_flush_tx(ar);
ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
if (ret == 0)
ath10k_warn("timed out while waiting for scan to stop\n");
@ -1678,10 +1695,6 @@ static int ath10k_start_scan(struct ath10k *ar,
if (ret)
return ret;
/* make sure we submit the command so the completion
* timeout makes sense */
ath10k_wmi_flush_tx(ar);
ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
if (ret == 0) {
ath10k_abort_scan(ar);
@ -1744,7 +1757,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
ath10k_tx_h_seq_no(skb);
}
memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
ATH10K_SKB_CB(skb)->htt.is_offchan = false;
ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
ATH10K_SKB_CB(skb)->htt.tid = tid;
@ -1886,7 +1899,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock(&ar->conf_mutex);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
ath10k_dbg(ATH10K_DBG_MAC, "mac config channel %d mhz\n",
conf->chandef.chan->center_freq);
spin_lock_bh(&ar->data_lock);
ar->rx_channel = conf->chandef.chan;
@ -1903,7 +1916,6 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
ret = ath10k_monitor_destroy(ar);
}
ath10k_wmi_flush_tx(ar);
mutex_unlock(&ar->conf_mutex);
return ret;
}
@ -1975,7 +1987,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
break;
}
ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n",
arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
@ -2054,7 +2066,12 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
mutex_lock(&ar->conf_mutex);
ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
spin_lock_bh(&ar->data_lock);
if (arvif->beacon) {
dev_kfree_skb_any(arvif->beacon);
arvif->beacon = NULL;
}
spin_unlock_bh(&ar->data_lock);
ar->free_vdev_map |= 1 << (arvif->vdev_id);
@ -2066,6 +2083,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
kfree(arvif->u.ap.noa_data);
}
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev delete %d (remove interface)\n",
arvif->vdev_id);
ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
if (ret)
ath10k_warn("WMI vdev delete failed: %d\n", ret);
@ -2107,18 +2127,20 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw,
if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
!ar->monitor_enabled) {
ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d start\n",
ar->monitor_vdev_id);
ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
if (ret)
ath10k_warn("Unable to start monitor mode\n");
else
ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
} else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
ar->monitor_enabled) {
ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d stop\n",
ar->monitor_vdev_id);
ret = ath10k_monitor_stop(ar);
if (ret)
ath10k_warn("Unable to stop monitor mode\n");
else
ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
}
mutex_unlock(&ar->conf_mutex);
@ -2143,41 +2165,41 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
WMI_VDEV_PARAM_BEACON_INTERVAL,
arvif->beacon_interval);
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d beacon_interval %d\n",
arvif->vdev_id, arvif->beacon_interval);
if (ret)
ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Beacon interval: %d set for VDEV: %d\n",
arvif->beacon_interval, arvif->vdev_id);
}
if (changed & BSS_CHANGED_BEACON) {
ath10k_dbg(ATH10K_DBG_MAC,
"vdev %d set beacon tx mode to staggered\n",
arvif->vdev_id);
ret = ath10k_wmi_pdev_set_param(ar,
WMI_PDEV_PARAM_BEACON_TX_MODE,
WMI_BEACON_STAGGERED_MODE);
if (ret)
ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Set staggered beacon mode for VDEV: %d\n",
arvif->vdev_id);
}
if (changed & BSS_CHANGED_BEACON_INFO) {
arvif->dtim_period = info->dtim_period;
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d dtim_period %d\n",
arvif->vdev_id, arvif->dtim_period);
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
WMI_VDEV_PARAM_DTIM_PERIOD,
arvif->dtim_period);
if (ret)
ath10k_warn("Failed to set dtim period for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Set dtim period: %d for VDEV: %d\n",
arvif->dtim_period, arvif->vdev_id);
}
if (changed & BSS_CHANGED_SSID &&
@ -2190,16 +2212,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BSSID) {
if (!is_zero_ether_addr(info->bssid)) {
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d create peer %pM\n",
arvif->vdev_id, info->bssid);
ret = ath10k_peer_create(ar, arvif->vdev_id,
info->bssid);
if (ret)
ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
info->bssid, arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Added peer: %pM for VDEV: %d\n",
info->bssid, arvif->vdev_id);
if (vif->type == NL80211_IFTYPE_STATION) {
/*
@ -2209,11 +2230,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
memcpy(arvif->u.sta.bssid, info->bssid,
ETH_ALEN);
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d start %pM\n",
arvif->vdev_id, info->bssid);
/* FIXME: check return value */
ret = ath10k_vdev_start(arvif);
if (!ret)
ath10k_dbg(ATH10K_DBG_MAC,
"VDEV: %d started with BSSID: %pM\n",
arvif->vdev_id, info->bssid);
}
/*
@ -2237,16 +2259,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
else
cts_prot = 0;
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
arvif->vdev_id, cts_prot);
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
WMI_VDEV_PARAM_ENABLE_RTSCTS,
cts_prot);
if (ret)
ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Set CTS prot: %d for VDEV: %d\n",
cts_prot, arvif->vdev_id);
}
if (changed & BSS_CHANGED_ERP_SLOT) {
@ -2257,16 +2278,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
else
slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
arvif->vdev_id, slottime);
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
WMI_VDEV_PARAM_SLOT_TIME,
slottime);
if (ret)
ath10k_warn("Failed to set erp slot for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Set slottime: %d for VDEV: %d\n",
slottime, arvif->vdev_id);
}
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@ -2276,16 +2296,16 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
else
preamble = WMI_VDEV_PREAMBLE_LONG;
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d preamble %dn",
arvif->vdev_id, preamble);
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
WMI_VDEV_PARAM_PREAMBLE,
preamble);
if (ret)
ath10k_warn("Failed to set preamble for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Set preamble: %d for VDEV: %d\n",
preamble, arvif->vdev_id);
}
if (changed & BSS_CHANGED_ASSOC) {
@ -2476,27 +2496,26 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
/*
* New station addition.
*/
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d peer create %pM (new sta)\n",
arvif->vdev_id, sta->addr);
ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Added peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id);
} else if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST)) {
/*
* Existing station deletion.
*/
ath10k_dbg(ATH10K_DBG_MAC,
"mac vdev %d peer delete %pM (sta gone)\n",
arvif->vdev_id, sta->addr);
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Removed peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id);
if (vif->type == NL80211_IFTYPE_STATION)
ath10k_bss_disassoc(hw, vif);
@ -2507,14 +2526,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
/*
* New association.
*/
ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
sta->addr);
ret = ath10k_station_assoc(ar, arvif, sta);
if (ret)
ath10k_warn("Failed to associate station: %pM\n",
sta->addr);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Station %pM moved to assoc state\n",
sta->addr);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH &&
(vif->type == NL80211_IFTYPE_AP ||
@ -2522,14 +2540,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
/*
* Disassociation.
*/
ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
sta->addr);
ret = ath10k_station_disassoc(ar, arvif, sta);
if (ret)
ath10k_warn("Failed to disassociate station: %pM\n",
sta->addr);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Station %pM moved to disassociated state\n",
sta->addr);
}
mutex_unlock(&ar->conf_mutex);
@ -2749,14 +2766,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
return;
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts_threshold %d\n",
arvif->vdev_id, rts);
ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
if (ar_iter->ret)
ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Set RTS threshold: %d for VDEV: %d\n",
rts, arvif->vdev_id);
}
static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
@ -2791,14 +2807,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
return;
ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation_threshold %d\n",
arvif->vdev_id, frag);
ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
if (ar_iter->ret)
ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
arvif->vdev_id);
else
ath10k_dbg(ATH10K_DBG_MAC,
"Set frag threshold: %d for VDEV: %d\n",
frag, arvif->vdev_id);
}
static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
@ -2838,8 +2853,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
bool empty;
spin_lock_bh(&ar->htt.tx_lock);
empty = bitmap_empty(ar->htt.used_msdu_ids,
ar->htt.max_num_pending_tx);
empty = (ar->htt.num_pending_tx == 0);
spin_unlock_bh(&ar->htt.tx_lock);
skip = (ar->state == ATH10K_STATE_WEDGED);
@ -3328,6 +3342,10 @@ int ath10k_mac_register(struct ath10k *ar)
IEEE80211_HW_WANT_MONITOR_VIF |
IEEE80211_HW_AP_LINK_PS;
/* MSDU can have HTT TX fragment pushed in front. The additional 4
* bytes is used for padding/alignment if necessary. */
ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;

View File

@ -612,31 +612,20 @@ exit:
}
/* Called by lower (CE) layer when a send to Target completes. */
static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state,
void *transfer_context,
u32 ce_data,
unsigned int nbytes,
unsigned int transfer_id)
static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
{
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
struct ath10k_pci_compl *compl;
bool process = false;
do {
/*
* For the send completion of an item in sendlist, just
* increment num_sends_allowed. The upper layer callback will
* be triggered when last fragment is done with send.
*/
if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
spin_lock_bh(&pipe_info->pipe_lock);
pipe_info->num_sends_allowed++;
spin_unlock_bh(&pipe_info->pipe_lock);
continue;
}
void *transfer_context;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
&ce_data, &nbytes,
&transfer_id) == 0) {
compl = get_free_compl(pipe_info);
if (!compl)
break;
@ -655,38 +644,28 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state,
spin_lock_bh(&ar_pci->compl_lock);
list_add_tail(&compl->list, &ar_pci->compl_process);
spin_unlock_bh(&ar_pci->compl_lock);
process = true;
} while (ath10k_ce_completed_send_next(ce_state,
&transfer_context,
&ce_data, &nbytes,
&transfer_id) == 0);
/*
* If only some of the items within a sendlist have completed,
* don't invoke completion processing until the entire sendlist
* has been sent.
*/
if (!process)
return;
}
ath10k_pci_process_ce(ar);
}
/* Called by lower (CE) layer when data is received from the Target. */
static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state,
void *transfer_context, u32 ce_data,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags)
static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
{
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
struct ath10k_pci_compl *compl;
struct sk_buff *skb;
void *transfer_context;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
unsigned int flags;
do {
while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
&ce_data, &nbytes, &transfer_id,
&flags) == 0) {
compl = get_free_compl(pipe_info);
if (!compl)
break;
@ -709,12 +688,7 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state,
spin_lock_bh(&ar_pci->compl_lock);
list_add_tail(&compl->list, &ar_pci->compl_process);
spin_unlock_bh(&ar_pci->compl_lock);
} while (ath10k_ce_completed_recv_next(ce_state,
&transfer_context,
&ce_data, &nbytes,
&transfer_id,
&flags) == 0);
}
ath10k_pci_process_ce(ar);
}
@ -728,13 +702,10 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe_info = &(ar_pci->pipe_info[pipe_id]);
struct ath10k_ce_pipe *ce_hdl = pipe_info->ce_hdl;
struct ce_sendlist sendlist;
unsigned int len;
u32 flags = 0;
int ret;
memset(&sendlist, 0, sizeof(struct ce_sendlist));
len = min(bytes, nbuf->len);
bytes -= len;
@ -749,8 +720,6 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
"ath10k tx: data: ",
nbuf->data, nbuf->len);
ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags);
/* Make sure we have resources to handle this request */
spin_lock_bh(&pipe_info->pipe_lock);
if (!pipe_info->num_sends_allowed) {
@ -761,7 +730,8 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
pipe_info->num_sends_allowed--;
spin_unlock_bh(&pipe_info->pipe_lock);
ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, transfer_id,
skb_cb->paddr, len, flags);
if (ret)
ath10k_warn("CE send failed: %p\n", nbuf);
@ -1316,15 +1286,14 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
&ce_data, &nbytes, &id) == 0) {
if (netbuf != CE_SENDLIST_ITEM_CTXT)
/*
* Indicate the completion to higer layer to free
* the buffer
*/
ATH10K_SKB_CB(netbuf)->is_aborted = true;
ar_pci->msg_callbacks_current.tx_completion(ar,
netbuf,
id);
/*
* Indicate the completion to higer layer to free
* the buffer
*/
ATH10K_SKB_CB(netbuf)->is_aborted = true;
ar_pci->msg_callbacks_current.tx_completion(ar,
netbuf,
id);
}
}
@ -1490,13 +1459,16 @@ err_dma:
return ret;
}
static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state,
void *transfer_context,
u32 data,
unsigned int nbytes,
unsigned int transfer_id)
static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
{
struct bmi_xfer *xfer = transfer_context;
struct bmi_xfer *xfer;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data,
&nbytes, &transfer_id))
return;
if (xfer->wait_for_resp)
return;
@ -1504,14 +1476,17 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state,
complete(&xfer->done);
}
static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state,
void *transfer_context,
u32 data,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags)
static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
{
struct bmi_xfer *xfer = transfer_context;
struct bmi_xfer *xfer;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
unsigned int flags;
if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data,
&nbytes, &transfer_id, &flags))
return;
if (!xfer->wait_for_resp) {
ath10k_warn("unexpected: BMI data received; ignoring\n");
@ -2374,10 +2349,10 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
switch (i) {
case ATH10K_PCI_FEATURE_MSI_X:
ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
ath10k_dbg(ATH10K_DBG_BOOT, "device supports MSI-X\n");
break;
case ATH10K_PCI_FEATURE_SOC_POWER_SAVE:
ath10k_dbg(ATH10K_DBG_PCI, "QCA98XX SoC power save enabled\n");
ath10k_dbg(ATH10K_DBG_BOOT, "QCA98XX SoC power save enabled\n");
break;
}
}
@ -2503,6 +2478,8 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
ath10k_do_pci_sleep(ar);
ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
ret = ath10k_core_register(ar, chip_id);
if (ret) {
ath10k_err("could not register driver core (%d)\n", ret);

View File

@ -422,10 +422,30 @@ struct rx_mpdu_end {
#define RX_MSDU_START_INFO1_IP_FRAG (1 << 14)
#define RX_MSDU_START_INFO1_TCP_ONLY_ACK (1 << 15)
/* The decapped header (rx_hdr_status) contains the following:
* a) 802.11 header
* [padding to 4 bytes]
* b) HW crypto parameter
* - 0 bytes for no security
* - 4 bytes for WEP
* - 8 bytes for TKIP, AES
* [padding to 4 bytes]
* c) A-MSDU subframe header (14 bytes) if appliable
* d) LLC/SNAP (RFC1042, 8 bytes)
*
* In case of A-MSDU only first frame in sequence contains (a) and (b). */
enum rx_msdu_decap_format {
RX_MSDU_DECAP_RAW = 0,
RX_MSDU_DECAP_NATIVE_WIFI = 1,
RX_MSDU_DECAP_RAW = 0,
/* Note: QoS frames are reported as non-QoS. The rx_hdr_status in
* htt_rx_desc contains the original decapped 802.11 header. */
RX_MSDU_DECAP_NATIVE_WIFI = 1,
/* Payload contains an ethernet header (struct ethhdr). */
RX_MSDU_DECAP_ETHERNET2_DIX = 2,
/* Payload contains two 48-bit addresses and 2-byte length (14 bytes
* total), followed by an RFC1042 header (8 bytes). */
RX_MSDU_DECAP_8023_SNAP_LLC = 3
};

View File

@ -111,26 +111,29 @@ TRACE_EVENT(ath10k_log_dbg_dump,
);
TRACE_EVENT(ath10k_wmi_cmd,
TP_PROTO(int id, void *buf, size_t buf_len),
TP_PROTO(int id, void *buf, size_t buf_len, int ret),
TP_ARGS(id, buf, buf_len),
TP_ARGS(id, buf, buf_len, ret),
TP_STRUCT__entry(
__field(unsigned int, id)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
__field(int, ret)
),
TP_fast_assign(
__entry->id = id;
__entry->buf_len = buf_len;
__entry->ret = ret;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"id %d len %zu",
"id %d len %zu ret %d",
__entry->id,
__entry->buf_len
__entry->buf_len,
__entry->ret
)
);
@ -158,6 +161,27 @@ TRACE_EVENT(ath10k_wmi_event,
)
);
TRACE_EVENT(ath10k_htt_stats,
TP_PROTO(void *buf, size_t buf_len),
TP_ARGS(buf, buf_len),
TP_STRUCT__entry(
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"len %zu",
__entry->buf_len
)
);
#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */

View File

@ -44,70 +44,15 @@ out:
spin_unlock_bh(&ar->data_lock);
}
void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done)
{
struct device *dev = htt->ar->dev;
struct ieee80211_tx_info *info;
struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag;
struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu;
struct ath10k_skb_cb *skb_cb;
struct sk_buff *msdu;
int ret;
if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0)
return;
ATH10K_SKB_CB(txdesc)->htt.refcount--;
if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
return;
if (txfrag) {
ret = ath10k_skb_unmap(dev, txfrag);
if (ret)
ath10k_warn("txfrag unmap failed (%d)\n", ret);
dev_kfree_skb_any(txfrag);
}
ret = ath10k_skb_unmap(dev, msdu);
if (ret)
ath10k_warn("data skb unmap failed (%d)\n", ret);
ath10k_report_offchan_tx(htt->ar, msdu);
info = IEEE80211_SKB_CB(msdu);
memset(&info->status, 0, sizeof(info->status));
if (ATH10K_SKB_CB(txdesc)->htt.discard) {
ieee80211_free_txskb(htt->ar->hw, msdu);
goto exit;
}
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
if (ATH10K_SKB_CB(txdesc)->htt.no_ack)
info->flags &= ~IEEE80211_TX_STAT_ACK;
ieee80211_tx_status(htt->ar->hw, msdu);
/* we do not own the msdu anymore */
exit:
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id);
__ath10k_htt_tx_dec_pending(htt);
if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx))
wake_up(&htt->empty_tx_wq);
spin_unlock_bh(&htt->tx_lock);
dev_kfree_skb_any(txdesc);
}
void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done)
{
struct sk_buff *txdesc;
ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
@ -117,12 +62,42 @@ void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
return;
}
txdesc = htt->pending_tx[tx_done->msdu_id];
msdu = htt->pending_tx[tx_done->msdu_id];
skb_cb = ATH10K_SKB_CB(msdu);
ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard;
ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack;
ret = ath10k_skb_unmap(dev, msdu);
if (ret)
ath10k_warn("data skb unmap failed (%d)\n", ret);
ath10k_txrx_tx_unref(htt, txdesc);
if (skb_cb->htt.frag_len)
skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
ath10k_report_offchan_tx(htt->ar, msdu);
info = IEEE80211_SKB_CB(msdu);
if (tx_done->discard) {
ieee80211_free_txskb(htt->ar->hw, msdu);
goto exit;
}
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
if (tx_done->no_ack)
info->flags &= ~IEEE80211_TX_STAT_ACK;
ieee80211_tx_status(htt->ar->hw, msdu);
/* we do not own the msdu anymore */
exit:
spin_lock_bh(&htt->tx_lock);
htt->pending_tx[tx_done->msdu_id] = NULL;
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
__ath10k_htt_tx_dec_pending(htt);
if (htt->num_pending_tx == 0)
wake_up(&htt->empty_tx_wq);
spin_unlock_bh(&htt->tx_lock);
}
static const u8 rx_legacy_rate_idx[] = {
@ -293,6 +268,8 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
status->vht_nss,
status->freq,
status->band);
ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
info->skb->data, info->skb->len);
ieee80211_rx(ar->hw, info->skb);
}

View File

@ -19,9 +19,8 @@
#include "htt.h"
void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc);
void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done);
void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done);
void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info);
struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,

View File

@ -23,30 +23,6 @@
#include "wmi.h"
#include "mac.h"
void ath10k_wmi_flush_tx(struct ath10k *ar)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
if (ar->state == ATH10K_STATE_WEDGED) {
ath10k_warn("wmi flush skipped - device is wedged anyway\n");
return;
}
ret = wait_event_timeout(ar->wmi.wq,
atomic_read(&ar->wmi.pending_tx_count) == 0,
5*HZ);
if (atomic_read(&ar->wmi.pending_tx_count) == 0)
return;
if (ret == 0)
ret = -ETIMEDOUT;
if (ret < 0)
ath10k_warn("wmi flush failed (%d)\n", ret);
}
int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
{
int ret;
@ -85,18 +61,14 @@ static struct sk_buff *ath10k_wmi_alloc_skb(u32 len)
static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
{
dev_kfree_skb(skb);
if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0)
wake_up(&ar->wmi.wq);
}
/* WMI command API */
static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
enum wmi_cmd_id cmd_id)
static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
enum wmi_cmd_id cmd_id)
{
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct wmi_cmd_hdr *cmd_hdr;
int status;
int ret;
u32 cmd = 0;
if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
@ -107,26 +79,87 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
cmd_hdr->cmd_id = __cpu_to_le32(cmd);
if (atomic_add_return(1, &ar->wmi.pending_tx_count) >
WMI_MAX_PENDING_TX_COUNT) {
/* avoid using up memory when FW hangs */
dev_kfree_skb(skb);
atomic_dec(&ar->wmi.pending_tx_count);
return -EBUSY;
}
memset(skb_cb, 0, sizeof(*skb_cb));
ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len, ret);
trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
if (status) {
dev_kfree_skb_any(skb);
atomic_dec(&ar->wmi.pending_tx_count);
return status;
}
if (ret)
goto err_pull;
return 0;
err_pull:
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
return ret;
}
static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
{
struct wmi_bcn_tx_arg arg = {0};
int ret;
lockdep_assert_held(&arvif->ar->data_lock);
if (arvif->beacon == NULL)
return;
arg.vdev_id = arvif->vdev_id;
arg.tx_rate = 0;
arg.tx_power = 0;
arg.bcn = arvif->beacon->data;
arg.bcn_len = arvif->beacon->len;
ret = ath10k_wmi_beacon_send_nowait(arvif->ar, &arg);
if (ret)
return;
dev_kfree_skb_any(arvif->beacon);
arvif->beacon = NULL;
}
static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
ath10k_wmi_tx_beacon_nowait(arvif);
}
static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar)
{
spin_lock_bh(&ar->data_lock);
ieee80211_iterate_active_interfaces_atomic(ar->hw,
IEEE80211_IFACE_ITER_NORMAL,
ath10k_wmi_tx_beacons_iter,
NULL);
spin_unlock_bh(&ar->data_lock);
}
static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar)
{
/* try to send pending beacons first. they take priority */
ath10k_wmi_tx_beacons_nowait(ar);
wake_up(&ar->wmi.tx_credits_wq);
}
static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
enum wmi_cmd_id cmd_id)
{
int ret = -EINVAL;
wait_event_timeout(ar->wmi.tx_credits_wq, ({
/* try to send pending beacons first. they take priority */
ath10k_wmi_tx_beacons_nowait(ar);
ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
(ret != -EAGAIN);
}), 3*HZ);
if (ret)
dev_kfree_skb_any(skb);
return ret;
}
static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
@ -748,10 +781,8 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
int i = -1;
struct wmi_bcn_info *bcn_info;
struct ath10k_vif *arvif;
struct wmi_bcn_tx_arg arg;
struct sk_buff *bcn;
int vdev_id = 0;
int ret;
ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
@ -808,17 +839,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
arg.vdev_id = arvif->vdev_id;
arg.tx_rate = 0;
arg.tx_power = 0;
arg.bcn = bcn->data;
arg.bcn_len = bcn->len;
spin_lock_bh(&ar->data_lock);
if (arvif->beacon) {
ath10k_warn("SWBA overrun on vdev %d\n",
arvif->vdev_id);
dev_kfree_skb_any(arvif->beacon);
}
ret = ath10k_wmi_beacon_send(ar, &arg);
if (ret)
ath10k_warn("could not send beacon (%d)\n", ret);
arvif->beacon = bcn;
dev_kfree_skb_any(bcn);
ath10k_wmi_tx_beacon_nowait(arvif);
spin_unlock_bh(&ar->data_lock);
}
}
@ -1024,7 +1055,7 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
return 0;
}
static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr;
enum wmi_event_id id;
@ -1143,64 +1174,18 @@ static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
dev_kfree_skb(skb);
}
static void ath10k_wmi_event_work(struct work_struct *work)
{
struct ath10k *ar = container_of(work, struct ath10k,
wmi.wmi_event_work);
struct sk_buff *skb;
for (;;) {
skb = skb_dequeue(&ar->wmi.wmi_event_list);
if (!skb)
break;
ath10k_wmi_event_process(ar, skb);
}
}
static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
enum wmi_event_id event_id;
event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
/* some events require to be handled ASAP
* thus can't be defered to a worker thread */
switch (event_id) {
case WMI_HOST_SWBA_EVENTID:
case WMI_MGMT_RX_EVENTID:
ath10k_wmi_event_process(ar, skb);
return;
default:
break;
}
skb_queue_tail(&ar->wmi.wmi_event_list, skb);
queue_work(ar->workqueue, &ar->wmi.wmi_event_work);
}
/* WMI Initialization functions */
int ath10k_wmi_attach(struct ath10k *ar)
{
init_completion(&ar->wmi.service_ready);
init_completion(&ar->wmi.unified_ready);
init_waitqueue_head(&ar->wmi.wq);
skb_queue_head_init(&ar->wmi.wmi_event_list);
INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work);
init_waitqueue_head(&ar->wmi.tx_credits_wq);
return 0;
}
void ath10k_wmi_detach(struct ath10k *ar)
{
/* HTC should've drained the packets already */
if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0))
ath10k_warn("there are still pending packets\n");
cancel_work_sync(&ar->wmi.wmi_event_work);
skb_queue_purge(&ar->wmi.wmi_event_list);
}
int ath10k_wmi_connect_htc_service(struct ath10k *ar)
@ -1215,6 +1200,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
/* these fields are the same for all service endpoints */
conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete;
conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx;
conn_req.ep_ops.ep_tx_credits = ath10k_wmi_op_ep_tx_credits;
/* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
@ -2125,7 +2111,8 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
}
int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
const struct wmi_bcn_tx_arg *arg)
{
struct wmi_bcn_tx_cmd *cmd;
struct sk_buff *skb;
@ -2141,7 +2128,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len);
memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID);
return ath10k_wmi_cmd_send_nowait(ar, skb, WMI_BCN_TX_CMDID);
}
static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,

View File

@ -508,6 +508,48 @@ enum wmi_phy_mode {
MODE_MAX = 14
};
static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode)
{
switch (mode) {
case MODE_11A:
return "11a";
case MODE_11G:
return "11g";
case MODE_11B:
return "11b";
case MODE_11GONLY:
return "11gonly";
case MODE_11NA_HT20:
return "11na-ht20";
case MODE_11NG_HT20:
return "11ng-ht20";
case MODE_11NA_HT40:
return "11na-ht40";
case MODE_11NG_HT40:
return "11ng-ht40";
case MODE_11AC_VHT20:
return "11ac-vht20";
case MODE_11AC_VHT40:
return "11ac-vht40";
case MODE_11AC_VHT80:
return "11ac-vht80";
case MODE_11AC_VHT20_2G:
return "11ac-vht20-2g";
case MODE_11AC_VHT40_2G:
return "11ac-vht40-2g";
case MODE_11AC_VHT80_2G:
return "11ac-vht80-2g";
case MODE_UNKNOWN:
/* skip */
break;
/* no default handler to allow compiler to check that the
* enum is fully handled */
};
return "<unknown>";
}
#define WMI_CHAN_LIST_TAG 0x1
#define WMI_SSID_LIST_TAG 0x2
#define WMI_BSSID_LIST_TAG 0x3
@ -763,14 +805,6 @@ struct wmi_service_ready_event {
struct wlan_host_mem_req mem_reqs[1];
} __packed;
/*
* status consists of upper 16 bits fo int status and lower 16 bits of
* module ID that retuned status
*/
#define WLAN_INIT_STATUS_SUCCESS 0x0
#define WLAN_GET_INIT_STATUS_REASON(status) ((status) & 0xffff)
#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff)
#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
#define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
@ -3010,7 +3044,6 @@ struct wmi_force_fw_hang_cmd {
#define WMI_MAX_EVENT 0x1000
/* Maximum number of pending TXed WMI packets */
#define WMI_MAX_PENDING_TX_COUNT 128
#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr)
/* By default disable power save for IBSS */
@ -3023,7 +3056,6 @@ int ath10k_wmi_attach(struct ath10k *ar);
void ath10k_wmi_detach(struct ath10k *ar);
int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
void ath10k_wmi_flush_tx(struct ath10k *ar);
int ath10k_wmi_connect_htc_service(struct ath10k *ar);
int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
@ -3076,7 +3108,8 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
enum wmi_ap_ps_peer_param param_id, u32 value);
int ath10k_wmi_scan_chan_list(struct ath10k *ar,
const struct wmi_scan_chan_list_arg *arg);
int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
const struct wmi_bcn_tx_arg *arg);
int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
const struct wmi_pdev_set_wmm_params_arg *arg);
int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);