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 <michal.kazior@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Michal Kazior 2014-02-28 15:59:06 +01:00 committed by Johannes Berg
parent 7c8d5e03ac
commit 37fa2bdd16
1 changed files with 126 additions and 113 deletions

View File

@ -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(&params->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, &params->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(&params->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(&params->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, &params->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(&params->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;