diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex')
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); |