wifi: mac80211: generate EMA beacons in AP mode
authorAloka Dixit <quic_alokad@quicinc.com>
Tue, 6 Dec 2022 00:50:37 +0000 (16:50 -0800)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 22 Mar 2023 12:54:57 +0000 (13:54 +0100)
Add APIs to generate an array of beacons for an EMA AP (enhanced
multiple BSSID advertisements), each including a single MBSSID element.
EMA profile periodicity equals the count of elements.

- ieee80211_beacon_get_template_ema_list() - Generate and return all
EMA beacon templates. Drivers must call ieee80211_beacon_free_ema_list()
to free the memory. No change in the prototype for the existing API,
ieee80211_beacon_get_template(), which should be used for non-EMA AP.

- ieee80211_beacon_get_template_ema_index() - Generate a beacon which
includes the multiple BSSID element at the given index. Drivers can use
this function in a loop until NULL is returned which indicates end of
available MBSSID elements.

- ieee80211_beacon_free_ema_list() - free the memory allocated for the
list of EMA beacon templates.

Modify existing functions ieee80211_beacon_get_ap(),
ieee80211_get_mbssid_beacon_len() and ieee80211_beacon_add_mbssid()
to accept a new parameter for EMA index.

Signed-off-by: Aloka Dixit <quic_alokad@quicinc.com>
Co-developed-by: John Crispin <john@phrozen.org>
Signed-off-by: John Crispin <john@phrozen.org>
Link: https://lore.kernel.org/r/20221206005040.3177-2-quic_alokad@quicinc.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/tx.c

index fcfe3e9aff3dae9d612d79893af6fd1640a8ad07..679421d37a423283dbc294acdc16b0686c01d5f7 100644 (file)
@@ -5285,6 +5285,74 @@ ieee80211_beacon_get_template(struct ieee80211_hw *hw,
                              struct ieee80211_mutable_offsets *offs,
                              unsigned int link_id);
 
+/**
+ * ieee80211_beacon_get_template_ema_index - EMA beacon template generation
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @offs: &struct ieee80211_mutable_offsets pointer to struct that will
+ *     receive the offsets that may be updated by the driver.
+ * @link_id: the link id to which the beacon belongs (or 0 for a non-MLD AP).
+ * @ema_index: index of the beacon in the EMA set.
+ *
+ * This function follows the same rules as ieee80211_beacon_get_template()
+ * but returns a beacon template which includes multiple BSSID element at the
+ * requested index.
+ *
+ * Return: The beacon template. %NULL indicates the end of EMA templates.
+ */
+struct sk_buff *
+ieee80211_beacon_get_template_ema_index(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_mutable_offsets *offs,
+                                       unsigned int link_id, u8 ema_index);
+
+/**
+ * struct ieee80211_ema_beacons - List of EMA beacons
+ * @cnt: count of EMA beacons.
+ *
+ * @bcn: array of EMA beacons.
+ * @bcn.skb: the skb containing this specific beacon
+ * @bcn.offs: &struct ieee80211_mutable_offsets pointer to struct that will
+ *     receive the offsets that may be updated by the driver.
+ */
+struct ieee80211_ema_beacons {
+       u8 cnt;
+       struct {
+               struct sk_buff *skb;
+               struct ieee80211_mutable_offsets offs;
+       } bcn[];
+};
+
+/**
+ * ieee80211_beacon_get_template_ema_list - EMA beacon template generation
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @link_id: the link id to which the beacon belongs (or 0 for a non-MLD AP)
+ *
+ * This function follows the same rules as ieee80211_beacon_get_template()
+ * but allocates and returns a pointer to list of all beacon templates required
+ * to cover all profiles in the multiple BSSID set. Each template includes only
+ * one multiple BSSID element.
+ *
+ * Driver must call ieee80211_beacon_free_ema_list() to free the memory.
+ *
+ * Return: EMA beacon templates of type struct ieee80211_ema_beacons *.
+ *     %NULL on error.
+ */
+struct ieee80211_ema_beacons *
+ieee80211_beacon_get_template_ema_list(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      unsigned int link_id);
+
+/**
+ * ieee80211_beacon_free_ema_list - free an EMA beacon template list
+ * @ema_beacons: list of EMA beacons of type &struct ieee80211_ema_beacons pointers.
+ *
+ * This function will free a list previously acquired by calling
+ * ieee80211_beacon_get_template_ema_list()
+ */
+void ieee80211_beacon_free_ema_list(struct ieee80211_ema_beacons *ema_beacons);
+
 /**
  * ieee80211_beacon_get_tim - beacon generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
index 760ad934f9e12e7713df0d040ba459a14da7df3b..db5fa334b8014e6f162f4760f32fc0052f535e42 100644 (file)
@@ -1122,11 +1122,11 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
        if (params->mbssid_ies) {
                mbssid = params->mbssid_ies;
                size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
-               size += ieee80211_get_mbssid_beacon_len(mbssid);
+               size += ieee80211_get_mbssid_beacon_len(mbssid, mbssid->cnt);
        } else if (old && old->mbssid_ies) {
                mbssid = old->mbssid_ies;
                size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
-               size += ieee80211_get_mbssid_beacon_len(mbssid);
+               size += ieee80211_get_mbssid_beacon_len(mbssid, mbssid->cnt);
        }
 
        new = kzalloc(size, GFP_KERNEL);
@@ -3406,8 +3406,11 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
 
        len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
              beacon->proberesp_ies_len + beacon->assocresp_ies_len +
-             beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len +
-             ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies);
+             beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
+
+       if (beacon->mbssid_ies)
+               len += ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies,
+                                                      beacon->mbssid_ies->cnt);
 
        new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
        if (!new_beacon)
index c8c037a9e4e2afca6192d7eb7378c0659f5ceed3..84d10e993eca3b1c2ac9f6d8e26708cce7d319cc 100644 (file)
@@ -1186,13 +1186,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
 }
 
 static inline int
-ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems)
+ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems, u8 i)
 {
-       int i, len = 0;
+       int len = 0;
 
-       if (!elems)
+       if (!elems || !elems->cnt || i > elems->cnt)
                return 0;
 
+       if (i < elems->cnt)
+               return elems->elem[i].len;
+
+       /* i == elems->cnt, calculate total length of all MBSSID elements */
        for (i = 0; i < elems->cnt; i++)
                len += elems->elem[i].len;
 
