Skip to content

Commit 73da7d5

Browse files
Simon Wunderlichjmberg-intel
authored andcommitted
mac80211: add channel switch command and beacon callbacks
The count field in CSA must be decremented with each beacon transmitted. This patch implements the functionality for drivers using ieee80211_beacon_get(). Other drivers must call back manually after reaching count == 0. This patch also contains the handling and finish worker for the channel switch command, and mac80211/chanctx code to allow to change a channel definition of an active channel context. Signed-off-by: Simon Wunderlich <[email protected]> Signed-off-by: Mathias Kretschmer <[email protected]> [small cleanups, catch identical chandef] Signed-off-by: Johannes Berg <[email protected]>
1 parent 16ef1fe commit 73da7d5

File tree

8 files changed

+423
-2
lines changed

8 files changed

+423
-2
lines changed

include/net/mac80211.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,14 @@ struct ieee80211_low_level_stats {
152152
* @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
153153
* @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
154154
* @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
155+
* @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel,
156+
* this is used only with channel switching with CSA
155157
*/
156158
enum ieee80211_chanctx_change {
157159
IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0),
158160
IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1),
159161
IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2),
162+
IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3),
160163
};
161164

162165
/**
@@ -1084,6 +1087,7 @@ enum ieee80211_vif_flags {
10841087
* @addr: address of this interface
10851088
* @p2p: indicates whether this AP or STA interface is a p2p
10861089
* interface, i.e. a GO or p2p-sta respectively
1090+
* @csa_active: marks whether a channel switch is going on
10871091
* @driver_flags: flags/capabilities the driver has for this interface,
10881092
* these need to be set (or cleared) when the interface is added
10891093
* or, if supported by the driver, the interface type is changed
@@ -1106,6 +1110,7 @@ struct ieee80211_vif {
11061110
struct ieee80211_bss_conf bss_conf;
11071111
u8 addr[ETH_ALEN];
11081112
bool p2p;
1113+
bool csa_active;
11091114

11101115
u8 cab_queue;
11111116
u8 hw_queue[IEEE80211_NUM_ACS];
@@ -2637,6 +2642,16 @@ enum ieee80211_roc_type {
26372642
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
26382643
* Currently, this is only called for managed or P2P client interfaces.
26392644
* This callback is optional; it must not sleep.
2645+
*
2646+
* @channel_switch_beacon: Starts a channel switch to a new channel.
2647+
* Beacons are modified to include CSA or ECSA IEs before calling this
2648+
* function. The corresponding count fields in these IEs must be
2649+
* decremented, and when they reach zero the driver must call
2650+
* ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
2651+
* get the csa counter decremented by mac80211, but must check if it is
2652+
* zero using ieee80211_csa_is_complete() after the beacon has been
2653+
* transmitted and then call ieee80211_csa_finish().
2654+
*
26402655
*/
26412656
struct ieee80211_ops {
26422657
void (*tx)(struct ieee80211_hw *hw,
@@ -2824,6 +2839,9 @@ struct ieee80211_ops {
28242839
struct ieee80211_vif *vif,
28252840
struct inet6_dev *idev);
28262841
#endif
2842+
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
2843+
struct ieee80211_vif *vif,
2844+
struct cfg80211_chan_def *chandef);
28272845
};
28282846

28292847
/**
@@ -3318,6 +3336,25 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
33183336
return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
33193337
}
33203338

3339+
/**
3340+
* ieee80211_csa_finish - notify mac80211 about channel switch
3341+
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
3342+
*
3343+
* After a channel switch announcement was scheduled and the counter in this
3344+
* announcement hit zero, this function must be called by the driver to
3345+
* notify mac80211 that the channel can be changed.
3346+
*/
3347+
void ieee80211_csa_finish(struct ieee80211_vif *vif);
3348+
3349+
/**
3350+
* ieee80211_csa_is_complete - find out if counters reached zero
3351+
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
3352+
*
3353+
* This function returns whether the channel switch counters reached zero.
3354+
*/
3355+
bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
3356+
3357+
33213358
/**
33223359
* ieee80211_proberesp_get - retrieve a Probe Response template
33233360
* @hw: pointer obtained from ieee80211_alloc_hw().

net/mac80211/cfg.c

Lines changed: 185 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -860,8 +860,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
860860
return 0;
861861
}
862862

863-
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
864-
struct cfg80211_beacon_data *params)
863+
int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
864+
struct cfg80211_beacon_data *params)
865865
{
866866
struct beacon_data *new, *old;
867867
int new_head_len, new_tail_len;
@@ -1024,6 +1024,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
10241024

10251025
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
10261026

1027+
/* don't allow changing the beacon while CSA is in place - offset
1028+
* of channel switch counter may change
1029+
*/
1030+
if (sdata->vif.csa_active)
1031+
return -EBUSY;
1032+
10271033
old = rtnl_dereference(sdata->u.ap.beacon);
10281034
if (!old)
10291035
return -ENOENT;
@@ -1048,6 +1054,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
10481054
return -ENOENT;
10491055
old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
10501056

