diff options
-rw-r--r-- | include/net/mac80211.h | 39 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 11 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 49 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 56 |
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 | */ | ||
729 | struct 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 | */ |
1635 | struct ieee80211_ops { | 1662 | struct 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 | */ | ||
2484 | void 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 | |||
377 | static 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 | ||
777 | TRACE_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 | ||
1023 | TRACE_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); |
1000 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | 1000 | void 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); | ||
1003 | void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); | 1004 | void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); |
1004 | void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); | 1005 | void 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 | ||
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); |