index de17926484bddbf762282457da8efc9e10395a31..139eec6c64da2e7465527a5c8b98eab63f926c0e 100644 (file)
@@ -5212,13 +5212,20 @@ ieee80211_beacon_get_finish(struct ieee80211_hw *hw,
 }
 
 static void
-ieee80211_beacon_add_mbssid(struct sk_buff *skb, struct beacon_data *beacon)
+ieee80211_beacon_add_mbssid(struct sk_buff *skb, struct beacon_data *beacon,
+                           u8 i)
 {
-       int i;
+       if (!beacon->mbssid_ies || !beacon->mbssid_ies->cnt ||
+           i > beacon->mbssid_ies->cnt)
+               return;
 
-       if (!beacon->mbssid_ies)
+       if (i < beacon->mbssid_ies->cnt) {
+               skb_put_data(skb, beacon->mbssid_ies->elem[i].data,
+                            beacon->mbssid_ies->elem[i].len);
                return;
+       }
 
+       /* i == beacon->mbssid_ies->cnt, include all MBSSID elements */
        for (i = 0; i < beacon->mbssid_ies->cnt; i++)
                skb_put_data(skb, beacon->mbssid_ies->elem[i].data,
                             beacon->mbssid_ies->elem[i].len);
@@ -5231,7 +5238,8 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
                        struct ieee80211_mutable_offsets *offs,
                        bool is_template,
                        struct beacon_data *beacon,
-                       struct ieee80211_chanctx_conf *chanctx_conf)
+                       struct ieee80211_chanctx_conf *chanctx_conf,
+                       u8 ema_index)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@ -5250,7 +5258,9 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
        /* headroom, head length,
         * tail length, maximum TIM length and multiple BSSID length
         */
-       mbssid_len = ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies);
+       mbssid_len = ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies,
+                                                    ema_index);
+
        skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
                            beacon->tail_len + 256 +
                            local->hw.extra_beacon_tailroom + mbssid_len);
@@ -5268,7 +5278,7 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
                offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
 
                if (mbssid_len) {
-                       ieee80211_beacon_add_mbssid(skb, beacon);
+                       ieee80211_beacon_add_mbssid(skb, beacon, ema_index);
                        offs->mbssid_off = skb->len - mbssid_len;
                }
 
@@ -5287,12 +5297,51 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
        return skb;
 }
 
