mac80211: determine chandef from HE 6 GHz operation

Support connecting to HE 6 GHz APs and mesh networks on 6 GHz,
where the HT/VHT information is missing but instead the HE 6 GHz
band capability is present, and the 6 GHz Operation information
field is used to encode the channel configuration instead of the
HT/VHT operation elements.

Also add some other bits needed to connect to 6 GHz networks.

Link: https://lore.kernel.org/r/1589399105-25472-10-git-send-email-rmanohar@codeaurora.org
Co-developed-by: Rajkumar Manoharan <rmanohar@codeaurora.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@codeaurora.org>
Link: https://lore.kernel.org/r/20200528213443.25687d2695bc.I3f9747c1147480f65445f13eda5c4a5ed4e86757@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2020-05-28 21:34:36 +02:00
parent 2a333a0db2
commit 57fa5e85d5
4 changed files with 160 additions and 19 deletions

View file

@ -3244,6 +3244,112 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
return true;
}
bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_he_operation *he_oper,
struct cfg80211_chan_def *chandef)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
const struct ieee80211_sta_he_cap *he_cap;
struct cfg80211_chan_def he_chandef = *chandef;
const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
bool support_80_80, support_160;
u8 he_phy_cap;
u32 freq;
if (chandef->chan->band != NL80211_BAND_6GHZ)
return true;
sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ];
he_cap = ieee80211_get_he_iftype_cap(sband, iftype);
if (!he_cap) {
sdata_info(sdata, "Missing iftype sband data/HE cap");
return false;
}
he_phy_cap = he_cap->he_cap_elem.phy_cap_info[0];
support_160 =
he_phy_cap &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
support_80_80 =
he_phy_cap &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
if (!he_oper) {
sdata_info(sdata,
"HE is not advertised on (on %d MHz), expect issues\n",
chandef->chan->center_freq);
return false;
}
he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper);
if (!he_6ghz_oper) {
sdata_info(sdata,
"HE 6GHz operation missing (on %d MHz), expect issues\n",
chandef->chan->center_freq);
return false;
}
freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary,
NL80211_BAND_6GHZ);
he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
switch (u8_get_bits(he_6ghz_oper->control,
IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) {
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_20;
break;
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_40;
break;
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_80;
break;
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ:
he_chandef.width = NL80211_CHAN_WIDTH_80;
if (!he_6ghz_oper->ccfs1)
break;
if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) {
if (support_160)
he_chandef.width = NL80211_CHAN_WIDTH_160;
} else {
if (support_80_80)
he_chandef.width = NL80211_CHAN_WIDTH_80P80;
}
break;
}
if (he_chandef.width == NL80211_CHAN_WIDTH_160) {
he_chandef.center_freq1 =
ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
NL80211_BAND_6GHZ);
} else {
he_chandef.center_freq1 =
ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0,
NL80211_BAND_6GHZ);
he_chandef.center_freq2 =
ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
NL80211_BAND_6GHZ);
}
if (!cfg80211_chandef_valid(&he_chandef)) {
sdata_info(sdata,
"HE 6GHz operation resulted in invalid chandef: %d MHz/%d/%d MHz/%d MHz\n",
he_chandef.chan ? he_chandef.chan->center_freq : 0,
he_chandef.width,
he_chandef.center_freq1,
he_chandef.center_freq2);
return false;
}
*chandef = he_chandef;
return true;
}
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates)