1 From: John Crispin <john@phrozen.org>
2 Date: Fri, 2 Jul 2021 19:44:08 +0200
3 Subject: [PATCH] mac80211: add support for BSS color change
5 The color change announcement is very similar to how CSA works where
6 we have an IE that includes a counter. When the counter hits 0, the new
7 color is applied via an updated beacon.
9 This patch makes the CSA counter functionality reusable, rather than
10 implementing it again. This also allows for future reuse incase support
11 for other counter IEs gets added.
13 Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
14 Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
15 Signed-off-by: John Crispin <john@phrozen.org>
16 Link: https://lore.kernel.org/r/057c1e67b82bee561ea44ce6a45a8462d3da6995.1625247619.git.lorenzo@kernel.org
17 Signed-off-by: Johannes Berg <johannes.berg@intel.com>
20 --- a/include/net/mac80211.h
21 +++ b/include/net/mac80211.h
22 @@ -1713,6 +1713,10 @@ enum ieee80211_offload_flags {
23 * protected by fq->lock.
24 * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
25 * &enum ieee80211_offload_flags.
26 + * @color_change_active: marks whether a color change is ongoing. Internally it is
27 + * write-protected by sdata_lock and local->mtx so holding either is fine
29 + * @color_change_color: the bss color that will be used after the change.
31 struct ieee80211_vif {
32 enum nl80211_iftype type;
33 @@ -1741,6 +1745,9 @@ struct ieee80211_vif {
35 bool txqs_stopped[IEEE80211_NUM_ACS];
37 + bool color_change_active;
38 + u8 color_change_color;
41 u8 drv_priv[] __aligned(sizeof(void *));
43 @@ -4988,6 +4995,16 @@ void ieee80211_csa_finish(struct ieee802
44 bool ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif);
47 + * ieee80211_color_change_finish - notify mac80211 about color change
48 + * @vif: &struct ieee80211_vif pointer from the add_interface callback.
50 + * After a color change announcement was scheduled and the counter in this
51 + * announcement hits 1, this function must be called by the driver to
52 + * notify mac80211 that the color can be changed
54 +void ieee80211_color_change_finish(struct ieee80211_vif *vif);
57 * ieee80211_proberesp_get - retrieve a Probe Response template
58 * @hw: pointer obtained from ieee80211_alloc_hw().
59 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
60 @@ -6752,6 +6769,18 @@ ieee80211_get_unsol_bcast_probe_resp_tmp
61 struct ieee80211_vif *vif);
64 + * ieeee80211_obss_color_collision_notify - notify userland about a BSS color
67 + * @vif: &struct ieee80211_vif pointer from the add_interface callback.
68 + * @color_bitmap: a 64 bit bitmap representing the colors that the local BSS is
72 +ieeee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
76 * ieee80211_is_tx_data - check if frame is a data frame
78 * The function is used to check if a frame is a data frame. Frames with
79 --- a/net/mac80211/cfg.c
80 +++ b/net/mac80211/cfg.c
81 @@ -809,9 +809,11 @@ static int ieee80211_set_monitor_channel
85 -static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
86 - const u8 *resp, size_t resp_len,
87 - const struct ieee80211_csa_settings *csa)
89 +ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
90 + const u8 *resp, size_t resp_len,
91 + const struct ieee80211_csa_settings *csa,
92 + const struct ieee80211_color_change_settings *cca)
94 struct probe_resp *new, *old;
96 @@ -831,6 +833,8 @@ static int ieee80211_set_probe_resp(stru
97 memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_presp,
98 csa->n_counter_offsets_presp *
99 sizeof(new->cntdwn_counter_offsets[0]));
101 + new->cntdwn_counter_offsets[0] = cca->counter_offset_presp;
103 rcu_assign_pointer(sdata->u.ap.probe_resp, new);
105 @@ -936,7 +940,8 @@ static int ieee80211_set_ftm_responder_p
107 static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
108 struct cfg80211_beacon_data *params,
109 - const struct ieee80211_csa_settings *csa)
110 + const struct ieee80211_csa_settings *csa,
111 + const struct ieee80211_color_change_settings *cca)
113 struct beacon_data *new, *old;
114 int new_head_len, new_tail_len;
115 @@ -985,6 +990,9 @@ static int ieee80211_assign_beacon(struc
116 memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_beacon,
117 csa->n_counter_offsets_beacon *
118 sizeof(new->cntdwn_counter_offsets[0]));
120 + new->cntdwn_current_counter = cca->count;
121 + new->cntdwn_counter_offsets[0] = cca->counter_offset_beacon;
125 @@ -1001,7 +1009,7 @@ static int ieee80211_assign_beacon(struc
126 memcpy(new->tail, old->tail, new_tail_len);
128 err = ieee80211_set_probe_resp(sdata, params->probe_resp,
129 - params->probe_resp_len, csa);
130 + params->probe_resp_len, csa, cca);
134 @@ -1156,7 +1164,7 @@ static int ieee80211_start_ap(struct wip
135 if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
136 sdata->vif.bss_conf.beacon_tx_rate = params->beacon_rate;
138 - err = ieee80211_assign_beacon(sdata, ¶ms->beacon, NULL);
139 + err = ieee80211_assign_beacon(sdata, ¶ms->beacon, NULL, NULL);
143 @@ -1211,17 +1219,17 @@ static int ieee80211_change_beacon(struc
144 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
145 sdata_assert_lock(sdata);
147 - /* don't allow changing the beacon while CSA is in place - offset
148 + /* don't allow changing the beacon while a countdown is in place - offset
149 * of channel switch counter may change
151 - if (sdata->vif.csa_active)
152 + if (sdata->vif.csa_active || sdata->vif.color_change_active)
155 old = sdata_dereference(sdata->u.ap.beacon, sdata);
159 - err = ieee80211_assign_beacon(sdata, params, NULL);
160 + err = ieee80211_assign_beacon(sdata, params, NULL, NULL);
163 ieee80211_bss_info_change_notify(sdata, err);
164 @@ -3144,7 +3152,7 @@ static int ieee80211_set_after_csa_beaco
165 switch (sdata->vif.type) {
166 case NL80211_IFTYPE_AP:
167 err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
170 kfree(sdata->u.ap.next_beacon);
171 sdata->u.ap.next_beacon = NULL;
173 @@ -3310,7 +3318,7 @@ static int ieee80211_set_csa_beacon(stru
174 csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
175 csa.count = params->count;
177 - err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa);
178 + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa, NULL);
180 kfree(sdata->u.ap.next_beacon);
182 @@ -3399,6 +3407,15 @@ static int ieee80211_set_csa_beacon(stru
186 +static void ieee80211_color_change_abort(struct ieee80211_sub_if_data *sdata)
188 + sdata->vif.color_change_active = false;
189 + kfree(sdata->u.ap.next_beacon);
190 + sdata->u.ap.next_beacon = NULL;
192 + cfg80211_color_change_aborted_notify(sdata->dev);
196 __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
197 struct cfg80211_csa_settings *params)
198 @@ -3467,6 +3484,10 @@ __ieee80211_channel_switch(struct wiphy
202 + /* if there is a color change in progress, abort it */
203 + if (sdata->vif.color_change_active)
204 + ieee80211_color_change_abort(sdata);
206 err = ieee80211_set_csa_beacon(sdata, params, &changed);
208 ieee80211_vif_unreserve_chanctx(sdata);
209 @@ -4118,6 +4139,196 @@ static int ieee80211_set_sar_specs(struc
210 return local->ops->set_sar_specs(&local->hw, sar);
214 +ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata,
217 + switch (sdata->vif.type) {
218 + case NL80211_IFTYPE_AP: {
221 + ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
223 + kfree(sdata->u.ap.next_beacon);
224 + sdata->u.ap.next_beacon = NULL;
241 +ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata,
242 + struct cfg80211_color_change_settings *params,
245 + struct ieee80211_color_change_settings color_change = {};
248 + switch (sdata->vif.type) {
249 + case NL80211_IFTYPE_AP:
250 + sdata->u.ap.next_beacon =
251 + cfg80211_beacon_dup(¶ms->beacon_next);
252 + if (!sdata->u.ap.next_beacon)
255 + if (params->count <= 1)
258 + color_change.counter_offset_beacon =
259 + params->counter_offset_beacon;
260 + color_change.counter_offset_presp =
261 + params->counter_offset_presp;
262 + color_change.count = params->count;
264 + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_color_change,
265 + NULL, &color_change);
267 + kfree(sdata->u.ap.next_beacon);
273 + return -EOPNOTSUPP;
280 +ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata,
281 + u8 color, int enable, u32 changed)
283 + sdata->vif.bss_conf.he_bss_color.color = color;
284 + sdata->vif.bss_conf.he_bss_color.enabled = enable;
285 + changed |= BSS_CHANGED_HE_BSS_COLOR;
287 + ieee80211_bss_info_change_notify(sdata, changed);
290 +static int ieee80211_color_change_finalize(struct ieee80211_sub_if_data *sdata)
292 + struct ieee80211_local *local = sdata->local;
296 + sdata_assert_lock(sdata);
297 + lockdep_assert_held(&local->mtx);
299 + sdata->vif.color_change_active = false;
301 + err = ieee80211_set_after_color_change_beacon(sdata, &changed);
303 + cfg80211_color_change_aborted_notify(sdata->dev);
307 + ieee80211_color_change_bss_config_notify(sdata,
308 + sdata->vif.color_change_color,
310 + cfg80211_color_change_notify(sdata->dev);
315 +void ieee80211_color_change_finalize_work(struct work_struct *work)
317 + struct ieee80211_sub_if_data *sdata =
318 + container_of(work, struct ieee80211_sub_if_data,
319 + color_change_finalize_work);
320 + struct ieee80211_local *local = sdata->local;
323 + mutex_lock(&local->mtx);
325 + /* AP might have been stopped while waiting for the lock. */
326 + if (!sdata->vif.color_change_active)
329 + if (!ieee80211_sdata_running(sdata))
332 + ieee80211_color_change_finalize(sdata);
335 + mutex_unlock(&local->mtx);
336 + sdata_unlock(sdata);
339 +void ieee80211_color_change_finish(struct ieee80211_vif *vif)
341 + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
343 + ieee80211_queue_work(&sdata->local->hw,
344 + &sdata->color_change_finalize_work);
346 +EXPORT_SYMBOL_GPL(ieee80211_color_change_finish);
349 +ieeee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
352 + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
354 + if (sdata->vif.color_change_active || sdata->vif.csa_active)
357 + cfg80211_obss_color_collision_notify(sdata->dev, color_bitmap);
359 +EXPORT_SYMBOL_GPL(ieeee80211_obss_color_collision_notify);
362 +ieee80211_color_change(struct wiphy *wiphy, struct net_device *dev,
363 + struct cfg80211_color_change_settings *params)
365 + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
366 + struct ieee80211_local *local = sdata->local;
370 + sdata_assert_lock(sdata);
372 + mutex_lock(&local->mtx);
374 + /* don't allow another color change if one is already active or if csa
377 + if (sdata->vif.color_change_active || sdata->vif.csa_active) {
382 + err = ieee80211_set_color_change_beacon(sdata, params, &changed);
386 + sdata->vif.color_change_active = true;
387 + sdata->vif.color_change_color = params->color;
389 + cfg80211_color_change_started_notify(sdata->dev, params->count);
392 + ieee80211_color_change_bss_config_notify(sdata, 0, 0, changed);
394 + /* if the beacon didn't change, we can finalize immediately */
395 + ieee80211_color_change_finalize(sdata);
398 + mutex_unlock(&local->mtx);
403 const struct cfg80211_ops mac80211_config_ops = {
404 .add_virtual_intf = ieee80211_add_iface,
405 .del_virtual_intf = ieee80211_del_iface,
406 @@ -4221,4 +4432,5 @@ const struct cfg80211_ops mac80211_confi
407 .set_tid_config = ieee80211_set_tid_config,
408 .reset_tid_config = ieee80211_reset_tid_config,
409 .set_sar_specs = ieee80211_set_sar_specs,
410 + .color_change = ieee80211_color_change,
412 --- a/net/mac80211/ieee80211_i.h
413 +++ b/net/mac80211/ieee80211_i.h
414 @@ -248,6 +248,12 @@ struct ieee80211_csa_settings {
418 +struct ieee80211_color_change_settings {
419 + u16 counter_offset_beacon;
420 + u16 counter_offset_presp;
426 int head_len, tail_len;
427 @@ -927,6 +933,8 @@ struct ieee80211_sub_if_data {
428 bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
429 struct cfg80211_chan_def csa_chandef;
431 + struct work_struct color_change_finalize_work;
433 struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */
434 struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */
436 @@ -1893,6 +1901,9 @@ void ieee80211_csa_finalize_work(struct
437 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
438 struct cfg80211_csa_settings *params);
440 +/* color change handling */
441 +void ieee80211_color_change_finalize_work(struct work_struct *work);
443 /* interface handling */
444 #define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
445 NETIF_F_HW_CSUM | NETIF_F_SG | \
446 --- a/net/mac80211/iface.c
447 +++ b/net/mac80211/iface.c
448 @@ -461,6 +461,7 @@ static void ieee80211_do_stop(struct iee
451 cancel_work_sync(&sdata->csa_finalize_work);
452 + cancel_work_sync(&sdata->color_change_finalize_work);
454 cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
456 @@ -1572,6 +1573,7 @@ static void ieee80211_setup_sdata(struct
457 INIT_WORK(&sdata->work, ieee80211_iface_work);
458 INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
459 INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
460 + INIT_WORK(&sdata->color_change_finalize_work, ieee80211_color_change_finalize_work);
461 INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
462 INIT_LIST_HEAD(&sdata->reserved_chanctx_list);
464 --- a/net/mac80211/tx.c
465 +++ b/net/mac80211/tx.c
466 @@ -4773,11 +4773,11 @@ static int ieee80211_beacon_add_tim(stru
467 static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata,
468 struct beacon_data *beacon)
470 + u8 *beacon_data, count, max_count = 1;
471 struct probe_resp *resp;
473 size_t beacon_data_len;
476 - u8 count = beacon->cntdwn_current_counter;
478 switch (sdata->vif.type) {
479 case NL80211_IFTYPE_AP:
480 @@ -4797,21 +4797,27 @@ static void ieee80211_set_beacon_cntdwn(
484 - for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; ++i) {
485 - resp = rcu_dereference(sdata->u.ap.probe_resp);
486 + resp = rcu_dereference(sdata->u.ap.probe_resp);
488 - if (beacon->cntdwn_counter_offsets[i]) {
489 - if (WARN_ON_ONCE(beacon->cntdwn_counter_offsets[i] >=
490 - beacon_data_len)) {
491 + bcn_offsets = beacon->cntdwn_counter_offsets;
492 + count = beacon->cntdwn_current_counter;
493 + if (sdata->vif.csa_active)
494 + max_count = IEEE80211_MAX_CNTDWN_COUNTERS_NUM;
496 + for (i = 0; i < max_count; ++i) {
497 + if (bcn_offsets[i]) {
498 + if (WARN_ON_ONCE(bcn_offsets[i] >= beacon_data_len)) {
503 - beacon_data[beacon->cntdwn_counter_offsets[i]] = count;
504 + beacon_data[bcn_offsets[i]] = count;
507 - if (sdata->vif.type == NL80211_IFTYPE_AP && resp)
508 - resp->data[resp->cntdwn_counter_offsets[i]] = count;
509 + if (sdata->vif.type == NL80211_IFTYPE_AP && resp) {
510 + u16 *resp_offsets = resp->cntdwn_counter_offsets;
512 + resp->data[resp_offsets[i]] = count;
517 @@ -5021,6 +5027,7 @@ __ieee80211_beacon_get(struct ieee80211_
519 offs->tim_offset = beacon->head_len;
520 offs->tim_length = skb->len - beacon->head_len;
521 + offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
523 /* for AP the csa offsets are from tail */
524 csa_off_base = skb->len;