mac80211: backport support for BSS color changes
authorFelix Fietkau <nbd@nbd.name>
Fri, 24 Sep 2021 14:53:33 +0000 (16:53 +0200)
committerFelix Fietkau <nbd@nbd.name>
Sat, 25 Sep 2021 07:38:37 +0000 (09:38 +0200)
This is needed for an upcoming mt76 update

Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/kernel/mac80211/patches/subsys/387-nl80211-add-support-for-BSS-coloring.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/388-mac80211-add-support-for-BSS-color-change.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch

diff --git a/package/kernel/mac80211/patches/subsys/387-nl80211-add-support-for-BSS-coloring.patch b/package/kernel/mac80211/patches/subsys/387-nl80211-add-support-for-BSS-coloring.patch
new file mode 100644 (file)
index 0000000..36b705d
--- /dev/null
@@ -0,0 +1,485 @@
+From: John Crispin <john@phrozen.org>
+Date: Fri, 2 Jul 2021 19:44:07 +0200
+Subject: [PATCH] nl80211: add support for BSS coloring
+
+This patch adds support for BSS color collisions to the wireless subsystem.
+Add the required functionality to nl80211 that will notify about color
+collisions, triggering the color change and notifying when it is completed.
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: John Crispin <john@phrozen.org>
+Link: https://lore.kernel.org/r/500b3582aec8fe2c42ef46f3117b148cb7cbceb5.1625247619.git.lorenzo@kernel.org
+[remove unnecessary NULL initialisation]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1252,6 +1252,27 @@ struct cfg80211_csa_settings {
+ #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
+ /**
++ * struct cfg80211_color_change_settings - color change settings
++ *
++ * Used for bss color change
++ *
++ * @beacon_color_change: beacon data while performing the color countdown
++ * @counter_offsets_beacon: offsets of the counters within the beacon (tail)
++ * @counter_offsets_presp: offsets of the counters within the probe response
++ * @beacon_next: beacon data to be used after the color change
++ * @count: number of beacons until the color change
++ * @color: the color used after the change
++ */
++struct cfg80211_color_change_settings {
++      struct cfg80211_beacon_data beacon_color_change;
++      u16 counter_offset_beacon;
++      u16 counter_offset_presp;
++      struct cfg80211_beacon_data beacon_next;
++      u8 count;
++      u8 color;
++};
++
++/**
+  * struct iface_combination_params - input parameters for interface combinations
+  *
+  * Used to pass interface combination parameters
+@@ -3979,6 +4000,8 @@ struct mgmt_frame_regs {
+  *    This callback may sleep.
+  * @reset_tid_config: Reset TID specific configuration for the peer, for the
+  *    given TIDs. This callback may sleep.
++ *
++ * @color_change: Initiate a color change.
+  */
+ struct cfg80211_ops {
+       int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
+@@ -4309,6 +4332,9 @@ struct cfg80211_ops {
+                                   const u8 *peer, u8 tids);
+       int     (*set_sar_specs)(struct wiphy *wiphy,
+                                struct cfg80211_sar_specs *sar);
++      int     (*color_change)(struct wiphy *wiphy,
++                              struct net_device *dev,
++                              struct cfg80211_color_change_settings *params);
+ };
+ /*
+@@ -8094,4 +8120,70 @@ void cfg80211_update_owe_info_event(stru
+  */
+ void cfg80211_bss_flush(struct wiphy *wiphy);
++/**
++ * cfg80211_bss_color_notify - notify about bss color event
++ * @dev: network device
++ * @gfp: allocation flags
++ * @cmd: the actual event we want to notify
++ * @count: the number of TBTTs until the color change happens
++ * @color_bitmap: representations of the colors that the local BSS is aware of
++ */
++int cfg80211_bss_color_notify(struct net_device *dev, gfp_t gfp,
++                            enum nl80211_commands cmd, u8 count,
++                            u64 color_bitmap);
++
++/**
++ * cfg80211_obss_color_collision_notify - notify about bss color collision
++ * @dev: network device
++ * @color_bitmap: representations of the colors that the local BSS is aware of
++ */
++static inline int cfg80211_obss_color_collision_notify(struct net_device *dev,
++                                                     u64 color_bitmap)
++{
++      return cfg80211_bss_color_notify(dev, GFP_KERNEL,
++                                       NL80211_CMD_OBSS_COLOR_COLLISION,
++                                       0, color_bitmap);
++}
++
++/**
++ * cfg80211_color_change_started_notify - notify color change start
++ * @dev: the device on which the color is switched
++ * @count: the number of TBTTs until the color change happens
++ *
++ * Inform the userspace about the color change that has started.
++ */
++static inline int cfg80211_color_change_started_notify(struct net_device *dev,
++                                                     u8 count)
++{
++      return cfg80211_bss_color_notify(dev, GFP_KERNEL,
++                                       NL80211_CMD_COLOR_CHANGE_STARTED,
++                                       count, 0);
++}
++
++/**
++ * cfg80211_color_change_aborted_notify - notify color change abort
++ * @dev: the device on which the color is switched
++ *
++ * Inform the userspace about the color change that has aborted.
++ */
++static inline int cfg80211_color_change_aborted_notify(struct net_device *dev)
++{
++      return cfg80211_bss_color_notify(dev, GFP_KERNEL,
++                                       NL80211_CMD_COLOR_CHANGE_ABORTED,
++                                       0, 0);
++}
++
++/**
++ * cfg80211_color_change_notify - notify color change completion
++ * @dev: the device on which the color was switched
++ *
++ * Inform the userspace about the color change that has completed.
++ */
++static inline int cfg80211_color_change_notify(struct net_device *dev)
++{
++      return cfg80211_bss_color_notify(dev, GFP_KERNEL,
++                                       NL80211_CMD_COLOR_CHANGE_COMPLETED,
++                                       0, 0);
++}
++
+ #endif /* __NET_CFG80211_H */
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -1185,6 +1185,21 @@
+  *    passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to
+  *    specify the wiphy index to be applied to.
+  *
++ * @NL80211_CMD_OBSS_COLOR_COLLISION: This notification is sent out whenever
++ *    mac80211/drv detects a bss color collision.
++ *
++ * @NL80211_CMD_COLOR_CHANGE_REQUEST: This command is used to indicate that
++ *    userspace wants to change the BSS color.
++ *
++ * @NL80211_CMD_COLOR_CHANGE_STARTED: Notify userland, that a color change has
++ *    started
++ *
++ * @NL80211_CMD_COLOR_CHANGE_ABORTED: Notify userland, that the color change has
++ *    been aborted
++ *
++ * @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change
++ *    has completed
++ *
+  * @NL80211_CMD_MAX: highest used command number
+  * @__NL80211_CMD_AFTER_LAST: internal use
+  */
+@@ -1417,6 +1432,14 @@ enum nl80211_commands {
+       NL80211_CMD_SET_SAR_SPECS,
++      NL80211_CMD_OBSS_COLOR_COLLISION,
++
++      NL80211_CMD_COLOR_CHANGE_REQUEST,
++
++      NL80211_CMD_COLOR_CHANGE_STARTED,
++      NL80211_CMD_COLOR_CHANGE_ABORTED,
++      NL80211_CMD_COLOR_CHANGE_COMPLETED,
++
+       /* add new commands above here */
+       /* used to define NL80211_CMD_MAX below */
+@@ -2560,6 +2583,16 @@ enum nl80211_commands {
+  *    disassoc events to indicate that an immediate reconnect to the AP
+  *    is desired.
+  *
++ * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
++ *    %NL80211_CMD_OBSS_COLOR_COLLISION event.
++ *
++ * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
++ *    until the color switch event.
++ * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
++ *    switching to
++ * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
++ *    information for the time while performing a color switch.
++ *
+  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+  * @NL80211_ATTR_MAX: highest attribute number currently defined
+  * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3057,6 +3090,12 @@ enum nl80211_attrs {
+       NL80211_ATTR_DISABLE_HE,
++      NL80211_ATTR_OBSS_COLOR_BITMAP,
++
++      NL80211_ATTR_COLOR_CHANGE_COUNT,
++      NL80211_ATTR_COLOR_CHANGE_COLOR,
++      NL80211_ATTR_COLOR_CHANGE_ELEMS,
++
+       /* add attributes here, update the policy in nl80211.c */
+       __NL80211_ATTR_AFTER_LAST,
+@@ -5950,6 +5989,9 @@ enum nl80211_feature_flags {
+  *      frame protection for all management frames exchanged during the
+  *      negotiation and range measurement procedure.
+  *
++ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
++ *    detection and change announcemnts.
++ *
+  * @NUM_NL80211_EXT_FEATURES: number of extended features.
+  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+  */
+@@ -6014,6 +6056,7 @@ enum nl80211_ext_feature_index {
+       NL80211_EXT_FEATURE_SECURE_LTF,
+       NL80211_EXT_FEATURE_SECURE_RTT,
+       NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
++      NL80211_EXT_FEATURE_BSS_COLOR,
+       /* add new features before the definition below */
+       NUM_NL80211_EXT_FEATURES,
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -753,6 +753,10 @@ static const struct nla_policy nl80211_p
+                                NL80211_SAE_PWE_BOTH),
+       [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
+       [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
++      [NL80211_ATTR_OBSS_COLOR_BITMAP] = { .type = NLA_U64 },
++      [NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 },
++      [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
++      [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
+ };
+ /* policy for the key attributes */
+@@ -14677,6 +14681,106 @@ bad_tid_conf:
+       return ret;
+ }
++static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
++{
++      struct cfg80211_registered_device *rdev = info->user_ptr[0];
++      struct cfg80211_color_change_settings params = {};
++      struct net_device *dev = info->user_ptr[1];
++      struct wireless_dev *wdev = dev->ieee80211_ptr;
++      struct nlattr **tb;
++      u16 offset;
++      int err;
++
++      if (!rdev->ops->color_change)
++              return -EOPNOTSUPP;
++
++      if (!wiphy_ext_feature_isset(&rdev->wiphy,
++                                   NL80211_EXT_FEATURE_BSS_COLOR))
++              return -EOPNOTSUPP;
++
++      if (wdev->iftype != NL80211_IFTYPE_AP)
++              return -EOPNOTSUPP;
++
++      if (!info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT] ||
++          !info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR] ||
++          !info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS])
++              return -EINVAL;
++
++      params.count = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT]);
++      params.color = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR]);
++
++      err = nl80211_parse_beacon(rdev, info->attrs, &params.beacon_next);
++      if (err)
++              return err;
++
++      tb = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*tb), GFP_KERNEL);
++      if (!tb)
++              return -ENOMEM;
++
++      err = nla_parse_nested(tb, NL80211_ATTR_MAX,
++                             info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS],
++                             nl80211_policy, info->extack);
++      if (err)
++              goto out;
++
++      err = nl80211_parse_beacon(rdev, tb, &params.beacon_color_change);
++      if (err)
++              goto out;
++
++      if (!tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]) {
++              err = -EINVAL;
++              goto out;
++      }
++
++      if (nla_len(tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]) != sizeof(u16)) {
++              err = -EINVAL;
++              goto out;
++      }
++
++      offset = nla_get_u16(tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]);
++      if (offset >= params.beacon_color_change.tail_len) {
++              err = -EINVAL;
++              goto out;
++      }
++
++      if (params.beacon_color_change.tail[offset] != params.count) {
++              err = -EINVAL;
++              goto out;
++      }
++
++      params.counter_offset_beacon = offset;
++
++      if (tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]) {
++              if (nla_len(tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]) !=
++                  sizeof(u16)) {
++                      err = -EINVAL;
++                      goto out;
++              }
++
++              offset = nla_get_u16(tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]);
++              if (offset >= params.beacon_color_change.probe_resp_len) {
++                      err = -EINVAL;
++                      goto out;
++              }
++
++              if (params.beacon_color_change.probe_resp[offset] !=
++                  params.count) {
++                      err = -EINVAL;
++                      goto out;
++              }
++
++              params.counter_offset_presp = offset;
++      }
++
++      wdev_lock(wdev);
++      err = rdev_color_change(rdev, dev, &params);
++      wdev_unlock(wdev);
++
++out:
++      kfree(tb);
++      return err;
++}
++
+ #define NL80211_FLAG_NEED_WIPHY               0x01
+ #define NL80211_FLAG_NEED_NETDEV      0x02
+ #define NL80211_FLAG_NEED_RTNL                0x04
+@@ -15758,6 +15862,14 @@ static const struct genl_small_ops nl802
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
++      {
++              .cmd = NL80211_CMD_COLOR_CHANGE_REQUEST,
++              .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
++              .doit = nl80211_color_change,
++              .flags = GENL_UNS_ADMIN_PERM,
++              .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
++                                NL80211_FLAG_NEED_RTNL,
++      },
+ };
+ static struct genl_family nl80211_fam __genl_ro_after_init = {
+@@ -17384,6 +17496,51 @@ void cfg80211_ch_switch_started_notify(s
+ }
+ EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
++int cfg80211_bss_color_notify(struct net_device *dev, gfp_t gfp,
++                            enum nl80211_commands cmd, u8 count,
++                            u64 color_bitmap)
++{
++      struct wireless_dev *wdev = dev->ieee80211_ptr;
++      struct wiphy *wiphy = wdev->wiphy;
++      struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++      struct sk_buff *msg;
++      void *hdr;
++
++      ASSERT_WDEV_LOCK(wdev);
++
++      trace_cfg80211_bss_color_notify(dev, cmd, count, color_bitmap);
++
++      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
++      if (!msg)
++              return -ENOMEM;
++
++      hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
++      if (!hdr)
++              goto nla_put_failure;
++
++      if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
++              goto nla_put_failure;
++
++      if (cmd == NL80211_CMD_COLOR_CHANGE_STARTED &&
++          nla_put_u32(msg, NL80211_ATTR_COLOR_CHANGE_COUNT, count))
++              goto nla_put_failure;
++
++      if (cmd == NL80211_CMD_OBSS_COLOR_COLLISION &&
++          nla_put_u64_64bit(msg, NL80211_ATTR_OBSS_COLOR_BITMAP,
++                            color_bitmap, NL80211_ATTR_PAD))
++              goto nla_put_failure;
++
++      genlmsg_end(msg, hdr);
++
++      return genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
++                                     msg, 0, NL80211_MCGRP_MLME, gfp);
++
++nla_put_failure:
++      nlmsg_free(msg);
++      return -EINVAL;
++}
++EXPORT_SYMBOL(cfg80211_bss_color_notify);
++
+ void
+ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+                    const struct cfg80211_chan_def *chandef,
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1368,4 +1368,17 @@ static inline int rdev_set_sar_specs(str
+       return ret;
+ }
++static inline int rdev_color_change(struct cfg80211_registered_device *rdev,
++                                  struct net_device *dev,
++                                  struct cfg80211_color_change_settings *params)
++{
++      int ret;
++
++      trace_rdev_color_change(&rdev->wiphy, dev, params);
++      ret = rdev->ops->color_change(&rdev->wiphy, dev, params);
++      trace_rdev_return_int(&rdev->wiphy, ret);
++
++      return ret;
++}
++
+ #endif /* __CFG80211_RDEV_OPS */
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -3570,6 +3570,52 @@ TRACE_EVENT(rdev_set_sar_specs,
+                 WIPHY_PR_ARG, __entry->type, __entry->num)
+ );
++TRACE_EVENT(rdev_color_change,
++      TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
++               struct cfg80211_color_change_settings *params),
++      TP_ARGS(wiphy, netdev, params),
++      TP_STRUCT__entry(
++              WIPHY_ENTRY
++              NETDEV_ENTRY
++              __field(u8, count)
++              __field(u16, bcn_ofs)
++              __field(u16, pres_ofs)
++      ),
++      TP_fast_assign(
++              WIPHY_ASSIGN;
++              NETDEV_ASSIGN;
++              __entry->count = params->count;
++              __entry->bcn_ofs = params->counter_offset_beacon;
++              __entry->pres_ofs = params->counter_offset_presp;
++      ),
++      TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
++                ", count: %u",
++                WIPHY_PR_ARG, NETDEV_PR_ARG,
++                __entry->count)
++);
++
++TRACE_EVENT(cfg80211_bss_color_notify,
++      TP_PROTO(struct net_device *netdev,
++               enum nl80211_commands cmd,
++               u8 count, u64 color_bitmap),
++      TP_ARGS(netdev, cmd, count, color_bitmap),
++      TP_STRUCT__entry(
++              NETDEV_ENTRY
++              __field(enum nl80211_bss_scan_width, cmd)
++              __field(u8, count)
++              __field(u64, color_bitmap)
++      ),
++      TP_fast_assign(
++              NETDEV_ASSIGN;
++              __entry->cmd = cmd;
++              __entry->count = count;
++              __entry->color_bitmap = color_bitmap;
++      ),
++      TP_printk(NETDEV_PR_FMT ", cmd: %x, count: %u, bitmap: %llx",
++                NETDEV_PR_ARG, __entry->cmd, __entry->count,
++                __entry->color_bitmap)
++);
++
+ #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
+ #undef TRACE_INCLUDE_PATH
diff --git a/package/kernel/mac80211/patches/subsys/388-mac80211-add-support-for-BSS-color-change.patch b/package/kernel/mac80211/patches/subsys/388-mac80211-add-support-for-BSS-color-change.patch
new file mode 100644 (file)
index 0000000..0a31185
--- /dev/null
@@ -0,0 +1,524 @@
+From: John Crispin <john@phrozen.org>
+Date: Fri, 2 Jul 2021 19:44:08 +0200
+Subject: [PATCH] mac80211: add support for BSS color change
+
+The color change announcement is very similar to how CSA works where
+we have an IE that includes a counter. When the counter hits 0, the new
+color is applied via an updated beacon.
+
+This patch makes the CSA counter functionality reusable, rather than
+implementing it again. This also allows for future reuse incase support
+for other counter IEs gets added.
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: John Crispin <john@phrozen.org>
+Link: https://lore.kernel.org/r/057c1e67b82bee561ea44ce6a45a8462d3da6995.1625247619.git.lorenzo@kernel.org
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1710,6 +1710,10 @@ enum ieee80211_offload_flags {
+  *    protected by fq->lock.
+  * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
+  *    &enum ieee80211_offload_flags.
++ * @color_change_active: marks whether a color change is ongoing. Internally it is
++ *    write-protected by sdata_lock and local->mtx so holding either is fine
++ *    for read access.
++ * @color_change_color: the bss color that will be used after the change.
+  */
+ struct ieee80211_vif {
+       enum nl80211_iftype type;
+@@ -1738,6 +1742,9 @@ struct ieee80211_vif {
+       bool txqs_stopped[IEEE80211_NUM_ACS];
++      bool color_change_active;
++      u8 color_change_color;
++
+       /* must be last */
+       u8 drv_priv[] __aligned(sizeof(void *));
+ };
+@@ -4982,6 +4989,16 @@ void ieee80211_csa_finish(struct ieee802
+ bool ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif);
+ /**
++ * ieee80211_color_change_finish - notify mac80211 about color change
++ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
++ *
++ * After a color change announcement was scheduled and the counter in this
++ * announcement hits 1, this function must be called by the driver to
++ * notify mac80211 that the color can be changed
++ */
++void ieee80211_color_change_finish(struct ieee80211_vif *vif);
++
++/**
+  * ieee80211_proberesp_get - retrieve a Probe Response template
+  * @hw: pointer obtained from ieee80211_alloc_hw().
+  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+@@ -6726,6 +6743,18 @@ ieee80211_get_unsol_bcast_probe_resp_tmp
+                                         struct ieee80211_vif *vif);
+ /**
++ * ieeee80211_obss_color_collision_notify - notify userland about a BSS color
++ * collision.
++ *
++ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
++ * @color_bitmap: a 64 bit bitmap representing the colors that the local BSS is
++ *    aware of.
++ */
++void
++ieeee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
++                                     u64 color_bitmap);
++
++/**
+  * ieee80211_is_tx_data - check if frame is a data frame
+  *
+  * The function is used to check if a frame is a data frame. Frames with
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -827,9 +827,11 @@ static int ieee80211_set_monitor_channel
+       return ret;
+ }
+-static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
+-                                  const u8 *resp, size_t resp_len,
+-                                  const struct ieee80211_csa_settings *csa)
++static int
++ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
++                       const u8 *resp, size_t resp_len,
++                       const struct ieee80211_csa_settings *csa,
++                       const struct ieee80211_color_change_settings *cca)
+ {
+       struct probe_resp *new, *old;
+@@ -849,6 +851,8 @@ static int ieee80211_set_probe_resp(stru
+               memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_presp,
+                      csa->n_counter_offsets_presp *
+                      sizeof(new->cntdwn_counter_offsets[0]));
++      else if (cca)
++              new->cntdwn_counter_offsets[0] = cca->counter_offset_presp;
+       rcu_assign_pointer(sdata->u.ap.probe_resp, new);
+       if (old)
+@@ -954,7 +958,8 @@ static int ieee80211_set_ftm_responder_p
+ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+                                  struct cfg80211_beacon_data *params,
+-                                 const struct ieee80211_csa_settings *csa)
++                                 const struct ieee80211_csa_settings *csa,
++                                 const struct ieee80211_color_change_settings *cca)
+ {
+       struct beacon_data *new, *old;
+       int new_head_len, new_tail_len;
+@@ -1003,6 +1008,9 @@ static int ieee80211_assign_beacon(struc
+               memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_beacon,
+                      csa->n_counter_offsets_beacon *
+                      sizeof(new->cntdwn_counter_offsets[0]));
++      } else if (cca) {
++              new->cntdwn_current_counter = cca->count;
++              new->cntdwn_counter_offsets[0] = cca->counter_offset_beacon;
+       }
+       /* copy in head */
+@@ -1019,7 +1027,7 @@ static int ieee80211_assign_beacon(struc
+                       memcpy(new->tail, old->tail, new_tail_len);
+       err = ieee80211_set_probe_resp(sdata, params->probe_resp,
+-                                     params->probe_resp_len, csa);
++                                     params->probe_resp_len, csa, cca);
+       if (err < 0) {
+               kfree(new);
+               return err;
+@@ -1176,7 +1184,7 @@ static int ieee80211_start_ap(struct wip
+       if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
+               sdata->vif.bss_conf.beacon_tx_rate = params->beacon_rate;
+-      err = ieee80211_assign_beacon(sdata, &params->beacon, NULL);
++      err = ieee80211_assign_beacon(sdata, &params->beacon, NULL, NULL);
+       if (err < 0)
+               goto error;
+       changed |= err;
+@@ -1231,17 +1239,17 @@ static int ieee80211_change_beacon(struc
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       sdata_assert_lock(sdata);
+-      /* don't allow changing the beacon while CSA is in place - offset
++      /* don't allow changing the beacon while a countdown is in place - offset
+        * of channel switch counter may change
+        */
+-      if (sdata->vif.csa_active)
++      if (sdata->vif.csa_active || sdata->vif.color_change_active)
+               return -EBUSY;
+       old = sdata_dereference(sdata->u.ap.beacon, sdata);
+       if (!old)
+               return -ENOENT;
+-      err = ieee80211_assign_beacon(sdata, params, NULL);
++      err = ieee80211_assign_beacon(sdata, params, NULL, NULL);
+       if (err < 0)
+               return err;
+       ieee80211_bss_info_change_notify(sdata, err);
+@@ -3174,7 +3182,7 @@ static int ieee80211_set_after_csa_beaco
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
+-                                            NULL);
++                                            NULL, NULL);
+               kfree(sdata->u.ap.next_beacon);
+               sdata->u.ap.next_beacon = NULL;
+@@ -3340,7 +3348,7 @@ static int ieee80211_set_csa_beacon(stru
+               csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
+               csa.count = params->count;
+-              err = ieee80211_assign_beacon(sdata, &params->beacon_csa, &csa);
++              err = ieee80211_assign_beacon(sdata, &params->beacon_csa, &csa, NULL);
+               if (err < 0) {
+                       kfree(sdata->u.ap.next_beacon);
+                       return err;
+@@ -3428,6 +3436,15 @@ static int ieee80211_set_csa_beacon(stru
+       return 0;
+ }
++static void ieee80211_color_change_abort(struct ieee80211_sub_if_data  *sdata)
++{
++      sdata->vif.color_change_active = false;
++      kfree(sdata->u.ap.next_beacon);
++      sdata->u.ap.next_beacon = NULL;
++
++      cfg80211_color_change_aborted_notify(sdata->dev);
++}
++
+ static int
+ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                          struct cfg80211_csa_settings *params)
+@@ -3496,6 +3513,10 @@ __ieee80211_channel_switch(struct wiphy
+               goto out;
+       }
++      /* if there is a color change in progress, abort it */
++      if (sdata->vif.color_change_active)
++              ieee80211_color_change_abort(sdata);
++
+       err = ieee80211_set_csa_beacon(sdata, params, &changed);
+       if (err) {
+               ieee80211_vif_unreserve_chanctx(sdata);
+@@ -4147,6 +4168,196 @@ static int ieee80211_set_sar_specs(struc
+       return local->ops->set_sar_specs(&local->hw, sar);
+ }
++static int
++ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata,
++                                      u32 *changed)
++{
++      switch (sdata->vif.type) {
++      case NL80211_IFTYPE_AP: {
++              int ret;
++
++              ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
++                                            NULL, NULL);
++              kfree(sdata->u.ap.next_beacon);
++              sdata->u.ap.next_beacon = NULL;
++
++              if (ret < 0)
++                      return ret;
++
++              *changed |= ret;
++              break;
++      }
++      default:
++              WARN_ON_ONCE(1);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int
++ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata,
++                                struct cfg80211_color_change_settings *params,
++                                u32 *changed)
++{
++      struct ieee80211_color_change_settings color_change = {};
++      int err;
++
++      switch (sdata->vif.type) {
++      case NL80211_IFTYPE_AP:
++              sdata->u.ap.next_beacon =
++                      cfg80211_beacon_dup(&params->beacon_next);
++              if (!sdata->u.ap.next_beacon)
++                      return -ENOMEM;
++
++              if (params->count <= 1)
++                      break;
++
++              color_change.counter_offset_beacon =
++                      params->counter_offset_beacon;
++              color_change.counter_offset_presp =
++                      params->counter_offset_presp;
++              color_change.count = params->count;
++
++              err = ieee80211_assign_beacon(sdata, &params->beacon_color_change,
++                                            NULL, &color_change);
++              if (err < 0) {
++                      kfree(sdata->u.ap.next_beacon);
++                      return err;
++              }
++              *changed |= err;
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      return 0;
++}
++
++static void
++ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata,
++                                       u8 color, int enable, u32 changed)
++{
++      sdata->vif.bss_conf.he_bss_color.color = color;
++      sdata->vif.bss_conf.he_bss_color.enabled = enable;
++      changed |= BSS_CHANGED_HE_BSS_COLOR;
++
++      ieee80211_bss_info_change_notify(sdata, changed);
++}
++
++static int ieee80211_color_change_finalize(struct ieee80211_sub_if_data *sdata)
++{
++      struct ieee80211_local *local = sdata->local;
++      u32 changed = 0;
++      int err;
++
++      sdata_assert_lock(sdata);
++      lockdep_assert_held(&local->mtx);
++
++      sdata->vif.color_change_active = false;
++
++      err = ieee80211_set_after_color_change_beacon(sdata, &changed);
++      if (err) {
++              cfg80211_color_change_aborted_notify(sdata->dev);
++              return err;
++      }
++
++      ieee80211_color_change_bss_config_notify(sdata,
++                                               sdata->vif.color_change_color,
++                                               1, changed);
++      cfg80211_color_change_notify(sdata->dev);
++
++      return 0;
++}
++
++void ieee80211_color_change_finalize_work(struct work_struct *work)
++{
++      struct ieee80211_sub_if_data *sdata =
++              container_of(work, struct ieee80211_sub_if_data,
++                           color_change_finalize_work);
++      struct ieee80211_local *local = sdata->local;
++
++      sdata_lock(sdata);
++      mutex_lock(&local->mtx);
++
++      /* AP might have been stopped while waiting for the lock. */
++      if (!sdata->vif.color_change_active)
++              goto unlock;
++
++      if (!ieee80211_sdata_running(sdata))
++              goto unlock;
++
++      ieee80211_color_change_finalize(sdata);
++
++unlock:
++      mutex_unlock(&local->mtx);
++      sdata_unlock(sdata);
++}
++
++void ieee80211_color_change_finish(struct ieee80211_vif *vif)
++{
++      struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
++
++      ieee80211_queue_work(&sdata->local->hw,
++                           &sdata->color_change_finalize_work);
++}
++EXPORT_SYMBOL_GPL(ieee80211_color_change_finish);
++
++void
++ieeee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
++                                     u64 color_bitmap)
++{
++      struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
++
++      if (sdata->vif.color_change_active || sdata->vif.csa_active)
++              return;
++
++      cfg80211_obss_color_collision_notify(sdata->dev, color_bitmap);
++}
++EXPORT_SYMBOL_GPL(ieeee80211_obss_color_collision_notify);
++
++static int
++ieee80211_color_change(struct wiphy *wiphy, struct net_device *dev,
++                     struct cfg80211_color_change_settings *params)
++{
++      struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++      struct ieee80211_local *local = sdata->local;
++      u32 changed = 0;
++      int err;
++
++      sdata_assert_lock(sdata);
++
++      mutex_lock(&local->mtx);
++
++      /* don't allow another color change if one is already active or if csa
++       * is active
++       */
++      if (sdata->vif.color_change_active || sdata->vif.csa_active) {
++              err = -EBUSY;
++              goto out;
++      }
++
++      err = ieee80211_set_color_change_beacon(sdata, params, &changed);
++      if (err)
++              goto out;
++
++      sdata->vif.color_change_active = true;
++      sdata->vif.color_change_color = params->color;
++
++      cfg80211_color_change_started_notify(sdata->dev, params->count);
++
++      if (changed)
++              ieee80211_color_change_bss_config_notify(sdata, 0, 0, changed);
++      else
++              /* if the beacon didn't change, we can finalize immediately */
++              ieee80211_color_change_finalize(sdata);
++
++out:
++      mutex_unlock(&local->mtx);
++
++      return err;
++}
++
+ const struct cfg80211_ops mac80211_config_ops = {
+       .add_virtual_intf = ieee80211_add_iface,
+       .del_virtual_intf = ieee80211_del_iface,
+@@ -4251,4 +4462,5 @@ const struct cfg80211_ops mac80211_confi
+       .set_tid_config = ieee80211_set_tid_config,
+       .reset_tid_config = ieee80211_reset_tid_config,
+       .set_sar_specs = ieee80211_set_sar_specs,
++      .color_change = ieee80211_color_change,
+ };
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -248,6 +248,12 @@ struct ieee80211_csa_settings {
+       u8 count;
+ };
++struct ieee80211_color_change_settings {
++      u16 counter_offset_beacon;
++      u16 counter_offset_presp;
++      u8 count;
++};
++
+ struct beacon_data {
+       u8 *head, *tail;
+       int head_len, tail_len;
+@@ -932,6 +938,8 @@ struct ieee80211_sub_if_data {
+       bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
+       struct cfg80211_chan_def csa_chandef;
++      struct work_struct color_change_finalize_work;
++
+       struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */
+       struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */
+@@ -1900,6 +1908,9 @@ void ieee80211_csa_finalize_work(struct
+ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                            struct cfg80211_csa_settings *params);
++/* color change handling */
++void ieee80211_color_change_finalize_work(struct work_struct *work);
++
+ /* interface handling */
+ #define MAC80211_SUPPORTED_FEATURES_TX        (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
+                                        NETIF_F_HW_CSUM | NETIF_F_SG | \
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -465,6 +465,7 @@ static void ieee80211_do_stop(struct iee
+       sdata_unlock(sdata);
+       cancel_work_sync(&sdata->csa_finalize_work);
++      cancel_work_sync(&sdata->color_change_finalize_work);
+       cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+@@ -1639,6 +1640,7 @@ static void ieee80211_setup_sdata(struct
+       INIT_WORK(&sdata->work, ieee80211_iface_work);
+       INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
+       INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
++      INIT_WORK(&sdata->color_change_finalize_work, ieee80211_color_change_finalize_work);
+       INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
+       INIT_LIST_HEAD(&sdata->reserved_chanctx_list);
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4790,11 +4790,11 @@ static int ieee80211_beacon_add_tim(stru
+ static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata,
+                                       struct beacon_data *beacon)
+ {
++      u8 *beacon_data, count, max_count = 1;
+       struct probe_resp *resp;
+-      u8 *beacon_data;
+       size_t beacon_data_len;
++      u16 *bcn_offsets;
+       int i;
+-      u8 count = beacon->cntdwn_current_counter;
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+@@ -4814,21 +4814,27 @@ static void ieee80211_set_beacon_cntdwn(
+       }
+       rcu_read_lock();
+-      for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; ++i) {
+-              resp = rcu_dereference(sdata->u.ap.probe_resp);
++      resp = rcu_dereference(sdata->u.ap.probe_resp);
+-              if (beacon->cntdwn_counter_offsets[i]) {
+-                      if (WARN_ON_ONCE(beacon->cntdwn_counter_offsets[i] >=
+-                                       beacon_data_len)) {
++      bcn_offsets = beacon->cntdwn_counter_offsets;
++      count = beacon->cntdwn_current_counter;
++      if (sdata->vif.csa_active)
++              max_count = IEEE80211_MAX_CNTDWN_COUNTERS_NUM;
++
++      for (i = 0; i < max_count; ++i) {
++              if (bcn_offsets[i]) {
++                      if (WARN_ON_ONCE(bcn_offsets[i] >= beacon_data_len)) {
+                               rcu_read_unlock();
+                               return;
+                       }
+-
+-                      beacon_data[beacon->cntdwn_counter_offsets[i]] = count;
++                      beacon_data[bcn_offsets[i]] = count;
+               }
+-              if (sdata->vif.type == NL80211_IFTYPE_AP && resp)
+-                      resp->data[resp->cntdwn_counter_offsets[i]] = count;
++              if (sdata->vif.type == NL80211_IFTYPE_AP && resp) {
++                      u16 *resp_offsets = resp->cntdwn_counter_offsets;
++
++                      resp->data[resp_offsets[i]] = count;
++              }
+       }
+       rcu_read_unlock();
+ }
+@@ -5038,6 +5044,7 @@ __ieee80211_beacon_get(struct ieee80211_
+                       if (offs) {
+                               offs->tim_offset = beacon->head_len;
+                               offs->tim_length = skb->len - beacon->head_len;
++                              offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
+                               /* for AP the csa offsets are from tail */
+                               csa_off_base = skb->len;
index b2ee61a6dc9786df64073a296d29dc579ff19d09..2755efb194853da7503dfa3b8cf2d3cb24ae3726 100644 (file)
@@ -1,6 +1,6 @@
 --- a/include/net/cfg80211.h
 +++ b/include/net/cfg80211.h
-@@ -3793,6 +3793,7 @@ struct mgmt_frame_regs {
+@@ -3814,6 +3814,7 @@ struct mgmt_frame_regs {
   *    (as advertised by the nl80211 feature flag.)
   * @get_tx_power: store the current TX power into the dbm variable;
   *    return 0 if successful
@@ -8,7 +8,7 @@
   *
   * @set_wds_peer: set the WDS peer for a WDS interface
   *
-@@ -4115,6 +4116,7 @@ struct cfg80211_ops {
+@@ -4138,6 +4139,7 @@ struct cfg80211_ops {
                                enum nl80211_tx_power_setting type, int mbm);
        int     (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
                                int *dbm);
@@ -36,9 +36,9 @@
        u8 ps_dtim_period;
 --- a/include/uapi/linux/nl80211.h
 +++ b/include/uapi/linux/nl80211.h
-@@ -2560,6 +2560,9 @@ enum nl80211_commands {
-  *    disassoc events to indicate that an immediate reconnect to the AP
-  *    is desired.
+@@ -2593,6 +2593,9 @@ enum nl80211_commands {
+  * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
+  *    information for the time while performing a color switch.
   *
 + * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
 + *    transmit power to stay within regulatory limits. u32, dBi.
@@ -46,9 +46,9 @@
   * @NUM_NL80211_ATTR: total number of nl80211_attrs available
   * @NL80211_ATTR_MAX: highest attribute number currently defined
   * @__NL80211_ATTR_AFTER_LAST: internal use
-@@ -3057,6 +3060,8 @@ enum nl80211_attrs {
-       NL80211_ATTR_DISABLE_HE,
+@@ -3096,6 +3099,8 @@ enum nl80211_attrs {
+       NL80211_ATTR_COLOR_CHANGE_COLOR,
+       NL80211_ATTR_COLOR_CHANGE_ELEMS,
  
 +      NL80211_ATTR_WIPHY_ANTENNA_GAIN,
 +
@@ -57,7 +57,7 @@
        __NL80211_ATTR_AFTER_LAST,
 --- a/net/mac80211/cfg.c
 +++ b/net/mac80211/cfg.c
-@@ -2761,6 +2761,19 @@ static int ieee80211_get_tx_power(struct
+@@ -2769,6 +2769,19 @@ static int ieee80211_get_tx_power(struct
        return 0;
  }
  
@@ -77,7 +77,7 @@
  static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
                                  const u8 *addr)
  {
-@@ -4202,6 +4215,7 @@ const struct cfg80211_ops mac80211_confi
+@@ -4413,6 +4426,7 @@ const struct cfg80211_ops mac80211_confi
        .set_wiphy_params = ieee80211_set_wiphy_params,
        .set_tx_power = ieee80211_set_tx_power,
        .get_tx_power = ieee80211_get_tx_power,
@@ -87,7 +87,7 @@
        CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
 --- a/net/mac80211/ieee80211_i.h
 +++ b/net/mac80211/ieee80211_i.h
-@@ -1426,6 +1426,7 @@ struct ieee80211_local {
+@@ -1434,6 +1434,7 @@ struct ieee80211_local {
        int dynamic_ps_forced_timeout;
  
        int user_power_level; /* in dBm, for all interfaces */
        local->hw.max_mtu = IEEE80211_MAX_DATA_LEN;
 --- a/net/wireless/nl80211.c
 +++ b/net/wireless/nl80211.c
-@@ -753,6 +753,7 @@ static const struct nla_policy nl80211_p
-                                NL80211_SAE_PWE_BOTH),
-       [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
-       [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
+@@ -757,6 +757,7 @@ static const struct nla_policy nl80211_p
+       [NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 },
+       [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
+       [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
 +      [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
  };
  
  /* policy for the key attributes */
-@@ -3318,6 +3319,20 @@ static int nl80211_set_wiphy(struct sk_b
+@@ -3322,6 +3323,20 @@ static int nl80211_set_wiphy(struct sk_b
                if (result)
                        return result;
        }