summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/mwifiex
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/mwifiex')
-rw-r--r--drivers/net/wireless/mwifiex/11n.c4
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.c6
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c57
-rw-r--r--drivers/net/wireless/mwifiex/decl.h4
-rw-r--r--drivers/net/wireless/mwifiex/fw.h17
-rw-r--r--drivers/net/wireless/mwifiex/init.c3
-rw-r--r--drivers/net/wireless/mwifiex/main.c68
-rw-r--r--drivers/net/wireless/mwifiex/main.h13
-rw-r--r--drivers/net/wireless/mwifiex/scan.c2
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c5
-rw-r--r--drivers/net/wireless/mwifiex/sta_tx.c6
-rw-r--r--drivers/net/wireless/mwifiex/txrx.c31
-rw-r--r--drivers/net/wireless/mwifiex/uap_event.c5
-rw-r--r--drivers/net/wireless/mwifiex/uap_txrx.c11
-rw-r--r--drivers/net/wireless/mwifiex/util.c2
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c42
16 files changed, 236 insertions, 40 deletions
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index 62f5dbe602d3..9d4786e7ddff 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -544,6 +544,7 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
u32 tx_win_size = priv->add_ba_param.tx_win_size;
static u8 dialog_tok;
int ret;
+ unsigned long flags;
u16 block_ack_param_set;
dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
@@ -554,15 +555,18 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
struct mwifiex_sta_node *sta_ptr;
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
if (!sta_ptr) {
dev_warn(priv->adapter->dev,
"BA setup with unknown TDLS peer %pM!\n",
peer_mac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
return -1;
}
if (sta_ptr->is_11ac_enabled)
tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index 5ef5a0eeba50..d73fda312c87 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -351,6 +351,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
new_node->init_win = seq_num;
new_node->flags = 0;
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
if (mwifiex_queuing_ra_based(priv)) {
dev_dbg(priv->adapter->dev,
"info: AP/ADHOC:last_seq=%d start_win=%d\n",
@@ -367,6 +368,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
else
last_seq = priv->rx_seq[tid];
}
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
last_seq >= new_node->start_win) {
@@ -455,22 +457,26 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
u32 rx_win_size = priv->add_ba_param.rx_win_size;
u8 tid;
int win_size;
+ unsigned long flags;
uint16_t block_ack_param_set;
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
priv->adapter->is_hw_11ac_capable &&
memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
sta_ptr = mwifiex_get_sta_entry(priv,
cmd_addba_req->peer_mac_addr);
if (!sta_ptr) {
dev_warn(priv->adapter->dev,
"BA setup with unknown TDLS peer %pM!\n",
cmd_addba_req->peer_mac_addr);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
return -1;
}
if (sta_ptr->is_11ac_enabled)
rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 17f0ee02d6e7..4a66a6555366 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -194,10 +194,17 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
tx_info->pkt_len = pkt_len;
mwifiex_form_mgmt_frame(skb, buf, len);
- mwifiex_queue_tx_pkt(priv, skb);
-
*cookie = prandom_u32() | 1;
- cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_ATOMIC);
+
+ if (ieee80211_is_action(mgmt->frame_control))
+ skb = mwifiex_clone_skb_for_tx_status(priv,
+ skb,
+ MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie);
+ else
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+ GFP_ATOMIC);
+
+ mwifiex_queue_tx_pkt(priv, skb);
wiphy_dbg(wiphy, "info: management frame transmitted\n");
return 0;
@@ -1289,33 +1296,30 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct mwifiex_sta_node *sta_node;
+ u8 deauth_mac[ETH_ALEN];
unsigned long flags;
if (list_empty(&priv->sta_list) || !priv->bss_started)
return 0;
- if (!params->mac || is_broadcast_ether_addr(params->mac)) {
- wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
- list_for_each_entry(sta_node, &priv->sta_list, list) {
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
- HostCmd_ACT_GEN_SET, 0,
- sta_node->mac_addr, true))
- return -1;
- mwifiex_uap_del_sta_data(priv, sta_node);
- }
- } else {
- wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__,
- params->mac);
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- sta_node = mwifiex_get_sta_entry(priv, params->mac);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- if (sta_node) {
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
- HostCmd_ACT_GEN_SET, 0,
- sta_node->mac_addr, true))
- return -1;
- mwifiex_uap_del_sta_data(priv, sta_node);
- }
+ if (!params->mac || is_broadcast_ether_addr(params->mac))
+ return 0;
+
+ wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, params->mac);
+
+ memset(deauth_mac, 0, ETH_ALEN);
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_node = mwifiex_get_sta_entry(priv, params->mac);
+ if (sta_node)
+ ether_addr_copy(deauth_mac, params->mac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (is_valid_ether_addr(deauth_mac)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ deauth_mac, true))
+ return -1;
}
return 0;
@@ -2988,6 +2992,9 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
NL80211_FEATURE_INACTIVITY_TIMER |
NL80211_FEATURE_NEED_OBSS_SCAN;
+ if (adapter->fw_api_ver == MWIFIEX_FW_V15)
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+
/* Reserve space for mwifiex specific private data for BSS */
wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index fc0b1ed80a6a..2269acf41ad8 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -76,6 +76,8 @@
#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
+#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3)
+#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4)
#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
@@ -159,6 +161,8 @@ struct mwifiex_txinfo {
u8 bss_num;
u8 bss_type;
u32 pkt_len;
+ u8 ack_frame_id;
+ u64 cookie;
};
enum mwifiex_wmm_ac_e {
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index e095f371545a..fb5936eb82e3 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -494,6 +494,7 @@ enum P2P_MODES {
#define EVENT_TDLS_GENERIC_EVENT 0x00000052
#define EVENT_EXT_SCAN_REPORT 0x00000058
#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f
+#define EVENT_TX_STATUS_REPORT 0x00000074
#define EVENT_ID_MASK 0xffff
#define BSS_NUM_MASK 0xf
@@ -542,6 +543,7 @@ struct mwifiex_ie_types_data {
#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10
#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01
+#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20
struct txpd {
u8 bss_type;
@@ -553,7 +555,9 @@ struct txpd {
u8 priority;
u8 flags;
u8 pkt_delay_2ms;
- u8 reserved1;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
} __packed;
struct rxpd {
@@ -598,8 +602,9 @@ struct uap_txpd {
u8 priority;
u8 flags;
u8 pkt_delay_2ms;
- u8 reserved1;
- __le32 reserved2;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
};
struct uap_rxpd {
@@ -1224,6 +1229,12 @@ struct mwifiex_event_scan_result {
u8 num_of_set;
} __packed;
+struct tx_status_event {
+ u8 packet_type;
+ u8 tx_token_id;
+ u8 status;
+} __packed;
+
#define MWIFIEX_USER_SCAN_CHAN_MAX 50
#define MWIFIEX_MAX_SSID_LIST_LENGTH 10
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index cc15ab81aa66..520ad4a3018b 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -473,6 +473,9 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
spin_lock_init(&priv->tx_ba_stream_tbl_lock);
spin_lock_init(&priv->rx_reorder_tbl_lock);
+
+ spin_lock_init(&priv->ack_status_lock);
+ idr_init(&priv->ack_status_frames);
}
return 0;
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 0e50120eb807..d4d2223d1f31 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -149,7 +149,8 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
/* Check for Rx data */
while ((skb = skb_dequeue(&adapter->rx_data_q))) {
atomic_dec(&adapter->rx_pending);
- if (adapter->delay_main_work &&
+ if ((adapter->delay_main_work ||
+ adapter->iface_type == MWIFIEX_USB) &&
(atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
if (adapter->if_ops.submit_rem_rx_urbs)
adapter->if_ops.submit_rem_rx_urbs(adapter);
@@ -202,12 +203,15 @@ process_start:
(adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
break;
- /* If we process interrupts first, it would increase RX pending
- * even further. Avoid this by checking if rx_pending has
- * crossed high threshold and schedule rx work queue
- * and then process interrupts
+ /* For non-USB interfaces, If we process interrupts first, it
+ * would increase RX pending even further. Avoid this by
+ * checking if rx_pending has crossed high threshold and
+ * schedule rx work queue and then process interrupts.
+ * For USB interface, there are no interrupts. We already have
+ * HIGH_RX_PENDING check in usb.c
*/
- if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING) {
+ if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
+ adapter->iface_type != MWIFIEX_USB) {
adapter->delay_main_work = true;
if (!adapter->rx_processing)
queue_work(adapter->rx_workqueue,
@@ -604,6 +608,48 @@ int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
return 0;
}
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie)
+{
+ struct sk_buff *orig_skb = skb;
+ struct mwifiex_txinfo *tx_info, *orig_tx_info;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (skb) {
+ unsigned long flags;
+ int id;
+
+ spin_lock_irqsave(&priv->ack_status_lock, flags);
+ id = idr_alloc(&priv->ack_status_frames, orig_skb,
+ 1, 0xff, GFP_ATOMIC);
+ spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+ if (id >= 0) {
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tx_info->ack_frame_id = id;
+ tx_info->flags |= flag;
+ orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
+ orig_tx_info->ack_frame_id = id;
+ orig_tx_info->flags |= flag;
+
+ if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
+ orig_tx_info->cookie = *cookie;
+
+ } else if (skb_shared(skb)) {
+ kfree_skb(orig_skb);
+ } else {
+ kfree_skb(skb);
+ skb = orig_skb;
+ }
+ } else {
+ /* couldn't clone -- lose tx status ... */
+ skb = orig_skb;
+ }
+
+ return skb;
+}
+
/*
* CFG802.11 network device handler for data transmission.
*/
@@ -613,6 +659,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct sk_buff *new_skb;
struct mwifiex_txinfo *tx_info;
+ bool multicast;
dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
jiffies, priv->bss_type, priv->bss_num);
@@ -653,6 +700,15 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_info->bss_type = priv->bss_type;
tx_info->pkt_len = skb->len;
+ multicast = is_multicast_ether_addr(skb->data);
+
+ if (unlikely(!multicast && skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
+ priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
+ skb = mwifiex_clone_skb_for_tx_status(priv,
+ skb,
+ MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
+
/* Record the current time the packet was queued; used to
* determine the amount of time the packet was queued in
* the driver before it was sent to the firmware.
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 5a690d5210f0..e66993cb5daf 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -34,6 +34,7 @@
#include <linux/firmware.h>
#include <linux/ctype.h>
#include <linux/of.h>
+#include <linux/idr.h>
#include "decl.h"
#include "ioctl.h"
@@ -578,6 +579,9 @@ struct mwifiex_private {
u8 check_tdls_tx;
struct timer_list auto_tdls_timer;
bool auto_tdls_timer_active;
+ struct idr ack_status_frames;
+ /* spin lock for ack status */
+ spinlock_t ack_status_lock;
};
enum mwifiex_ba_status {
@@ -971,6 +975,8 @@ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
int mwifiex_process_sta_event(struct mwifiex_private *);
int mwifiex_process_uap_event(struct mwifiex_private *);
void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
+void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv,
+ const u8 *ra_addr);
void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta);
@@ -1335,6 +1341,13 @@ void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+ void *event_body);
+
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie);
+
#ifdef CONFIG_DEBUG_FS
void mwifiex_debugfs_init(void);
void mwifiex_debugfs_remove(void);
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 3a17821157d7..984a7a4fa93b 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1623,7 +1623,7 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
if (*bytes_left >= sizeof(beacon_size)) {
/* Extract & convert beacon size from command buffer */
- memcpy(&beacon_size, *bss_info, sizeof(beacon_size));
+ beacon_size = le16_to_cpu(*(__le16 *)(*bss_info));
*bytes_left -= sizeof(beacon_size);
*bss_info += sizeof(beacon_size);
}
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index 204ecc8faa5b..b8c171df6223 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -504,6 +504,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
break;
+ case EVENT_TX_STATUS_REPORT:
+ dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+ mwifiex_parse_tx_status_event(priv, adapter->event_body);
+ break;
+
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index dab7b33c54be..b896d7375b52 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -77,6 +77,12 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
local_tx_pd->pkt_delay_2ms =
mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+ tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+ local_tx_pd->tx_token_id = tx_info->ack_frame_id;
+ local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+ }
+
if (local_tx_pd->priority <
ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
/*
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index a5983fc4e83a..6ae133333363 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -203,3 +203,34 @@ done:
}
EXPORT_SYMBOL_GPL(mwifiex_write_data_complete);
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+ void *event_body)
+{
+ struct tx_status_event *tx_status = (void *)priv->adapter->event_body;
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+ struct mwifiex_txinfo *tx_info;
+
+ if (!tx_status->tx_token_id)
+ return;
+
+ spin_lock_irqsave(&priv->ack_status_lock, flags);
+ ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id);
+ if (ack_skb)
+ idr_remove(&priv->ack_status_frames, tx_status->tx_token_id);
+ spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+ if (ack_skb) {
+ tx_info = MWIFIEX_SKB_TXCB(ack_skb);
+
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) {
+ /* consumes ack_skb */
+ skb_complete_wifi_ack(ack_skb, !tx_status->status);
+ } else {
+ cfg80211_mgmt_tx_status(priv->wdev, tx_info->cookie,
+ ack_skb->data, ack_skb->len,
+ !tx_status->status, GFP_ATOMIC);
+ dev_kfree_skb_any(ack_skb);
+ }
+ }
+}
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 7c2b97660a03..c54a537e31fb 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -110,6 +110,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
}
+ mwifiex_wmm_del_peer_ra_list(priv, deauth_mac);
mwifiex_del_sta_entry(priv, deauth_mac);
break;
case EVENT_UAP_BSS_IDLE:
@@ -172,6 +173,10 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
return mwifiex_handle_event_ext_scan_report(priv,
adapter->event_skb->data);
break;
+ case EVENT_TX_STATUS_REPORT:
+ dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+ mwifiex_parse_tx_status_event(priv, adapter->event_body);
+ break;
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
index ec7309d096ab..be3a203a529b 100644
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ b/drivers/net/wireless/mwifiex/uap_txrx.c
@@ -266,6 +266,7 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
struct rx_packet_hdr *rx_pkt_hdr;
u16 rx_pkt_type;
u8 ta[ETH_ALEN], pkt_type;
+ unsigned long flags;
struct mwifiex_sta_node *node;
uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -294,10 +295,12 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
node = mwifiex_get_sta_entry(priv, ta);
if (node)
node->rx_seq[uap_rx_pd->priority] =
le16_to_cpu(uap_rx_pd->seq_num);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
if (!priv->ap_11n_enabled ||
@@ -370,10 +373,16 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv,
txpd->bss_num = priv->bss_num;
txpd->bss_type = priv->bss_type;
txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len));
-
txpd->priority = (u8)skb->priority;
+
txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+ tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+ txpd->tx_token_id = tx_info->ack_frame_id;
+ txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+ }
+
if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
/*
* Set the priority specific tx_control field, setting of 0 will
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index a113ef8f0b8b..b1768fbf98f2 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -149,7 +149,7 @@ mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len,
u8 category, action_code;
struct ieee80211_hdr *ieee_hdr = (void *)payload;
- stype = (cpu_to_le16(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
+ stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
switch (stype) {
case IEEE80211_STYPE_ACTION:
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 94c98a86ebbe..ffffd2c5a76e 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -147,9 +147,6 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
struct mwifiex_sta_node *node;
unsigned long flags;
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- node = mwifiex_get_sta_entry(priv, ra);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
for (i = 0; i < MAX_NUM_TID; ++i) {
ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra);
@@ -170,10 +167,13 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
}
} else {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ node = mwifiex_get_sta_entry(priv, ra);
ra_list->is_11n_enabled =
mwifiex_is_sta_11n_enabled(priv, node);
if (ra_list->is_11n_enabled)
ra_list->max_amsdu = node->max_amsdu;
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
dev_dbg(adapter->dev, "data: ralist %p: is_11n_enabled=%d\n",
@@ -523,6 +523,13 @@ static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv)
}
}
+static int mwifiex_free_ack_frame(int id, void *p, void *data)
+{
+ pr_warn("Have pending ack frames!\n");
+ kfree_skb(p);
+ return 0;
+}
+
/*
* This function cleans up the Tx and Rx queues.
*
@@ -558,6 +565,9 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
skb_queue_walk_safe(&priv->tdls_txq, skb, tmp)
mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+
+ idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL);
+ idr_destroy(&priv->ack_status_frames);
}
/*
@@ -601,6 +611,32 @@ mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
}
/*
+ * This function deletes RA list nodes for given mac for all TIDs.
+ * Function also decrements TX pending count accordingly.
+ */
+void
+mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr);
+
+ if (!ra_list)
+ continue;
+ mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list);
+ atomic_sub(ra_list->total_pkt_count, &priv->wmm.tx_pkts_queued);
+ list_del(&ra_list->list);
+ kfree(ra_list);
+ }
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
+/*
* This function checks if a particular RA list node exists in a given TID
* table index.
*/