aboutsummaryrefslogtreecommitdiffstats
path: root/net
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
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')
-rw-r--r--net/mac80211/driver-ops.h11
-rw-r--r--net/mac80211/driver-trace.h49
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/mlme.c56
4 files changed, 114 insertions, 5 deletions
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 997008e236ff..5662bb5190c3 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -373,4 +373,15 @@ static inline void drv_flush(struct ieee80211_local *local, bool drop)
373 if (local->ops->flush) 373 if (local->ops->flush)
374 local->ops->flush(&local->hw, drop); 374 local->ops->flush(&local->hw, drop);
375} 375}
376
377static inline void drv_channel_switch(struct ieee80211_local *local,
378 struct ieee80211_channel_switch *ch_switch)
379{
380 might_sleep();
381
382 local->ops->channel_switch(&local->hw, ch_switch);
383
384 trace_drv_channel_switch(local, ch_switch);
385}
386
376#endif /* __MAC80211_DRIVER_OPS */ 387#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index ce734b58d07a..6a9b2342a9c2 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -774,6 +774,34 @@ TRACE_EVENT(drv_flush,
774 ) 774 )
775); 775);
776 776
777TRACE_EVENT(drv_channel_switch,
778 TP_PROTO(struct ieee80211_local *local,
779 struct ieee80211_channel_switch *ch_switch),
780
781 TP_ARGS(local, ch_switch),
782
783 TP_STRUCT__entry(
784 LOCAL_ENTRY
785 __field(u64, timestamp)
786 __field(bool, block_tx)
787 __field(u16, freq)
788 __field(u8, count)
789 ),
790
791 TP_fast_assign(
792 LOCAL_ASSIGN;
793 __entry->timestamp = ch_switch->timestamp;
794 __entry->block_tx = ch_switch->block_tx;
795 __entry->freq = ch_switch->channel->center_freq;
796 __entry->count = ch_switch->count;
797 ),
798
799 TP_printk(
800 LOCAL_PR_FMT " new freq:%u count:%d",
801 LOCAL_PR_ARG, __entry->freq, __entry->count
802 )
803);
804
777/* 805/*
778 * Tracing for API calls that drivers call. 806 * Tracing for API calls that drivers call.
779 */ 807 */
@@ -992,6 +1020,27 @@ TRACE_EVENT(api_sta_block_awake,
992 ) 1020 )
993); 1021);
994 1022
1023TRACE_EVENT(api_chswitch_done,
1024 TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success),
1025
1026 TP_ARGS(sdata, success),
1027
1028 TP_STRUCT__entry(
1029 VIF_ENTRY
1030 __field(bool, success)
1031 ),
1032
1033 TP_fast_assign(
1034 VIF_ASSIGN;
1035 __entry->success = success;
1036 ),
1037
1038 TP_printk(
1039 VIF_PR_FMT " success=%d",
1040 VIF_PR_ARG, __entry->success
1041 )
1042);
1043
995/* 1044/*
996 * Tracing for internal functions 1045 * Tracing for internal functions
997 * (which may also be called in response to driver calls) 1046 * (which may also be called in response to driver calls)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 69e7f4131f46..1c8e24706685 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -999,7 +999,8 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
999 unsigned long data, void *dummy); 999 unsigned long data, void *dummy);
1000void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 1000void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1001 struct ieee80211_channel_sw_ie *sw_elem, 1001 struct ieee80211_channel_sw_ie *sw_elem,
1002 struct ieee80211_bss *bss); 1002 struct ieee80211_bss *bss,
1003 u64 timestamp);
1003void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); 1004void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
1004void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); 1005void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
1005 1006
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);