| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Off-channel operation helpers |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
|---|
| 8 | 9 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
|---|
| 9 | 10 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 12 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 13 | | - * published by the Free Software Foundation. |
|---|
| 11 | + * Copyright (C) 2019 Intel Corporation |
|---|
| 14 | 12 | */ |
|---|
| 15 | 13 | #include <linux/export.h> |
|---|
| 16 | 14 | #include <net/mac80211.h> |
|---|
| .. | .. |
|---|
| 28 | 26 | { |
|---|
| 29 | 27 | struct ieee80211_local *local = sdata->local; |
|---|
| 30 | 28 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
|---|
| 31 | | - |
|---|
| 32 | | - local->offchannel_ps_enabled = false; |
|---|
| 29 | + bool offchannel_ps_enabled = false; |
|---|
| 33 | 30 | |
|---|
| 34 | 31 | /* FIXME: what to do when local->pspolling is true? */ |
|---|
| 35 | 32 | |
|---|
| .. | .. |
|---|
| 40 | 37 | cancel_work_sync(&local->dynamic_ps_enable_work); |
|---|
| 41 | 38 | |
|---|
| 42 | 39 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { |
|---|
| 43 | | - local->offchannel_ps_enabled = true; |
|---|
| 40 | + offchannel_ps_enabled = true; |
|---|
| 44 | 41 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; |
|---|
| 45 | 42 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); |
|---|
| 46 | 43 | } |
|---|
| 47 | 44 | |
|---|
| 48 | | - if (!local->offchannel_ps_enabled || |
|---|
| 45 | + if (!offchannel_ps_enabled || |
|---|
| 49 | 46 | !ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) |
|---|
| 50 | 47 | /* |
|---|
| 51 | 48 | * If power save was enabled, no need to send a nullfunc |
|---|
| .. | .. |
|---|
| 60 | 57 | ieee80211_send_nullfunc(local, sdata, true); |
|---|
| 61 | 58 | } |
|---|
| 62 | 59 | |
|---|
| 63 | | -/* inform AP that we are awake again, unless power save is enabled */ |
|---|
| 60 | +/* inform AP that we are awake again */ |
|---|
| 64 | 61 | static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) |
|---|
| 65 | 62 | { |
|---|
| 66 | 63 | struct ieee80211_local *local = sdata->local; |
|---|
| 67 | 64 | |
|---|
| 68 | 65 | if (!local->ps_sdata) |
|---|
| 69 | 66 | ieee80211_send_nullfunc(local, sdata, false); |
|---|
| 70 | | - else if (local->offchannel_ps_enabled) { |
|---|
| 67 | + else if (local->hw.conf.dynamic_ps_timeout > 0) { |
|---|
| 71 | 68 | /* |
|---|
| 72 | | - * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware |
|---|
| 73 | | - * will send a nullfunc frame with the powersave bit set |
|---|
| 74 | | - * even though the AP already knows that we are sleeping. |
|---|
| 75 | | - * This could be avoided by sending a null frame with power |
|---|
| 76 | | - * save bit disabled before enabling the power save, but |
|---|
| 77 | | - * this doesn't gain anything. |
|---|
| 78 | | - * |
|---|
| 79 | | - * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need |
|---|
| 80 | | - * to send a nullfunc frame because AP already knows that |
|---|
| 81 | | - * we are sleeping, let's just enable power save mode in |
|---|
| 82 | | - * hardware. |
|---|
| 83 | | - */ |
|---|
| 84 | | - /* TODO: Only set hardware if CONF_PS changed? |
|---|
| 85 | | - * TODO: Should we set offchannel_ps_enabled to false? |
|---|
| 86 | | - */ |
|---|
| 87 | | - local->hw.conf.flags |= IEEE80211_CONF_PS; |
|---|
| 88 | | - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); |
|---|
| 89 | | - } else if (local->hw.conf.dynamic_ps_timeout > 0) { |
|---|
| 90 | | - /* |
|---|
| 91 | | - * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer |
|---|
| 92 | | - * had been running before leaving the operating channel, |
|---|
| 93 | | - * restart the timer now and send a nullfunc frame to inform |
|---|
| 94 | | - * the AP that we are awake. |
|---|
| 69 | + * the dynamic_ps_timer had been running before leaving the |
|---|
| 70 | + * operating channel, restart the timer now and send a nullfunc |
|---|
| 71 | + * frame to inform the AP that we are awake so that AP sends |
|---|
| 72 | + * the buffered packets (if any). |
|---|
| 95 | 73 | */ |
|---|
| 96 | 74 | ieee80211_send_nullfunc(local, sdata, false); |
|---|
| 97 | 75 | mod_timer(&local->dynamic_ps_timer, jiffies + |
|---|
| .. | .. |
|---|
| 202 | 180 | cfg80211_remain_on_channel_expired(&roc->sdata->wdev, |
|---|
| 203 | 181 | roc->cookie, roc->chan, |
|---|
| 204 | 182 | GFP_KERNEL); |
|---|
| 183 | + else |
|---|
| 184 | + cfg80211_tx_mgmt_expired(&roc->sdata->wdev, |
|---|
| 185 | + roc->mgmt_tx_cookie, |
|---|
| 186 | + roc->chan, GFP_KERNEL); |
|---|
| 205 | 187 | |
|---|
| 206 | 188 | list_del(&roc->list); |
|---|
| 207 | 189 | kfree(roc); |
|---|
| .. | .. |
|---|
| 262 | 244 | if (roc->mgmt_tx_cookie) { |
|---|
| 263 | 245 | if (!WARN_ON(!roc->frame)) { |
|---|
| 264 | 246 | ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7, |
|---|
| 265 | | - roc->chan->band, 0); |
|---|
| 247 | + roc->chan->band); |
|---|
| 266 | 248 | roc->frame = NULL; |
|---|
| 267 | 249 | } |
|---|
| 268 | 250 | } else { |
|---|
| .. | .. |
|---|
| 555 | 537 | |
|---|
| 556 | 538 | lockdep_assert_held(&local->mtx); |
|---|
| 557 | 539 | |
|---|
| 540 | + if (channel->freq_offset) |
|---|
| 541 | + /* this may work, but is untested */ |
|---|
| 542 | + return -EOPNOTSUPP; |
|---|
| 543 | + |
|---|
| 558 | 544 | if (local->use_chanctx && !local->ops->remain_on_channel) |
|---|
| 559 | 545 | return -EOPNOTSUPP; |
|---|
| 560 | 546 | |
|---|
| .. | .. |
|---|
| 731 | 717 | } |
|---|
| 732 | 718 | |
|---|
| 733 | 719 | if (local->ops->remain_on_channel) { |
|---|
| 734 | | - ret = drv_cancel_remain_on_channel(local); |
|---|
| 720 | + ret = drv_cancel_remain_on_channel(local, roc->sdata); |
|---|
| 735 | 721 | if (WARN_ON_ONCE(ret)) { |
|---|
| 736 | 722 | mutex_unlock(&local->mtx); |
|---|
| 737 | 723 | return ret; |
|---|
| .. | .. |
|---|
| 802 | 788 | if (!sdata->vif.bss_conf.ibss_joined) |
|---|
| 803 | 789 | need_offchan = true; |
|---|
| 804 | 790 | #ifdef CONFIG_MAC80211_MESH |
|---|
| 805 | | - /* fall through */ |
|---|
| 791 | + fallthrough; |
|---|
| 806 | 792 | case NL80211_IFTYPE_MESH_POINT: |
|---|
| 807 | 793 | if (ieee80211_vif_is_mesh(&sdata->vif) && |
|---|
| 808 | 794 | !sdata->u.mesh.mesh_id_len) |
|---|
| 809 | 795 | need_offchan = true; |
|---|
| 810 | 796 | #endif |
|---|
| 811 | | - /* fall through */ |
|---|
| 797 | + fallthrough; |
|---|
| 812 | 798 | case NL80211_IFTYPE_AP: |
|---|
| 813 | 799 | case NL80211_IFTYPE_AP_VLAN: |
|---|
| 814 | 800 | case NL80211_IFTYPE_P2P_GO: |
|---|
| .. | .. |
|---|
| 910 | 896 | if (beacon) |
|---|
| 911 | 897 | for (i = 0; i < params->n_csa_offsets; i++) |
|---|
| 912 | 898 | data[params->csa_offsets[i]] = |
|---|
| 913 | | - beacon->csa_current_counter; |
|---|
| 899 | + beacon->cntdwn_current_counter; |
|---|
| 914 | 900 | |
|---|
| 915 | 901 | rcu_read_unlock(); |
|---|
| 916 | 902 | } |
|---|
| .. | .. |
|---|
| 990 | 976 | if (roc->started) { |
|---|
| 991 | 977 | if (local->ops->remain_on_channel) { |
|---|
| 992 | 978 | /* can race, so ignore return value */ |
|---|
| 993 | | - drv_cancel_remain_on_channel(local); |
|---|
| 979 | + drv_cancel_remain_on_channel(local, sdata); |
|---|
| 994 | 980 | ieee80211_roc_notify_destroy(roc); |
|---|
| 995 | 981 | } else { |
|---|
| 996 | 982 | roc->abort = true; |
|---|