wifi: mac80211: enable EHT mesh support

Similar to AP beacon, this enables the basic mesh EHT mode, including
EHT operation IE and the fixed field of EHT operation information IE.
As for the optional part (i.e. preamble puncturing bitmap) will be
added in future patch.

Tested-by: Lian Chen <lian.chen@mediatek.com>
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Link: https://lore.kernel.org/r/1e0ddb9001312451c3e99c4eed2072caf8075f61.1679935259.git.ryder.lee@mediatek.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Ryder Lee 2023-03-28 01:07:42 +08:00 committed by Johannes Berg
parent 0b354b8b09
commit df1875c4f3
5 changed files with 162 additions and 4 deletions

View File

@ -2479,6 +2479,8 @@ void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode,
struct sk_buff *skb);
u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef);
u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef,
const struct ieee80211_sta_eht_cap *eht_cap);
int ieee80211_parse_bitrates(enum nl80211_chan_width width,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates);

View File

@ -105,7 +105,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
ie->vht_operation, ie->ht_operation,
&sta_chan_def);
ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, NULL,
ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, ie->eht_operation,
&sta_chan_def);
if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
@ -639,6 +639,65 @@ int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata,
return 0;
}
int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u8 ie_len)
{
const struct ieee80211_sta_he_cap *he_cap;
const struct ieee80211_sta_eht_cap *eht_cap;
struct ieee80211_supported_band *sband;
u8 *pos;
sband = ieee80211_get_sband(sdata);
if (!sband)
return -EINVAL;
he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
if (!he_cap || !eht_cap ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
return 0;
if (skb_tailroom(skb) < ie_len)
return -ENOMEM;
pos = skb_put(skb, ie_len);
ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + ie_len, false);
return 0;
}
int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
const struct ieee80211_sta_eht_cap *eht_cap;
struct ieee80211_supported_band *sband;
u32 len;
u8 *pos;
sband = ieee80211_get_sband(sdata);
if (!sband)
return -EINVAL;
eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
if (!eht_cap ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
return 0;
len = 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) +
offsetof(struct ieee80211_eht_operation_info, optional);
if (skb_tailroom(skb) < len)
return -ENOMEM;
pos = skb_put(skb, len);
ieee80211_ie_build_eht_oper(pos, &sdata->vif.bss_conf.chandef, eht_cap);
return 0;
}
static void ieee80211_mesh_path_timer(struct timer_list *t)
{
struct ieee80211_sub_if_data *sdata =
@ -697,6 +756,9 @@ ieee80211_mesh_update_bss_params(struct ieee80211_sub_if_data *sdata,
if (he_oper)
sdata->vif.bss_conf.he_oper.params =
__le32_to_cpu(he_oper->he_oper_params);
sdata->vif.bss_conf.eht_support =
!!ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
}
bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
@ -903,7 +965,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
struct ieee80211_chanctx_conf *chanctx_conf;
struct mesh_csa_settings *csa;
enum nl80211_band band;
u8 ie_len_he_cap;
u8 ie_len_he_cap, ie_len_eht_cap;
u8 *pos;
struct ieee80211_sub_if_data *sdata;
int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon);
@ -916,6 +978,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
NL80211_IFTYPE_MESH_POINT);
ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata,
NL80211_IFTYPE_MESH_POINT);
head_len = hdr_len +
2 + /* NULL SSID */
/* Channel Switch Announcement */
@ -939,6 +1003,9 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
2 + 1 + sizeof(struct ieee80211_he_operation) +
sizeof(struct ieee80211_he_6ghz_oper) +
2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
ie_len_eht_cap +
2 + 1 + offsetof(struct ieee80211_eht_operation, optional) +
offsetof(struct ieee80211_eht_operation_info, optional) +
ifmsh->ie_len;
bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
@ -1059,6 +1126,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
mesh_add_he_oper_ie(sdata, skb) ||
mesh_add_he_6ghz_cap_ie(sdata, skb) ||
mesh_add_eht_cap_ie(sdata, skb, ie_len_eht_cap) ||
mesh_add_eht_oper_ie(sdata, skb) ||
mesh_add_vendor_ies(sdata, skb))
goto out_free;

