]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: cfg80211: support configuring an S1G short beaconing BSS
authorLachlan Hodges <lachlan.hodges@morsemicro.com>
Thu, 17 Jul 2025 07:42:02 +0000 (17:42 +1000)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 18 Jul 2025 12:14:43 +0000 (14:14 +0200)
S1G short beacons are an optional frame type used in an S1G BSS
that contain a limited set of elements. While they are optional,
they are a fundamental part of S1G that enables significant
power saving.

Expose 2 additional netlink attributes,
NL80211_ATTR_S1G_LONG_BEACON_PERIOD which denotes the number of beacon
intervals between each long beacon and NL80211_ATTR_S1G_SHORT_BEACON
which is a nested attribute containing the short beacon tail and
head. We split them as the long beacon period cannot be updated,
and is only used when initialisng the interface, whereas the short
beacon data can be used to both initialise and update the templates.
This follows how things such as the beacon interval and DTIM period
currently operate.

During the initialisation path, we ensure we have the long beacon
period if the short beacon data is being passed down, whereas
the update path will simply update the template if its sent down.

The short beacon data is validated using the same routines for regular
beacons as they support correctly parsing the short beacon format
while ensuring the frame is well-formed.

Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
Link: https://patch.msgid.link/20250717074205.312577-2-lachlan.hodges@morsemicro.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/nl80211.c

index 17f2a665dce6d52c94f9e3af72e2855d289af981..44a1055a81ba05e3c6499fd7a25866839d31d8af 100644 (file)
@@ -1423,6 +1423,23 @@ struct cfg80211_unsol_bcast_probe_resp {
        const u8 *tmpl;
 };
 
+/**
+ * struct cfg80211_s1g_short_beacon - S1G short beacon data.
+ *
+ * @update: Set to true if the feature configuration should be updated.
+ * @short_head: Short beacon head.
+ * @short_tail: Short beacon tail.
+ * @short_head_len: Short beacon head len.
+ * @short_tail_len: Short beacon tail len.
+ */
+struct cfg80211_s1g_short_beacon {
+       bool update;
+       const u8 *short_head;
+       const u8 *short_tail;
+       size_t short_head_len;
+       size_t short_tail_len;
+};
+
 /**
  * struct cfg80211_ap_settings - AP configuration
  *
@@ -1463,6 +1480,8 @@ struct cfg80211_unsol_bcast_probe_resp {
  * @fils_discovery: FILS discovery transmission parameters
  * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
  * @mbssid_config: AP settings for multiple bssid
+ * @s1g_long_beacon_period: S1G long beacon period
+ * @s1g_short_beacon: S1G short beacon data
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -1496,6 +1515,8 @@ struct cfg80211_ap_settings {
        struct cfg80211_fils_discovery fils_discovery;
        struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
        struct cfg80211_mbssid_config mbssid_config;
+       u8 s1g_long_beacon_period;
+       struct cfg80211_s1g_short_beacon s1g_short_beacon;
 };
 
 
@@ -1507,11 +1528,13 @@ struct cfg80211_ap_settings {
  * @beacon: beacon data
  * @fils_discovery: FILS discovery transmission parameters
  * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
+ * @s1g_short_beacon: S1G short beacon data
  */
 struct cfg80211_ap_update {
        struct cfg80211_beacon_data beacon;
        struct cfg80211_fils_discovery fils_discovery;
        struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
+       struct cfg80211_s1g_short_beacon s1g_short_beacon;
 };
 
 /**
index 39460334dafb3d5d778ae74ef0d8eb8a6b28b89f..d1a14f2892d9ee4d3077ad73e90f729bf70c7c53 100644 (file)
@@ -2915,6 +2915,19 @@ enum nl80211_commands {
  *     applicable to that specific radio only. If the radio id is greater
  *     thank the number of radios, error denoting invalid value is returned.
  *
+ * @NL80211_ATTR_S1G_LONG_BEACON_PERIOD: (u8) Integer attribute that represents
+ *     the number of beacon intervals between each long beacon transmission
+ *     for an S1G BSS with short beaconing enabled. This is a required
+ *     attribute for initialising an S1G short beaconing BSS. When updating
+ *     the short beacon data, this is not required. It has a minimum value of
+ *     2 (i.e 2 beacon intervals).
+ *
+ * @NL80211_ATTR_S1G_SHORT_BEACON: Nested attribute containing the short beacon
+ *     head and tail used to set or update the short beacon templates. When
+ *     bringing up a new interface, %NL80211_ATTR_S1G_LONG_BEACON_PERIOD is
+ *     required alongside this attribute. Refer to
+ *     @enum nl80211_s1g_short_beacon_attrs for the attribute definitions.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3474,6 +3487,9 @@ enum nl80211_attrs {
 
        NL80211_ATTR_WIPHY_RADIO_INDEX,
 
+       NL80211_ATTR_S1G_LONG_BEACON_PERIOD,
+       NL80211_ATTR_S1G_SHORT_BEACON,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -8148,4 +8164,27 @@ enum nl80211_wiphy_radio_freq_range {
        NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
 };
 
+/**
+ * enum nl80211_s1g_short_beacon_attrs - S1G short beacon data
+ *
+ * @__NL80211_S1G_SHORT_BEACON_ATTR_INVALID: Invalid
+ *
+ * @NL80211_S1G_SHORT_BEACON_ATTR_HEAD: Short beacon head (binary).
+ * @NL80211_S1G_SHORT_BEACON_ATTR_TAIL: Short beacon tail (binary).
+ *
+ * @__NL80211_S1G_SHORT_BEACON_ATTR_LAST: Internal
+ * @NL80211_S1G_SHORT_BEACON_ATTR_MAX: Highest attribute
+ */
+enum nl80211_s1g_short_beacon_attrs {
+       __NL80211_S1G_SHORT_BEACON_ATTR_INVALID,
+
+       NL80211_S1G_SHORT_BEACON_ATTR_HEAD,
+       NL80211_S1G_SHORT_BEACON_ATTR_TAIL,
+
+       /* keep last */
+       __NL80211_S1G_SHORT_BEACON_ATTR_LAST,
+       NL80211_S1G_SHORT_BEACON_ATTR_MAX =
+               __NL80211_S1G_SHORT_BEACON_ATTR_LAST - 1
+};
+
 #endif /* __LINUX_NL80211_H */
