Commit 60ad72da authored by Sven Eckelmann's avatar Sven Eckelmann Committed by Johannes Berg

mac80211: implement HE support for mesh

Implement the basics required for supporting high efficiency with mesh:
include HE information elements in beacons, probe responses, and peering
action frames, and check for compatible HE configurations when peering.
Signed-off-by: default avatarSven Eckelmann <seckelmann@datto.com>

Forwarded: https://patchwork.kernel.org/patch/11029299/
Link: https://lore.kernel.org/r/20190724163359.3507-2-sven@narfation.orgSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent a0b4496a
...@@ -2136,9 +2136,11 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, ...@@ -2136,9 +2136,11 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap); u32 cap);
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
const struct cfg80211_chan_def *chandef); const struct cfg80211_chan_def *chandef);
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype);
u8 *ieee80211_ie_build_he_cap(u8 *pos, u8 *ieee80211_ie_build_he_cap(u8 *pos,
const struct ieee80211_sta_he_cap *he_cap, const struct ieee80211_sta_he_cap *he_cap,
u8 *end); u8 *end);
u8 *ieee80211_ie_build_he_oper(u8 *pos);
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband, const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates); const u8 *srates, int srates_len, u32 *rates);
......
...@@ -532,6 +532,61 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, ...@@ -532,6 +532,61 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
return 0; return 0;
} }
int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u8 ie_len)
{
const struct ieee80211_sta_he_cap *he_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);
if (!he_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_he_cap(pos, he_cap, pos + ie_len);
return 0;
}
int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
const struct ieee80211_sta_he_cap *he_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);
if (!he_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) < 2 + 1 + sizeof(struct ieee80211_he_operation))
return -ENOMEM;
pos = skb_put(skb, 2 + 1 + sizeof(struct ieee80211_he_operation));
ieee80211_ie_build_he_oper(pos);
return 0;
}
static void ieee80211_mesh_path_timer(struct timer_list *t) static void ieee80211_mesh_path_timer(struct timer_list *t)
{ {
struct ieee80211_sub_if_data *sdata = struct ieee80211_sub_if_data *sdata =
...@@ -677,6 +732,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ...@@ -677,6 +732,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
struct mesh_csa_settings *csa; struct mesh_csa_settings *csa;
enum nl80211_band band; enum nl80211_band band;
u8 ie_len_he_cap;
u8 *pos; u8 *pos;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon); int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon);
...@@ -687,6 +743,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ...@@ -687,6 +743,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
rcu_read_unlock(); rcu_read_unlock();
ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
NL80211_IFTYPE_MESH_POINT);
head_len = hdr_len + head_len = hdr_len +
2 + /* NULL SSID */ 2 + /* NULL SSID */
/* Channel Switch Announcement */ /* Channel Switch Announcement */
...@@ -706,6 +764,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ...@@ -706,6 +764,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
2 + sizeof(__le16) + /* awake window */ 2 + sizeof(__le16) + /* awake window */
2 + sizeof(struct ieee80211_vht_cap) + 2 + sizeof(struct ieee80211_vht_cap) +
2 + sizeof(struct ieee80211_vht_operation) + 2 + sizeof(struct ieee80211_vht_operation) +
ie_len_he_cap +
2 + 1 + sizeof(struct ieee80211_he_operation) +
ifmsh->ie_len; ifmsh->ie_len;
bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL); bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
...@@ -823,6 +883,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ...@@ -823,6 +883,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
mesh_add_awake_window_ie(sdata, skb) || mesh_add_awake_window_ie(sdata, skb) ||
mesh_add_vht_cap_ie(sdata, skb) || mesh_add_vht_cap_ie(sdata, skb) ||
mesh_add_vht_oper_ie(sdata, skb) || 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_vendor_ies(sdata, skb)) mesh_add_vendor_ies(sdata, skb))
goto out_free; goto out_free;
......
...@@ -218,6 +218,10 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata, ...@@ -218,6 +218,10 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb); struct sk_buff *skb);
int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb); struct sk_buff *skb);
int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u8 ie_len);
int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
void ieee80211s_init(void); void ieee80211s_init(void);
......
...@@ -218,9 +218,12 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, ...@@ -218,9 +218,12 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
bool include_plid = false; bool include_plid = false;
u16 peering_proto = 0; u16 peering_proto = 0;
u8 *pos, ie_len = 4; u8 *pos, ie_len = 4;
u8 ie_len_he_cap;
int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot); int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot);
int err = -ENOMEM; int err = -ENOMEM;
ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
NL80211_IFTYPE_MESH_POINT);
skb = dev_alloc_skb(local->tx_headroom + skb = dev_alloc_skb(local->tx_headroom +
hdr_len + hdr_len +
2 + /* capability info */ 2 + /* capability info */
...@@ -233,6 +236,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, ...@@ -233,6 +236,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
2 + sizeof(struct ieee80211_ht_operation) + 2 + sizeof(struct ieee80211_ht_operation) +
2 + sizeof(struct ieee80211_vht_cap) + 2 + sizeof(struct ieee80211_vht_cap) +
2 + sizeof(struct ieee80211_vht_operation) + 2 + sizeof(struct ieee80211_vht_operation) +
ie_len_he_cap +
2 + 1 + sizeof(struct ieee80211_he_operation) +
2 + 8 + /* peering IE */ 2 + 8 + /* peering IE */
sdata->u.mesh.ie_len); sdata->u.mesh.ie_len);
if (!skb) if (!skb)
...@@ -321,7 +326,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, ...@@ -321,7 +326,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
if (mesh_add_ht_cap_ie(sdata, skb) || if (mesh_add_ht_cap_ie(sdata, skb) ||
mesh_add_ht_oper_ie(sdata, skb) || mesh_add_ht_oper_ie(sdata, skb) ||
mesh_add_vht_cap_ie(sdata, skb) || mesh_add_vht_cap_ie(sdata, skb) ||
mesh_add_vht_oper_ie(sdata, skb)) 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))
goto free; goto free;
} }
...@@ -433,6 +440,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, ...@@ -433,6 +440,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
elems->vht_cap_elem, sta); elems->vht_cap_elem, sta);
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap,
elems->he_cap_len, sta);
if (bw != sta->sta.bandwidth) if (bw != sta->sta.bandwidth)
changed |= IEEE80211_RC_BW_CHANGED; changed |= IEEE80211_RC_BW_CHANGED;
......
...@@ -2713,6 +2713,27 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, ...@@ -2713,6 +2713,27 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
return pos; return pos;
} }
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
{
const struct ieee80211_sta_he_cap *he_cap;
struct ieee80211_supported_band *sband;
u8 n;
sband = ieee80211_get_sband(sdata);
if (!sband)
return 0;
he_cap = ieee80211_get_he_iftype_cap(sband, iftype);
if (!he_cap)
return 0;
n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem);
return 2 + 1 +
sizeof(he_cap->he_cap_elem) + n +
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
he_cap->he_cap_elem.phy_cap_info);
}
u8 *ieee80211_ie_build_he_cap(u8 *pos, u8 *ieee80211_ie_build_he_cap(u8 *pos,
const struct ieee80211_sta_he_cap *he_cap, const struct ieee80211_sta_he_cap *he_cap,
u8 *end) u8 *end)
...@@ -2902,6 +2923,34 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, ...@@ -2902,6 +2923,34 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
return pos + sizeof(struct ieee80211_vht_operation); return pos + sizeof(struct ieee80211_vht_operation);
} }
u8 *ieee80211_ie_build_he_oper(u8 *pos)
{
struct ieee80211_he_operation *he_oper;
u32 he_oper_params;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + sizeof(struct ieee80211_he_operation);
*pos++ = WLAN_EID_EXT_HE_OPERATION;
he_oper_params = 0;
he_oper_params |= u32_encode_bits(1023, /* disabled */
IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
he_oper_params |= u32_encode_bits(1,
IEEE80211_HE_OPERATION_ER_SU_DISABLE);
he_oper_params |= u32_encode_bits(1,
IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
he_oper = (struct ieee80211_he_operation *)pos;
he_oper->he_oper_params = cpu_to_le32(he_oper_params);
/* don't require special HE peer rates */
he_oper->he_mcs_nss_set = cpu_to_le16(0xffff);
/* TODO add VHT operational and 6GHz operational subelement? */
return pos + sizeof(struct ieee80211_vht_operation);
}
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment