aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2010-05-11 10:20:57 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-05-12 16:39:05 -0400
commit5ce6e438d5d9ed8ed775cd1e94f92002c8da2bad (patch)
tree9b6ce4bc8e7600e30124c8b0f1cbc2ae06499722 /net/mac80211/mlme.c
parentb29e7eb4b8b3e5f4ff8066af648e9fe2fc707b16 (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.c56
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
360void 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}
381EXPORT_SYMBOL(ieee80211_chswitch_done);
382
356static void ieee80211_chswitch_timer(unsigned long data) 383static 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
370void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 397void 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);