Skip to content

Commit

Permalink
mac80211: add support for BSS color change
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
John Crispin authored and Johannes Berg committed Aug 17, 2021
1 parent 0d2ab3a commit 5f9404a
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 22 deletions.
29 changes: 29 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,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;
Expand Down Expand Up @@ -1739,6 +1743,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 *));
};
Expand Down Expand Up @@ -5007,6 +5014,16 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif);
*/
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().
Expand Down Expand Up @@ -6771,6 +6788,18 @@ struct sk_buff *
ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw,
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
*
Expand Down
234 changes: 223 additions & 11 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,9 +828,11 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
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;

Expand All @@ -850,6 +852,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
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)
Expand Down Expand Up @@ -955,7 +959,8 @@ static int ieee80211_set_ftm_responder_params(

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;
Expand Down Expand Up @@ -1004,6 +1009,9 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
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 */
Expand All @@ -1020,7 +1028,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
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;
Expand Down Expand Up @@ -1175,7 +1183,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
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;
Expand Down Expand Up @@ -1230,17 +1238,17 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
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);
Expand Down Expand Up @@ -3156,7 +3164,7 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
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;

Expand Down Expand Up @@ -3322,7 +3330,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
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;
Expand Down Expand Up @@ -3411,6 +3419,15 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
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)
Expand Down Expand Up @@ -3479,6 +3496,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
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);
Expand Down Expand Up @@ -4130,6 +4151,196 @@ static int ieee80211_set_sar_specs(struct wiphy *wiphy,
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,
Expand Down Expand Up @@ -4233,4 +4444,5 @@ const struct cfg80211_ops mac80211_config_ops = {
.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,
};
Loading

0 comments on commit 5f9404a

Please sign in to comment.