summaryrefslogtreecommitdiffstats
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.c26
-rw-r--r--drivers/net/wireless/mwifiex/11n.h15
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.c14
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.c66
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.h5
-rw-r--r--drivers/net/wireless/mwifiex/Makefile2
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c17
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c5
-rw-r--r--drivers/net/wireless/mwifiex/decl.h3
-rw-r--r--drivers/net/wireless/mwifiex/fw.h42
-rw-r--r--drivers/net/wireless/mwifiex/init.c107
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h5
-rw-r--r--drivers/net/wireless/mwifiex/main.c3
-rw-r--r--drivers/net/wireless/mwifiex/main.h38
-rw-r--r--drivers/net/wireless/mwifiex/scan.c6
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c55
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c64
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c12
-rw-r--r--drivers/net/wireless/mwifiex/sta_rx.c38
-rw-r--r--drivers/net/wireless/mwifiex/txrx.c5
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c1
-rw-r--r--drivers/net/wireless/mwifiex/uap_event.c290
-rw-r--r--drivers/net/wireless/mwifiex/uap_txrx.c255
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c59
24 files changed, 960 insertions, 173 deletions
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index e535c93..d273273 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -726,3 +726,29 @@ int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
return count;
}
+
+/*
+ * This function retrieves the entry for specific tx BA stream table by RA and
+ * deletes it.
+ */
+void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra)
+{
+ struct mwifiex_tx_ba_stream_tbl *tbl, *tmp;
+ unsigned long flags;
+
+ if (!ra)
+ return;
+
+ spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+ list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) {
+ if (!memcmp(tbl->ra, ra, ETH_ALEN)) {
+ spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock,
+ flags);
+ mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);
+ spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+
+ return;
+}
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
index 28366e9..67c087c 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/mwifiex/11n.h
@@ -69,6 +69,7 @@ int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
int cmd_action,
struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
+void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra);
/*
* This function checks whether AMPDU is allowed or not for a particular TID.
@@ -157,4 +158,18 @@ mwifiex_is_ba_stream_setup(struct mwifiex_private *priv,
return false;
}
+
+/*
+ * This function checks whether associated station is 11n enabled
+ */
+static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv,
+ struct mwifiex_sta_node *node)
+{
+
+ if (!node || (priv->bss_role != MWIFIEX_BSS_ROLE_UAP) ||
+ !priv->ap_11n_enabled)
+ return 0;
+
+ return node->is_11n_enabled;
+}
#endif /* !_MWIFIEX_11N_H_ */
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
index ab84eb9..395f1bf 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.c
+++ b/drivers/net/wireless/mwifiex/11n_aggr.c
@@ -62,9 +62,7 @@ mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
};
struct tx_packet_hdr *tx_header;
- skb_put(skb_aggr, sizeof(*tx_header));
-
- tx_header = (struct tx_packet_hdr *) skb_aggr->data;
+ tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header));
/* Copy DA and SA */
dt_offset = 2 * ETH_ALEN;
@@ -82,12 +80,10 @@ mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
/* Add payload */
- skb_put(skb_aggr, skb_src->len);
- memcpy(skb_aggr->data + sizeof(*tx_header), skb_src->data,
- skb_src->len);
- *pad = (((skb_src->len + LLC_SNAP_LEN) & 3)) ? (4 - (((skb_src->len +
- LLC_SNAP_LEN)) & 3)) : 0;
- skb_put(skb_aggr, *pad);
+ memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len);
+
+ /* Add padding for new MSDU to start from 4 byte boundary */
+ *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4;
return skb_aggr->len + *pad;
}
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index 591ccd3..24e2582 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -54,8 +54,13 @@ mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv,
tbl->rx_reorder_ptr[i] = NULL;
}
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- if (rx_tmp_ptr)
- mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr);
+ if (rx_tmp_ptr) {
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr);
+ else
+ mwifiex_process_rx_packet(priv->adapter,
+ rx_tmp_ptr);
+ }
}
spin_lock_irqsave(&priv->rx_pkt_lock, flags);
@@ -97,7 +102,11 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
rx_tmp_ptr = tbl->rx_reorder_ptr[i];
tbl->rx_reorder_ptr[i] = NULL;
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
- mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr);
+
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr);
+ else
+ mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr);
}
spin_lock_irqsave(&priv->rx_pkt_lock, flags);
@@ -148,7 +157,7 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
* This function returns the pointer to an entry in Rx reordering
* table which matches the given TA/TID pair.
*/
-static struct mwifiex_rx_reorder_tbl *
+struct mwifiex_rx_reorder_tbl *
mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
{
struct mwifiex_rx_reorder_tbl *tbl;
@@ -167,6 +176,31 @@ mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
return NULL;
}
+/* This function retrieves the pointer to an entry in Rx reordering
+ * table which matches the given TA and deletes it.
+ */
+void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
+{
+ struct mwifiex_rx_reorder_tbl *tbl, *tmp;
+ unsigned long flags;
+
+ if (!ta)
+ return;
+
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) {
+ if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+ flags);
+ mwifiex_del_rx_reorder_entry(priv, tbl);
+ spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+ return;
+}
+
/*
* This function finds the last sequence number used in the packets
* buffered in Rx reordering table.
@@ -226,6 +260,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
struct mwifiex_rx_reorder_tbl *tbl, *new_node;
u16 last_seq = 0;
unsigned long flags;
+ struct mwifiex_sta_node *node;
/*
* If we get a TID, ta pair which is already present dispatch all the
@@ -248,13 +283,19 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
new_node->tid = tid;
memcpy(new_node->ta, ta, ETH_ALEN);
new_node->start_win = seq_num;
- if (mwifiex_queuing_ra_based(priv))
- /* TODO for adhoc */
+
+ if (mwifiex_queuing_ra_based(priv)) {
dev_dbg(priv->adapter->dev,
- "info: ADHOC:last_seq=%d start_win=%d\n",
+ "info: AP/ADHOC:last_seq=%d start_win=%d\n",
last_seq, new_node->start_win);
- else
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) {
+ node = mwifiex_get_sta_entry(priv, ta);
+ if (node)
+ last_seq = node->rx_seq[tid];
+ }
+ } else {
last_seq = priv->rx_seq[tid];
+ }
if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
last_seq >= new_node->start_win)
@@ -396,8 +437,13 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
if (!tbl) {
- if (pkt_type != PKT_TYPE_BAR)
- mwifiex_process_rx_packet(priv->adapter, payload);
+ if (pkt_type != PKT_TYPE_BAR) {
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ mwifiex_handle_uap_rx_forward(priv, payload);
+ else
+ mwifiex_process_rx_packet(priv->adapter,
+ payload);
+ }
return 0;
}
start_win = tbl->start_win;
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h
index 6c9815a..7284859 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h
@@ -38,6 +38,8 @@
#define ADDBA_RSP_STATUS_ACCEPT 0
#define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff
+#define BA_SETUP_MAX_PACKET_THRESHOLD 16
+#define BA_SETUP_PACKET_OFFSET 16
static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv)
{
@@ -68,5 +70,8 @@ struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct
mwifiex_private
*priv, int tid,
u8 *ta);
+struct mwifiex_rx_reorder_tbl *
+mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta);
+void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta);
#endif /* _MWIFIEX_11N_RXREORDER_H_ */
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
index 3f66ebb..dd0410d 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/mwifiex/Makefile
@@ -33,8 +33,10 @@ mwifiex-y += uap_cmd.o
mwifiex-y += ie.o
mwifiex-y += sta_cmdresp.o
mwifiex-y += sta_event.o
+mwifiex-y += uap_event.o
mwifiex-y += sta_tx.o
mwifiex-y += sta_rx.o
+mwifiex-y += uap_txrx.o
mwifiex-y += cfg80211.o
mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MWIFIEX) += mwifiex.o
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index fe42137..e57f543 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -99,7 +99,7 @@ mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
- if (mwifiex_set_encode(priv, NULL, 0, key_index, peer_mac, 1)) {
+ if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) {
wiphy_err(wiphy, "deleting the crypto keys\n");
return -EFAULT;
}
@@ -171,7 +171,8 @@ mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) {
priv->wep_key_curr_index = key_index;
- } else if (mwifiex_set_encode(priv, NULL, 0, key_index, NULL, 0)) {
+ } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index,
+ NULL, 0)) {
wiphy_err(wiphy, "set default Tx key index\n");
return -EFAULT;
}
@@ -207,7 +208,7 @@ mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
return 0;
}
- if (mwifiex_set_encode(priv, params->key, params->key_len,
+ if (mwifiex_set_encode(priv, params, params->key, params->key_len,
key_index, peer_mac, 0)) {
wiphy_err(wiphy, "crypto keys added\n");
return -EFAULT;
@@ -748,6 +749,7 @@ static const u32 mwifiex_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC,
};
/*
@@ -906,6 +908,8 @@ static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
if (mwifiex_del_mgmt_ies(priv))
wiphy_err(wiphy, "Failed to delete mgmt IEs!\n");
+ priv->ap_11n_enabled = 0;
+
if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP,
HostCmd_ACT_GEN_SET, 0, NULL)) {
wiphy_err(wiphy, "Failed to stop the BSS\n");
@@ -1159,7 +1163,7 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
priv->wep_key_curr_index = 0;
priv->sec_info.encryption_mode = 0;
priv->sec_info.is_authtype_auto = 0;
- ret = mwifiex_set_encode(priv, NULL, 0, 0, NULL, 1);
+ ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1);
if (mode == NL80211_IFTYPE_ADHOC) {
/* "privacy" is set only for ad-hoc mode */
@@ -1206,8 +1210,9 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
"info: setting wep encryption"
" with key len %d\n", sme->key_len);
priv->wep_key_curr_index = sme->key_idx;
- ret = mwifiex_set_encode(priv, sme->key, sme->key_len,
- sme->key_idx, NULL, 0);
+ ret = mwifiex_set_encode(priv, NULL, sme->key,
+ sme->key_len, sme->key_idx,
+ NULL, 0);
}
}
done:
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index c68adec..c229ddd 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -447,7 +447,10 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
}
- ret = mwifiex_process_sta_event(priv);
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ ret = mwifiex_process_uap_event(priv);
+ else
+ ret = mwifiex_process_sta_event(priv);
adapter->event_cause = 0;
adapter->event_skb = NULL;
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 070ef25..400d360 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -60,6 +60,9 @@
#define MWIFIEX_SDIO_BLOCK_SIZE 256
#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
+#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
+
+#define MWIFIEX_BRIDGED_PKTS_THRESHOLD 1024
enum mwifiex_bss_type {
MWIFIEX_BSS_TYPE_STA = 0,
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index e831b44..ae06f31 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -65,10 +65,12 @@ enum KEY_TYPE_ID {
KEY_TYPE_ID_TKIP,
KEY_TYPE_ID_AES,
KEY_TYPE_ID_WAPI,
+ KEY_TYPE_ID_AES_CMAC,
};
#define KEY_MCAST BIT(0)
#define KEY_UNICAST BIT(1)
#define KEY_ENABLED BIT(2)
+#define KEY_IGTK BIT(10)
#define WAPI_KEY_LEN 50
@@ -424,10 +426,10 @@ struct txpd {
struct rxpd {
u8 bss_type;
u8 bss_num;
- u16 rx_pkt_length;
- u16 rx_pkt_offset;
- u16 rx_pkt_type;
- u16 seq_num;
+ __le16 rx_pkt_length;
+ __le16 rx_pkt_offset;
+ __le16 rx_pkt_type;
+ __le16 seq_num;
u8 priority;
u8 rx_rate;
s8 snr;
@@ -439,6 +441,31 @@ struct rxpd {
u8 reserved;
} __packed;
+struct uap_txpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 tx_pkt_length;
+ __le16 tx_pkt_offset;
+ __le16 tx_pkt_type;
+ __le32 tx_control;
+ u8 priority;
+ u8 flags;
+ u8 pkt_delay_2ms;
+ u8 reserved1;
+ __le32 reserved2;
+};
+
+struct uap_rxpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 rx_pkt_length;
+ __le16 rx_pkt_offset;
+ __le16 rx_pkt_type;
+ __le16 seq_num;
+ u8 priority;
+ u8 reserved1;
+};
+
enum mwifiex_chan_scan_mode_bitmasks {
MWIFIEX_PASSIVE_SCAN = BIT(0),
MWIFIEX_DISABLE_CHAN_FILT = BIT(1),
@@ -558,6 +585,13 @@ struct mwifiex_ie_type_key_param_set {
u8 key[50];
} __packed;
+#define IGTK_PN_LEN 8
+
+struct mwifiex_cmac_param {
+ u8 ipn[IGTK_PN_LEN];
+ u8 key[WLAN_KEY_LEN_AES_CMAC];
+} __packed;
+
struct host_cmd_ds_802_11_key_material {
__le16 action;
struct mwifiex_ie_type_key_param_set key_param_set;
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 21fdc6c..fad2c8d2b 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -64,60 +64,72 @@ static void scan_delay_timer_fn(unsigned long data)
struct cmd_ctrl_node *cmd_node, *tmp_node;
unsigned long flags;
- if (!mwifiex_wmm_lists_empty(adapter)) {
- if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
+ if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
+ /*
+ * Abort scan operation by cancelling all pending scan
+ * commands
+ */
+ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+ list_for_each_entry_safe(cmd_node, tmp_node,
+ &adapter->scan_pending_q, list) {
+ list_del(&cmd_node->list);
+ cmd_node->wait_q_enabled = false;
+ mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ }
+ spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+ spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+ adapter->scan_processing = false;
+ adapter->scan_delay_cnt = 0;
+ adapter->empty_tx_q_cnt = 0;
+ spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+ if (priv->user_scan_cfg) {
+ dev_dbg(priv->adapter->dev,
+ "info: %s: scan aborted\n", __func__);
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ kfree(priv->user_scan_cfg);
+ priv->user_scan_cfg = NULL;
+ }
+ goto done;
+ }
+
+ if (!atomic_read(&priv->adapter->is_tx_received)) {
+ adapter->empty_tx_q_cnt++;
+ if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) {
/*
- * Abort scan operation by cancelling all pending scan
- * command
+ * No Tx traffic for 200msec. Get scan command from
+ * scan pending queue and put to cmd pending queue to
+ * resume scan operation
*/
+ adapter->scan_delay_cnt = 0;
+ adapter->empty_tx_q_cnt = 0;
spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
- list_for_each_entry_safe(cmd_node, tmp_node,
- &adapter->scan_pending_q,
- list) {
- list_del(&cmd_node->list);
- cmd_node->wait_q_enabled = false;
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
- }
+ cmd_node = list_first_entry(&adapter->scan_pending_q,
+ struct cmd_ctrl_node, list);
+ list_del(&cmd_node->list);
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
flags);
- spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
- adapter->scan_processing = false;
- spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock,
- flags);
-
- if (priv->user_scan_cfg) {
- dev_dbg(priv->adapter->dev,
- "info: %s: scan aborted\n", __func__);
- cfg80211_scan_done(priv->scan_request, 1);
- priv->scan_request = NULL;
- kfree(priv->user_scan_cfg);
- priv->user_scan_cfg = NULL;
- }
- } else {
- /*
- * Tx data queue is still not empty, delay scan
- * operation further by 20msec.
- */
- mod_timer(&priv->scan_delay_timer, jiffies +
- msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
- adapter->scan_delay_cnt++;
+ mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
+ true);
+ goto done;
}
- queue_work(priv->adapter->workqueue, &priv->adapter->main_work);
} else {
- /*
- * Tx data queue is empty. Get scan command from scan_pending_q
- * and put to cmd_pending_q to resume scan operation
- */
- adapter->scan_delay_cnt = 0;
- spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
- cmd_node = list_first_entry(&adapter->scan_pending_q,
- struct cmd_ctrl_node, list);
- list_del(&cmd_node->list);
- spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
-
- mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
+ adapter->empty_tx_q_cnt = 0;
}
+
+ /* Delay scan operation further by 20msec */
+ mod_timer(&priv->scan_delay_timer, jiffies +
+ msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+ adapter->scan_delay_cnt++;
+
+done:
+ if (atomic_read(&priv->adapter->is_tx_received))
+ atomic_set(&priv->adapter->is_tx_received, false);
+
+ return;
}
/*
@@ -196,6 +208,7 @@ static int mwifiex_init_priv(struct mwifiex_private *priv)
priv->curr_bcn_size = 0;
priv->wps_ie = NULL;
priv->wps_ie_len = 0;
+ priv->ap_11n_enabled = 0;
priv->scan_block = false;
@@ -345,6 +358,7 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
adapter->arp_filter_size = 0;
adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
+ adapter->empty_tx_q_cnt = 0;
}
/*
@@ -410,6 +424,7 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
list_del(&priv->tx_ba_stream_tbl_ptr);
list_del(&priv->rx_reorder_tbl_ptr);
+ list_del(&priv->sta_list);
}
}
}
@@ -472,6 +487,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
spin_lock_init(&priv->rx_pkt_lock);
spin_lock_init(&priv->wmm.ra_list_spinlock);
spin_lock_init(&priv->curr_bcn_buf_lock);
+ spin_lock_init(&priv->sta_list_spinlock);
}
}
@@ -504,6 +520,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
}
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
+ INIT_LIST_HEAD(&priv->sta_list);
spin_lock_init(&priv->tx_ba_stream_tbl_lock);
spin_lock_init(&priv->rx_reorder_tbl_lock);
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 5019153..6a5eded 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -213,7 +213,7 @@ struct mwifiex_debug_info {
};
#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000
-#define WAPI_RXPN_LEN 16
+#define PN_LEN 16
struct mwifiex_ds_encrypt_key {
u32 key_disable;
@@ -222,7 +222,8 @@ struct mwifiex_ds_encrypt_key {
u8 key_material[WLAN_MAX_KEY_LEN];
u8 mac_addr[ETH_ALEN];
u32 is_wapi_key;
- u8 wapi_rxpn[WAPI_RXPN_LEN];
+ u8 pn[PN_LEN]; /* packet number */
+ u8 is_igtk_key;
};
struct mwifiex_power_cfg {
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 4680362..cb11552 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -520,6 +520,9 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
mwifiex_wmm_add_buf_txqueue(priv, skb);
atomic_inc(&priv->adapter->tx_pending);
+ if (priv->adapter->scan_delay_cnt)
+ atomic_set(&priv->adapter->is_tx_received, true);
+
if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) {
mwifiex_set_trans_start(dev);
mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index e7c2a82..994bc4f 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -88,6 +88,7 @@ enum {
#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S)
#define MWIFIEX_MAX_SCAN_DELAY_CNT 50
+#define MWIFIEX_MAX_EMPTY_TX_Q_CNT 10
#define MWIFIEX_SCAN_DELAY_MSEC 20
#define RSN_GTK_OUI_OFFSET 2
@@ -199,6 +200,9 @@ struct mwifiex_ra_list_tbl {
u8 ra[ETH_ALEN];
u32 total_pkts_size;
u32 is_11n_enabled;
+ u16 max_amsdu;
+ u16 pkt_count;
+ u8 ba_packet_thr;
};
struct mwifiex_tid_tbl {
@@ -431,6 +435,9 @@ struct mwifiex_private {
u8 wmm_enabled;
u8 wmm_qosinfo;
struct mwifiex_wmm_desc wmm;
+ struct list_head sta_list;
+ /* spin lock for associated station list */
+ spinlock_t sta_list_spinlock;
struct list_head tx_ba_stream_tbl_ptr;
/* spin lock for tx_ba_stream_tbl_ptr queue */
spinlock_t tx_ba_stream_tbl_lock;
@@ -486,6 +493,7 @@ struct mwifiex_private {
u16 assocresp_idx;
u16 rsn_idx;
struct timer_list scan_delay_timer;
+ u8 ap_11n_enabled;
};
enum mwifiex_ba_status {
@@ -550,6 +558,19 @@ struct mwifiex_bss_priv {
u64 fw_tsf;
};
+/* This is AP specific structure which stores information
+ * about associated STA
+ */
+struct mwifiex_sta_node {
+ struct list_head list;
+ u8 mac_addr[ETH_ALEN];
+ u8 is_wmm_enabled;
+ u8 is_11n_enabled;
+ u8 ampdu_sta[MAX_NUM_TID];
+ u16 rx_seq[MAX_NUM_TID];
+ u16 max_amsdu;
+};
+
struct mwifiex_if_ops {
int (*init_if) (struct mwifiex_adapter *);
void (*cleanup_if) (struct mwifiex_adapter *);
@@ -690,6 +711,9 @@ struct mwifiex_adapter {
u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
u16 max_mgmt_ie_index;
u8 scan_delay_cnt;
+ u8 empty_tx_q_cnt;
+ atomic_t is_tx_received;
+ atomic_t pending_bridged_pkts;
};
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -780,7 +804,15 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no,
struct host_cmd_ds_command *resp);
int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *,
struct sk_buff *skb);
+int mwifiex_process_uap_rx_packet(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb);
+int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
+ struct sk_buff *skb);
int mwifiex_process_sta_event(struct mwifiex_private *);
+int mwifiex_process_uap_event(struct mwifiex_private *);
+struct mwifiex_sta_node *
+mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
+void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta);
int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
@@ -949,9 +981,9 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,
const struct mwifiex_user_scan_cfg *user_scan_in);
int mwifiex_set_radio(struct mwifiex_private *priv, u8 option);
-int mwifiex_set_encode(struct mwifiex_private *priv, const u8 *key,
- int key_len, u8 key_index, const u8 *mac_addr,
- int disable);
+int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
+ const u8 *key, int key_len, u8 key_index,
+ const u8 *mac_addr, int disable);
int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len);
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 04dc7ca..215d07e6 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -989,6 +989,8 @@ mwifiex_config_scan(struct mwifiex_private *priv,
*max_chan_per_scan = 2;
else if (chan_num < MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD)
*max_chan_per_scan = 3;
+ else
+ *max_chan_per_scan = 4;
}
}
@@ -1433,9 +1435,9 @@ int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
if (ret)
dev_err(priv->adapter->dev, "cannot find ssid "
"%s\n", bss_desc->ssid.ssid);
- break;
+ break;
default:
- ret = 0;
+ ret = 0;
}
}
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index df3a33c..0cc3406 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -610,7 +610,7 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
memcpy(&key_material->key_param_set.key[2],
enc_key->key_material, enc_key->key_len);
memcpy(&key_material->key_param_set.key[2 + enc_key->key_len],
- enc_key->wapi_rxpn, WAPI_RXPN_LEN);
+ enc_key->pn, PN_LEN);
key_material->key_param_set.length =
cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN);
@@ -621,23 +621,38 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
return ret;
}
if (enc_key->key_len == WLAN_KEY_LEN_CCMP) {
- dev_dbg(priv->adapter->dev, "cmd: WPA_AES\n");
- key_material->key_param_set.key_type_id =
+ if (enc_key->is_igtk_key) {
+ dev_dbg(priv->adapter->dev, "cmd: CMAC_AES\n");
+ key_material->key_param_set.key_type_id =
+ cpu_to_le16(KEY_TYPE_ID_AES_CMAC);
+ if (cmd_oid == KEY_INFO_ENABLED)
+ key_material->key_param_set.key_info =
+ cpu_to_le16(KEY_ENABLED);
+ else
+ key_material->key_param_set.key_info =
+ cpu_to_le16(!KEY_ENABLED);
+
+ key_material->key_param_set.key_info |=
+ cpu_to_le16(KEY_IGTK);
+ } else {
+ dev_dbg(priv->adapter->dev, "cmd: WPA_AES\n");
+ key_material->key_param_set.key_type_id =
cpu_to_le16(KEY_TYPE_ID_AES);
- if (cmd_oid == KEY_INFO_ENABLED)
- key_material->key_param_set.key_info =
+ if (cmd_oid == KEY_INFO_ENABLED)
+ key_material->key_param_set.key_info =
cpu_to_le16(KEY_ENABLED);
- else
- key_material->key_param_set.key_info =
+ else
+ key_material->key_param_set.key_info =
cpu_to_le16(!KEY_ENABLED);
- if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST)
+ if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST)
/* AES pairwise key: unicast */
- key_material->key_param_set.key_info |=
+ key_material->key_param_set.key_info |=
cpu_to_le16(KEY_UNICAST);
- else /* AES group key: multicast */
- key_material->key_param_set.key_info |=
+ else /* AES group key: multicast */
+ key_material->key_param_set.key_info |=
cpu_to_le16(KEY_MCAST);
+ }
} else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) {
dev_dbg(priv->adapter->dev, "cmd: WPA_TKIP\n");
key_material->key_param_set.key_type_id =
@@ -668,6 +683,24 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
key_param_len = (u16)(enc_key->key_len + KEYPARAMSET_FIXED_LEN)
+ sizeof(struct mwifiex_ie_types_header);
+ if (le16_to_cpu(key_material->key_param_set.key_type_id) ==
+ KEY_TYPE_ID_AES_CMAC) {
+ struct mwifiex_cmac_param *param =
+ (void *)key_material->key_param_set.key;
+
+ memcpy(param->ipn, enc_key->pn, IGTK_PN_LEN);
+ memcpy(param->key, enc_key->key_material,
+ WLAN_KEY_LEN_AES_CMAC);
+
+ key_param_len = sizeof(struct mwifiex_cmac_param);
+ key_material->key_param_set.key_len =
+ cpu_to_le16(key_param_len);
+ key_param_len += KEYPARAMSET_FIXED_LEN;
+ key_material->key_param_set.length =
+ cpu_to_le16(key_param_len);
+ key_param_len += sizeof(struct mwifiex_ie_types_header);
+ }
+
cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN
+ key_param_len);
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index b8614a8..dff51d5 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -184,10 +184,9 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv)
int mwifiex_process_sta_event(struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
- int len, ret = 0;
+ int ret = 0;
u32 eventcause = adapter->event_cause;
- struct station_info sinfo;
- struct mwifiex_assoc_event *event;
+ u16 ctrl;
switch (eventcause) {
case EVENT_DUMMY_HOST_WAKEUP_SIGNAL:
@@ -279,10 +278,16 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_MIC_ERR_UNICAST:
dev_dbg(adapter->dev, "event: UNICAST MIC ERROR\n");
+ cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
+ NL80211_KEYTYPE_PAIRWISE,
+ -1, NULL, GFP_KERNEL);
break;
case EVENT_MIC_ERR_MULTICAST:
dev_dbg(adapter->dev, "event: MULTICAST MIC ERROR\n");
+ cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
+ NL80211_KEYTYPE_GROUP,
+ -1, NULL, GFP_KERNEL);
break;
case EVENT_MIB_CHANGED:
case EVENT_INIT_DONE:
@@ -384,11 +389,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
adapter->event_body);
break;
case EVENT_AMSDU_AGGR_CTRL:
- dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n",
- *(u16 *) adapter->event_body);
+ ctrl = le16_to_cpu(*(__le16 *)adapter->event_body);
+ dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", ctrl);
+
adapter->tx_buf_size =
- min(adapter->curr_tx_buf_size,
- le16_to_cpu(*(__le16 *) adapter->event_body));
+ min_t(u16, adapter->curr_tx_buf_size, ctrl);
dev_dbg(adapter->dev, "event: tx_buf_size %d\n",
adapter->tx_buf_size);
break;
@@ -405,51 +410,6 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
dev_dbg(adapter->dev, "event: HOSTWAKE_STAIE %d\n", eventcause);
break;
- case EVENT_UAP_STA_ASSOC:
- memset(&sinfo, 0, sizeof(sinfo));
- event = (struct mwifiex_assoc_event *)
- (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER);
- if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) {
- len = -1;
-
- if (ieee80211_is_assoc_req(event->frame_control))
- len = 0;
- else if (ieee80211_is_reassoc_req(event->frame_control))
- /* There will be ETH_ALEN bytes of
- * current_ap_addr before the re-assoc ies.
- */
- len = ETH_ALEN;
-
- if (len != -1) {
- sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
- sinfo.assoc_req_ies = &event->data[len];
- len = (u8 *)sinfo.assoc_req_ies -
- (u8 *)&event->frame_control;
- sinfo.assoc_req_ies_len =
- le16_to_cpu(event->len) - (u16)len;
- }
- }
- cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo,
- GFP_KERNEL);
- break;
- case EVENT_UAP_STA_DEAUTH:
- cfg80211_del_sta(priv->netdev, adapter->event_body +
- MWIFIEX_UAP_EVENT_EXTRA_HEADER, GFP_KERNEL);
- break;
- case EVENT_UAP_BSS_IDLE:
- priv->media_connected = false;
- break;
- case EVENT_UAP_BSS_ACTIVE:
- priv->media_connected = true;
- break;
- case EVENT_UAP_BSS_START:
- dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
- memcpy(priv->netdev->dev_addr, adapter->event_body+2, ETH_ALEN);
- break;
- case EVENT_UAP_MIC_COUNTERMEASURES:
- /* For future development */
- dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
- break;
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index fb21360..3f02597 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -942,20 +942,26 @@ mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
* This function allocates the IOCTL request buffer, fills it
* with requisite parameters and calls the IOCTL handler.
*/
-int mwifiex_set_encode(struct mwifiex_private *priv, const u8 *key,
- int key_len, u8 key_index,
- const u8 *mac_addr, int disable)
+int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
+ const u8 *key, int key_len, u8 key_index,
+ const u8 *mac_addr, int disable)
{
struct mwifiex_ds_encrypt_key encrypt_key;
memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key));
encrypt_key.key_len = key_len;
+
+ if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ encrypt_key.is_igtk_key = true;
+
if (!disable) {
encrypt_key.key_index = key_index;
if (key_len)
memcpy(encrypt_key.key_material, key, key_len);
if (mac_addr)
memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
+ if (kp && kp->seq && kp->seq_len)
+ memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
} else {
encrypt_key.key_disable = true;
if (mac_addr)
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c
index 02ce3b7..d91d5c0 100644
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ b/drivers/net/wireless/mwifiex/sta_rx.c
@@ -54,8 +54,8 @@ int mwifiex_process_rx_packet(struct mwifiex_adapter *adapter,
local_rx_pd = (struct rxpd *) (skb->data);
- rx_pkt_hdr = (struct rx_packet_hdr *) ((u8 *) local_rx_pd +
- local_rx_pd->rx_pkt_offset);
+ rx_pkt_hdr = (void *)local_rx_pd +
+ le16_to_cpu(local_rx_pd->rx_pkt_offset);
if (!memcmp(&rx_pkt_hdr->rfc1042_hdr,
rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) {
@@ -125,7 +125,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter,
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
struct rx_packet_hdr *rx_pkt_hdr;
u8 ta[ETH_ALEN];
- u16 rx_pkt_type;
+ u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
struct mwifiex_private *priv =
mwifiex_get_priv_by_id(adapter, rx_info->bss_num,
rx_info->bss_type);
@@ -134,16 +134,17 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter,
return -1;
local_rx_pd = (struct rxpd *) (skb->data);
- rx_pkt_type = local_rx_pd->rx_pkt_type;
+ rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
+ rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset);
+ rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length);
+ seq_num = le16_to_cpu(local_rx_pd->seq_num);
- rx_pkt_hdr = (struct rx_packet_hdr *) ((u8 *) local_rx_pd +
- local_rx_pd->rx_pkt_offset);
+ rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset;
- if ((local_rx_pd->rx_pkt_offset + local_rx_pd->rx_pkt_length) >
- (u16) skb->len) {
- dev_err(adapter->dev, "wrong rx packet: len=%d,"
- " rx_pkt_offset=%d, rx_pkt_length=%d\n", skb->len,
- local_rx_pd->rx_pkt_offset, local_rx_pd->rx_pkt_length);
+ if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) {
+ dev_err(adapter->dev,
+ "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n",
+ skb->len, rx_pkt_offset, rx_pkt_length);
priv->stats.rx_dropped++;
if (adapter->if_ops.data_complete)
@@ -154,14 +155,14 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter,
return ret;
}
- if (local_rx_pd->rx_pkt_type == PKT_TYPE_AMSDU) {
+ if (rx_pkt_type == PKT_TYPE_AMSDU) {
struct sk_buff_head list;
struct sk_buff *rx_skb;
__skb_queue_head_init(&list);
- skb_pull(skb, local_rx_pd->rx_pkt_offset);
- skb_trim(skb, local_rx_pd->rx_pkt_length);
+ skb_pull(skb, rx_pkt_offset);
+ skb_trim(skb, rx_pkt_length);
ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
priv->wdev->iftype, 0, false);
@@ -189,17 +190,14 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter,
memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
} else {
if (rx_pkt_type != PKT_TYPE_BAR)
- priv->rx_seq[local_rx_pd->priority] =
- local_rx_pd->seq_num;
+ priv->rx_seq[local_rx_pd->priority] = seq_num;
memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address,
ETH_ALEN);
}
/* Reorder and send to OS */
- ret = mwifiex_11n_rx_reorder_pkt(priv, local_rx_pd->seq_num,
- local_rx_pd->priority, ta,
- (u8) local_rx_pd->rx_pkt_type,
- skb);
+ ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority,
+ ta, (u8) rx_pkt_type, skb);
if (ret || (rx_pkt_type == PKT_TYPE_BAR)) {
if (adapter->if_ops.data_complete)
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index cecb272..985073d 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -51,6 +51,9 @@ int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter,
rx_info->bss_num = priv->bss_num;
rx_info->bss_type = priv->bss_type;
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
+ return mwifiex_process_uap_rx_packet(adapter, skb);
+
return mwifiex_process_sta_rx_packet(adapter, skb);
}
EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet);
@@ -157,6 +160,8 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
priv->stats.tx_errors++;
}
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT)
+ atomic_dec_return(&adapter->pending_bridged_pkts);
if (atomic_dec_return(&adapter->tx_pending) >= LOW_TX_PENDING)
goto done;
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index f40e93fe..c10aac0 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -167,6 +167,7 @@ mwifiex_set_ht_params(struct mwifiex_private *priv,
if (ht_ie) {
memcpy(&bss_cfg->ht_cap, ht_ie + 2,
sizeof(struct ieee80211_ht_cap));
+ priv->ap_11n_enabled = 1;
} else {
memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap));
bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP);
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
new file mode 100644
index 0000000..a33fa39
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -0,0 +1,290 @@
+/*
+ * Marvell Wireless LAN device driver: AP event handling
+ *
+ * Copyright (C) 2012, 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * 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.
+ */
+
+#include "decl.h"
+#include "main.h"
+#include "11n.h"
+
+/*
+ * This function will return the pointer to station entry in station list
+ * table which matches specified mac address.
+ * This function should be called after acquiring RA list spinlock.
+ * NULL is returned if station entry is not found in associated STA list.
+ */
+struct mwifiex_sta_node *
+mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_sta_node *node;
+
+ if (!mac)
+ return NULL;
+
+ list_for_each_entry(node, &priv->sta_list, list) {
+ if (!memcmp(node->mac_addr, mac, ETH_ALEN))
+ return node;
+ }
+
+ return NULL;
+}
+
+/*
+ * This function will add a sta_node entry to associated station list
+ * table with the given mac address.
+ * If entry exist already, existing entry is returned.
+ * If received mac address is NULL, NULL is returned.
+ */
+static struct mwifiex_sta_node *
+mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_sta_node *node;
+ unsigned long flags;
+
+ if (!mac)
+ return NULL;
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ node = mwifiex_get_sta_entry(priv, mac);
+ if (node)
+ goto done;
+
+ node = kzalloc(sizeof(struct mwifiex_sta_node), GFP_ATOMIC);
+ if (!node)
+ goto done;
+
+ memcpy(node->mac_addr, mac, ETH_ALEN);
+ list_add_tail(&node->list, &priv->sta_list);
+
+done:
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ return node;
+}
+
+/*
+ * This function will search for HT IE in association request IEs
+ * and set station HT parameters accordingly.
+ */
+static void
+mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
+ int ies_len, struct mwifiex_sta_node *node)
+{
+ const struct ieee80211_ht_cap *ht_cap;
+
+ if (!ies)
+ return;
+
+ ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
+ if (ht_cap) {
+ node->is_11n_enabled = 1;
+ node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
+ IEEE80211_HT_CAP_MAX_AMSDU ?
+ MWIFIEX_TX_DATA_BUF_SIZE_8K :
+ MWIFIEX_TX_DATA_BUF_SIZE_4K;
+ } else {
+ node->is_11n_enabled = 0;
+ }
+
+ return;
+}
+
+/*
+ * This function will delete a station entry from station list
+ */
+static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+ struct mwifiex_sta_node *node, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+
+ node = mwifiex_get_sta_entry(priv, mac);
+ if (node) {
+ list_for_each_entry_safe(node, tmp, &priv->sta_list,
+ list) {
+ list_del(&node->list);
+ kfree(node);
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ return;
+}
+
+/*
+ * This function will delete all stations from associated station list.
+ */
+static void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
+{
+ struct mwifiex_sta_node *node, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+
+ list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
+ list_del(&node->list);
+ kfree(node);
+ }
+
+ INIT_LIST_HEAD(&priv->sta_list);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ return;
+}
+
+/*
+ * This function handles AP interface specific events generated by firmware.
+ *
+ * Event specific routines are called by this function based
+ * upon the generated event cause.
+ *
+ *
+ * Events supported for AP -
+ * - EVENT_UAP_STA_ASSOC
+ * - EVENT_UAP_STA_DEAUTH
+ * - EVENT_UAP_BSS_ACTIVE
+ * - EVENT_UAP_BSS_START
+ * - EVENT_UAP_BSS_IDLE
+ * - EVENT_UAP_MIC_COUNTERMEASURES:
+ */
+int mwifiex_process_uap_event(struct mwifiex_private *priv)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ int len, i;
+ u32 eventcause = adapter->event_cause;
+ struct station_info sinfo;
+ struct mwifiex_assoc_event *event;
+ struct mwifiex_sta_node *node;
+ u8 *deauth_mac;
+ struct host_cmd_ds_11n_batimeout *ba_timeout;
+ u16 ctrl;
+
+ switch (eventcause) {
+ case EVENT_UAP_STA_ASSOC:
+ memset(&sinfo, 0, sizeof(sinfo));
+ event = (struct mwifiex_assoc_event *)
+ (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER);
+ if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) {
+ len = -1;
+
+ if (ieee80211_is_assoc_req(event->frame_control))
+ len = 0;
+ else if (ieee80211_is_reassoc_req(event->frame_control))
+ /* There will be ETH_ALEN bytes of
+ * current_ap_addr before the re-assoc ies.
+ */
+ len = ETH_ALEN;
+
+ if (len != -1) {
+ sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
+ sinfo.assoc_req_ies = &event->data[len];
+ len = (u8 *)sinfo.assoc_req_ies -
+ (u8 *)&event->frame_control;
+ sinfo.assoc_req_ies_len =
+ le16_to_cpu(event->len) - (u16)len;
+ }
+ }
+ cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo,
+ GFP_KERNEL);
+
+ node = mwifiex_add_sta_entry(priv, event->sta_addr);
+ if (!node) {
+ dev_warn(adapter->dev,
+ "could not create station entry!\n");
+ return -1;
+ }
+
+ if (!priv->ap_11n_enabled)
+ break;
+
+ mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies,
+ sinfo.assoc_req_ies_len, node);
+
+ for (i = 0; i < MAX_NUM_TID; i++) {
+ if (node->is_11n_enabled)
+ node->ampdu_sta[i] =
+ priv->aggr_prio_tbl[i].ampdu_user;
+ else
+ node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
+ }
+ memset(node->rx_seq, 0xff, sizeof(node->rx_seq));
+ break;
+ case EVENT_UAP_STA_DEAUTH:
+ deauth_mac = adapter->event_body +
+ MWIFIEX_UAP_EVENT_EXTRA_HEADER;
+ cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL);
+
+ if (priv->ap_11n_enabled) {
+ mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
+ mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
+ }
+ mwifiex_del_sta_entry(priv, deauth_mac);
+ break;
+ case EVENT_UAP_BSS_IDLE:
+ priv->media_connected = false;
+ mwifiex_clean_txrx(priv);
+ mwifiex_del_all_sta_list(priv);
+ break;
+ case EVENT_UAP_BSS_ACTIVE:
+ priv->media_connected = true;
+ break;
+ case EVENT_UAP_BSS_START:
+ dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
+ memcpy(priv->netdev->dev_addr, adapter->event_body + 2,
+ ETH_ALEN);
+ break;
+ case EVENT_UAP_MIC_COUNTERMEASURES:
+ /* For future development */
+ dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
+ break;
+ case EVENT_AMSDU_AGGR_CTRL:
+ ctrl = le16_to_cpu(*(__le16 *)adapter->event_body);
+ dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", ctrl);
+
+ if (priv->media_connected) {
+ adapter->tx_buf_size =
+ min_t(u16, adapter->curr_tx_buf_size, ctrl);
+ dev_dbg(adapter->dev, "event: tx_buf_size %d\n",
+ adapter->tx_buf_size);
+ }
+ break;
+ case EVENT_ADDBA:
+ dev_dbg(adapter->dev, "event: ADDBA Request\n");
+ if (priv->media_connected)
+ mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP,
+ HostCmd_ACT_GEN_SET, 0,
+ adapter->event_body);
+ break;
+ case EVENT_DELBA:
+ dev_dbg(adapter->dev, "event: DELBA Request\n");
+ if (priv->media_connected)
+ mwifiex_11n_delete_ba_stream(priv, adapter->event_body);
+ break;
+ case EVENT_BA_STREAM_TIEMOUT:
+ dev_dbg(adapter->dev, "event: BA Stream timeout\n");
+ if (priv->media_connected) {
+ ba_timeout = (void *)adapter->event_body;
+ mwifiex_11n_ba_stream_timeout(priv, ba_timeout);
+ }
+ break;
+ default:
+ dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
+ eventcause);
+ break;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
new file mode 100644
index 0000000..6d814f0
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/uap_txrx.c
@@ -0,0 +1,255 @@
+/*
+ * Marvell Wireless LAN device driver: AP TX and RX data handling
+ *
+ * Copyright (C) 2012, 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * 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.
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n_aggr.h"
+#include "11n_rxreorder.h"
+
+static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ struct sk_buff *new_skb;
+ struct mwifiex_txinfo *tx_info;
+ int hdr_chop;
+ struct timeval tv;
+ u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ if ((atomic_read(&adapter->pending_bridged_pkts) >=
+ MWIFIEX_BRIDGED_PKTS_THRESHOLD)) {
+ dev_err(priv->adapter->dev,
+ "Tx: Bridge packet limit reached. Drop packet!\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ if (!memcmp(&rx_pkt_hdr->rfc1042_hdr,
+ rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)))
+ /* Chop off the rxpd + the excess memory from
+ * 802.2/llc/snap header that was removed.
+ */
+ hdr_chop = (u8 *)eth_hdr - (u8 *)uap_rx_pd;
+ else
+ /* Chop off the rxpd */
+ hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
+
+ /* Chop off the leading header bytes so the it points
+ * to the start of either the reconstructed EthII frame
+ * or the 802.2/llc/snap frame.
+ */
+ skb_pull(skb, hdr_chop);
+
+ if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
+ dev_dbg(priv->adapter->dev,
+ "data: Tx: insufficient skb headroom %d\n",
+ skb_headroom(skb));
+ /* Insufficient skb headroom - allocate a new skb */
+ new_skb =
+ skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
+ if (unlikely(!new_skb)) {
+ dev_err(priv->adapter->dev,
+ "Tx: cannot allocate new_skb\n");
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return;
+ }
+
+ kfree_skb(skb);
+ skb = new_skb;
+ dev_dbg(priv->adapter->dev, "info: new skb headroom %d\n",
+ skb_headroom(skb));
+ }
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT;
+
+ do_gettimeofday(&tv);
+ skb->tstamp = timeval_to_ktime(tv);
+ mwifiex_wmm_add_buf_txqueue(priv, skb);
+ atomic_inc(&adapter->tx_pending);
+ atomic_inc(&adapter->pending_bridged_pkts);
+
+ if ((atomic_read(&adapter->tx_pending) >= MAX_TX_PENDING)) {
+ mwifiex_set_trans_start(priv->netdev);
+ mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
+ }
+ return;
+}
+
+/*
+ * This function contains logic for AP packet forwarding.
+ *
+ * If a packet is multicast/broadcast, it is sent to kernel/upper layer
+ * as well as queued back to AP TX queue so that it can be sent to other
+ * associated stations.
+ * If a packet is unicast and RA is present in associated station list,
+ * it is again requeued into AP TX queue.
+ * If a packet is unicast and RA is not in associated station list,
+ * packet is forwarded to kernel to handle routing logic.
+ */
+int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u8 ra[ETH_ALEN];
+ struct sk_buff *skb_uap;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ /* don't do packet forwarding in disconnected state */
+ if (!priv->media_connected) {
+ dev_err(adapter->dev, "drop packet in disconnected state.\n");
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN);
+
+ if (is_multicast_ether_addr(ra)) {
+ skb_uap = skb_copy(skb, GFP_ATOMIC);
+ mwifiex_uap_queue_bridged_pkt(priv, skb_uap);
+ } else {
+ if (mwifiex_get_sta_entry(priv, ra)) {
+ /* Requeue Intra-BSS packet */
+ mwifiex_uap_queue_bridged_pkt(priv, skb);
+ return 0;
+ }
+ }
+
+ /* Forward unicat/Inter-BSS packets to kernel. */
+ return mwifiex_process_rx_packet(adapter, skb);
+}
+
+/*
+ * This function processes the packet received on AP interface.
+ *
+ * The function looks into the RxPD and performs sanity tests on the
+ * received buffer to ensure its a valid packet before processing it
+ * further. If the packet is determined to be aggregated, it is
+ * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic.
+ *
+ * The completion callback is called after processing is complete.
+ */
+int mwifiex_process_uap_rx_packet(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ int ret;
+ struct uap_rxpd *uap_rx_pd;
+ struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u16 rx_pkt_type;
+ u8 ta[ETH_ALEN], pkt_type;
+ struct mwifiex_sta_node *node;
+
+ struct mwifiex_private *priv =
+ mwifiex_get_priv_by_id(adapter, rx_info->bss_num,
+ rx_info->bss_type);
+
+ if (!priv)
+ return -1;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
+ le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) {
+ dev_err(adapter->dev,
+ "wrong rx packet: len=%d, offset=%d, length=%d\n",
+ skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset),
+ le16_to_cpu(uap_rx_pd->rx_pkt_length));
+ priv->stats.rx_dropped++;
+
+ if (adapter->if_ops.data_complete)
+ adapter->if_ops.data_complete(adapter, skb);
+ else
+ dev_kfree_skb_any(skb);
+
+ return 0;
+ }
+
+ if (le16_to_cpu(uap_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
+ struct sk_buff_head list;
+ struct sk_buff *rx_skb;
+
+ __skb_queue_head_init(&list);
+ skb_pull(skb, le16_to_cpu(uap_rx_pd->rx_pkt_offset));
+ skb_trim(skb, le16_to_cpu(uap_rx_pd->rx_pkt_length));
+
+ ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
+ priv->wdev->iftype, 0, false);
+
+ while (!skb_queue_empty(&list)) {
+ rx_skb = __skb_dequeue(&list);
+ ret = mwifiex_recv_packet(adapter, rx_skb);
+ if (ret)
+ dev_err(adapter->dev,
+ "AP:Rx A-MSDU failed");
+ }
+
+ return 0;
+ }
+
+ 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) {
+ node = mwifiex_get_sta_entry(priv, ta);
+ if (node)
+ node->rx_seq[uap_rx_pd->priority] =
+ le16_to_cpu(uap_rx_pd->seq_num);
+ }
+
+ if (!priv->ap_11n_enabled ||
+ (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) &&
+ (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) {
+ ret = mwifiex_handle_uap_rx_forward(priv, skb);
+ return ret;
+ }
+
+ /* Reorder and send to kernel */
+ pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type);
+ ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num),
+ uap_rx_pd->priority, ta, pkt_type,
+ skb);
+
+ if (ret || (rx_pkt_type == PKT_TYPE_BAR)) {
+ if (adapter->if_ops.data_complete)
+ adapter->if_ops.data_complete(adapter, skb);
+ else
+ dev_kfree_skb_any(skb);
+ }
+
+ if (ret)
+ priv->stats.rx_dropped++;
+
+ return ret;
+}
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 3fa4d41..8ccd699 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -127,6 +127,29 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra)
return ra_list;
}
+/* This function returns random no between 16 and 32 to be used as threshold
+ * for no of packets after which BA setup is initiated.
+ */
+static u8 mwifiex_get_random_ba_threshold(void)
+{
+ u32 sec, usec;
+ struct timeval ba_tstamp;
+ u8 ba_threshold;
+
+ /* setup ba_packet_threshold here random number between
+ * [BA_SETUP_PACKET_OFFSET,
+ * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1]
+ */
+
+ do_gettimeofday(&ba_tstamp);
+ sec = (ba_tstamp.tv_sec & 0xFFFF) + (ba_tstamp.tv_sec >> 16);
+ usec = (ba_tstamp.tv_usec & 0xFFFF) + (ba_tstamp.tv_usec >> 16);
+ ba_threshold = (((sec << 16) + usec) % BA_SETUP_MAX_PACKET_THRESHOLD)
+ + BA_SETUP_PACKET_OFFSET;
+
+ return ba_threshold;
+}
+
/*
* This function allocates and adds a RA list for all TIDs
* with the given RA.
@@ -137,6 +160,12 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
int i;
struct mwifiex_ra_list_tbl *ra_list;
struct mwifiex_adapter *adapter = priv->adapter;
+ 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);
@@ -145,14 +174,24 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
if (!ra_list)
break;
- if (!mwifiex_queuing_ra_based(priv))
+ ra_list->is_11n_enabled = 0;
+ if (!mwifiex_queuing_ra_based(priv)) {
ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
- else
- ra_list->is_11n_enabled = false;
+ } else {
+ 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;
+ }
dev_dbg(adapter->dev, "data: ralist %p: is_11n_enabled=%d\n",
ra_list, ra_list->is_11n_enabled);
+ if (ra_list->is_11n_enabled) {
+ ra_list->pkt_count = 0;
+ ra_list->ba_packet_thr =
+ mwifiex_get_random_ba_threshold();
+ }
list_add_tail(&ra_list->list,
&priv->wmm.tid_tbl_ptr[i].ra_list);
@@ -647,6 +686,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
skb_queue_tail(&ra_list->skb_head, skb);
ra_list->total_pkts_size += skb->len;
+ ra_list->pkt_count++;
atomic_inc(&priv->wmm.tx_pkts_queued);
@@ -986,10 +1026,17 @@ mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv,
{
int count = 0, total_size = 0;
struct sk_buff *skb, *tmp;
+ int max_amsdu_size;
+
+ if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP && priv->ap_11n_enabled &&
+ ptr->is_11n_enabled)
+ max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size);
+ else
+ max_amsdu_size = max_buf_size;
skb_queue_walk_safe(&ptr->skb_head, skb, tmp) {
total_size += skb->len;
- if (total_size >= max_buf_size)
+ if (total_size >= max_amsdu_size)
break;
if (++count >= MIN_NUM_AMSDU)
return true;
@@ -1050,6 +1097,7 @@ mwifiex_send_single_packet(struct mwifiex_private *priv,
skb_queue_tail(&ptr->skb_head, skb);
ptr->total_pkts_size += skb->len;
+ ptr->pkt_count++;
tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
ra_list_flags);
@@ -1231,7 +1279,8 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
/* ra_list_spinlock has been freed in
mwifiex_send_single_packet() */
} else {
- if (mwifiex_is_ampdu_allowed(priv, tid)) {
+ if (mwifiex_is_ampdu_allowed(priv, tid) &&
+ ptr->pkt_count > ptr->ba_packet_thr) {
if (mwifiex_space_avail_for_new_ba_stream(adapter)) {
mwifiex_create_ba_tbl(priv, ptr->ra, tid,
BA_SETUP_INPROGRESS);
OpenPOWER on IntegriCloud