View File

@ -234,6 +234,10 @@ int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u8 ie_len);
int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
void ieee80211s_init(void);

View File

@ -219,12 +219,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
bool include_plid = false;
u16 peering_proto = 0;
u8 *pos, ie_len = 4;
u8 ie_len_he_cap;
u8 ie_len_he_cap, ie_len_eht_cap;
int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot);
int err = -ENOMEM;
ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
NL80211_IFTYPE_MESH_POINT);
ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata,
NL80211_IFTYPE_MESH_POINT);
skb = dev_alloc_skb(local->tx_headroom +
hdr_len +
2 + /* capability info */
@ -241,6 +243,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
2 + 1 + sizeof(struct ieee80211_he_operation) +
sizeof(struct ieee80211_he_6ghz_oper) +
2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
ie_len_eht_cap +
2 + 1 + offsetof(struct ieee80211_eht_operation, optional) +
offsetof(struct ieee80211_eht_operation_info, optional) +
2 + 8 + /* peering IE */
sdata->u.mesh.ie_len);
if (!skb)
@ -332,7 +337,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
mesh_add_vht_oper_ie(sdata, skb) ||
mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
mesh_add_he_oper_ie(sdata, skb) ||
mesh_add_he_6ghz_cap_ie(sdata, skb))
mesh_add_he_6ghz_cap_ie(sdata, skb) ||
mesh_add_eht_cap_ie(sdata, skb, ie_len_eht_cap) ||
mesh_add_eht_oper_ie(sdata, skb))
goto free;
}
@ -451,6 +458,11 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
elems->he_6ghz_capa,
&sta->deflink);
ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, elems->he_cap,
elems->he_cap_len,
elems->eht_cap, elems->eht_cap_len,
&sta->deflink);
if (bw != sta->sta.deflink.bandwidth)
changed |= IEEE80211_RC_BW_CHANGED;

View File

@ -3485,6 +3485,77 @@ out:
return pos;
}
u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef,
const struct ieee80211_sta_eht_cap *eht_cap)
{
const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss =
&eht_cap->eht_mcs_nss_supp.only_20mhz;
struct ieee80211_eht_operation *eht_oper;
struct ieee80211_eht_operation_info *eht_oper_info;
u8 eht_oper_len = offsetof(struct ieee80211_eht_operation, optional);
u8 eht_oper_info_len =
offsetof(struct ieee80211_eht_operation_info, optional);
u8 chan_width = 0;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + eht_oper_len + eht_oper_info_len;
*pos++ = WLAN_EID_EXT_EHT_OPERATION;
eht_oper = (struct ieee80211_eht_operation *)pos;
memcpy(&eht_oper->basic_mcs_nss, eht_mcs_nss, sizeof(*eht_mcs_nss));
eht_oper->params |= IEEE80211_EHT_OPER_INFO_PRESENT;
pos += eht_oper_len;
eht_oper_info =
(struct ieee80211_eht_operation_info *)eht_oper->optional;
eht_oper_info->ccfs0 =
ieee80211_frequency_to_channel(chandef->center_freq1);
if (chandef->center_freq2)
eht_oper_info->ccfs1 =
ieee80211_frequency_to_channel(chandef->center_freq2);
else
eht_oper_info->ccfs1 = 0;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_320:
chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ;
eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
if (chandef->chan->center_freq < chandef->center_freq1)
eht_oper_info->ccfs0 -= 16;
else
eht_oper_info->ccfs0 += 16;
break;
case NL80211_CHAN_WIDTH_160:
eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
if (chandef->chan->center_freq < chandef->center_freq1)
eht_oper_info->ccfs0 -= 8;
else
eht_oper_info->ccfs0 += 8;
fallthrough;
case NL80211_CHAN_WIDTH_80P80:
chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ;
break;
case NL80211_CHAN_WIDTH_80:
chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ;
break;
case NL80211_CHAN_WIDTH_40:
chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ;
break;
default:
chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ;
break;
}
eht_oper_info->control = chan_width;
pos += eht_oper_info_len;
/* TODO: eht_oper_info->optional */
return pos;
}
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef)
{