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 <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
[small cleanups, catch identical chandef]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Simon Wunderlich
2013-07-11 16:09:06 +02:00
committed by Johannes Berg
parent 16ef1fe272
commit 73da7d5bab
8 changed files with 423 additions and 2 deletions

View File

@@ -2338,6 +2338,81 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
return 0;
}
void ieee80211_csa_finish(struct ieee80211_vif *vif)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
ieee80211_queue_work(&sdata->local->hw,
&sdata->csa_finalize_work);
}
EXPORT_SYMBOL(ieee80211_csa_finish);
static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
struct beacon_data *beacon)
{
struct probe_resp *resp;
int counter_offset_beacon = sdata->csa_counter_offset_beacon;
int counter_offset_presp = sdata->csa_counter_offset_presp;
/* warn if the driver did not check for/react to csa completeness */
if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
return;
((u8 *)beacon->tail)[counter_offset_beacon]--;
if (sdata->vif.type == NL80211_IFTYPE_AP &&
counter_offset_presp) {
rcu_read_lock();
resp = rcu_dereference(sdata->u.ap.probe_resp);
/* if nl80211 accepted the offset, this should not happen. */
if (WARN_ON(!resp)) {
rcu_read_unlock();
return;
}
resp->data[counter_offset_presp]--;
rcu_read_unlock();
}
}
bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct beacon_data *beacon = NULL;
u8 *beacon_data;
size_t beacon_data_len;
int counter_beacon = sdata->csa_counter_offset_beacon;
int ret = false;
if (!ieee80211_sdata_running(sdata))
return false;
rcu_read_lock();
if (vif->type == NL80211_IFTYPE_AP) {
struct ieee80211_if_ap *ap = &sdata->u.ap;
beacon = rcu_dereference(ap->beacon);
if (WARN_ON(!beacon || !beacon->tail))
goto out;
beacon_data = beacon->tail;
beacon_data_len = beacon->tail_len;
} else {
WARN_ON(1);
goto out;
}
if (WARN_ON(counter_beacon > beacon_data_len))
goto out;
if (beacon_data[counter_beacon] == 0)
ret = true;
out:
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_csa_is_complete);
struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 *tim_offset, u16 *tim_length)
@@ -2368,6 +2443,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct beacon_data *beacon = rcu_dereference(ap->beacon);
if (beacon) {
if (sdata->vif.csa_active)
ieee80211_update_csa(sdata, beacon);
/*
* headroom, head length,
* tail length and maximum TIM length