mac80211: implement HE support for mesh
authorSven Eckelmann <seckelmann@datto.com>
Wed, 24 Jul 2019 16:33:56 +0000 (18:33 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 26 Jul 2019 14:14:12 +0000 (16:14 +0200)
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: Sven Eckelmann <seckelmann@datto.com>
Forwarded: https://patchwork.kernel.org/patch/11029299/
Link: https://lore.kernel.org/r/20190724163359.3507-2-sven@narfation.org
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/util.c

index 472c5a40317d474ae2b95ed31a8ce49f1e53f7c5..a5818ff0e2a493fbbbc44213f7d7205d5105bcc7 100644 (file)
@@ -2136,9 +2136,11 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
                               u32 cap);
 u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
                                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,
                              const struct ieee80211_sta_he_cap *he_cap,
                              u8 *end);
+u8 *ieee80211_ie_build_he_oper(u8 *pos);
 int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
                             const struct ieee80211_supported_band *sband,
                             const u8 *srates, int srates_len, u32 *rates);
index 2e7fa743c89228a40f39589aad6ee0ae040da860..d09b3c789314da9ade860f96892483a334609b2a 100644 (file)
@@ -532,6 +532,61 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
        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)
 {
        struct ieee80211_sub_if_data *sdata =
@@ -677,6 +732,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 *pos;
        struct ieee80211_sub_if_data *sdata;
        int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon);
@@ -687,6 +743,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        band = chanctx_conf->def.chan->band;
        rcu_read_unlock();
 
+       ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
+                                               NL80211_IFTYPE_MESH_POINT);
        head_len = hdr_len +
                   2 + /* NULL SSID */
                   /* Channel Switch Announcement */
@@ -706,6 +764,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                   2 + sizeof(__le16) + /* awake window */
                   2 + sizeof(struct ieee80211_vht_cap) +
                   2 + sizeof(struct ieee80211_vht_operation) +
+                  ie_len_he_cap +
+                  2 + 1 + sizeof(struct ieee80211_he_operation) +
                   ifmsh->ie_len;
 
        bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
@@ -823,6 +883,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
            mesh_add_awake_window_ie(sdata, skb) ||
            mesh_add_vht_cap_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))
                goto out_free;
 
index 94d57cce70dae29db187fe23004a2b2e2dbe7ee2..953f720754e82bfb16fdf0d535b753e211c88ad8 100644 (file)
@@ -218,6 +218,10 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
                        struct sk_buff *skb);
 int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
                         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);
 int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
 void ieee80211s_init(void);
index dd3aefd052a92adec044ecf4a61465d48504269c..737c5f4dbf5201b4c408059e1223be66880ac8f1 100644 (file)
@@ -218,9 +218,12 @@ 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;
        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);
        skb = dev_alloc_skb(local->tx_headroom +
                            hdr_len +
                            2 + /* capability info */
@@ -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_vht_cap) +
                            2 + sizeof(struct ieee80211_vht_operation) +
+                           ie_len_he_cap +
+                           2 + 1 + sizeof(struct ieee80211_he_operation) +
                            2 + 8 + /* peering IE */
                            sdata->u.mesh.ie_len);
        if (!skb)
@@ -321,7 +326,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
                if (mesh_add_ht_cap_ie(sdata, skb) ||
                    mesh_add_ht_oper_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;
        }
 
@@ -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,
                                            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)
                changed |= IEEE80211_RC_BW_CHANGED;
 
index 3e2aeb1a75b4ad3d2fef7c1f20fb291821255421..caa317faee3c835db1e226c8706b9c38d79bc217 100644 (file)
@@ -2713,6 +2713,27 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
        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,
                              const struct ieee80211_sta_he_cap *he_cap,
                              u8 *end)
@@ -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);
 }
 
+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,
                               struct cfg80211_chan_def *chandef)
 {