aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);