1057+
/* abort any running channel switch */
1058+
sdata->vif.csa_active = false;
1059+
cancel_work_sync(&sdata->csa_finalize_work);
1060+
10511061
/* turn off carrier for this interface and dependent VLANs */
10521062
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
10531063
netif_carrier_off(vlan->dev);
@@ -2775,6 +2785,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
27752785
return 0;
27762786
}
27772787

2788+
static struct cfg80211_beacon_data *
2789+
cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
2790+
{
2791+
struct cfg80211_beacon_data *new_beacon;
2792+
u8 *pos;
2793+
int len;
2794+
2795+
len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
2796+
beacon->proberesp_ies_len + beacon->assocresp_ies_len +
2797+
beacon->probe_resp_len;
2798+
2799+
new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
2800+
if (!new_beacon)
2801+
return NULL;
2802+
2803+
pos = (u8 *)(new_beacon + 1);
2804+
if (beacon->head_len) {
2805+
new_beacon->head_len = beacon->head_len;
2806+
new_beacon->head = pos;
2807+
memcpy(pos, beacon->head, beacon->head_len);
2808+
pos += beacon->head_len;
2809+
}
2810+
if (beacon->tail_len) {
2811+
new_beacon->tail_len = beacon->tail_len;
2812+
new_beacon->tail = pos;
2813+
memcpy(pos, beacon->tail, beacon->tail_len);
2814+
pos += beacon->tail_len;
2815+
}
2816+
if (beacon->beacon_ies_len) {
2817+
new_beacon->beacon_ies_len = beacon->beacon_ies_len;
2818+
new_beacon->beacon_ies = pos;
2819+
memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
2820+
pos += beacon->beacon_ies_len;
2821+
}
2822+
if (beacon->proberesp_ies_len) {
2823+
new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
2824+
new_beacon->proberesp_ies = pos;
2825+
memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
2826+
pos += beacon->proberesp_ies_len;
2827+
}
2828+
if (beacon->assocresp_ies_len) {
2829+
new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
2830+
new_beacon->assocresp_ies = pos;
2831+
memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
2832+
pos += beacon->assocresp_ies_len;
2833+
}
2834+
if (beacon->probe_resp_len) {
2835+
new_beacon->probe_resp_len = beacon->probe_resp_len;
2836+
beacon->probe_resp = pos;
2837+
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
2838+
pos += beacon->probe_resp_len;
2839+
}
2840+
2841+
return new_beacon;
2842+
}
2843+
2844+
void ieee80211_csa_finalize_work(struct work_struct *work)
2845+
{
2846+
struct ieee80211_sub_if_data *sdata =
2847+
container_of(work, struct ieee80211_sub_if_data,
2848+
csa_finalize_work);
2849+
struct ieee80211_local *local = sdata->local;
2850+
int err, changed;
2851+
2852+
if (!ieee80211_sdata_running(sdata))
2853+
return;
2854+
2855+
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
2856+
return;
2857+
2858+
sdata->radar_required = sdata->csa_radar_required;
2859+
err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
2860+
&changed);
2861+
if (WARN_ON(err < 0))
2862+
return;
2863+
2864+
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
2865+
if (err < 0)
2866+
return;
2867+
2868+
changed |= err;
2869+
kfree(sdata->u.ap.next_beacon);
2870+
sdata->u.ap.next_beacon = NULL;
2871+
sdata->vif.csa_active = false;
2872+
2873+
ieee80211_wake_queues_by_reason(&sdata->local->hw,
2874+
IEEE80211_MAX_QUEUE_MAP,
2875+
IEEE80211_QUEUE_STOP_REASON_CSA);
2876+
2877+
ieee80211_bss_info_change_notify(sdata, changed);
2878+
2879+
cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
2880+
}
2881+
2882+
static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
2883+
struct cfg80211_csa_settings *params)
2884+
{
2885+
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2886+
struct ieee80211_local *local = sdata->local;
2887+
struct ieee80211_chanctx_conf *chanctx_conf;
2888+
struct ieee80211_chanctx *chanctx;
2889+
int err, num_chanctx;
2890+
2891+
if (!list_empty(&local->roc_list) || local->scanning)
2892+
return -EBUSY;
2893+
2894+
if (sdata->wdev.cac_started)
2895+
return -EBUSY;
2896+
2897+
if (cfg80211_chandef_identical(&params->chandef,
2898+
&sdata->vif.bss_conf.chandef))
2899+
return -EINVAL;
2900+
2901+
rcu_read_lock();
2902+
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
2903+
if (!chanctx_conf) {
2904+
rcu_read_unlock();
2905+
return -EBUSY;
2906+
}
2907+
2908+
/* don't handle for multi-VIF cases */
2909+
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
2910+
if (chanctx->refcount > 1) {
2911+
rcu_read_unlock();
2912+
return -EBUSY;
2913+
}
2914+
num_chanctx = 0;
2915+
list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
2916+
num_chanctx++;
2917+
rcu_read_unlock();
2918+
2919+
if (num_chanctx > 1)
2920+
return -EBUSY;
2921+
2922+
/* don't allow another channel switch if one is already active. */
2923+
if (sdata->vif.csa_active)
2924+
return -EBUSY;
2925+
2926+
/* only handle AP for now. */
2927+
switch (sdata->vif.type) {
2928+
case NL80211_IFTYPE_AP:
2929+
break;
2930+
default:
2931+
return -EOPNOTSUPP;
2932+
}
2933+
2934+
sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
2935+
if (!sdata->u.ap.next_beacon)
2936+
return -ENOMEM;
2937+
2938+
sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
2939+
sdata->csa_counter_offset_presp = params->counter_offset_presp;
2940+
sdata->csa_radar_required = params->radar_required;
2941+
2942+
if (params->block_tx)
2943+
ieee80211_stop_queues_by_reason(&local->hw,
2944+
IEEE80211_MAX_QUEUE_MAP,
2945+
IEEE80211_QUEUE_STOP_REASON_CSA);
2946+
2947+
err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
2948+
if (err < 0)
2949+
return err;
2950+
2951+
local->csa_chandef = params->chandef;
2952+
sdata->vif.csa_active = true;
2953+
2954+
ieee80211_bss_info_change_notify(sdata, err);
2955+
drv_channel_switch_beacon(sdata, &params->chandef);
2956+
2957+
return 0;
2958+
}
2959+
27782960
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
27792961
struct ieee80211_channel *chan, bool offchan,
27802962
unsigned int wait, const u8 *buf, size_t len,
@@ -3492,4 +3674,5 @@ struct cfg80211_ops mac80211_config_ops = {
34923674
.get_et_strings = ieee80211_get_et_strings,
34933675
.get_channel = ieee80211_cfg_get_channel,
34943676
.start_radar_detection = ieee80211_start_radar_detection,
3677+
.channel_switch = ieee80211_channel_switch,
34953678
};

