mode = ATH9K_PM_FULL_SLEEP;
else if (sc->ps_enabled &&
!(sc->ps_flags & (PS_WAIT_FOR_BEACON |
+@@ -286,7 +286,7 @@ static bool ath_complete_reset(struct at
+ ath_start_ani(common);
+ }
+
+- if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) {
++ if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) {
+ struct ath_hw_antcomb_conf div_ant_conf;
+ u8 lna_conf;
+
@@ -332,7 +332,8 @@ static int ath_reset_internal(struct ath
hchan = ah->curchan;
}
IEEE80211_STYPE_ACTION);
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
-@@ -79,10 +79,13 @@ static void ieee80211_send_addba_request
+@@ -55,6 +55,8 @@
+ * @ampdu_action function will be called with the action
+ * %IEEE80211_AMPDU_TX_STOP. In this case, the call must not fail,
+ * and the driver must later call ieee80211_stop_tx_ba_cb_irqsafe().
++ * Note that the sta can get destroyed before the BA tear down is
++ * complete.
+ */
+
+ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+@@ -79,10 +81,13 @@ static void ieee80211_send_addba_request
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
-@@ -437,7 +440,9 @@ int ieee80211_start_tx_ba_session(struct
+@@ -319,6 +324,38 @@ ieee80211_wake_queue_agg(struct ieee8021
+ __release(agg_queue);
+ }
+
++/*
++ * splice packets from the STA's pending to the local pending,
++ * requires a call to ieee80211_agg_splice_finish later
++ */
++static void __acquires(agg_queue)
++ieee80211_agg_splice_packets(struct ieee80211_local *local,
++ struct tid_ampdu_tx *tid_tx, u16 tid)
++{
++ int queue = ieee80211_ac_from_tid(tid);
++ unsigned long flags;
++
++ ieee80211_stop_queue_agg(local, tid);
++
++ if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates"
++ " from the pending queue\n", tid))
++ return;
++
++ if (!skb_queue_empty(&tid_tx->pending)) {
++ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
++ /* copy over remaining packets */
++ skb_queue_splice_tail_init(&tid_tx->pending,
++ &local->pending[queue]);
++ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
++ }
++}
++
++static void __releases(agg_queue)
++ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid)
++{
++ ieee80211_wake_queue_agg(local, tid);
++}
++
+ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
+ {
+ struct tid_ampdu_tx *tid_tx;
+@@ -330,19 +367,17 @@ void ieee80211_tx_ba_session_handle_star
+ tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+
+ /*
+- * While we're asking the driver about the aggregation,
+- * stop the AC queue so that we don't have to worry
+- * about frames that came in while we were doing that,
+- * which would require us to put them to the AC pending
+- * afterwards which just makes the code more complex.
++ * Start queuing up packets for this aggregation session.
++ * We're going to release them once the driver is OK with
++ * that.
+ */
+- ieee80211_stop_queue_agg(local, tid);
+-
+ clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
+
+ /*
+- * make sure no packets are being processed to get
+- * valid starting sequence number
++ * Make sure no packets are being processed. This ensures that
++ * we have a valid starting sequence number and that in-flight
++ * packets have been flushed out and no packets for this TID
++ * will go into the driver during the ampdu_action call.
+ */
+ synchronize_net();
+
+@@ -356,10 +391,11 @@ void ieee80211_tx_ba_session_handle_star
+ " tid %d\n", tid);
+ #endif
+ spin_lock_bh(&sta->lock);
++ ieee80211_agg_splice_packets(local, tid_tx, tid);
+ ieee80211_assign_tid_tx(sta, tid, NULL);
++ ieee80211_agg_splice_finish(local, tid);
+ spin_unlock_bh(&sta->lock);
+
+- ieee80211_wake_queue_agg(local, tid);
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,40))
+ kfree_rcu(tid_tx, rcu_head);
+ #else
+@@ -368,9 +404,6 @@ void ieee80211_tx_ba_session_handle_star
+ return;
+ }
+
+- /* we can take packets again now */
+- ieee80211_wake_queue_agg(local, tid);
+-
+ /* activate the timer for the recipient's addBA response */
+ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
+ #ifdef CONFIG_MAC80211_HT_DEBUG
+@@ -437,7 +470,9 @@ int ieee80211_start_tx_ba_session(struct
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
return -EINVAL;
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
-@@ -448,6 +453,27 @@ int ieee80211_start_tx_ba_session(struct
+@@ -448,6 +483,27 @@ int ieee80211_start_tx_ba_session(struct
return -EINVAL;
}
spin_lock_bh(&sta->lock);
/* we have tried too many times, receiver does not want A-MPDU */
+@@ -508,38 +564,6 @@ int ieee80211_start_tx_ba_session(struct
+ }
+ EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
+
+-/*
+- * splice packets from the STA's pending to the local pending,
+- * requires a call to ieee80211_agg_splice_finish later
+- */
+-static void __acquires(agg_queue)
+-ieee80211_agg_splice_packets(struct ieee80211_local *local,
+- struct tid_ampdu_tx *tid_tx, u16 tid)
+-{
+- int queue = ieee80211_ac_from_tid(tid);
+- unsigned long flags;
+-
+- ieee80211_stop_queue_agg(local, tid);
+-
+- if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates"
+- " from the pending queue\n", tid))
+- return;
+-
+- if (!skb_queue_empty(&tid_tx->pending)) {
+- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+- /* copy over remaining packets */
+- skb_queue_splice_tail_init(&tid_tx->pending,
+- &local->pending[queue]);
+- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+- }
+-}
+-
+-static void __releases(agg_queue)
+-ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid)
+-{
+- ieee80211_wake_queue_agg(local, tid);
+-}
+-
+ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid)
+ {
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -63,11 +63,11 @@ static ssize_t sta_flags_read(struct fil
TEST(TDLS_PEER_AUTH));
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
-@@ -282,6 +282,8 @@ void ieee80211_send_delba(struct ieee802
+@@ -47,7 +47,9 @@ void ieee80211_apply_htcap_overrides(str
+ int i;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION) {
+- WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
++ /* AP interfaces call this code when adding new stations,
++ * so just silently ignore non station interfaces.
++ */
+ return;
+ }
+
+@@ -282,6 +284,8 @@ void ieee80211_send_delba(struct ieee802
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
break;
case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
case cpu_to_le16(IEEE80211_STYPE_DISASSOC):
-@@ -2805,10 +2808,16 @@ static int prepare_for_handlers(struct i
+@@ -2796,19 +2799,32 @@ static int prepare_for_handlers(struct i
+ return 0;
+ } else if (!ieee80211_bssid_match(bssid,
+ sdata->vif.addr)) {
++ /*
++ * Accept public action frames even when the
++ * BSSID doesn't match, this is used for P2P
++ * and location updates. Note that mac80211
++ * itself never looks at these frames.
++ */
+ if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) &&
+- !ieee80211_is_beacon(hdr->frame_control) &&
+- !(ieee80211_is_action(hdr->frame_control) &&
+- sdata->vif.p2p))
++ ieee80211_is_public_action(hdr, skb->len))
++ return 1;
++ if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) &&
++ !ieee80211_is_beacon(hdr->frame_control))
+ return 0;
+ status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
}
break;
case NL80211_IFTYPE_WDS:
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -1695,6 +1695,23 @@ static inline bool ieee80211_is_robust_m
+ }
+
+ /**
++ * ieee80211_is_public_action - check if frame is a public action frame
++ * @hdr: the frame
++ * @len: length of the frame
++ */
++static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr,
++ size_t len)
++{
++ struct ieee80211_mgmt *mgmt = (void *)hdr;
++
++ if (len < 25)
++ return false;
++ if (!ieee80211_is_action(hdr->frame_control))
++ return false;
++ return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC;
++}
++
++/**
+ * ieee80211_fhss_chan_to_freq - get channel frequency
+ * @channel: the FHSS channel
+ *
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1332,8 +1332,11 @@ static int invoke_tx_handlers(struct iee
+ if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
+ CALL_TXH(ieee80211_tx_h_rate_ctrl);
+
+- if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION))
++ if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
++ __skb_queue_tail(&tx->skbs, tx->skb);
++ tx->skb = NULL;
+ goto txh_done;
++ }
+
+ CALL_TXH(ieee80211_tx_h_michael_mic_add);
+ CALL_TXH(ieee80211_tx_h_sequence);
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -851,6 +851,7 @@ static int __must_check __sta_info_destr
+ struct ieee80211_sub_if_data *sdata;
+ unsigned long flags;
+ int ret, i, ac;
++ struct tid_ampdu_tx *tid_tx;
+
+ might_sleep();
+
+@@ -949,6 +950,30 @@ static int __must_check __sta_info_destr
+ }
+ #endif
+
++ /* There could be some memory leaks because of ampdu tx pending queue
++ * not being freed before destroying the station info.
++ *
++ * Make sure that such queues are purged before freeing the station
++ * info.
++ * TODO: We have to somehow postpone the full destruction
++ * until the aggregation stop completes. Refer
++ * http://thread.gmane.org/gmane.linux.kernel.wireless.general/81936
++ */
++ for (i = 0; i < STA_TID_NUM; i++) {
++ if (!sta->ampdu_mlme.tid_tx[i])
++ continue;
++ tid_tx = sta->ampdu_mlme.tid_tx[i];
++ if (skb_queue_len(&tid_tx->pending)) {
++#ifdef CONFIG_MAC80211_HT_DEBUG
++ wiphy_debug(local->hw.wiphy, "TX A-MPDU purging %d "
++ "packets for tid=%d\n",
++ skb_queue_len(&tid_tx->pending), i);
++#endif /* CONFIG_MAC80211_HT_DEBUG */
++ __skb_queue_purge(&tid_tx->pending);
++ }
++ kfree_rcu(tid_tx, rcu_head);
++ }
++
+ __sta_info_free(local, sta);
+
+ return 0;