From 7a6aff718d2de7dee6f9d82eae8af91667e8ee86 Mon Sep 17 00:00:00 2001 From: David Lin Date: Fri, 16 Oct 2015 12:52:21 +0800 Subject: Commit mwlwifi driver 10.3.0.10. 1. Enhance Tx throughput. 2. Add the mechanism to stop and wake up sub Tx queue via ieee80211_stop_queue() and ieee80211_wake_queue(). 3. Refine code related to BA. 4. Replace BUG_ON() with WARN_ON() as suggested by LWN. 5. Acknowledge all packets in AMSDU to mac80211 after transmission is done. 6. Merge patch from community: fix a problem where the resources are left claimed if the firmware fails to load. 7. Replace dma_alloc_coherent() with dmam_alloc_coherent(). 8. Add debugfs. 9. Add linux version check to apply new API ieee80211_hw_set() existed after kernel 4.2. 10. Rearrange spin lock sequences in flushing AMSDU function to avoid dead lock on OpenWrt platform. Signed-off-by: David Lin --- Makefile | 1 + Makefile.external | 1 + Makefile.kernel | 1 + debugfs.c | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ debugfs.h | 24 +++ dev.h | 32 +++- fwcmd.c | 264 +++++++++++++++++++++++++++++--- fwcmd.h | 31 +++- hostcmd.h | 60 +++++++- isr.c | 16 +- mac80211.c | 102 +++++-------- main.c | 138 ++++++----------- rx.c | 8 +- sysadpt.h | 4 +- tx.c | 398 ++++++++++++++++++++++++++++++++++++------------ tx.h | 7 + 16 files changed, 1231 insertions(+), 297 deletions(-) create mode 100644 debugfs.c create mode 100644 debugfs.h diff --git a/Makefile b/Makefile index 43c8632..ea3f265 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ mwlwifi-objs += fwcmd.o mwlwifi-objs += tx.o mwlwifi-objs += rx.o mwlwifi-objs += isr.o +mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld diff --git a/Makefile.external b/Makefile.external index 43c8632..ea3f265 100644 --- a/Makefile.external +++ b/Makefile.external @@ -7,6 +7,7 @@ mwlwifi-objs += fwcmd.o mwlwifi-objs += tx.o mwlwifi-objs += rx.o mwlwifi-objs += isr.o +mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld diff --git a/Makefile.kernel b/Makefile.kernel index 49bd311..88f7efd 100644 --- a/Makefile.kernel +++ b/Makefile.kernel @@ -7,5 +7,6 @@ mwlwifi-objs += fwcmd.o mwlwifi-objs += tx.o mwlwifi-objs += rx.o mwlwifi-objs += isr.o +mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/debugfs.c b/debugfs.c new file mode 100644 index 0000000..00aa12d --- /dev/null +++ b/debugfs.c @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2006-2015, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/* Description: This file implements debug fs related functions. */ + +#include + +#include "sysadpt.h" +#include "dev.h" +#include "hostcmd.h" +#include "fwcmd.h" +#include "debugfs.h" + +#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \ + if (!debugfs_create_file(#name, 0644, priv->debugfs_phy, \ + priv, &mwl_debugfs_##name##_fops)) \ + return; \ +} while (0) + +#define MWLWIFI_DEBUGFS_FILE_OPS(name) \ +static const struct file_operations mwl_debugfs_##name##_fops = { \ + .read = mwl_debugfs_##name##_read, \ + .write = mwl_debugfs_##name##_write, \ + .open = simple_open, \ +} + +#define MWLWIFI_DEBUGFS_FILE_READ_OPS(name) \ +static const struct file_operations mwl_debugfs_##name##_fops = { \ + .read = mwl_debugfs_##name##_read, \ + .open = simple_open, \ +} + +#define MWLWIFI_DEBUGFS_FILE_WRITE_OPS(name) \ +static const struct file_operations mwl_debugfs_##name##_fops = { \ + .write = mwl_debugfs_##name##_write, \ + .open = simple_open, \ +} + +static int print_mac_addr(char *p, u8 *mac_addr) +{ + int i; + char *str = p; + + str += sprintf(str, "mac address: %02x", mac_addr[0]); + for (i = 1; i < ETH_ALEN; i++) + str += sprintf(str, ":%02x", mac_addr[i]); + str += sprintf(str, "\n"); + + return str-p; +} + +static int dump_data(char *p, u8 *data, int len, char *title) +{ + char *str = p; + int cur_byte = 0; + int i; + + str += sprintf(str, "%s\n", title); + for (cur_byte = 0; cur_byte < len; cur_byte += 8) { + if ((cur_byte + 8) < len) { + for (i = 0; i < 8; i++) + str += sprintf(str, "0x%02x ", + *(data+cur_byte+i)); + str += sprintf(str, "\n"); + } else { + for (i = 0; i < (len - cur_byte); i++) + str += sprintf(str, "0x%02x ", + *(data+cur_byte+i)); + str += sprintf(str, "\n"); + break; + } + } + + return str-p; +} + +static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwl_priv *priv = (struct mwl_priv *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + ssize_t ret; + + if (!p) + return -ENOMEM; + + p += sprintf(p, "\n"); + p += sprintf(p, "driver name: %s\n", MWL_DRV_NAME); + p += sprintf(p, "chip type: %s\n", + (priv->chip_type == MWL8864) ? "88W8864" : "88W8897"); + p += sprintf(p, "hw version: %X\n", priv->hw_data.hw_version); + p += sprintf(p, "driver version: %s\n", MWL_DRV_VERSION); + p += sprintf(p, "firmware version: 0x%08x\n", + priv->hw_data.fw_release_num); + p += print_mac_addr(p, priv->hw_data.mac_addr); + p += sprintf(p, "2g: %s\n", priv->disable_2g ? "disable" : "enable"); + p += sprintf(p, "5g: %s\n", priv->disable_5g ? "disable" : "enable"); + p += sprintf(p, "antenna: %d %d\n", + (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? 4 : 2, + (priv->antenna_rx == ANTENNA_TX_4_AUTO) ? 4 : 2); + p += sprintf(p, "irq number: %d\n", priv->irq); + p += sprintf(p, "iobase0: %p\n", priv->iobase0); + p += sprintf(p, "iobase1: %p\n", priv->iobase1); + p += sprintf(p, "tx limit: %d\n", priv->txq_limit); + p += sprintf(p, "rx limit: %d\n", priv->recv_limit); + p += sprintf(p, "ap macid support: %08x\n", + priv->ap_macids_supported); + p += sprintf(p, "sta macid support: %08x\n", + priv->sta_macids_supported); + p += sprintf(p, "macid used: %08x\n", priv->macids_used); + p += sprintf(p, "\n"); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + free_page(page); + + return ret; +} + +static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwl_priv *priv = (struct mwl_priv *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + struct mwl_vif *mwl_vif; + struct ieee80211_vif *vif; + char ssid[IEEE80211_MAX_SSID_LEN+1]; + ssize_t ret; + + if (!p) + return -ENOMEM; + + p += sprintf(p, "\n"); + spin_lock_bh(&priv->vif_lock); + list_for_each_entry(mwl_vif, &priv->vif_list, list) { + vif = container_of((char *)mwl_vif, struct ieee80211_vif, + drv_priv[0]); + p += sprintf(p, "macid: %d\n", mwl_vif->macid); + switch (vif->type) { + case NL80211_IFTYPE_AP: + p += sprintf(p, "type: ap\n"); + memcpy(ssid, vif->bss_conf.ssid, + vif->bss_conf.ssid_len); + ssid[vif->bss_conf.ssid_len] = 0; + p += sprintf(p, "ssid: %s\n", ssid); + p += print_mac_addr(p, mwl_vif->bssid); + break; + case NL80211_IFTYPE_STATION: + p += sprintf(p, "type: sta\n"); + p += print_mac_addr(p, mwl_vif->sta_mac); + break; + default: + p += sprintf(p, "type: unknown\n"); + break; + } + p += sprintf(p, "hw_crypto_enabled: %s\n", + mwl_vif->is_hw_crypto_enabled ? "true" : "false"); + p += sprintf(p, "key idx: %d\n", mwl_vif->keyidx); + p += sprintf(p, "IV: %08x%04x\n", mwl_vif->iv32, mwl_vif->iv16); + p += dump_data(p, mwl_vif->beacon_info.ie_wmm_ptr, + mwl_vif->beacon_info.ie_wmm_len, "WMM:"); + p += dump_data(p, mwl_vif->beacon_info.ie_rsn_ptr, + mwl_vif->beacon_info.ie_rsn_len, "RSN:"); + p += dump_data(p, mwl_vif->beacon_info.ie_rsn48_ptr, + mwl_vif->beacon_info.ie_rsn48_len, "RSN48:"); + p += dump_data(p, mwl_vif->beacon_info.ie_ht_ptr, + mwl_vif->beacon_info.ie_ht_len, "HT:"); + p += dump_data(p, mwl_vif->beacon_info.ie_vht_ptr, + mwl_vif->beacon_info.ie_vht_len, "VHT:"); + p += sprintf(p, "\n"); + } + spin_unlock_bh(&priv->vif_lock); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + free_page(page); + + return ret; +} + +static ssize_t mwl_debugfs_sta_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwl_priv *priv = (struct mwl_priv *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + struct mwl_sta *sta_info; + struct ieee80211_sta *sta; + ssize_t ret; + + if (!p) + return -ENOMEM; + + p += sprintf(p, "\n"); + spin_lock_bh(&priv->sta_lock); + list_for_each_entry(sta_info, &priv->sta_list, list) { + sta = container_of((char *)sta_info, struct ieee80211_sta, + drv_priv[0]); + p += print_mac_addr(p, sta->addr); + p += sprintf(p, "aid: %u\n", sta->aid); + p += sprintf(p, "ampdu: %s\n", + sta_info->is_ampdu_allowed ? "true" : "false"); + p += sprintf(p, "amsdu: %s\n", + sta_info->is_amsdu_allowed ? "true" : "false"); + if (sta_info->is_amsdu_allowed) { + p += sprintf(p, "amsdu cap: 0x%02x\n", + sta_info->amsdu_ctrl.cap); + } + p += sprintf(p, "IV: %08x%04x\n", + sta_info->iv32, sta_info->iv16); + p += sprintf(p, "\n"); + } + spin_unlock_bh(&priv->sta_lock); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + free_page(page); + + return ret; +} + +static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwl_priv *priv = (struct mwl_priv *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + struct mwl_ampdu_stream *stream; + int i; + ssize_t ret; + + if (!p) + return -ENOMEM; + + p += sprintf(p, "\n"); + spin_lock_bh(&priv->stream_lock); + for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) { + stream = &priv->ampdu[i]; + p += sprintf(p, "stream: %d\n", i); + p += sprintf(p, "idx: %u\n", stream->idx); + p += sprintf(p, "state: %u\n", stream->state); + if (stream->sta) { + p += print_mac_addr(p, stream->sta->addr); + p += sprintf(p, "tid: %u\n", stream->tid); + } + } + spin_unlock_bh(&priv->stream_lock); + p += sprintf(p, "\n"); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + free_page(page); + + return ret; +} + +static int mwl_debugfs_reg_access(struct mwl_priv *priv, bool write) +{ + struct ieee80211_hw *hw = priv->hw; + u8 set; + u32 *addr_val; + int ret = 0; + + set = write ? WL_SET : WL_GET; + + switch (priv->reg_type) { + case MWL_ACCESS_MAC: + if (set == WL_GET) + priv->reg_value = + le32_to_cpu(MAC_REG_ADDR_PCI(priv->reg_offset)); + else + writel(cpu_to_le32(priv->reg_value), + MAC_REG_ADDR_PCI(priv->reg_offset)); + break; + case MWL_ACCESS_RF: + ret = mwl_fwcmd_reg_rf(hw, set, priv->reg_offset, + &priv->reg_value); + break; + case MWL_ACCESS_BBP: + ret = mwl_fwcmd_reg_bb(hw, set, priv->reg_offset, + &priv->reg_value); + break; + case MWL_ACCESS_CAU: + ret = mwl_fwcmd_reg_cau(hw, set, priv->reg_offset, + &priv->reg_value); + break; + case MWL_ACCESS_ADDR0: + if (set == WL_GET) + priv->reg_value = + readl(priv->iobase0 + priv->reg_offset); + else + writel(priv->reg_value, + priv->iobase0 + priv->reg_offset); + break; + case MWL_ACCESS_ADDR1: + if (set == WL_GET) + priv->reg_value = + readl(priv->iobase1 + priv->reg_offset); + else + writel(priv->reg_value, + priv->iobase1 + priv->reg_offset); + break; + case MWL_ACCESS_ADDR: + addr_val = kmalloc(64 * sizeof(u32), GFP_KERNEL); + if (addr_val) { + memset(addr_val, 0, 64 * sizeof(u32)); + addr_val[0] = priv->reg_value; + ret = mwl_fwcmd_get_addr_value(hw, priv->reg_offset, + 4, addr_val, set); + if ((!ret) && (set == WL_GET)) + priv->reg_value = addr_val[0]; + kfree(addr_val); + } else + ret = -ENOMEM; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static ssize_t mwl_debugfs_regrdwr_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwl_priv *priv = (struct mwl_priv *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + int pos = 0, ret = 0; + + if (!buf) + return -ENOMEM; + + if (!priv->reg_type) { + /* No command has been given */ + pos += snprintf(buf, PAGE_SIZE, "0"); + goto none; + } + + /* Set command has been given */ + if (priv->reg_value != UINT_MAX) { + ret = mwl_debugfs_reg_access(priv, true); + goto done; + } + /* Get command has been given */ + ret = mwl_debugfs_reg_access(priv, false); + +done: + pos += snprintf(buf, PAGE_SIZE, "%u 0x%08x 0x%08x\n", + priv->reg_type, priv->reg_offset, + priv->reg_value); + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + +none: + + free_page(addr); + return ret; +} + +static ssize_t mwl_debugfs_regrdwr_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwl_priv *priv = (struct mwl_priv *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret; + u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + ret = sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); + + if (!reg_type) { + ret = -EINVAL; + goto done; + } else { + priv->reg_type = reg_type; + priv->reg_offset = reg_offset; + priv->reg_value = reg_value; + ret = count; + } +done: + + free_page(addr); + return ret; +} + +MWLWIFI_DEBUGFS_FILE_READ_OPS(info); +MWLWIFI_DEBUGFS_FILE_READ_OPS(vif); +MWLWIFI_DEBUGFS_FILE_READ_OPS(sta); +MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu); +MWLWIFI_DEBUGFS_FILE_OPS(regrdwr); + +void mwl_debugfs_init(struct ieee80211_hw *hw) +{ + struct mwl_priv *priv = hw->priv; + + if (!priv->debugfs_phy) + priv->debugfs_phy = debugfs_create_dir("mwlwifi", + hw->wiphy->debugfsdir); + + if (!priv->debugfs_phy) + return; + + MWLWIFI_DEBUGFS_ADD_FILE(info); + MWLWIFI_DEBUGFS_ADD_FILE(vif); + MWLWIFI_DEBUGFS_ADD_FILE(sta); + MWLWIFI_DEBUGFS_ADD_FILE(ampdu); + MWLWIFI_DEBUGFS_ADD_FILE(regrdwr); +} + +void mwl_debugfs_remove(struct ieee80211_hw *hw) +{ + struct mwl_priv *priv = hw->priv; + + debugfs_remove(priv->debugfs_phy); + priv->debugfs_phy = NULL; +} diff --git a/debugfs.h b/debugfs.h new file mode 100644 index 0000000..dfc2a3c --- /dev/null +++ b/debugfs.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2006-2015, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/* Description: This file defines debug fs related functions. */ + +#ifndef _debugfs_h_ +#define _debugfs_h_ + +void mwl_debugfs_init(struct ieee80211_hw *hw); +void mwl_debugfs_remove(struct ieee80211_hw *hw); + +#endif /* _debugfs_h_ */ diff --git a/dev.h b/dev.h index 243af72..b488977 100644 --- a/dev.h +++ b/dev.h @@ -26,6 +26,9 @@ #include #include +#define MWL_DRV_NAME KBUILD_MODNAME +#define MWL_DRV_VERSION "10.3.0.10" + /* Map to 0x80000000 (Bus control) on BAR0 */ #define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 /* (From host to ARM) */ #define MACREG_REG_H2A_INTERRUPT_CAUSE 0x00000C1C /* (From host to ARM) */ @@ -54,8 +57,8 @@ #define MACREG_A2HRIC_BIT_RADAR_DETECT BIT(7) #define MACREG_A2HRIC_BIT_ICV_ERROR BIT(8) #define MACREG_A2HRIC_BIT_WEAKIV_ERROR BIT(9) -#define MACREG_A2HRIC_BIT_QUEUE_EMPTY BIT(10) -#define MACREG_A2HRIC_BIT_QUEUE_FULL BIT(11) +#define MACREG_A2HRIC_BIT_QUE_EMPTY BIT(10) +#define MACREG_A2HRIC_BIT_QUE_FULL BIT(11) #define MACREG_A2HRIC_BIT_CHAN_SWITCH BIT(12) #define MACREG_A2HRIC_BIT_TX_WATCHDOG BIT(13) #define MACREG_A2HRIC_BA_WATCHDOG BIT(14) @@ -73,7 +76,7 @@ MACREG_A2HRIC_BIT_RADAR_DETECT | \ MACREG_A2HRIC_BIT_CHAN_SWITCH | \ MACREG_A2HRIC_BIT_TX_WATCHDOG | \ - MACREG_A2HRIC_BIT_QUEUE_EMPTY | \ + MACREG_A2HRIC_BIT_QUE_EMPTY | \ MACREG_A2HRIC_BA_WATCHDOG | \ MACREG_A2HRIC_CONSEC_TXFAIL) @@ -260,6 +263,18 @@ struct mwl_ampdu_stream { u8 idx; }; +#ifdef CONFIG_DEBUG_FS +#define MAC_REG_ADDR_PCI(offset) ((priv->iobase1+0xA000) + offset) + +#define MWL_ACCESS_MAC 1 +#define MWL_ACCESS_RF 2 +#define MWL_ACCESS_BBP 3 +#define MWL_ACCESS_CAU 4 +#define MWL_ACCESS_ADDR0 5 +#define MWL_ACCESS_ADDR1 6 +#define MWL_ACCESS_ADDR 7 +#endif + struct mwl_priv { struct ieee80211_hw *hw; const struct firmware *fw_ucode; @@ -295,6 +310,7 @@ struct mwl_priv { /* various descriptor data */ spinlock_t tx_desc_lock; /* for tx descriptor data */ + spinlock_t rx_desc_lock; /* for rx descriptor data */ struct mwl_desc_data desc_data[SYSADPT_NUM_OF_DESC_DATA]; struct sk_buff_head txq[SYSADPT_NUM_OF_DESC_DATA]; struct sk_buff_head delay_q; @@ -337,6 +353,13 @@ struct mwl_priv { spinlock_t stream_lock; /* for ampdu stream */ struct mwl_ampdu_stream ampdu[SYSADPT_TX_AMPDU_QUEUES]; struct work_struct watchdog_ba_handle; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_phy; + u32 reg_type; + u32 reg_offset; + u32 reg_value; +#endif }; struct beacon_info { @@ -415,9 +438,10 @@ struct mwl_dma_data { /* Transmission information to transmit a socket buffer. */ struct mwl_tx_ctrl { - void *sta; void *vif; + void *sta; void *k_conf; + void *amsdu_pkts; u8 tx_priority; u8 type; u16 qos_ctrl; diff --git a/fwcmd.c b/fwcmd.c index 35da765..62ed88a 100644 --- a/fwcmd.c +++ b/fwcmd.c @@ -24,7 +24,7 @@ #include "fwcmd.h" #include "hostcmd.h" -#define MAX_WAIT_FW_COMPLETE_ITERATIONS 500 +#define MAX_WAIT_FW_COMPLETE_ITERATIONS 2000 #define MAX_WAIT_GET_HW_SPECS_ITERATONS 3 static bool mwl_fwcmd_chk_adapter(struct mwl_priv *priv) @@ -60,7 +60,10 @@ static char *mwl_fwcmd_get_cmd_string(unsigned short cmd) { HOSTCMD_CMD_GET_HW_SPEC, "GetHwSpecifications" }, { HOSTCMD_CMD_SET_HW_SPEC, "SetHwSepcifications" }, { HOSTCMD_CMD_802_11_GET_STAT, "80211GetStat" }, + { HOSTCMD_CMD_BBP_REG_ACCESS, "BBPRegAccess" }, + { HOSTCMD_CMD_RF_REG_ACCESS, "RFRegAccess" }, { HOSTCMD_CMD_802_11_RADIO_CONTROL, "80211RadioControl" }, + { HOSTCMD_CMD_MEM_ADDR_ACCESS, "MEMAddrAccess" }, { HOSTCMD_CMD_802_11_TX_POWER, "80211TxPower" }, { HOSTCMD_CMD_802_11_RF_ANTENNA, "80211RfAntenna" }, { HOSTCMD_CMD_BROADCAST_SSID_ENABLE, "broadcast_ssid_enable" }, @@ -70,10 +73,12 @@ static char *mwl_fwcmd_get_cmd_string(unsigned short cmd) { HOSTCMD_CMD_802_11_RTS_THSD, "80211RtsThreshold" }, { HOSTCMD_CMD_SET_EDCA_PARAMS, "SetEDCAParams" }, { HOSTCMD_CMD_SET_WMM_MODE, "SetWMMMode" }, + { HOSTCMD_CMD_HT_GUARD_INTERVAL, "HtGuardInterval" }, { HOSTCMD_CMD_SET_FIXED_RATE, "SetFixedRate" }, { HOSTCMD_CMD_SET_IES, "SetInformationElements" }, - { HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" }, + { HOSTCMD_CMD_SET_LINKADAPT_CS_MODE, "LinkAdaptCsMode" }, { HOSTCMD_CMD_SET_MAC_ADDR, "SetMacAddr" }, + { HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" }, { HOSTCMD_CMD_GET_WATCHDOG_BITMAP, "GetWatchdogBitMap" }, { HOSTCMD_CMD_DEL_MAC_ADDR, "DelMacAddr" }, { HOSTCMD_CMD_BSS_START, "BssStart" }, @@ -82,12 +87,14 @@ static char *mwl_fwcmd_get_cmd_string(unsigned short cmd) { HOSTCMD_CMD_SET_APMODE, "SetApMode" }, { HOSTCMD_CMD_UPDATE_ENCRYPTION, "UpdateEncryption" }, { HOSTCMD_CMD_BASTREAM, "BAStream" }, + { HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL, "SetOptimizationLevel"}, { HOSTCMD_CMD_DWDS_ENABLE, "DwdsEnable" }, { HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" }, { HOSTCMD_CMD_SET_CDD, "SetCDD" }, + { HOSTCMD_CMD_CAU_REG_ACCESS, "CAURegAccess" }, }; - max_entries = sizeof(cmds) / sizeof(cmds[0]); + max_entries = ARRAY_SIZE(cmds); for (curr_cmd = 0; curr_cmd < max_entries; curr_cmd++) if ((cmd & 0x7fff) == cmds[curr_cmd].cmd) @@ -99,7 +106,6 @@ static char *mwl_fwcmd_get_cmd_string(unsigned short cmd) static int mwl_fwcmd_wait_complete(struct mwl_priv *priv, unsigned short cmd) { unsigned int curr_iteration = MAX_WAIT_FW_COMPLETE_ITERATIONS; - unsigned short int_code = 0; do { @@ -110,6 +116,7 @@ static int mwl_fwcmd_wait_complete(struct mwl_priv *priv, unsigned short cmd) if (curr_iteration == 0) { wiphy_err(priv->hw->wiphy, "cmd 0x%04x=%s timed out\n", cmd, mwl_fwcmd_get_cmd_string(cmd)); + wiphy_err(priv->hw->wiphy, "return code: 0x%04x\n", int_code); return -EIO; } @@ -132,7 +139,7 @@ static int mwl_fwcmd_exec_cmd(struct mwl_priv *priv, unsigned short cmd) priv->in_send_cmd = true; mwl_fwcmd_send_cmd(priv); if (mwl_fwcmd_wait_complete(priv, 0x8000 | cmd)) { - wiphy_err(priv->hw->wiphy, "timeout\n"); + wiphy_err(priv->hw->wiphy, "timeout: 0x%04x\n", cmd); priv->in_send_cmd = false; return -EIO; } @@ -697,7 +704,7 @@ int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw) wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd); memset(pcmd, 0x00, sizeof(*pcmd)); - memset(&pcmd->permanent_addr[0], 0xff, ETH_ALEN); + eth_broadcast_addr(pcmd->permanent_addr); pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_HW_SPEC); pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); pcmd->fw_awake_cookie = cpu_to_le32(priv->pphys_cmd_buf + 2048); @@ -806,6 +813,64 @@ int mwl_fwcmd_get_stat(struct ieee80211_hw *hw, return 0; } +int mwl_fwcmd_reg_bb(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_bbp_reg_access *pcmd; + + pcmd = (struct hostcmd_cmd_bbp_reg_access *)&priv->pcmd_buf[0]; + + spin_lock_bh(&priv->fwcmd_lock); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BBP_REG_ACCESS); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->offset = cpu_to_le16(reg); + pcmd->action = cpu_to_le16(flag); + pcmd->value = *val; + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BBP_REG_ACCESS)) { + spin_unlock_bh(&priv->fwcmd_lock); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + *val = pcmd->value; + + spin_unlock_bh(&priv->fwcmd_lock); + + return 0; +} + +int mwl_fwcmd_reg_rf(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_rf_reg_access *pcmd; + + pcmd = (struct hostcmd_cmd_rf_reg_access *)&priv->pcmd_buf[0]; + + spin_lock_bh(&priv->fwcmd_lock); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_RF_REG_ACCESS); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->offset = cpu_to_le16(reg); + pcmd->action = cpu_to_le16(flag); + pcmd->value = *val; + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_RF_REG_ACCESS)) { + spin_unlock_bh(&priv->fwcmd_lock); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + *val = pcmd->value; + + spin_unlock_bh(&priv->fwcmd_lock); + + return 0; +} + int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw) { return mwl_fwcmd_802_11_radio_control(hw->priv, true, false); @@ -827,6 +892,39 @@ int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) return rc; } +int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len, + u32 *val, u16 set) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_mem_addr_access *pcmd; + int i; + + pcmd = (struct hostcmd_cmd_mem_addr_access *)&priv->pcmd_buf[0]; + + spin_lock_bh(&priv->fwcmd_lock); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_MEM_ADDR_ACCESS); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->address = cpu_to_le32(addr); + pcmd->length = cpu_to_le16(len); + pcmd->value[0] = cpu_to_le32(*val); + pcmd->reserved = cpu_to_le16(set); + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_MEM_ADDR_ACCESS)) { + spin_unlock_bh(&priv->fwcmd_lock); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + for (i = 0; i < len; i++) + val[i] = le32_to_cpu(pcmd->value[i]); + + spin_unlock_bh(&priv->fwcmd_lock); + + return 0; +} + int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw, struct ieee80211_conf *conf, u8 fraction) { @@ -1334,6 +1432,32 @@ int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) return 0; } +int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_ht_guard_interval *pcmd; + + pcmd = (struct hostcmd_cmd_ht_guard_interval *)&priv->pcmd_buf[0]; + + spin_lock_bh(&priv->fwcmd_lock); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_HT_GUARD_INTERVAL); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->action = cpu_to_le32(WL_SET); + pcmd->gi_type = cpu_to_le32(gi_type); + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_HT_GUARD_INTERVAL)) { + spin_unlock_bh(&priv->fwcmd_lock); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + spin_unlock_bh(&priv->fwcmd_lock); + + return 0; +} + int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt) { struct mwl_priv *priv = hw->priv; @@ -1362,6 +1486,32 @@ int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt) return 0; } +int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, u16 cs_mode) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_set_linkadapt_cs_mode *pcmd; + + pcmd = (struct hostcmd_cmd_set_linkadapt_cs_mode *)&priv->pcmd_buf[0]; + + spin_lock_bh(&priv->fwcmd_lock); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_LINKADAPT_CS_MODE); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET); + pcmd->cs_mode = cpu_to_le16(cs_mode); + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_LINKADAPT_CS_MODE)) { + spin_unlock_bh(&priv->fwcmd_lock); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + spin_unlock_bh(&priv->fwcmd_lock); + + return 0; +} + int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode) { struct mwl_priv *priv = hw->priv; @@ -1759,7 +1909,7 @@ int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw, } if (vif->type == NL80211_IFTYPE_STATION) { - if (memcmp(mwl_vif->bssid, addr, ETH_ALEN) == 0) + if (ether_addr_equal(mwl_vif->bssid, addr)) ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac); else ether_addr_copy(pcmd->mac_addr, mwl_vif->bssid); @@ -1847,7 +1997,7 @@ int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw, } if (vif->type == NL80211_IFTYPE_STATION) { - if (memcmp(mwl_vif->bssid, addr, ETH_ALEN) == 0) + if (ether_addr_equal(mwl_vif->bssid, addr)) ether_addr_copy(pcmd->key_param.mac_addr, mwl_vif->sta_mac); else @@ -1932,8 +2082,8 @@ int mwl_fwcmd_check_ba(struct ieee80211_hw *hw, pcmd->cmd_hdr.result = cpu_to_le16(0xffff); pcmd->action_type = cpu_to_le32(BA_CHECK_STREAM); - memcpy(&pcmd->ba_info.create_params.peer_mac_addr[0], - stream->sta->addr, ETH_ALEN); + ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0], + stream->sta->addr); pcmd->ba_info.create_params.tid = stream->tid; ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE; ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM; @@ -1944,13 +2094,14 @@ int mwl_fwcmd_check_ba(struct ieee80211_hw *hw, if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) { spin_unlock_bh(&priv->fwcmd_lock); - wiphy_err(hw->wiphy, "failed execution\n"); + wiphy_err(hw->wiphy, "check ba failed execution\n"); return -EIO; } if (pcmd->cmd_hdr.result != 0) { spin_unlock_bh(&priv->fwcmd_lock); - wiphy_err(hw->wiphy, "check ba result error\n"); + wiphy_err(hw->wiphy, "check ba result error %d\n", + le16_to_cpu(pcmd->cmd_hdr.result)); return -EINVAL; } @@ -1983,8 +2134,8 @@ int mwl_fwcmd_create_ba(struct ieee80211_hw *hw, pcmd->action_type = cpu_to_le32(BA_CREATE_STREAM); pcmd->ba_info.create_params.bar_thrs = cpu_to_le32(buf_size); pcmd->ba_info.create_params.window_size = cpu_to_le32(buf_size); - memcpy(&pcmd->ba_info.create_params.peer_mac_addr[0], - stream->sta->addr, ETH_ALEN); + ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0], + stream->sta->addr); pcmd->ba_info.create_params.tid = stream->tid; ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE; ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM; @@ -2002,13 +2153,14 @@ int mwl_fwcmd_create_ba(struct ieee80211_hw *hw, if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) { spin_unlock_bh(&priv->fwcmd_lock); - wiphy_err(hw->wiphy, "failed execution\n"); + wiphy_err(hw->wiphy, "create ba failed execution\n"); return -EIO; } if (pcmd->cmd_hdr.result != 0) { spin_unlock_bh(&priv->fwcmd_lock); - wiphy_err(hw->wiphy, "create ba result error\n"); + wiphy_err(hw->wiphy, "create ba result error %d\n", + le16_to_cpu(pcmd->cmd_hdr.result)); return -EINVAL; } @@ -2042,7 +2194,7 @@ int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw, if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) { spin_unlock_bh(&priv->fwcmd_lock); - wiphy_err(hw->wiphy, "failed execution\n"); + wiphy_err(hw->wiphy, "destroy ba failed execution\n"); return -EIO; } @@ -2075,6 +2227,23 @@ struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw, return NULL; } +void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct mwl_priv *priv = hw->priv; + struct mwl_ampdu_stream *stream; + int i; + + for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) { + stream = &priv->ampdu[i]; + + if (stream->sta == sta) { + mwl_fwcmd_destroy_ba(hw, stream->idx); + mwl_fwcmd_remove_stream(hw, stream); + } + } +} + int mwl_fwcmd_start_stream(struct ieee80211_hw *hw, struct mwl_ampdu_stream *stream) { @@ -2104,7 +2273,7 @@ struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw, if (stream->state == AMPDU_NO_STREAM) continue; - if (!memcmp(stream->sta->addr, addr, ETH_ALEN) && + if (ether_addr_equal(stream->sta->addr, addr) && stream->tid == tid) return stream; } @@ -2117,9 +2286,10 @@ bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) struct mwl_sta *sta_info; struct mwl_tx_info *tx_stats; - sta_info = mwl_dev_get_sta(sta); + if (WARN_ON(tid >= SYSADPT_MAX_TID)) + return false; - BUG_ON(tid >= SYSADPT_MAX_TID); + sta_info = mwl_dev_get_sta(sta); tx_stats = &sta_info->tx_stats[tid]; @@ -2127,6 +2297,31 @@ bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) tx_stats->pkts > SYSADPT_AMPDU_PACKET_THRESHOLD); } +int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_set_optimization_level *pcmd; + + pcmd = (struct hostcmd_cmd_set_optimization_level *)&priv->pcmd_buf[0]; + + spin_lock_bh(&priv->fwcmd_lock); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->opt_level = opt_level; + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL)) { + spin_unlock_bh(&priv->fwcmd_lock); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + spin_unlock_bh(&priv->fwcmd_lock); + + return 0; +} + int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable) { struct mwl_priv *priv = hw->priv; @@ -2201,3 +2396,32 @@ int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw) return 0; } + +int mwl_fwcmd_reg_cau(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val) +{ + struct mwl_priv *priv = hw->priv; + struct hostcmd_cmd_bbp_reg_access *pcmd; + + pcmd = (struct hostcmd_cmd_bbp_reg_access *)&priv->pcmd_buf[0]; + + spin_lock_bh(&priv->fwcmd_lock); + + memset(pcmd, 0x00, sizeof(*pcmd)); + pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_CAU_REG_ACCESS); + pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd)); + pcmd->offset = cpu_to_le16(reg); + pcmd->action = cpu_to_le16(flag); + pcmd->value = *val; + + if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_CAU_REG_ACCESS)) { + spin_unlock_bh(&priv->fwcmd_lock); + wiphy_err(hw->wiphy, "failed execution\n"); + return -EIO; + } + + *val = pcmd->value; + + spin_unlock_bh(&priv->fwcmd_lock); + + return 0; +} diff --git a/fwcmd.h b/fwcmd.h index 6b8ec74..6775c98 100644 --- a/fwcmd.h +++ b/fwcmd.h @@ -33,6 +33,15 @@ #define HOSTCMD_STA_FWRDY_SIGNATURE 0xF0F1F2F4 #define HOSTCMD_SOFTAP_FWRDY_SIGNATURE 0xF1F2F4A5 +#define GUARD_INTERVAL_STANDARD 1 +#define GUARD_INTERVAL_SHORT 2 +#define GUARD_INTERVAL_AUTO 3 + +#define LINK_CS_STATE_CONSERV 0 +#define LINK_CS_STATE_AGGR 1 +#define LINK_CS_STATE_AUTO 2 +#define LINK_CS_STATE_AUTO_DISABLED 3 + enum { WL_ANTENNATYPE_RX = 1, WL_ANTENNATYPE_TX = 2, @@ -59,6 +68,10 @@ int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw); int mwl_fwcmd_get_stat(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); +int mwl_fwcmd_reg_bb(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); + +int mwl_fwcmd_reg_rf(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); + int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw); int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw); @@ -66,6 +79,9 @@ int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw); int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble); +int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len, + u32 *val, u16 set); + int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw, struct ieee80211_conf *conf, u8 fraction); @@ -92,12 +108,16 @@ int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index, u16 cw_min, u16 cw_max, u8 aifs, u16 txop); -int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, - bool enable); +int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable); + +int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type); int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt); +int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, + u16 cs_mode); + int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode); @@ -155,6 +175,9 @@ struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid); +void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw, + struct ieee80211_sta *sta); + int mwl_fwcmd_start_stream(struct ieee80211_hw *hw, struct mwl_ampdu_stream *stream); @@ -166,10 +189,14 @@ struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw, bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid); +int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level); + int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable); int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value); int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw); +int mwl_fwcmd_reg_cau(struct ieee80211_hw *hw, u8 flag, u32 reg, u32 *val); + #endif /* _fwcmd_h_ */ diff --git a/hostcmd.h b/hostcmd.h index d9db0ee..8c03626 100644 --- a/hostcmd.h +++ b/hostcmd.h @@ -24,7 +24,10 @@ #define HOSTCMD_CMD_GET_HW_SPEC 0x0003 #define HOSTCMD_CMD_SET_HW_SPEC 0x0004 #define HOSTCMD_CMD_802_11_GET_STAT 0x0014 +#define HOSTCMD_CMD_BBP_REG_ACCESS 0x001a +#define HOSTCMD_CMD_RF_REG_ACCESS 0x001b #define HOSTCMD_CMD_802_11_RADIO_CONTROL 0x001c +#define HOSTCMD_CMD_MEM_ADDR_ACCESS 0x001d #define HOSTCMD_CMD_802_11_TX_POWER 0x001f #define HOSTCMD_CMD_802_11_RF_ANTENNA 0x0020 #define HOSTCMD_CMD_BROADCAST_SSID_ENABLE 0x0050 /* per-vif */ @@ -34,8 +37,10 @@ #define HOSTCMD_CMD_802_11_RTS_THSD 0x0113 #define HOSTCMD_CMD_SET_EDCA_PARAMS 0x0115 #define HOSTCMD_CMD_SET_WMM_MODE 0x0123 +#define HOSTCMD_CMD_HT_GUARD_INTERVAL 0x0124 #define HOSTCMD_CMD_SET_FIXED_RATE 0x0126 #define HOSTCMD_CMD_SET_IES 0x0127 +#define HOSTCMD_CMD_SET_LINKADAPT_CS_MODE 0x0129 #define HOSTCMD_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ #define HOSTCMD_CMD_SET_RATE_ADAPT_MODE 0x0203 #define HOSTCMD_CMD_GET_WATCHDOG_BITMAP 0x0205 @@ -46,9 +51,11 @@ #define HOSTCMD_CMD_SET_APMODE 0x1114 #define HOSTCMD_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ #define HOSTCMD_CMD_BASTREAM 0x1125 +#define HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL 0x1133 #define HOSTCMD_CMD_DWDS_ENABLE 0x1144 #define HOSTCMD_CMD_FW_FLUSH_TIMER 0x1148 #define HOSTCMD_CMD_SET_CDD 0x1150 +#define HOSTCMD_CMD_CAU_REG_ACCESS 0x1157 /* Define general result code for each command */ #define HOSTCMD_RESULT_OK 0x0000 @@ -261,6 +268,24 @@ struct hostcmd_cmd_802_11_get_stat { __le32 tx_cts_count; } __packed; +/* HOSTCMD_CMD_BBP_REG_ACCESS */ +struct hostcmd_cmd_bbp_reg_access { + struct hostcmd_header cmd_hdr; + __le16 action; + __le16 offset; + u8 value; + u8 reserverd[3]; +} __packed; + +/* HOSTCMD_CMD_RF_REG_ACCESS */ +struct hostcmd_cmd_rf_reg_access { + struct hostcmd_header cmd_hdr; + __le16 action; + __le16 offset; + u8 value; + u8 reserverd[3]; +} __packed; + /* HOSTCMD_CMD_802_11_RADIO_CONTROL */ struct hostcmd_cmd_802_11_radio_control { struct hostcmd_header cmd_hdr; @@ -270,6 +295,15 @@ struct hostcmd_cmd_802_11_radio_control { __le16 radio_on; } __packed; +/* HOSTCMD_CMD_MEM_ADDR_ACCESS */ +struct hostcmd_cmd_mem_addr_access { + struct hostcmd_header cmd_hdr; + __le32 address; + __le16 length; + __le16 reserved; + __le32 value[64]; +} __packed; + /* HOSTCMD_CMD_802_11_TX_POWER */ struct hostcmd_cmd_802_11_tx_power { struct hostcmd_header cmd_hdr; @@ -347,6 +381,13 @@ struct hostcmd_cmd_set_wmm_mode { __le16 action; /* 0->unset, 1->set */ } __packed; +/* HOSTCMD_CMD_HT_GUARD_INTERVAL */ +struct hostcmd_cmd_ht_guard_interval { + struct hostcmd_header cmd_hdr; + __le32 action; + __le32 gi_type; +} __packed; + /* HOSTCMD_CMD_SET_FIXED_RATE */ struct fix_rate_flag { /* lower rate after the retry count */ /* 0: legacy, 1: HT */ @@ -390,11 +431,11 @@ struct hostcmd_cmd_set_ies { u8 ie_list_proprietary[112]; } __packed; -/* HOSTCMD_CMD_SET_RATE_ADAPT_MODE */ -struct hostcmd_cmd_set_rate_adapt_mode { +/* HOSTCMD_CMD_SET_LINKADAPT_CS_MODE */ +struct hostcmd_cmd_set_linkadapt_cs_mode { struct hostcmd_header cmd_hdr; __le16 action; - __le16 rate_adapt_mode; /* 0:Indoor, 1:Outdoor */ + __le16 cs_mode; } __packed; /* HOSTCMD_CMD_SET_MAC_ADDR, HOSTCMD_CMD_DEL_MAC_ADDR */ @@ -404,6 +445,13 @@ struct hostcmd_cmd_set_mac_addr { u8 mac_addr[ETH_ALEN]; } __packed; +/* HOSTCMD_CMD_SET_RATE_ADAPT_MODE */ +struct hostcmd_cmd_set_rate_adapt_mode { + struct hostcmd_header cmd_hdr; + __le16 action; + __le16 rate_adapt_mode; /* 0:Indoor, 1:Outdoor */ +} __packed; + /* HOSTCMD_CMD_GET_WATCHDOG_BITMAP */ struct hostcmd_cmd_get_watchdog_bitmap { struct hostcmd_header cmd_hdr; @@ -731,6 +779,12 @@ struct hostcmd_cmd_bastream { union ba_info ba_info; } __packed; +/* HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL */ +struct hostcmd_cmd_set_optimization_level { + struct hostcmd_header cmd_hdr; + u8 opt_level; +} __packed; + /* HOSTCMD_CMD_DWDS_ENABLE */ struct hostcmd_cmd_dwds_enable { struct hostcmd_header cmd_hdr; diff --git a/isr.c b/isr.c index 9e7d473..20b6088 100644 --- a/isr.c +++ b/isr.c @@ -66,12 +66,12 @@ irqreturn_t mwl_isr(int irq, void *dev_id) } } - if (int_status & MACREG_A2HRIC_BIT_QUEUE_EMPTY) { - int_status &= ~MACREG_A2HRIC_BIT_QUEUE_EMPTY; + if (int_status & MACREG_A2HRIC_BIT_QUE_EMPTY) { + int_status &= ~MACREG_A2HRIC_BIT_QUE_EMPTY; if (!priv->is_qe_schedule) { status = readl(int_status_mask); - writel((status & ~MACREG_A2HRIC_BIT_QUEUE_EMPTY), + writel((status & ~MACREG_A2HRIC_BIT_QUE_EMPTY), int_status_mask); tasklet_schedule(&priv->qe_task); priv->is_qe_schedule = true; @@ -100,7 +100,6 @@ void mwl_watchdog_ba_events(struct work_struct *work) struct mwl_ampdu_stream *streams; struct mwl_priv *priv = container_of(work, struct mwl_priv, watchdog_ba_handle); - struct ieee80211_hw *hw = priv->hw; u32 status; rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap); @@ -123,13 +122,9 @@ void mwl_watchdog_ba_events(struct work_struct *work) /* Check if the stream is in use before disabling it */ streams = &priv->ampdu[stream_index]; - if (streams->state == AMPDU_STREAM_ACTIVE) { + if (streams->state == AMPDU_STREAM_ACTIVE) ieee80211_stop_tx_ba_session(streams->sta, streams->tid); - spin_unlock_bh(&priv->stream_lock); - mwl_fwcmd_destroy_ba(hw, stream_index); - spin_lock_bh(&priv->stream_lock); - } } else { for (stream_index = 0; stream_index < SYSADPT_TX_AMPDU_QUEUES; @@ -141,9 +136,6 @@ void mwl_watchdog_ba_events(struct work_struct *work) ieee80211_stop_tx_ba_session(streams->sta, streams->tid); - spin_unlock_bh(&priv->stream_lock); - mwl_fwcmd_destroy_ba(hw, stream_index); - spin_lock_bh(&priv->stream_lock); } } } diff --git a/mac80211.c b/mac80211.c index a4242e0..ec96f25 100644 --- a/mac80211.c +++ b/mac80211.c @@ -89,12 +89,18 @@ static int mwl_mac80211_start(struct ieee80211_hw *hw) if (rc) goto fwcmd_fail; rc = mwl_fwcmd_set_wmm_mode(hw, true); + if (rc) + goto fwcmd_fail; + rc = mwl_fwcmd_ht_guard_interval(hw, GUARD_INTERVAL_AUTO); if (rc) goto fwcmd_fail; rc = mwl_fwcmd_set_dwds_stamode(hw, true); if (rc) goto fwcmd_fail; rc = mwl_fwcmd_set_fw_flush_timer(hw, SYSADPT_AMSDU_FLUSH_TIME); + if (rc) + goto fwcmd_fail; + rc = mwl_fwcmd_set_optimization_level(hw, 1); if (rc) goto fwcmd_fail; @@ -194,27 +200,12 @@ static int mwl_mac80211_add_interface(struct ieee80211_hw *hw, static void mwl_mac80211_remove_vif(struct mwl_priv *priv, struct ieee80211_vif *vif) { - int num; - struct sk_buff *skb, *tmp; - struct ieee80211_tx_info *tx_info; - struct mwl_tx_ctrl *tx_ctrl; struct mwl_vif *mwl_vif = mwl_dev_get_vif(vif); if (!priv->macids_used) return; - for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) { - spin_lock_bh(&priv->txq[num].lock); - skb_queue_walk_safe(&priv->txq[num], skb, tmp) { - tx_info = IEEE80211_SKB_CB(skb); - tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; - if (tx_ctrl->vif == vif) { - __skb_unlink(skb, &priv->txq[num]); - dev_kfree_skb_any(skb); - } - } - spin_unlock_bh(&priv->txq[num].lock); - } + mwl_tx_del_pkts_via_vif(priv->hw, vif); priv->macids_used &= ~(1 << mwl_vif->macid); spin_lock_bh(&priv->vif_lock); @@ -262,9 +253,13 @@ static int mwl_mac80211_config(struct ieee80211_hw *hw, if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { mwl_fwcmd_set_apmode(hw, AP_MODE_2_4GHZ_11AC_MIXED); + mwl_fwcmd_set_linkadapt_cs_mode(hw, + LINK_CS_STATE_CONSERV); rate = mwl_rates_24[0].hw_value; } else if (conf->chandef.chan->band == IEEE80211_BAND_5GHZ) { mwl_fwcmd_set_apmode(hw, AP_MODE_11AC); + mwl_fwcmd_set_linkadapt_cs_mode(hw, + LINK_CS_STATE_AUTO); rate = mwl_rates_50[0].hw_value; } @@ -496,34 +491,16 @@ static int mwl_mac80211_sta_remove(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct mwl_priv *priv = hw->priv; - struct mwl_sta *sta_info; - int num; - struct sk_buff *skb, *tmp; - struct ieee80211_tx_info *tx_info; - struct mwl_tx_ctrl *tx_ctrl; int rc; + struct mwl_sta *sta_info = mwl_dev_get_sta(sta); - sta_info = mwl_dev_get_sta(sta); + mwl_tx_del_sta_amsdu_pkts(sta); - for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) { - spin_lock_bh(&priv->txq[num].lock); - skb_queue_walk_safe(&priv->txq[num], skb, tmp) { - tx_info = IEEE80211_SKB_CB(skb); - tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; - if (tx_ctrl->sta == sta) { - __skb_unlink(skb, &priv->txq[num]); - dev_kfree_skb_any(skb); - } - } - spin_unlock_bh(&priv->txq[num].lock); - } + spin_lock_bh(&priv->stream_lock); + mwl_fwcmd_del_sta_streams(hw, sta); + spin_unlock_bh(&priv->stream_lock); - spin_lock_bh(&sta_info->amsdu_lock); - for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) { - if (!sta_info->amsdu_ctrl.frag[num].skb) - dev_kfree_skb_any(sta_info->amsdu_ctrl.frag[num].skb); - } - spin_unlock_bh(&sta_info->amsdu_lock); + mwl_tx_del_pkts_via_sta(hw, sta); rc = mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr); @@ -542,7 +519,8 @@ static int mwl_mac80211_conf_tx(struct ieee80211_hw *hw, struct mwl_priv *priv = hw->priv; int rc = 0; - BUG_ON(queue > SYSADPT_TX_WMM_QUEUES - 1); + if (WARN_ON(queue > SYSADPT_TX_WMM_QUEUES - 1)) + return -EINVAL; memcpy(&priv->wmm_params[queue], params, sizeof(*params)); @@ -570,7 +548,7 @@ static int mwl_mac80211_get_stats(struct ieee80211_hw *hw, static int mwl_mac80211_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) + struct survey_info *survey) { struct mwl_priv *priv = hw->priv; struct ieee80211_conf *conf = &hw->conf; @@ -591,7 +569,7 @@ static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { - int i, rc = 0; + int rc = 0; struct mwl_priv *priv = hw->priv; struct mwl_ampdu_stream *stream; u8 *addr = sta->addr, idx; @@ -639,30 +617,23 @@ static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, /* Release the lock before we do the time consuming stuff */ spin_unlock_bh(&priv->stream_lock); - for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { - /* Check if link is still valid */ - if (!sta_info->is_ampdu_allowed) { - spin_lock_bh(&priv->stream_lock); - mwl_fwcmd_remove_stream(hw, stream); - spin_unlock_bh(&priv->stream_lock); - wiphy_warn(hw->wiphy, - "link is no valid now\n"); - return -EBUSY; - } - - rc = mwl_fwcmd_check_ba(hw, stream, vif); - - if (!rc) - break; - - mdelay(1000); + /* Check if link is still valid */ + if (!sta_info->is_ampdu_allowed) { + spin_lock_bh(&priv->stream_lock); + mwl_fwcmd_remove_stream(hw, stream); + spin_unlock_bh(&priv->stream_lock); + wiphy_warn(hw->wiphy, + "link is not valid now\n"); + return -EBUSY; } + rc = mwl_fwcmd_check_ba(hw, stream, vif); spin_lock_bh(&priv->stream_lock); if (rc) { mwl_fwcmd_remove_stream(hw, stream); - wiphy_err(hw->wiphy, "error code: %d\n", rc); + wiphy_err(hw->wiphy, + "ampdu start error code: %d\n", rc); rc = -EBUSY; break; } @@ -674,6 +645,7 @@ static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { + mwl_tx_del_ampdu_pkts(hw, sta, tid); idx = stream->idx; spin_unlock_bh(&priv->stream_lock); mwl_fwcmd_destroy_ba(hw, idx); @@ -686,7 +658,10 @@ static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: - BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); + if (WARN_ON(stream->state != AMPDU_STREAM_IN_PROGRESS)) { + rc = -EPERM; + break; + } spin_unlock_bh(&priv->stream_lock); rc = mwl_fwcmd_create_ba(hw, stream, buf_size, vif); spin_lock_bh(&priv->stream_lock); @@ -699,10 +674,13 @@ static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, mwl_fwcmd_destroy_ba(hw, idx); spin_lock_bh(&priv->stream_lock); mwl_fwcmd_remove_stream(hw, stream); + wiphy_err(hw->wiphy, + "ampdu operation error code: %d\n", rc); } break; default: rc = -ENOTSUPP; + break; } spin_unlock_bh(&priv->stream_lock); diff --git a/main.c b/main.c index 667c231..5a000c0 100644 --- a/main.c +++ b/main.c @@ -27,11 +27,12 @@ #include "tx.h" #include "rx.h" #include "isr.h" +#ifdef CONFIG_DEBUG_FS +#include "debugfs.h" +#endif #define MWL_DESC "Marvell 802.11ac Wireless Network Driver" #define MWL_DEV_NAME "Marvell 802.11ac Adapter" -#define MWL_DRV_NAME KBUILD_MODNAME -#define MWL_DRV_VERSION "10.3.0.8" #define FILE_PATH_LEN 64 #define CMD_BUF_SIZE 0x4000 @@ -142,131 +143,59 @@ static const struct ieee80211_iface_combination ap_if_comb = { static int mwl_alloc_pci_resource(struct mwl_priv *priv) { - struct pci_dev *pdev; - u32 phys_addr = 0; - u32 flags; - void __iomem *phys_addr1[2]; - void __iomem *phys_addr2[2]; - - pdev = priv->pdev; - - phys_addr = pci_resource_start(pdev, 0); - flags = pci_resource_flags(pdev, 0); + struct pci_dev *pdev = priv->pdev; + void __iomem *addr; priv->next_bar_num = 1; /* 32-bit */ - - if (flags & 0x04) + if (pci_resource_flags(pdev, 0) & 0x04) priv->next_bar_num = 2; /* 64-bit */ - if (!request_mem_region(phys_addr, pci_resource_len(pdev, 0), - MWL_DRV_NAME)) { + addr = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]); + if (IS_ERR(addr)) { wiphy_err(priv->hw->wiphy, "%s: cannot reserve PCI memory region 0\n", MWL_DRV_NAME); - goto err_reserve_mem_region_bar0; + goto err; } - - phys_addr1[0] = ioremap(phys_addr, pci_resource_len(pdev, 0)); - phys_addr1[1] = NULL; - - priv->iobase0 = phys_addr1[0]; - if (!priv->iobase0) { - wiphy_err(priv->hw->wiphy, - "%s: cannot remap PCI memory region 0\n", - MWL_DRV_NAME); - goto err_release_mem_region_bar0; - } - + priv->iobase0 = addr; wiphy_debug(priv->hw->wiphy, "priv->iobase0 = %p\n", priv->iobase0); - phys_addr = pci_resource_start(pdev, priv->next_bar_num); - - if (!request_mem_region(phys_addr, - pci_resource_len(pdev, priv->next_bar_num), - MWL_DRV_NAME)) { + addr = devm_ioremap_resource(&pdev->dev, + &pdev->resource[priv->next_bar_num]); + if (IS_ERR(addr)) { wiphy_err(priv->hw->wiphy, "%s: cannot reserve PCI memory region 1\n", MWL_DRV_NAME); - goto err_iounmap_iobase0; - } - - phys_addr2[0] = ioremap(phys_addr, - pci_resource_len(pdev, priv->next_bar_num)); - phys_addr2[1] = NULL; - priv->iobase1 = phys_addr2[0]; - - if (!priv->iobase1) { - wiphy_err(priv->hw->wiphy, - "%s: cannot remap PCI memory region 1\n", - MWL_DRV_NAME); - goto err_release_mem_region_bar1; + goto err; } - + priv->iobase1 = addr; wiphy_debug(priv->hw->wiphy, "priv->iobase1 = %p\n", priv->iobase1); priv->pcmd_buf = - (unsigned short *)dma_alloc_coherent(&priv->pdev->dev, - CMD_BUF_SIZE, - &priv->pphys_cmd_buf, - GFP_KERNEL); - + (unsigned short *)dmam_alloc_coherent(&priv->pdev->dev, + CMD_BUF_SIZE, + &priv->pphys_cmd_buf, + GFP_KERNEL); if (!priv->pcmd_buf) { wiphy_err(priv->hw->wiphy, "%s: cannot alloc memory for command buffer\n", MWL_DRV_NAME); - goto err_iounmap_iobase1; + goto err; } - wiphy_debug(priv->hw->wiphy, "priv->pcmd_buf = %p priv->pphys_cmd_buf = %p\n", priv->pcmd_buf, (void *)priv->pphys_cmd_buf); - memset(priv->pcmd_buf, 0x00, CMD_BUF_SIZE); return 0; -err_iounmap_iobase1: - - iounmap(priv->iobase1); - -err_release_mem_region_bar1: - - release_mem_region(pci_resource_start(pdev, 1), - pci_resource_len(pdev, 1)); - -err_iounmap_iobase0: - - iounmap(priv->iobase0); - -err_release_mem_region_bar0: - - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - -err_reserve_mem_region_bar0: - +err: wiphy_err(priv->hw->wiphy, "pci alloc fail\n"); return -EIO; } -static void mwl_free_pci_resource(struct mwl_priv *priv) -{ - struct pci_dev *pdev; - - pdev = priv->pdev; - - iounmap(priv->iobase0); - iounmap(priv->iobase1); - release_mem_region(pci_resource_start(pdev, priv->next_bar_num), - pci_resource_len(pdev, priv->next_bar_num)); - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - dma_free_coherent(&priv->pdev->dev, CMD_BUF_SIZE, - priv->pcmd_buf, priv->pphys_cmd_buf); -} - static int mwl_init_firmware(struct mwl_priv *priv, const char *fw_name) { struct pci_dev *pdev; @@ -465,7 +394,11 @@ static void mwl_set_ht_caps(struct mwl_priv *priv, band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; +#else + ieee80211_hw_set(hw, AMPDU_AGGREGATION); +#endif band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; @@ -567,12 +500,21 @@ static int mwl_wl_init(struct mwl_priv *priv) hw->queues = SYSADPT_TX_WMM_QUEUES; /* Set rssi values to dBm */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_HAS_RATE_CONTROL; +#else + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, HAS_RATE_CONTROL); +#endif - /* Ask mac80211 to not to trigger PS mode + /* Ask mac80211 not to trigger PS mode * based on PM bit of incoming frames. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) hw->flags |= IEEE80211_HW_AP_LINK_PS; +#else + ieee80211_hw_set(hw, AP_LINK_PS); +#endif hw->vif_data_size = sizeof(struct mwl_vif); hw->sta_data_size = sizeof(struct mwl_sta); @@ -607,6 +549,7 @@ static int mwl_wl_init(struct mwl_priv *priv) priv->is_qe_schedule = false; spin_lock_init(&priv->tx_desc_lock); + spin_lock_init(&priv->rx_desc_lock); spin_lock_init(&priv->fwcmd_lock); spin_lock_init(&priv->vif_lock); spin_lock_init(&priv->sta_lock); @@ -822,6 +765,10 @@ static int mwl_probe(struct pci_dev *pdev, const struct pci_device_id *id) else wiphy_info(priv->hw->wiphy, "RX: unknown\n"); +#ifdef CONFIG_DEBUG_FS + mwl_debugfs_init(hw); +#endif + return rc; err_wl_init: @@ -852,10 +799,13 @@ static void mwl_remove(struct pci_dev *pdev) priv = hw->priv; mwl_wl_deinit(priv); - mwl_free_pci_resource(priv); pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); pci_disable_device(pdev); + +#ifdef CONFIG_DEBUG_FS + mwl_debugfs_remove(hw); +#endif } static struct pci_driver mwl_pci_driver = { diff --git a/rx.c b/rx.c index 3390c5d..210269b 100644 --- a/rx.c +++ b/rx.c @@ -15,6 +15,7 @@ /* Description: This file implements receive related functions. */ +#include #include #include "sysadpt.h" @@ -288,7 +289,7 @@ static inline struct mwl_vif *mwl_rx_find_vif_bss(struct mwl_priv *priv, spin_lock_bh(&priv->vif_lock); list_for_each_entry(mwl_vif, &priv->vif_list, list) { - if (memcmp(bssid, mwl_vif->bssid, ETH_ALEN) == 0) { + if (ether_addr_equal(bssid, mwl_vif->bssid)) { spin_unlock_bh(&priv->vif_lock); return mwl_vif; } @@ -399,6 +400,7 @@ void mwl_rx_recv(unsigned long data) struct ieee80211_hdr *wh; u32 status_mask; + spin_lock(&priv->rx_desc_lock); desc = &priv->desc_data[0]; curr_hndl = desc->pnext_rx_hndl; @@ -409,8 +411,8 @@ void mwl_rx_recv(unsigned long data) priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); priv->is_rx_schedule = false; - wiphy_warn(hw->wiphy, "busy or no receiving packets\n"); + spin_unlock(&priv->rx_desc_lock); return; } @@ -501,4 +503,6 @@ out: priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); priv->is_rx_schedule = false; + + spin_unlock(&priv->rx_desc_lock); } diff --git a/sysadpt.h b/sysadpt.h index bf8f3bd..0f6ab4f 100644 --- a/sysadpt.h +++ b/sysadpt.h @@ -40,7 +40,9 @@ #define SYSADPT_MAX_NUM_TX_DESC 256 -#define SYSADPT_TX_QUEUE_LIMIT 1024 +#define SYSADPT_TX_QUEUE_LIMIT (3 * SYSADPT_MAX_NUM_TX_DESC) + +#define SYSADPT_TX_WAKE_Q_THRESHOLD (2 * SYSADPT_MAX_NUM_TX_DESC) #define SYSADPT_DELAY_FREE_Q_LIMIT SYSADPT_MAX_NUM_TX_DESC diff --git a/tx.c b/tx.c index 7687e38..1db6b71 100644 --- a/tx.c +++ b/tx.c @@ -324,8 +324,6 @@ static inline void mwl_tx_insert_ccmp_hdr(u8 *pccmp_hdr, static inline int mwl_tx_tid_queue_mapping(u8 tid) { - BUG_ON(tid > 7); - switch (tid) { case 0: case 3: @@ -351,7 +349,8 @@ static inline void mwl_tx_count_packet(struct ieee80211_sta *sta, u8 tid) struct mwl_sta *sta_info; struct mwl_tx_info *tx_stats; - BUG_ON(tid >= SYSADPT_MAX_TID); + if (WARN_ON(tid >= SYSADPT_MAX_TID)) + return; sta_info = mwl_dev_get_sta(sta); @@ -410,7 +409,8 @@ static inline void mwl_tx_skb(struct mwl_priv *priv, int desc_num, struct ieee80211_hdr *wh; dma_addr_t dma; - BUG_ON(!tx_skb); + if (WARN_ON(!tx_skb)) + return; tx_info = IEEE80211_SKB_CB(tx_skb); tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; @@ -490,10 +490,13 @@ static inline void mwl_tx_skb(struct mwl_priv *priv, int desc_num, static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv, int desc_num, struct sk_buff *tx_skb, - struct mwl_tx_ctrl *tx_ctrl) + struct ieee80211_tx_info *tx_info) { struct ieee80211_sta *sta; struct mwl_sta *sta_info; + struct mwl_tx_ctrl *tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; + struct ieee80211_tx_info *amsdu_info; + struct sk_buff_head *amsdu_pkts; struct mwl_amsdu_frag *amsdu; int amsdu_allow_size; struct ieee80211_hdr *wh; @@ -553,18 +556,22 @@ static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv, if (amsdu->num == 0) { struct sk_buff *newskb; - struct ieee80211_tx_info *tx_info; + amsdu_pkts = (struct sk_buff_head *) + kmalloc(sizeof(*amsdu_pkts), GFP_KERNEL); + if (!amsdu_pkts) { + spin_unlock_bh(&sta_info->amsdu_lock); + return tx_skb; + } newskb = dev_alloc_skb(amsdu_allow_size + SYSADPT_MIN_BYTES_HEADROOM); - - if (newskb == NULL) { + if (!newskb) { spin_unlock_bh(&sta_info->amsdu_lock); + kfree(amsdu_pkts); return tx_skb; } data = newskb->data; - memcpy(data, tx_skb->data, wh_len); ether_addr_copy(data + wh_len, ieee80211_get_DA(wh)); ether_addr_copy(data + wh_len + ETH_ALEN, ieee80211_get_SA(wh)); @@ -574,8 +581,11 @@ static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv, skb_put(newskb, tx_skb->len + ETH_HLEN); tx_ctrl->qos_ctrl |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; - tx_info = IEEE80211_SKB_CB(newskb); - memcpy(&tx_info->status, tx_ctrl, sizeof(*tx_ctrl)); + amsdu_info = IEEE80211_SKB_CB(newskb); + memcpy(amsdu_info, tx_info, sizeof(*tx_info)); + skb_queue_head_init(amsdu_pkts); + ((struct mwl_tx_ctrl *)&amsdu_info->status)->amsdu_pkts = + (void *)amsdu_pkts; amsdu->skb = newskb; } else { amsdu->cur_pos += amsdu->pad; @@ -588,12 +598,15 @@ static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv, memcpy(data + ETH_HLEN, tx_skb->data + wh_len, len); skb_put(amsdu->skb, len + ETH_HLEN + amsdu->pad); + amsdu_info = IEEE80211_SKB_CB(amsdu->skb); + amsdu_pkts = (struct sk_buff_head *) + ((struct mwl_tx_ctrl *)&amsdu_info->status)->amsdu_pkts; } amsdu->num++; amsdu->pad = ((len + ETH_HLEN) % 4) ? (4 - (len + ETH_HLEN) % 4) : 0; amsdu->cur_pos = amsdu->skb->data + amsdu->skb->len; - dev_kfree_skb_any(tx_skb); + skb_queue_tail(amsdu_pkts, tx_skb); if (amsdu->num > SYSADPT_AMSDU_PACKET_THRESHOLD) { amsdu->num = 0; @@ -609,7 +622,7 @@ static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv, static inline void mwl_tx_skbs(struct ieee80211_hw *hw) { struct mwl_priv *priv = hw->priv; - int num = SYSADPT_NUM_OF_DESC_DATA; + int num = SYSADPT_TX_WMM_QUEUES; struct sk_buff *tx_skb; spin_lock_bh(&priv->tx_desc_lock); @@ -628,16 +641,92 @@ static inline void mwl_tx_skbs(struct ieee80211_hw *hw) if ((tx_skb->protocol != cpu_to_be16(ETH_P_PAE)) && (tx_ctrl->tx_priority >= SYSADPT_TX_WMM_QUEUES)) { tx_skb = mwl_tx_do_amsdu(priv, num, - tx_skb, tx_ctrl); + tx_skb, tx_info); } if (tx_skb) mwl_tx_skb(priv, num, tx_skb); } + + if (skb_queue_len(&priv->txq[num]) < + SYSADPT_TX_WAKE_Q_THRESHOLD) { + int queue; + + queue = SYSADPT_TX_WMM_QUEUES - num - 1; + if (ieee80211_queue_stopped(hw, queue)) + ieee80211_wake_queue(hw, queue); + } } spin_unlock_bh(&priv->tx_desc_lock); } +static inline void mwl_tx_prepare_info(struct ieee80211_hw *hw, u32 rate, + struct ieee80211_tx_info *info) +{ + u32 format, bandwidth, short_gi, rate_id; + + ieee80211_tx_info_clear_status(info); + + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + info->status.rates[0].flags = 0; + + if (rate) { + /* Prepare rate information */ + format = rate & MWL_TX_RATE_FORMAT_MASK; + bandwidth = + (rate & MWL_TX_RATE_BANDWIDTH_MASK) >> + MWL_TX_RATE_BANDWIDTH_SHIFT; + short_gi = (rate & MWL_TX_RATE_SHORTGI_MASK) >> + MWL_TX_RATE_SHORTGI_SHIFT; + rate_id = (rate & MWL_TX_RATE_RATEIDMCS_MASK) >> + MWL_TX_RATE_RATEIDMCS_SHIFT; + + info->status.rates[0].idx = rate_id; + if (format == TX_RATE_FORMAT_LEGACY) { + if (hw->conf.chandef.chan->hw_value > + BAND_24_CHANNEL_NUM) { + info->status.rates[0].idx -= 5; + } + } + if (format == TX_RATE_FORMAT_11N) + info->status.rates[0].flags |= + IEEE80211_TX_RC_MCS; + if (format == TX_RATE_FORMAT_11AC) + info->status.rates[0].flags |= + IEEE80211_TX_RC_VHT_MCS; + if (bandwidth == TX_RATE_BANDWIDTH_40) + info->status.rates[0].flags |= + IEEE80211_TX_RC_40_MHZ_WIDTH; + if (bandwidth == TX_RATE_BANDWIDTH_80) + info->status.rates[0].flags |= + IEEE80211_TX_RC_80_MHZ_WIDTH; + if (short_gi == TX_RATE_INFO_SHORT_GI) + info->status.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + info->status.rates[0].count = 1; + info->status.rates[1].idx = -1; + } +} + +static inline void mwl_tx_ack_amsdu_pkts(struct ieee80211_hw *hw, u32 rate, + struct sk_buff_head *amsdu_pkts) +{ + struct sk_buff *amsdu_pkt; + struct ieee80211_tx_info *info; + + while (skb_queue_len(amsdu_pkts) > 0) { + amsdu_pkt = skb_dequeue(amsdu_pkts); + info = IEEE80211_SKB_CB(amsdu_pkt); + mwl_tx_prepare_info(hw, rate, info); + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status(hw, amsdu_pkt); + } + + kfree(amsdu_pkts); +} + int mwl_tx_init(struct ieee80211_hw *hw) { struct mwl_priv *priv = hw->priv; @@ -755,7 +844,7 @@ void mwl_tx_xmit(struct ieee80211_hw *hw, */ if (mgmtframe) { u16 capab; - + if (unlikely(ieee80211_is_action(wh->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK && mgmt->u.action.u.addba_req.action_code == @@ -788,7 +877,12 @@ void mwl_tx_xmit(struct ieee80211_hw *hw, if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { - WARN_ON(!(qos & MWL_QOS_ACK_POLICY_BLOCKACK)); + if (WARN_ON(!(qos & + MWL_QOS_ACK_POLICY_BLOCKACK))) { + spin_unlock_bh(&priv->stream_lock); + dev_kfree_skb_any(skb); + return; + } txpriority = (SYSADPT_TX_WMM_QUEUES + stream->idx) % @@ -821,11 +915,6 @@ void mwl_tx_xmit(struct ieee80211_hw *hw, return; } } else { - /* Defer calling mwl_fwcmd_start_stream so that the current - * skb can go out before the ADDBA request. This - * prevents sequence number mismatch at the recipient - * as described above. - */ if (mwl_fwcmd_ampdu_allowed(sta, tid)) { stream = mwl_fwcmd_add_stream(hw, sta, tid); @@ -841,18 +930,19 @@ void mwl_tx_xmit(struct ieee80211_hw *hw, } tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; + tx_ctrl->vif = (void *)tx_info->control.vif; + tx_ctrl->sta = (void *)sta; + tx_ctrl->k_conf = (void *)k_conf; + tx_ctrl->amsdu_pkts = NULL; tx_ctrl->tx_priority = txpriority; - tx_ctrl->qos_ctrl = qos; tx_ctrl->type = (mgmtframe ? IEEE_TYPE_MANAGEMENT : IEEE_TYPE_DATA); + tx_ctrl->qos_ctrl = qos; tx_ctrl->xmit_control = xmitcontrol; - tx_ctrl->sta = (void *)sta; - tx_ctrl->vif = (void *)tx_info->control.vif; - tx_ctrl->k_conf = (void *)k_conf; if (skb_queue_len(&priv->txq[index]) > priv->txq_limit) - dev_kfree_skb_any(skb); - else - skb_queue_tail(&priv->txq[index], skb); + ieee80211_stop_queue(hw, SYSADPT_TX_WMM_QUEUES - index - 1); + + skb_queue_tail(&priv->txq[index], skb); mwl_tx_skbs(hw); @@ -865,6 +955,115 @@ void mwl_tx_xmit(struct ieee80211_hw *hw, } } +void mwl_tx_del_pkts_via_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl_priv *priv = hw->priv; + int num; + struct sk_buff *skb, *tmp; + struct ieee80211_tx_info *tx_info; + struct mwl_tx_ctrl *tx_ctrl; + struct sk_buff_head *amsdu_pkts; + + for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) { + spin_lock_bh(&priv->txq[num].lock); + skb_queue_walk_safe(&priv->txq[num], skb, tmp) { + tx_info = IEEE80211_SKB_CB(skb); + tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; + if (tx_ctrl->vif == vif) { + amsdu_pkts = (struct sk_buff_head *) + tx_ctrl->amsdu_pkts; + if (amsdu_pkts) { + skb_queue_purge(amsdu_pkts); + kfree(amsdu_pkts); + } + __skb_unlink(skb, &priv->txq[num]); + dev_kfree_skb_any(skb); + } + } + spin_unlock_bh(&priv->txq[num].lock); + } +} + +void mwl_tx_del_pkts_via_sta(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct mwl_priv *priv = hw->priv; + int num; + struct sk_buff *skb, *tmp; + struct ieee80211_tx_info *tx_info; + struct mwl_tx_ctrl *tx_ctrl; + struct sk_buff_head *amsdu_pkts; + + for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) { + spin_lock_bh(&priv->txq[num].lock); + skb_queue_walk_safe(&priv->txq[num], skb, tmp) { + tx_info = IEEE80211_SKB_CB(skb); + tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; + if (tx_ctrl->sta == sta) { + amsdu_pkts = (struct sk_buff_head *) + tx_ctrl->amsdu_pkts; + if (amsdu_pkts) { + skb_queue_purge(amsdu_pkts); + kfree(amsdu_pkts); + } + __skb_unlink(skb, &priv->txq[num]); + dev_kfree_skb_any(skb); + } + } + spin_unlock_bh(&priv->txq[num].lock); + } +} + +void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u8 tid) +{ + struct mwl_priv *priv = hw->priv; + struct mwl_sta *sta_info = mwl_dev_get_sta(sta); + int desc_num = SYSADPT_TX_WMM_QUEUES - tid - 1; + struct mwl_amsdu_frag *amsdu_frag; + struct sk_buff *skb, *tmp; + struct ieee80211_tx_info *tx_info; + struct mwl_tx_ctrl *tx_ctrl; + struct sk_buff_head *amsdu_pkts; + + spin_lock_bh(&priv->txq[desc_num].lock); + skb_queue_walk_safe(&priv->txq[desc_num], skb, tmp) { + tx_info = IEEE80211_SKB_CB(skb); + tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; + if (tx_ctrl->sta == sta) { + amsdu_pkts = (struct sk_buff_head *) + tx_ctrl->amsdu_pkts; + if (amsdu_pkts) { + skb_queue_purge(amsdu_pkts); + kfree(amsdu_pkts); + } + __skb_unlink(skb, &priv->txq[desc_num]); + dev_kfree_skb_any(skb); + } + } + spin_unlock_bh(&priv->txq[desc_num].lock); + + spin_lock_bh(&sta_info->amsdu_lock); + amsdu_frag = &sta_info->amsdu_ctrl.frag[desc_num]; + if (amsdu_frag->num) { + amsdu_frag->num = 0; + amsdu_frag->cur_pos = NULL; + if (amsdu_frag->skb) { + tx_info = IEEE80211_SKB_CB(amsdu_frag->skb); + tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status; + amsdu_pkts = (struct sk_buff_head *) + tx_ctrl->amsdu_pkts; + if (amsdu_pkts) { + skb_queue_purge(amsdu_pkts); + kfree(amsdu_pkts); + } + dev_kfree_skb_any(amsdu_frag->skb); + } + } + spin_unlock_bh(&sta_info->amsdu_lock); +} + void mwl_tx_done(unsigned long data) { struct ieee80211_hw *hw = (struct ieee80211_hw *)data; @@ -874,13 +1073,15 @@ void mwl_tx_done(unsigned long data) struct mwl_tx_hndl *tx_hndl; struct mwl_tx_desc *tx_desc; struct sk_buff *done_skb; - u32 rate, format, bandwidth, short_gi, rate_id; + u32 rate; struct mwl_dma_data *tr; struct ieee80211_tx_info *info; + struct mwl_tx_ctrl *tx_ctrl; + struct sk_buff_head *amsdu_pkts; int hdrlen; spin_lock_bh(&priv->tx_desc_lock); - for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) { + for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) { desc = &priv->desc_data[num]; tx_hndl = desc->pstale_tx_hndl; tx_desc = tx_hndl->pdesc; @@ -901,69 +1102,40 @@ void mwl_tx_done(unsigned long data) tx_hndl->psk_buff = NULL; wmb(); /* memory barrier */ + skb_get(done_skb); + skb_queue_tail(&priv->delay_q, done_skb); + if (skb_queue_len(&priv->delay_q) > + SYSADPT_DELAY_FREE_Q_LIMIT) + dev_kfree_skb_any(skb_dequeue(&priv->delay_q)); + tr = (struct mwl_dma_data *)done_skb->data; info = IEEE80211_SKB_CB(done_skb); - ieee80211_tx_info_clear_status(info); - - info->status.rates[0].idx = -1; - info->status.rates[0].count = 0; - info->status.rates[0].flags = 0; if (ieee80211_is_data(tr->wh.frame_control) || ieee80211_is_data_qos(tr->wh.frame_control)) { - skb_get(done_skb); - skb_queue_tail(&priv->delay_q, done_skb); - - if (skb_queue_len(&priv->delay_q) > - SYSADPT_DELAY_FREE_Q_LIMIT) - dev_kfree_skb_any( - skb_dequeue(&priv->delay_q)); - - /* Prepare rate information */ - format = rate & MWL_TX_RATE_FORMAT_MASK; - bandwidth = - (rate & MWL_TX_RATE_BANDWIDTH_MASK) >> - MWL_TX_RATE_BANDWIDTH_SHIFT; - short_gi = (rate & MWL_TX_RATE_SHORTGI_MASK) >> - MWL_TX_RATE_SHORTGI_SHIFT; - rate_id = (rate & MWL_TX_RATE_RATEIDMCS_MASK) >> - MWL_TX_RATE_RATEIDMCS_SHIFT; - - info->status.rates[0].idx = rate_id; - if (format == TX_RATE_FORMAT_LEGACY) { - if (hw->conf.chandef.chan->hw_value > - BAND_24_CHANNEL_NUM) { - info->status.rates[0].idx -= 5; - } - } - if (format == TX_RATE_FORMAT_11N) - info->status.rates[0].flags |= - IEEE80211_TX_RC_MCS; - if (format == TX_RATE_FORMAT_11AC) - info->status.rates[0].flags |= - IEEE80211_TX_RC_VHT_MCS; - if (bandwidth == TX_RATE_BANDWIDTH_40) - info->status.rates[0].flags |= - IEEE80211_TX_RC_40_MHZ_WIDTH; - if (bandwidth == TX_RATE_BANDWIDTH_80) - info->status.rates[0].flags |= - IEEE80211_TX_RC_80_MHZ_WIDTH; - if (short_gi == TX_RATE_INFO_SHORT_GI) - info->status.rates[0].flags |= - IEEE80211_TX_RC_SHORT_GI; - info->status.rates[0].count = 1; - - info->status.rates[1].idx = -1; + tx_ctrl = (struct mwl_tx_ctrl *)&info->status; + amsdu_pkts = (struct sk_buff_head *) + tx_ctrl->amsdu_pkts; + if (amsdu_pkts) { + mwl_tx_ack_amsdu_pkts(hw, rate, + amsdu_pkts); + dev_kfree_skb_any(done_skb); + done_skb = NULL; + } else + mwl_tx_prepare_info(hw, rate, info); + } else + mwl_tx_prepare_info(hw, 0, info); + + if (done_skb) { + /* Remove H/W dma header */ + hdrlen = ieee80211_hdrlen(tr->wh.frame_control); + memmove(tr->data - hdrlen, &tr->wh, hdrlen); + skb_pull(done_skb, sizeof(*tr) - hdrlen); + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status(hw, done_skb); } - /* Remove H/W dma header */ - hdrlen = ieee80211_hdrlen(tr->wh.frame_control); - memmove(tr->data - hdrlen, &tr->wh, hdrlen); - skb_pull(done_skb, sizeof(*tr) - hdrlen); - - info->flags |= IEEE80211_TX_STAT_ACK; - ieee80211_tx_status(hw, done_skb); - tx_hndl = tx_hndl->pnext; tx_desc = tx_hndl->pdesc; priv->fw_desc_cnt[num]--; @@ -996,33 +1168,65 @@ void mwl_tx_flush_amsdu(unsigned long data) int i; struct mwl_amsdu_frag *amsdu_frag; - spin_lock_bh(&priv->sta_lock); - + spin_lock(&priv->sta_lock); list_for_each_entry(sta_info, &priv->sta_list, list) { - spin_lock_bh(&sta_info->amsdu_lock); + spin_lock(&priv->tx_desc_lock); + spin_lock(&sta_info->amsdu_lock); for (i = 0; i < SYSADPT_TX_WMM_QUEUES; i++) { amsdu_frag = &sta_info->amsdu_ctrl.frag[i]; if (amsdu_frag->num) { - if (jiffies > (amsdu_frag->jiffies + 1)) { - spin_lock_bh(&priv->tx_desc_lock); + if (time_after(jiffies, + (amsdu_frag->jiffies + 1))) { if (mwl_tx_available(priv, i)) { - mwl_tx_skb(priv, i, amsdu_frag->skb); + mwl_tx_skb(priv, i, + amsdu_frag->skb); amsdu_frag->num = 0; amsdu_frag->cur_pos = NULL; } - spin_unlock_bh(&priv->tx_desc_lock); } } } - spin_unlock_bh(&sta_info->amsdu_lock); + spin_unlock(&sta_info->amsdu_lock); + spin_unlock(&priv->tx_desc_lock); } - - spin_unlock_bh(&priv->sta_lock); + spin_unlock(&priv->sta_lock); status_mask = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); - writel(status_mask | MACREG_A2HRIC_BIT_QUEUE_EMPTY, + writel(status_mask | MACREG_A2HRIC_BIT_QUE_EMPTY, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); priv->is_qe_schedule = false; } + +void mwl_tx_del_sta_amsdu_pkts(struct ieee80211_sta *sta) +{ + struct mwl_sta *sta_info = mwl_dev_get_sta(sta); + int num; + struct mwl_amsdu_frag *amsdu_frag; + struct ieee80211_tx_info *tx_info; + struct mwl_tx_ctrl *tx_ctrl; + struct sk_buff_head *amsdu_pkts; + + spin_lock_bh(&sta_info->amsdu_lock); + for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) { + amsdu_frag = &sta_info->amsdu_ctrl.frag[num]; + if (amsdu_frag->num) { + amsdu_frag->num = 0; + amsdu_frag->cur_pos = NULL; + if (amsdu_frag->skb) { + tx_info = IEEE80211_SKB_CB(amsdu_frag->skb); + tx_ctrl = (struct mwl_tx_ctrl *) + &tx_info->status; + amsdu_pkts = (struct sk_buff_head *) + tx_ctrl->amsdu_pkts; + if (amsdu_pkts) { + skb_queue_purge(amsdu_pkts); + kfree(amsdu_pkts); + } + dev_kfree_skb_any(amsdu_frag->skb); + } + } + } + spin_unlock_bh(&sta_info->amsdu_lock); +} diff --git a/tx.h b/tx.h index 0dafc80..ef1c7c5 100644 --- a/tx.h +++ b/tx.h @@ -23,7 +23,14 @@ void mwl_tx_deinit(struct ieee80211_hw *hw); void mwl_tx_xmit(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); +void mwl_tx_del_pkts_via_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void mwl_tx_del_pkts_via_sta(struct ieee80211_hw *hw, + struct ieee80211_sta *sta); +void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u8 tid); void mwl_tx_done(unsigned long data); void mwl_tx_flush_amsdu(unsigned long data); +void mwl_tx_del_sta_amsdu_pkts(struct ieee80211_sta *sta); #endif /* _tx_h_ */ -- cgit v1.2.3