net/mac80211/chan.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,64 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
410410
return ret;
411411
}
412412

413+
int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
414+
const struct cfg80211_chan_def *chandef,
415+
u32 *changed)
416+
{
417+
struct ieee80211_local *local = sdata->local;
418+
struct ieee80211_chanctx_conf *conf;
419+
struct ieee80211_chanctx *ctx;
420+
int ret;
421+
u32 chanctx_changed = 0;
422+
423+
/* should never be called if not performing a channel switch. */
424+
if (WARN_ON(!sdata->vif.csa_active))
425+
return -EINVAL;
426+
427+
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
428+
IEEE80211_CHAN_DISABLED))
429+
return -EINVAL;
430+
431+
mutex_lock(&local->chanctx_mtx);
432+
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
433+
lockdep_is_held(&local->chanctx_mtx));
434+
if (!conf) {
435+
ret = -EINVAL;
436+
goto out;
437+
}
438+
439+
ctx = container_of(conf, struct ieee80211_chanctx, conf);
440+
if (ctx->refcount != 1) {
441+
ret = -EINVAL;
442+
goto out;
443+
}
444+
445+
if (sdata->vif.bss_conf.chandef.width != chandef->width) {
446+
chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
447+
*changed |= BSS_CHANGED_BANDWIDTH;
448+
}
449+
450+
sdata->vif.bss_conf.chandef = *chandef;
451+
ctx->conf.def = *chandef;
452+
453+
chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
454+
drv_change_chanctx(local, ctx, chanctx_changed);
455+
456+
if (!local->use_chanctx) {
457+
local->_oper_chandef = *chandef;
458+
ieee80211_hw_config(local, 0);
459+
}
460+
461+
ieee80211_recalc_chanctx_chantype(local, ctx);
462+
ieee80211_recalc_smps_chanctx(local, ctx);
463+
ieee80211_recalc_radar_chanctx(local, ctx);
464+
465+
ret = 0;
466+
out:
467+
mutex_unlock(&local->chanctx_mtx);
468+
return ret;
469+
}
470+
413471
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
414472
const struct cfg80211_chan_def *chandef,
415473
u32 *changed)

net/mac80211/driver-ops.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,4 +1072,17 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
10721072
}
10731073
#endif
10741074

1075+
static inline void
1076+
drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
1077+
struct cfg80211_chan_def *chandef)
1078+
{
1079+
struct ieee80211_local *local = sdata->local;
1080+
1081+
if (local->ops->channel_switch_beacon) {
1082+
trace_drv_channel_switch_beacon(local, sdata, chandef);
1083+
local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
1084+
chandef);
1085+
}
1086+
}
1087+
10751088
#endif /* __MAC80211_DRIVER_OPS */

0 commit comments

Comments
 (0)