+static struct ieee80211_ema_beacons *
+ieee80211_beacon_get_ap_ema_list(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_link_data *link,
+                                struct ieee80211_mutable_offsets *offs,
+                                bool is_template, struct beacon_data *beacon,
+                                struct ieee80211_chanctx_conf *chanctx_conf)
+{
+       struct ieee80211_ema_beacons *ema = NULL;
+
+       if (!beacon->mbssid_ies || !beacon->mbssid_ies->cnt)
+               return NULL;
+
+       ema = kzalloc(struct_size(ema, bcn, beacon->mbssid_ies->cnt),
+                     GFP_ATOMIC);
+       if (!ema)
+               return NULL;
+
+       for (ema->cnt = 0; ema->cnt < beacon->mbssid_ies->cnt; ema->cnt++) {
+               ema->bcn[ema->cnt].skb =
+                       ieee80211_beacon_get_ap(hw, vif, link,
+                                               &ema->bcn[ema->cnt].offs,
+                                               is_template, beacon,
+                                               chanctx_conf, ema->cnt);
+               if (!ema->bcn[ema->cnt].skb)
+                       break;
+       }
+
+       if (ema->cnt == beacon->mbssid_ies->cnt)
+               return ema;
+
+       ieee80211_beacon_free_ema_list(ema);
+       return NULL;
+}
+
+#define IEEE80211_INCLUDE_ALL_MBSSID_ELEMS -1
+
 static struct sk_buff *
 __ieee80211_beacon_get(struct ieee80211_hw *hw,
                       struct ieee80211_vif *vif,
                       struct ieee80211_mutable_offsets *offs,
                       bool is_template,
-                      unsigned int link_id)
+                      unsigned int link_id,
+                      int ema_index,
+                      struct ieee80211_ema_beacons **ema_beacons)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct beacon_data *beacon = NULL;
@@ -5321,8 +5370,29 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                if (!beacon)
                        goto out;
 
-               skb = ieee80211_beacon_get_ap(hw, vif, link, offs, is_template,
-                                             beacon, chanctx_conf);
+               if (ema_beacons) {
+                       *ema_beacons =
+                               ieee80211_beacon_get_ap_ema_list(hw, vif, link,
+                                                                offs,
+                                                                is_template,
+                                                                beacon,
+                                                                chanctx_conf);
+               } else {
+                       if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
+                               if (ema_index >= beacon->mbssid_ies->cnt)
+                                       goto out; /* End of MBSSID elements */
+
+                               if (ema_index <= IEEE80211_INCLUDE_ALL_MBSSID_ELEMS)
+                                       ema_index = beacon->mbssid_ies->cnt;
+                       } else {
+                               ema_index = 0;
+                       }
+
+                       skb = ieee80211_beacon_get_ap(hw, vif, link, offs,
+                                                     is_template, beacon,
+                                                     chanctx_conf,
+                                                     ema_index);
+               }
        } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
                struct ieee80211_hdr *hdr;
@@ -5410,10 +5480,50 @@ ieee80211_beacon_get_template(struct ieee80211_hw *hw,
                              struct ieee80211_mutable_offsets *offs,
                              unsigned int link_id)
 {
-       return __ieee80211_beacon_get(hw, vif, offs, true, link_id);
+       return __ieee80211_beacon_get(hw, vif, offs, true, link_id,
+                                     IEEE80211_INCLUDE_ALL_MBSSID_ELEMS, NULL);
 }
 EXPORT_SYMBOL(ieee80211_beacon_get_template);
 
+struct sk_buff *
+ieee80211_beacon_get_template_ema_index(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_mutable_offsets *offs,
+                                       unsigned int link_id, u8 ema_index)
+{
+       return __ieee80211_beacon_get(hw, vif, offs, true, link_id, ema_index,
+                                     NULL);
+}
+EXPORT_SYMBOL(ieee80211_beacon_get_template_ema_index);
+
+void ieee80211_beacon_free_ema_list(struct ieee80211_ema_beacons *ema_beacons)
+{
+       u8 i;
+
+       if (!ema_beacons)
+               return;
+
+       for (i = 0; i < ema_beacons->cnt; i++)
+               kfree_skb(ema_beacons->bcn[i].skb);
+
+       kfree(ema_beacons);
+}
+EXPORT_SYMBOL(ieee80211_beacon_free_ema_list);
+
+struct ieee80211_ema_beacons *
+ieee80211_beacon_get_template_ema_list(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      unsigned int link_id)
+{
+       struct ieee80211_ema_beacons *ema_beacons = NULL;
+
+       WARN_ON(__ieee80211_beacon_get(hw, vif, NULL, false, link_id, 0,
+                                      &ema_beacons));
+
+       return ema_beacons;
+}
+EXPORT_SYMBOL(ieee80211_beacon_get_template_ema_list);
+
 struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                                         struct ieee80211_vif *vif,
                                         u16 *tim_offset, u16 *tim_length,
@@ -5421,7 +5531,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 {
        struct ieee80211_mutable_offsets offs = {};
        struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false,
-                                                    link_id);
+                                                    link_id,
+                                                    IEEE80211_INCLUDE_ALL_MBSSID_ELEMS,
+                                                    NULL);
        struct sk_buff *copy;
        int shift;