Commit 5ce6e438d5d9ed8ed775cd1e94f92002c8da2bad

Authored by Johannes Berg
Committed by John W. Linville
1 parent b29e7eb4b8

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>

Showing 5 changed files with 153 additions and 5 deletions Side-by-side Diff

include/net/mac80211.h
... ... @@ -712,6 +712,28 @@
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 737 * struct ieee80211_vif - per-interface data
716 738 *
717 739 * Data in this structure is continually present for driver
... ... @@ -1631,6 +1653,11 @@
1631 1653 * @flush: Flush all pending frames from the hardware queue, making sure
1632 1654 * that the hardware queues are empty. If the parameter @drop is set
1633 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 1662 struct ieee80211_ops {
1636 1663 int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
... ... @@ -1694,6 +1721,8 @@
1694 1721 int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
1695 1722 #endif
1696 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 /**
... ... @@ -2443,6 +2472,16 @@
2443 2472 void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
2444 2473 enum nl80211_cqm_rssi_threshold_event rssi_event,
2445 2474 gfp_t gfp);
  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);
2446 2485  
2447 2486 /* Rate control API */
2448 2487  
net/mac80211/driver-ops.h
... ... @@ -373,5 +373,16 @@
373 373 if (local->ops->flush)
374 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 387 #endif /* __MAC80211_DRIVER_OPS */
net/mac80211/driver-trace.h
... ... @@ -774,6 +774,34 @@
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 806 * Tracing for API calls that drivers call.
779 807 */
... ... @@ -989,6 +1017,27 @@
989 1017 TP_printk(
990 1018 LOCAL_PR_FMT STA_PR_FMT " block:%d",
991 1019 LOCAL_PR_ARG, STA_PR_FMT, __entry->block
  1020 + )
  1021 +);
  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
992 1041 )
993 1042 );
994 1043  
net/mac80211/ieee80211_i.h
... ... @@ -999,7 +999,8 @@
999 999 unsigned long data, void *dummy);
1000 1000 void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1001 1001 struct ieee80211_channel_sw_ie *sw_elem,
1002   - struct ieee80211_bss *bss);
  1002 + struct ieee80211_bss *bss,
  1003 + u64 timestamp);
1003 1004 void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
1004 1005 void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
1005 1006  
... ... @@ -341,7 +341,11 @@
341 341 goto out;
342 342  
343 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 350 /* XXX: shouldn't really modify cfg80211-owned data! */
347 351 ifmgd->associated->channel = sdata->local->oper_channel;
... ... @@ -353,6 +357,29 @@
353 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 383 static void ieee80211_chswitch_timer(unsigned long data)
357 384 {
358 385 struct ieee80211_sub_if_data *sdata =
... ... @@ -369,7 +396,8 @@
369 396  
370 397 void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
371 398 struct ieee80211_channel_sw_ie *sw_elem,
372   - struct ieee80211_bss *bss)
  399 + struct ieee80211_bss *bss,
  400 + u64 timestamp)
373 401 {
374 402 struct cfg80211_bss *cbss =
375 403 container_of((void *)bss, struct cfg80211_bss, priv);
... ... @@ -397,6 +425,24 @@
397 425  
398 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 446 if (sw_elem->count <= 1) {
401 447 ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
402 448 } else {
... ... @@ -1316,7 +1362,8 @@
1316 1362 ETH_ALEN) == 0)) {
1317 1363 struct ieee80211_channel_sw_ie *sw_elem =
1318 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 @@
1648 1695  
1649 1696 ieee80211_sta_process_chanswitch(sdata,
1650 1697 &mgmt->u.action.u.chan_switch.sw_elem,
1651   - (void *)ifmgd->associated->priv);
  1698 + (void *)ifmgd->associated->priv,
  1699 + rx_status->mactime);
1652 1700 break;
1653 1701 }
1654 1702 mutex_unlock(&ifmgd->mtx);