index 20bc0f052c162ea57e3505af301a076aef7ef1c1..1c808b08b74729ed46a77653e9b66a4e7367bdff 100644 (file)
@@ -482,6 +482,16 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
        [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
 };
 
+static const struct nla_policy
+nl80211_s1g_short_beacon[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1] = {
+       [NL80211_S1G_SHORT_BEACON_ATTR_HEAD] =
+               NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_beacon_head,
+                                      IEEE80211_MAX_DATA_LEN),
+       [NL80211_S1G_SHORT_BEACON_ATTR_TAIL] =
+               NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr,
+                                      IEEE80211_MAX_DATA_LEN),
+};
+
 static const struct netlink_range_validation nl80211_punct_bitmap_range = {
        .min = 0,
        .max = 0xffff,
@@ -858,6 +868,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_EPCS] = { .type = NLA_FLAG },
        [NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 },
        [NL80211_ATTR_WIPHY_RADIO_INDEX] = { .type = NLA_U8 },
+       [NL80211_ATTR_S1G_LONG_BEACON_PERIOD] = NLA_POLICY_MIN(NLA_U8, 2),
+       [NL80211_ATTR_S1G_SHORT_BEACON] =
+               NLA_POLICY_NESTED(nl80211_s1g_short_beacon),
 };
 
 /* policy for the key attributes */
@@ -6202,6 +6215,41 @@ static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params
        return 0;
 }
 
+static int
+nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev,
+                              struct nlattr *attrs,
+                              struct cfg80211_s1g_short_beacon *sb)
+{
+       struct nlattr *tb[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1];
+       int ret;
+
+       if (!rdev->wiphy.bands[NL80211_BAND_S1GHZ])
+               return -EINVAL;
+
+       ret = nla_parse_nested(tb, NL80211_S1G_SHORT_BEACON_ATTR_MAX, attrs,
+                              NULL, NULL);
+       if (ret)
+               return ret;
+
+       /* Short beacon tail is optional (i.e might only include the TIM) */
+       if (!tb[NL80211_S1G_SHORT_BEACON_ATTR_HEAD])
+               return -EINVAL;
+
+       sb->short_head = nla_data(tb[NL80211_S1G_SHORT_BEACON_ATTR_HEAD]);
+       sb->short_head_len = nla_len(tb[NL80211_S1G_SHORT_BEACON_ATTR_HEAD]);
+       sb->short_tail_len = 0;
+
+       if (tb[NL80211_S1G_SHORT_BEACON_ATTR_TAIL]) {
+               sb->short_tail =
+                       nla_data(tb[NL80211_S1G_SHORT_BEACON_ATTR_TAIL]);
+               sb->short_tail_len =
+                       nla_len(tb[NL80211_S1G_SHORT_BEACON_ATTR_TAIL]);
+       }
+
+       sb->update = true;
+       return 0;
+}
+
 static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -6442,6 +6490,22 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
+       if (info->attrs[NL80211_ATTR_S1G_SHORT_BEACON]) {
+               if (!info->attrs[NL80211_ATTR_S1G_LONG_BEACON_PERIOD]) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               params->s1g_long_beacon_period = nla_get_u8(
+                       info->attrs[NL80211_ATTR_S1G_LONG_BEACON_PERIOD]);
+
+               err = nl80211_parse_s1g_short_beacon(
+                       rdev, info->attrs[NL80211_ATTR_S1G_SHORT_BEACON],
+                       &params->s1g_short_beacon);
+               if (err)
+                       goto out;
+       }
+
        err = nl80211_calculate_ap_params(params);
        if (err)
                goto out;
@@ -6550,6 +6614,14 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
                        goto out;
        }
 
+       attr = info->attrs[NL80211_ATTR_S1G_SHORT_BEACON];
+       if (attr) {
+               err = nl80211_parse_s1g_short_beacon(rdev, attr,
+                                                    &params->s1g_short_beacon);
+               if (err)
+                       goto out;
+       }
+
        err = rdev_change_beacon(rdev, dev, params);
 
 out: