diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2010-05-11 10:20:57 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-05-12 16:39:05 -0400 |
commit | 5ce6e438d5d9ed8ed775cd1e94f92002c8da2bad (patch) | |
tree | 9b6ce4bc8e7600e30124c8b0f1cbc2ae06499722 /net/mac80211/mlme.c | |
parent | b29e7eb4b8b3e5f4ff8066af648e9fe2fc707b16 (diff) |
mac80211: add offload channel switch support
This adds support for offloading the channel switch
operation to devices that support such, typically
by having specific firmware API for it. The reasons
for this could be that the firmware provides better
timing or that regulatory enforcement done by the
device requires special handling of CSAs.
In order to allow drivers to specify the timing to
the device, the new channel_switch callback will
pass through the received frame's mactime, where
available.
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 56 |
1 files changed, 52 insertions, 4 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7bfb0ebaaf00..6b74489fb9c6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -341,7 +341,11 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
341 | goto out; | 341 | goto out; |
342 | 342 | ||
343 | sdata->local->oper_channel = sdata->local->csa_channel; | 343 | sdata->local->oper_channel = sdata->local->csa_channel; |
344 | ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); | 344 | if (!sdata->local->ops->channel_switch) { |
345 | /* call "hw_config" only if doing sw channel switch */ | ||
346 | ieee80211_hw_config(sdata->local, | ||
347 | IEEE80211_CONF_CHANGE_CHANNEL); | ||
348 | } | ||
345 | 349 | ||
346 | /* XXX: shouldn't really modify cfg80211-owned data! */ | 350 | /* XXX: shouldn't really modify cfg80211-owned data! */ |
347 | ifmgd->associated->channel = sdata->local->oper_channel; | 351 | ifmgd->associated->channel = sdata->local->oper_channel; |
@@ -353,6 +357,29 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
353 | mutex_unlock(&ifmgd->mtx); | 357 | mutex_unlock(&ifmgd->mtx); |
354 | } | 358 | } |
355 | 359 | ||
360 | void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) | ||
361 | { | ||
362 | struct ieee80211_sub_if_data *sdata; | ||
363 | struct ieee80211_if_managed *ifmgd; | ||
364 | |||
365 | sdata = vif_to_sdata(vif); | ||
366 | ifmgd = &sdata->u.mgd; | ||
367 | |||
368 | trace_api_chswitch_done(sdata, success); | ||
369 | if (!success) { | ||
370 | /* | ||
371 | * If the channel switch was not successful, stay | ||
372 | * around on the old channel. We currently lack | ||
373 | * good handling of this situation, possibly we | ||
374 | * should just drop the association. | ||
375 | */ | ||
376 | sdata->local->csa_channel = sdata->local->oper_channel; | ||
377 | } | ||
378 | |||
379 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | ||
380 | } | ||
381 | EXPORT_SYMBOL(ieee80211_chswitch_done); | ||
382 | |||
356 | static void ieee80211_chswitch_timer(unsigned long data) | 383 | static void ieee80211_chswitch_timer(unsigned long data) |
357 | { | 384 | { |
358 | struct ieee80211_sub_if_data *sdata = | 385 | struct ieee80211_sub_if_data *sdata = |
@@ -369,7 +396,8 @@ static void ieee80211_chswitch_timer(unsigned long data) | |||
369 | 396 | ||
370 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | 397 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, |
371 | struct ieee80211_channel_sw_ie *sw_elem, | 398 | struct ieee80211_channel_sw_ie *sw_elem, |
372 | struct ieee80211_bss *bss) | 399 | struct ieee80211_bss *bss, |
400 | u64 timestamp) | ||
373 | { | 401 | { |
374 | struct cfg80211_bss *cbss = | 402 | struct cfg80211_bss *cbss = |
375 | container_of((void *)bss, struct cfg80211_bss, priv); | 403 | container_of((void *)bss, struct cfg80211_bss, priv); |
@@ -397,6 +425,24 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
397 | 425 | ||
398 | sdata->local->csa_channel = new_ch; | 426 | sdata->local->csa_channel = new_ch; |
399 | 427 | ||
428 | if (sdata->local->ops->channel_switch) { | ||
429 | /* use driver's channel switch callback */ | ||
430 | struct ieee80211_channel_switch ch_switch; | ||
431 | memset(&ch_switch, 0, sizeof(ch_switch)); | ||
432 | ch_switch.timestamp = timestamp; | ||
433 | if (sw_elem->mode) { | ||
434 | ch_switch.block_tx = true; | ||
435 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
436 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
437 | } | ||
438 | ch_switch.channel = new_ch; | ||
439 | ch_switch.count = sw_elem->count; | ||
440 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | ||
441 | drv_channel_switch(sdata->local, &ch_switch); | ||
442 | return; | ||
443 | } | ||
444 | |||
445 | /* channel switch handled in software */ | ||
400 | if (sw_elem->count <= 1) { | 446 | if (sw_elem->count <= 1) { |
401 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | 447 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); |
402 | } else { | 448 | } else { |
@@ -1316,7 +1362,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
1316 | ETH_ALEN) == 0)) { | 1362 | ETH_ALEN) == 0)) { |
1317 | struct ieee80211_channel_sw_ie *sw_elem = | 1363 | struct ieee80211_channel_sw_ie *sw_elem = |
1318 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; | 1364 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; |
1319 | ieee80211_sta_process_chanswitch(sdata, sw_elem, bss); | 1365 | ieee80211_sta_process_chanswitch(sdata, sw_elem, |
1366 | bss, rx_status->mactime); | ||
1320 | } | 1367 | } |
1321 | } | 1368 | } |
1322 | 1369 | ||
@@ -1648,7 +1695,8 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1648 | 1695 | ||
1649 | ieee80211_sta_process_chanswitch(sdata, | 1696 | ieee80211_sta_process_chanswitch(sdata, |
1650 | &mgmt->u.action.u.chan_switch.sw_elem, | 1697 | &mgmt->u.action.u.chan_switch.sw_elem, |
1651 | (void *)ifmgd->associated->priv); | 1698 | (void *)ifmgd->associated->priv, |
1699 | rx_status->mactime); | ||
1652 | break; | 1700 | break; |
1653 | } | 1701 | } |
1654 | mutex_unlock(&ifmgd->mtx); | 1702 | mutex_unlock(&ifmgd->mtx); |