mac80211: backport support for BSS color changes
This is needed for an upcoming mt76 update Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		| @@ -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, ¶ms.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, ¶ms.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, ¶ms); | ||||||
|  | +	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 | ||||||
| @@ -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, ¶ms->beacon, NULL); | ||||||
|  | +	err = ieee80211_assign_beacon(sdata, ¶ms->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, ¶ms->beacon_csa, &csa); | ||||||
|  | +		err = ieee80211_assign_beacon(sdata, ¶ms->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(¶ms->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, ¶ms->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; | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| --- a/include/net/cfg80211.h | --- a/include/net/cfg80211.h | ||||||
| +++ b/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.) |   *	(as advertised by the nl80211 feature flag.) | ||||||
|   * @get_tx_power: store the current TX power into the dbm variable; |   * @get_tx_power: store the current TX power into the dbm variable; | ||||||
|   *	return 0 if successful |   *	return 0 if successful | ||||||
| @@ -8,7 +8,7 @@ | |||||||
|   * |   * | ||||||
|   * @set_wds_peer: set the WDS peer for a WDS interface |   * @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); |  				enum nl80211_tx_power_setting type, int mbm); | ||||||
|  	int	(*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, |  	int	(*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, | ||||||
|  				int *dbm); |  				int *dbm); | ||||||
| @@ -36,9 +36,9 @@ | |||||||
|  	u8 ps_dtim_period; |  	u8 ps_dtim_period; | ||||||
| --- a/include/uapi/linux/nl80211.h | --- a/include/uapi/linux/nl80211.h | ||||||
| +++ b/include/uapi/linux/nl80211.h | +++ b/include/uapi/linux/nl80211.h | ||||||
| @@ -2560,6 +2560,9 @@ enum nl80211_commands { | @@ -2593,6 +2593,9 @@ enum nl80211_commands { | ||||||
|   *	disassoc events to indicate that an immediate reconnect to the AP |   * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE | ||||||
|   *	is desired. |   *	information for the time while performing a color switch. | ||||||
|   * |   * | ||||||
| + * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce | + * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce | ||||||
| + *	transmit power to stay within regulatory limits. u32, dBi. | + *	transmit power to stay within regulatory limits. u32, dBi. | ||||||
| @@ -46,9 +46,9 @@ | |||||||
|   * @NUM_NL80211_ATTR: total number of nl80211_attrs available |   * @NUM_NL80211_ATTR: total number of nl80211_attrs available | ||||||
|   * @NL80211_ATTR_MAX: highest attribute number currently defined |   * @NL80211_ATTR_MAX: highest attribute number currently defined | ||||||
|   * @__NL80211_ATTR_AFTER_LAST: internal use |   * @__NL80211_ATTR_AFTER_LAST: internal use | ||||||
| @@ -3057,6 +3060,8 @@ enum nl80211_attrs { | @@ -3096,6 +3099,8 @@ enum nl80211_attrs { | ||||||
|   |  	NL80211_ATTR_COLOR_CHANGE_COLOR, | ||||||
|  	NL80211_ATTR_DISABLE_HE, |  	NL80211_ATTR_COLOR_CHANGE_ELEMS, | ||||||
|   |   | ||||||
| +	NL80211_ATTR_WIPHY_ANTENNA_GAIN, | +	NL80211_ATTR_WIPHY_ANTENNA_GAIN, | ||||||
| + | + | ||||||
| @@ -57,7 +57,7 @@ | |||||||
|  	__NL80211_ATTR_AFTER_LAST, |  	__NL80211_ATTR_AFTER_LAST, | ||||||
| --- a/net/mac80211/cfg.c | --- a/net/mac80211/cfg.c | ||||||
| +++ b/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; |  	return 0; | ||||||
|  } |  } | ||||||
|   |   | ||||||
| @@ -77,7 +77,7 @@ | |||||||
|  static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, |  static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, | ||||||
|  				  const u8 *addr) |  				  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_wiphy_params = ieee80211_set_wiphy_params, | ||||||
|  	.set_tx_power = ieee80211_set_tx_power, |  	.set_tx_power = ieee80211_set_tx_power, | ||||||
|  	.get_tx_power = ieee80211_get_tx_power, |  	.get_tx_power = ieee80211_get_tx_power, | ||||||
| @@ -87,7 +87,7 @@ | |||||||
|  	CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) |  	CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) | ||||||
| --- a/net/mac80211/ieee80211_i.h | --- a/net/mac80211/ieee80211_i.h | ||||||
| +++ b/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 dynamic_ps_forced_timeout; | ||||||
|   |   | ||||||
|  	int user_power_level; /* in dBm, for all interfaces */ |  	int user_power_level; /* in dBm, for all interfaces */ | ||||||
| @@ -129,15 +129,15 @@ | |||||||
|  	local->hw.max_mtu = IEEE80211_MAX_DATA_LEN; |  	local->hw.max_mtu = IEEE80211_MAX_DATA_LEN; | ||||||
| --- a/net/wireless/nl80211.c | --- a/net/wireless/nl80211.c | ||||||
| +++ b/net/wireless/nl80211.c | +++ b/net/wireless/nl80211.c | ||||||
| @@ -753,6 +753,7 @@ static const struct nla_policy nl80211_p | @@ -757,6 +757,7 @@ static const struct nla_policy nl80211_p | ||||||
|  				 NL80211_SAE_PWE_BOTH), |  	[NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 }, | ||||||
|  	[NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy), |  	[NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 }, | ||||||
|  	[NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT }, |  	[NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy), | ||||||
| +	[NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, | +	[NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, | ||||||
|  }; |  }; | ||||||
|   |   | ||||||
|  /* policy for the key attributes */ |  /* 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) |  		if (result) | ||||||
|  			return result; |  			return result; | ||||||
|  	} |  	} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau