+ struct ieee80211_channel *sec_chan;
+ int diff;
+@@ -75,6 +76,7 @@ static bool can_beacon_sec_chan(struct w
+
+ return true;
+ }
++EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
+
+ int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int freq,
+@@ -109,8 +111,8 @@ int cfg80211_set_freq(struct cfg80211_re
+ switch (channel_type) {
+ case NL80211_CHAN_HT40PLUS:
+ case NL80211_CHAN_HT40MINUS:
+- if (!can_beacon_sec_chan(&rdev->wiphy, chan,
+- channel_type)) {
++ if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan,
++ channel_type)) {
+ printk(KERN_DEBUG
+ "cfg80211: Secondary channel not "
+ "allowed to initiate communication\n");
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -4684,13 +4684,41 @@ static int nl80211_join_ibss(struct sk_b
+ ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+- ibss.channel = ieee80211_get_channel(wiphy,
+- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
++ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
++ enum nl80211_channel_type channel_type;
++
++ channel_type = nla_get_u32(
++ info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
++ if (channel_type != NL80211_CHAN_NO_HT &&
++ channel_type != NL80211_CHAN_HT20 &&
++ channel_type != NL80211_CHAN_HT40MINUS &&
++ channel_type != NL80211_CHAN_HT40PLUS)
++ return -EINVAL;
++
++ if (channel_type != NL80211_CHAN_NO_HT &&
++ !(wiphy->features & NL80211_FEATURE_HT_IBSS))
++ return -EINVAL;
++
++ ibss.channel_type = channel_type;
++ } else {
++ ibss.channel_type = NL80211_CHAN_NO_HT;
++ }
++
++ ibss.channel = rdev_freq_to_chan(rdev,
++ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
++ ibss.channel_type);
+ if (!ibss.channel ||
+ ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
+ ibss.channel->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
++ /* Both channels should be able to initiate communication */
++ if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
++ ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
++ !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
++ ibss.channel_type))
++ return -EINVAL;
++
+ 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);