|
@@ -291,6 +291,60 @@ static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct sta_info *sta)
|
|
|
+{
|
|
|
+ /* IEEE802.11ac-2013 Table E-4 */
|
|
|
+ u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 };
|
|
|
+ struct cfg80211_chan_def uc = sta->tdls_chandef;
|
|
|
+ enum nl80211_chan_width max_width = ieee80211_get_sta_bw(&sta->sta);
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* only support upgrading non-narrow channels up to 80Mhz */
|
|
|
+ if (max_width == NL80211_CHAN_WIDTH_5 ||
|
|
|
+ max_width == NL80211_CHAN_WIDTH_10)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (max_width > NL80211_CHAN_WIDTH_80)
|
|
|
+ max_width = NL80211_CHAN_WIDTH_80;
|
|
|
+
|
|
|
+ if (uc.width == max_width)
|
|
|
+ return;
|
|
|
+ /*
|
|
|
+ * Channel usage constrains in the IEEE802.11ac-2013 specification only
|
|
|
+ * allow expanding a 20MHz channel to 80MHz in a single way. In
|
|
|
+ * addition, there are no 40MHz allowed channels that are not part of
|
|
|
+ * the allowed 80MHz range in the 5GHz spectrum (the relevant one here).
|
|
|
+ */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++)
|
|
|
+ if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) {
|
|
|
+ uc.center_freq1 = centers_80mhz[i];
|
|
|
+ uc.width = NL80211_CHAN_WIDTH_80;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!uc.center_freq1)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* proceed to downgrade the chandef until usable or the same */
|
|
|
+ while (uc.width > max_width &&
|
|
|
+ !cfg80211_reg_can_beacon(sdata->local->hw.wiphy,
|
|
|
+ &uc, sdata->wdev.iftype))
|
|
|
+ ieee80211_chandef_downgrade(&uc);
|
|
|
+
|
|
|
+ if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
|
|
|
+ tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n",
|
|
|
+ sta->tdls_chandef.width, uc.width);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * the station is not yet authorized when BW upgrade is done,
|
|
|
+ * locking is not required
|
|
|
+ */
|
|
|
+ sta->tdls_chandef = uc;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void
|
|
|
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
|
|
struct sk_buff *skb, const u8 *peer,
|
|
@@ -358,15 +412,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
|
|
offset = noffset;
|
|
|
}
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
+ mutex_lock(&local->sta_mtx);
|
|
|
|
|
|
/* we should have the peer STA if we're already responding */
|
|
|
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
|
|
|
sta = sta_info_get(sdata, peer);
|
|
|
if (WARN_ON_ONCE(!sta)) {
|
|
|
- rcu_read_unlock();
|
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ sta->tdls_chandef = sdata->vif.bss_conf.chandef;
|
|
|
}
|
|
|
|
|
|
ieee80211_tdls_add_oper_classes(sdata, skb);
|
|
@@ -456,9 +512,16 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
|
|
|
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * if both peers support WIDER_BW, we can expand the chandef to
|
|
|
+ * a wider compatible one, up to 80MHz
|
|
|
+ */
|
|
|
+ if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
|
|
|
+ ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
|
|
|
}
|
|
|
|
|
|
- rcu_read_unlock();
|
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
|
|
|
|
/* add any remaining IEs */
|
|
|
if (extra_ies_len) {
|
|
@@ -482,15 +545,17 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
|
|
|
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
|
|
u8 *pos;
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
+ mutex_lock(&local->sta_mtx);
|
|
|
|
|
|
sta = sta_info_get(sdata, peer);
|
|
|
ap_sta = sta_info_get(sdata, ifmgd->bssid);
|
|
|
if (WARN_ON_ONCE(!sta || !ap_sta)) {
|
|
|
- rcu_read_unlock();
|
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ sta->tdls_chandef = sdata->vif.bss_conf.chandef;
|
|
|
+
|
|
|
/* add any custom IEs that go before the QoS IE */
|
|
|
if (extra_ies_len) {
|
|
|
static const u8 before_qos[] = {
|
|
@@ -538,12 +603,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
/* only include VHT-operation if not on the 2.4GHz band */
|
|
|
if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
|
|
|
+ /*
|
|
|
+ * if both peers support WIDER_BW, we can expand the chandef to
|
|
|
+ * a wider compatible one, up to 80MHz
|
|
|
+ */
|
|
|
+ if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
|
|
|
+ ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
|
|
|
+
|
|
|
pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
|
|
|
ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
|
|
|
- &sdata->vif.bss_conf.chandef);
|
|
|
+ &sta->tdls_chandef);
|
|
|
}
|
|
|
|
|
|
- rcu_read_unlock();
|
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
|
|
|
|
/* add any remaining IEs */
|
|
|
if (extra_ies_len) {
|
|
@@ -1154,6 +1226,22 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata)
|
|
|
+{
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
+ struct ieee80211_chanctx_conf *conf;
|
|
|
+ struct ieee80211_chanctx *ctx;
|
|
|
+
|
|
|
+ mutex_lock(&local->chanctx_mtx);
|
|
|
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
|
|
+ lockdep_is_held(&local->chanctx_mtx));
|
|
|
+ if (conf) {
|
|
|
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
|
|
+ ieee80211_recalc_chanctx_chantype(local, ctx);
|
|
|
+ }
|
|
|
+ mutex_unlock(&local->chanctx_mtx);
|
|
|
+}
|
|
|
+
|
|
|
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|
|
const u8 *peer, enum nl80211_tdls_operation oper)
|
|
|
{
|
|
@@ -1190,6 +1278,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ iee80211_tdls_recalc_chanctx(sdata);
|
|
|
+
|
|
|
rcu_read_lock();
|
|
|
sta = sta_info_get(sdata, peer);
|
|
|
if (!sta) {
|
|
@@ -1221,6 +1311,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|
|
ieee80211_flush_queues(local, sdata, false);
|
|
|
|
|
|
ret = sta_info_destroy_addr(sdata, peer);
|
|
|
+ iee80211_tdls_recalc_chanctx(sdata);
|
|
|
break;
|
|
|
default:
|
|
|
ret = -ENOTSUPP;
|