From 37fa2bdd16a12fef7804606f56525ba5747bf172 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 28 Feb 2014 15:59:06 +0100 Subject: [PATCH] mac80211: refactor channel switch function The function was quite big. This splits out beacon updating into a separate function for improved maintenance and extension. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 239 ++++++++++++++++++++++++--------------------- 1 file changed, 126 insertions(+), 113 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 80534f524fd6..aaa59d719592 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3089,6 +3089,129 @@ unlock: sdata_unlock(sdata); } +static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *params, + u32 *changed) +{ + int err; + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + sdata->u.ap.next_beacon = + cfg80211_beacon_dup(¶ms->beacon_after); + if (!sdata->u.ap.next_beacon) + return -ENOMEM; + + /* + * With a count of 0, we don't have to wait for any + * TBTT before switching, so complete the CSA + * immediately. In theory, with a count == 1 we + * should delay the switch until just before the next + * TBTT, but that would complicate things so we switch + * immediately too. If we would delay the switch + * until the next TBTT, we would have to set the probe + * response here. + * + * TODO: A channel switch with count <= 1 without + * sending a CSA action frame is kind of useless, + * because the clients won't know we're changing + * channels. The action frame must be implemented + * either here or in the userspace. + */ + if (params->count <= 1) + break; + + sdata->csa_counter_offset_beacon = + params->counter_offset_beacon; + sdata->csa_counter_offset_presp = params->counter_offset_presp; + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); + if (err < 0) { + kfree(sdata->u.ap.next_beacon); + return err; + } + *changed |= err; + + break; + case NL80211_IFTYPE_ADHOC: + if (!sdata->vif.bss_conf.ibss_joined) + return -EINVAL; + + if (params->chandef.width != sdata->u.ibss.chandef.width) + return -EINVAL; + + switch (params->chandef.width) { + case NL80211_CHAN_WIDTH_40: + if (cfg80211_get_chandef_type(¶ms->chandef) != + cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) + return -EINVAL; + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + break; + default: + return -EINVAL; + } + + /* changes into another band are not supported */ + if (sdata->u.ibss.chandef.chan->band != + params->chandef.chan->band) + return -EINVAL; + + /* see comments in the NL80211_IFTYPE_AP block */ + if (params->count > 1) { + err = ieee80211_ibss_csa_beacon(sdata, params); + if (err < 0) + return err; + *changed |= err; + } + + ieee80211_send_action_csa(sdata, params); + + break; +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + + if (params->chandef.width != sdata->vif.bss_conf.chandef.width) + return -EINVAL; + + /* changes into another band are not supported */ + if (sdata->vif.bss_conf.chandef.chan->band != + params->chandef.chan->band) + return -EINVAL; + + if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) { + ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT; + if (!ifmsh->pre_value) + ifmsh->pre_value = 1; + else + ifmsh->pre_value++; + } + + /* see comments in the NL80211_IFTYPE_AP block */ + if (params->count > 1) { + err = ieee80211_mesh_csa_beacon(sdata, params); + if (err < 0) { + ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; + return err; + } + *changed |= err; + } + + if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) + ieee80211_send_action_csa(sdata, params); + + break; + } +#endif + default: + return -EOPNOTSUPP; + } + + return 0; +} + int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params) { @@ -3096,7 +3219,6 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx *chanctx; - struct ieee80211_if_mesh __maybe_unused *ifmsh; int err, num_chanctx, changed = 0; sdata_assert_lock(sdata); @@ -3136,118 +3258,9 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->vif.csa_active) return -EBUSY; - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP: - sdata->u.ap.next_beacon = - cfg80211_beacon_dup(¶ms->beacon_after); - if (!sdata->u.ap.next_beacon) - return -ENOMEM; - - /* - * With a count of 0, we don't have to wait for any - * TBTT before switching, so complete the CSA - * immediately. In theory, with a count == 1 we - * should delay the switch until just before the next - * TBTT, but that would complicate things so we switch - * immediately too. If we would delay the switch - * until the next TBTT, we would have to set the probe - * response here. - * - * TODO: A channel switch with count <= 1 without - * sending a CSA action frame is kind of useless, - * because the clients won't know we're changing - * channels. The action frame must be implemented - * either here or in the userspace. - */ - if (params->count <= 1) - break; - - sdata->csa_counter_offset_beacon = - params->counter_offset_beacon; - sdata->csa_counter_offset_presp = params->counter_offset_presp; - err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); - if (err < 0) { - kfree(sdata->u.ap.next_beacon); - return err; - } - changed |= err; - - break; - case NL80211_IFTYPE_ADHOC: - if (!sdata->vif.bss_conf.ibss_joined) - return -EINVAL; - - if (params->chandef.width != sdata->u.ibss.chandef.width) - return -EINVAL; - - switch (params->chandef.width) { - case NL80211_CHAN_WIDTH_40: - if (cfg80211_get_chandef_type(¶ms->chandef) != - cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) - return -EINVAL; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - break; - default: - return -EINVAL; - } - - /* changes into another band are not supported */ - if (sdata->u.ibss.chandef.chan->band != - params->chandef.chan->band) - return -EINVAL; - - /* see comments in the NL80211_IFTYPE_AP block */ - if (params->count > 1) { - err = ieee80211_ibss_csa_beacon(sdata, params); - if (err < 0) - return err; - changed |= err; - } - - ieee80211_send_action_csa(sdata, params); - - break; -#ifdef CONFIG_MAC80211_MESH - case NL80211_IFTYPE_MESH_POINT: - ifmsh = &sdata->u.mesh; - - if (params->chandef.width != sdata->vif.bss_conf.chandef.width) - return -EINVAL; - - /* changes into another band are not supported */ - if (sdata->vif.bss_conf.chandef.chan->band != - params->chandef.chan->band) - return -EINVAL; - - if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) { - ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT; - if (!ifmsh->pre_value) - ifmsh->pre_value = 1; - else - ifmsh->pre_value++; - } - - /* see comments in the NL80211_IFTYPE_AP block */ - if (params->count > 1) { - err = ieee80211_mesh_csa_beacon(sdata, params); - if (err < 0) { - ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; - return err; - } - changed |= err; - } - - if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) - ieee80211_send_action_csa(sdata, params); - - break; -#endif - default: - return -EOPNOTSUPP; - } + err = ieee80211_set_csa_beacon(sdata, params, &changed); + if (err) + return err; sdata->csa_radar_required = params->radar_required;