aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/net/mac80211.h39
-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
5 files changed, 153 insertions, 5 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9448a5b1bb15..389e86a54fc4 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -712,6 +712,28 @@ struct ieee80211_conf {
712}; 712};
713 713
714/** 714/**
715 * struct ieee80211_channel_switch - holds the channel switch data
716 *
717 * The information provided in this structure is required for channel switch
718 * operation.
719 *
720 * @timestamp: value in microseconds of the 64-bit Time Synchronization
721 * Function (TSF) timer when the frame containing the channel switch
722 * announcement was received. This is simply the rx.mactime parameter
723 * the driver passed into mac80211.
724 * @block_tx: Indicates whether transmission must be blocked before the
725 * scheduled channel switch, as indicated by the AP.
726 * @channel: the new channel to switch to
727 * @count: the number of TBTT's until the channel switch event
728 */
729struct ieee80211_channel_switch {
730 u64 timestamp;
731 bool block_tx;
732 struct ieee80211_channel *channel;
733 u8 count;
734};
735
736/**
715 * struct ieee80211_vif - per-interface data 737 * struct ieee80211_vif - per-interface data
716 * 738 *
717 * Data in this structure is continually present for driver 739 * Data in this structure is continually present for driver
@@ -1631,6 +1653,11 @@ enum ieee80211_ampdu_mlme_action {
1631 * @flush: Flush all pending frames from the hardware queue, making sure 1653 * @flush: Flush all pending frames from the hardware queue, making sure
1632 * that the hardware queues are empty. If the parameter @drop is set 1654 * that the hardware queues are empty. If the parameter @drop is set
1633 * to %true, pending frames may be dropped. The callback can sleep. 1655 * to %true, pending frames may be dropped. The callback can sleep.
1656 *
1657 * @channel_switch: Drivers that need (or want) to offload the channel
1658 * switch operation for CSAs received from the AP may implement this
1659 * callback. They must then call ieee80211_chswitch_done() to indicate
1660 * completion of the channel switch.
1634 */ 1661 */
1635struct ieee80211_ops { 1662struct ieee80211_ops {
1636 int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); 1663 int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -1694,6 +1721,8 @@ struct ieee80211_ops {
1694 int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); 1721 int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
1695#endif 1722#endif
1696 void (*flush)(struct ieee80211_hw *hw, bool drop); 1723 void (*flush)(struct ieee80211_hw *hw, bool drop);
1724 void (*channel_switch)(struct ieee80211_hw *hw,
1725 struct ieee80211_channel_switch *ch_switch);
1697}; 1726};
1698 1727
1699/** 1728/**
@@ -2444,6 +2473,16 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
2444 enum nl80211_cqm_rssi_threshold_event rssi_event, 2473 enum nl80211_cqm_rssi_threshold_event rssi_event,
2445 gfp_t gfp); 2474 gfp_t gfp);
2446 2475
2476/**
2477 * ieee80211_chswitch_done - Complete channel switch process
2478 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
2479 * @success: make the channel switch successful or not
2480 *
2481 * Complete the channel switch post-process: set the new operational channel
2482 * and wake up the suspended queues.
2483 */
2484void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success);
2485
2447/* Rate control API */ 2486/* Rate control API */
2448 2487
2449/** 2488/**
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);