2014-06-11 03:47:49 -07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014 Qualcomm Atheros, Inc.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ath9k.h"
|
|
|
|
|
|
|
|
/* Set/change channels. If the channel is really being changed, it's done
|
|
|
|
* by reseting the chip. To accomplish this we must first cleanup any pending
|
|
|
|
* DMA, then restart stuff.
|
|
|
|
*/
|
|
|
|
static int ath_set_channel(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
|
|
struct ieee80211_hw *hw = sc->hw;
|
|
|
|
struct ath9k_channel *hchan;
|
|
|
|
struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef;
|
|
|
|
struct ieee80211_channel *chan = chandef->chan;
|
|
|
|
int pos = chan->hw_value;
|
2017-09-18 12:59:19 -07:00
|
|
|
unsigned long flags;
|
2014-06-11 03:47:49 -07:00
|
|
|
int old_pos = -1;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (test_bit(ATH_OP_INVALID, &common->op_flags))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (ah->curchan)
|
|
|
|
old_pos = ah->curchan - &ah->channels[0];
|
|
|
|
|
|
|
|
ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
|
|
|
|
chan->center_freq, chandef->width);
|
|
|
|
|
|
|
|
/* update survey stats for the old channel before switching */
|
2017-09-18 12:59:19 -07:00
|
|
|
spin_lock_irqsave(&common->cc_lock, flags);
|
2014-06-11 03:47:49 -07:00
|
|
|
ath_update_survey_stats(sc);
|
2017-09-18 12:59:19 -07:00
|
|
|
spin_unlock_irqrestore(&common->cc_lock, flags);
|
2014-06-11 03:47:49 -07:00
|
|
|
|
|
|
|
ath9k_cmn_get_channel(hw, ah, chandef);
|
|
|
|
|
|
|
|
/* If the operating channel changes, change the survey in-use flags
|
|
|
|
* along with it.
|
|
|
|
* Reset the survey data for the new channel, unless we're switching
|
|
|
|
* back to the operating channel from an off-channel operation.
|
|
|
|
*/
|
|
|
|
if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) {
|
|
|
|
if (sc->cur_survey)
|
|
|
|
sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
|
|
|
|
|
|
|
|
sc->cur_survey = &sc->survey[pos];
|
|
|
|
|
|
|
|
memset(sc->cur_survey, 0, sizeof(struct survey_info));
|
|
|
|
sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
|
|
|
|
} else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
|
|
|
|
memset(&sc->survey[pos], 0, sizeof(struct survey_info));
|
|
|
|
}
|
|
|
|
|
|
|
|
hchan = &sc->sc_ah->channels[pos];
|
2014-10-16 19:10:11 -07:00
|
|
|
r = ath_reset(sc, hchan);
|
2014-06-11 03:47:49 -07:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
/* The most recent snapshot of channel->noisefloor for the old
|
|
|
|
* channel is only available after the hardware reset. Copy it to
|
|
|
|
* the survey stats now.
|
|
|
|
*/
|
|
|
|
if (old_pos >= 0)
|
|
|
|
ath_update_survey_nf(sc, old_pos);
|
|
|
|
|
|
|
|
/* Enable radar pulse detection if on a DFS channel. Spectral
|
|
|
|
* scanning and radar detection can not be used concurrently.
|
|
|
|
*/
|
|
|
|
if (hw->conf.radar_enabled) {
|
|
|
|
u32 rxfilter;
|
|
|
|
|
|
|
|
rxfilter = ath9k_hw_getrxfilter(ah);
|
|
|
|
rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
|
|
|
|
ATH9K_RX_FILTER_PHYERR;
|
|
|
|
ath9k_hw_setrxfilter(ah, rxfilter);
|
|
|
|
ath_dbg(common, DFS, "DFS enabled at freq %d\n",
|
|
|
|
chan->center_freq);
|
|
|
|
} else {
|
|
|
|
/* perform spectral scan if requested. */
|
|
|
|
if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
|
2014-11-06 00:53:19 -07:00
|
|
|
sc->spec_priv.spectral_mode == SPECTRAL_CHANSCAN)
|
2014-11-06 00:53:30 -07:00
|
|
|
ath9k_cmn_spectral_scan_trigger(common, &sc->spec_priv);
|
2014-06-11 03:47:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath_chanctx_init(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct ath_chanctx *ctx;
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
struct ieee80211_channel *chan;
|
2014-06-11 03:47:51 -07:00
|
|
|
int i, j;
|
2014-06-11 03:47:49 -07:00
|
|
|
|
2016-04-12 06:56:15 -07:00
|
|
|
sband = &common->sbands[NL80211_BAND_2GHZ];
|
2014-06-11 03:47:49 -07:00
|
|
|
if (!sband->n_channels)
|
2016-04-12 06:56:15 -07:00
|
|
|
sband = &common->sbands[NL80211_BAND_5GHZ];
|
2014-06-11 03:47:49 -07:00
|
|
|
|
|
|
|
chan = &sband->channels[0];
|
|
|
|
for (i = 0; i < ATH9K_NUM_CHANCTX; i++) {
|
|
|
|
ctx = &sc->chanctx[i];
|
|
|
|
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
|
|
|
|
INIT_LIST_HEAD(&ctx->vifs);
|
2014-06-11 03:47:50 -07:00
|
|
|
ctx->txpower = ATH_TXPOWER_MAX;
|
2014-10-16 19:10:15 -07:00
|
|
|
ctx->flush_timeout = HZ / 5; /* 200ms */
|
2016-12-05 04:27:37 -07:00
|
|
|
for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) {
|
|
|
|
INIT_LIST_HEAD(&ctx->acq[j].acq_new);
|
|
|
|
INIT_LIST_HEAD(&ctx->acq[j].acq_old);
|
|
|
|
spin_lock_init(&ctx->acq[j].lock);
|
|
|
|
}
|
2014-06-11 03:47:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-11 03:47:52 -07:00
|
|
|
void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
|
|
|
|
struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
2014-08-24 08:46:13 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2014-06-11 03:47:52 -07:00
|
|
|
bool cur_chan;
|
|
|
|
|
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
if (chandef)
|
|
|
|
memcpy(&ctx->chandef, chandef, sizeof(*chandef));
|
|
|
|
cur_chan = sc->cur_chan == ctx;
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
|
2014-08-24 08:46:13 -07:00
|
|
|
if (!cur_chan) {
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Current context differs from the new context\n");
|
2014-06-11 03:47:52 -07:00
|
|
|
return;
|
2014-08-24 08:46:13 -07:00
|
|
|
}
|
2014-06-11 03:47:52 -07:00
|
|
|
|
|
|
|
ath_set_channel(sc);
|
2014-06-11 03:47:49 -07:00
|
|
|
}
|
2014-06-11 03:47:55 -07:00
|
|
|
|
2014-08-23 00:59:16 -07:00
|
|
|
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
|
|
|
|
|
2014-10-16 19:10:21 -07:00
|
|
|
/*************/
|
|
|
|
/* Utilities */
|
|
|
|
/*************/
|
|
|
|
|
|
|
|
struct ath_chanctx* ath_is_go_chanctx_present(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct ath_chanctx *ctx;
|
|
|
|
struct ath_vif *avp;
|
|
|
|
struct ieee80211_vif *vif;
|
|
|
|
|
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
|
|
|
|
ath_for_each_chanctx(sc, ctx) {
|
|
|
|
if (!ctx->active)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
list_for_each_entry(avp, &ctx->vifs, list) {
|
|
|
|
vif = avp->vif;
|
|
|
|
|
|
|
|
if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO) {
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:19 -07:00
|
|
|
/**********************************************************/
|
|
|
|
/* Functions to handle the channel context state machine. */
|
|
|
|
/**********************************************************/
|
|
|
|
|
2014-08-23 00:59:16 -07:00
|
|
|
static const char *offchannel_state_string(enum ath_offchannel_state state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case_rtn_string(ATH_OFFCHANNEL_IDLE);
|
|
|
|
case_rtn_string(ATH_OFFCHANNEL_PROBE_SEND);
|
|
|
|
case_rtn_string(ATH_OFFCHANNEL_PROBE_WAIT);
|
|
|
|
case_rtn_string(ATH_OFFCHANNEL_SUSPEND);
|
|
|
|
case_rtn_string(ATH_OFFCHANNEL_ROC_START);
|
|
|
|
case_rtn_string(ATH_OFFCHANNEL_ROC_WAIT);
|
|
|
|
case_rtn_string(ATH_OFFCHANNEL_ROC_DONE);
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-24 08:46:11 -07:00
|
|
|
static const char *chanctx_event_string(enum ath_chanctx_event ev)
|
|
|
|
{
|
|
|
|
switch (ev) {
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_BEACON_PREPARE);
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_BEACON_SENT);
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_TSF_TIMER);
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_BEACON_RECEIVED);
|
2014-10-16 19:10:09 -07:00
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_AUTHORIZED);
|
2014-08-24 08:46:11 -07:00
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_SWITCH);
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_ASSIGN);
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_UNASSIGN);
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_CHANGE);
|
|
|
|
case_rtn_string(ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL);
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *chanctx_state_string(enum ath_chanctx_state state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case_rtn_string(ATH_CHANCTX_STATE_IDLE);
|
|
|
|
case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_BEACON);
|
|
|
|
case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_TIMER);
|
|
|
|
case_rtn_string(ATH_CHANCTX_STATE_SWITCH);
|
|
|
|
case_rtn_string(ATH_CHANCTX_STATE_FORCE_ACTIVE);
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-15 06:41:30 -07:00
|
|
|
static u32 chanctx_event_delta(struct ath_softc *sc)
|
2015-11-27 01:37:17 -07:00
|
|
|
{
|
|
|
|
u64 ms;
|
2018-06-18 08:11:17 -07:00
|
|
|
struct timespec64 ts, *old;
|
2015-11-27 01:37:17 -07:00
|
|
|
|
2018-06-18 08:11:17 -07:00
|
|
|
ktime_get_raw_ts64(&ts);
|
2015-11-27 01:37:17 -07:00
|
|
|
old = &sc->last_event_time;
|
|
|
|
ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
|
|
|
ms -= old->tv_sec * 1000 + old->tv_nsec / 1000000;
|
|
|
|
sc->last_event_time = ts;
|
|
|
|
|
|
|
|
return (u32)ms;
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:21 -07:00
|
|
|
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2014-10-16 19:10:16 -07:00
|
|
|
struct ath_chanctx *ictx;
|
2014-08-23 00:59:21 -07:00
|
|
|
struct ath_vif *avp;
|
|
|
|
bool active = false;
|
|
|
|
u8 n_active = 0;
|
|
|
|
|
|
|
|
if (!ctx)
|
|
|
|
return;
|
|
|
|
|
2014-10-16 19:10:17 -07:00
|
|
|
if (ctx == &sc->offchannel.chan) {
|
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
|
|
|
|
if (likely(sc->sched.channel_switch_time))
|
|
|
|
ctx->flush_timeout =
|
|
|
|
usecs_to_jiffies(sc->sched.channel_switch_time);
|
|
|
|
else
|
|
|
|
ctx->flush_timeout =
|
|
|
|
msecs_to_jiffies(10);
|
|
|
|
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is no need to iterate over the
|
|
|
|
* active/assigned channel contexts if
|
|
|
|
* the current context is offchannel.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-16 19:10:16 -07:00
|
|
|
ictx = ctx;
|
|
|
|
|
2014-08-23 00:59:21 -07:00
|
|
|
list_for_each_entry(avp, &ctx->vifs, list) {
|
|
|
|
struct ieee80211_vif *vif = avp->vif;
|
|
|
|
|
|
|
|
switch (vif->type) {
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2014-09-17 02:15:56 -07:00
|
|
|
if (avp->assoc)
|
2014-08-23 00:59:21 -07:00
|
|
|
active = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
active = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ctx->active = active;
|
|
|
|
|
|
|
|
ath_for_each_chanctx(sc, ctx) {
|
|
|
|
if (!ctx->assigned || list_empty(&ctx->vifs))
|
|
|
|
continue;
|
|
|
|
n_active++;
|
|
|
|
}
|
|
|
|
|
2014-10-16 19:10:16 -07:00
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
|
2014-08-23 00:59:21 -07:00
|
|
|
if (n_active <= 1) {
|
2014-10-16 19:10:16 -07:00
|
|
|
ictx->flush_timeout = HZ / 5;
|
2014-08-23 00:59:21 -07:00
|
|
|
clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
|
2014-10-16 19:10:16 -07:00
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
2014-08-23 00:59:21 -07:00
|
|
|
return;
|
|
|
|
}
|
2014-10-16 19:10:16 -07:00
|
|
|
|
|
|
|
ictx->flush_timeout = usecs_to_jiffies(sc->sched.channel_switch_time);
|
|
|
|
|
|
|
|
if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
2014-08-23 00:59:21 -07:00
|
|
|
return;
|
2014-10-16 19:10:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
2014-08-23 00:59:21 -07:00
|
|
|
|
|
|
|
if (ath9k_is_chanctx_enabled()) {
|
|
|
|
ath_chanctx_event(sc, NULL,
|
|
|
|
ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-11 03:48:08 -07:00
|
|
|
static struct ath_chanctx *
|
|
|
|
ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx)
|
|
|
|
{
|
|
|
|
int idx = ctx - &sc->chanctx[0];
|
|
|
|
|
|
|
|
return &sc->chanctx[!idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct ath_chanctx *prev, *cur;
|
2018-06-18 08:11:17 -07:00
|
|
|
struct timespec64 ts;
|
2014-06-11 03:48:08 -07:00
|
|
|
u32 cur_tsf, prev_tsf, beacon_int;
|
|
|
|
s32 offset;
|
|
|
|
|
|
|
|
beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
|
|
|
|
|
|
|
|
cur = sc->cur_chan;
|
|
|
|
prev = ath_chanctx_get_next(sc, cur);
|
|
|
|
|
2014-09-14 22:55:51 -07:00
|
|
|
if (!prev->switch_after_beacon)
|
|
|
|
return;
|
|
|
|
|
2018-06-18 08:11:17 -07:00
|
|
|
ktime_get_raw_ts64(&ts);
|
2014-06-11 03:48:08 -07:00
|
|
|
cur_tsf = (u32) cur->tsf_val +
|
|
|
|
ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts);
|
|
|
|
|
|
|
|
prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf;
|
|
|
|
prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts);
|
|
|
|
|
|
|
|
/* Adjust the TSF time of the AP chanctx to keep its beacons
|
|
|
|
* at half beacon interval offset relative to the STA chanctx.
|
|
|
|
*/
|
|
|
|
offset = cur_tsf - prev_tsf;
|
|
|
|
|
|
|
|
/* Ignore stale data or spurious timestamps */
|
|
|
|
if (offset < 0 || offset > 3 * beacon_int)
|
|
|
|
return;
|
|
|
|
|
|
|
|
offset = beacon_int / 2 - (offset % beacon_int);
|
|
|
|
prev->tsf_val += offset;
|
|
|
|
}
|
|
|
|
|
2014-06-11 03:48:14 -07:00
|
|
|
/* Configure the TSF based hardware timer for a channel switch.
|
|
|
|
* Also set up backup software timer, in case the gen timer fails.
|
|
|
|
* This could be caused by a hardware reset.
|
|
|
|
*/
|
|
|
|
static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
|
|
|
|
{
|
2014-08-26 23:37:24 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2014-06-11 03:48:14 -07:00
|
|
|
struct ath_hw *ah = sc->sc_ah;
|
2015-11-27 01:37:07 -07:00
|
|
|
unsigned long timeout;
|
2014-06-11 03:48:14 -07:00
|
|
|
|
|
|
|
ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
|
|
|
|
tsf_time -= ath9k_hw_gettsf32(ah);
|
2015-11-27 01:37:07 -07:00
|
|
|
timeout = msecs_to_jiffies(tsf_time / 1000) + 1;
|
|
|
|
mod_timer(&sc->sched.timer, jiffies + timeout);
|
2014-08-26 23:37:24 -07:00
|
|
|
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
2015-11-27 01:37:07 -07:00
|
|
|
"Setup chanctx timer with timeout: %d (%d) ms\n",
|
|
|
|
tsf_time / 1000, jiffies_to_msecs(timeout));
|
2014-06-11 03:48:14 -07:00
|
|
|
}
|
|
|
|
|
2014-10-16 19:10:25 -07:00
|
|
|
static void ath_chanctx_handle_bmiss(struct ath_softc *sc,
|
|
|
|
struct ath_chanctx *ctx,
|
|
|
|
struct ath_vif *avp)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Clear the extend_absence flag if it had been
|
|
|
|
* set during the previous beacon transmission,
|
|
|
|
* since we need to revert to the normal NoA
|
|
|
|
* schedule.
|
|
|
|
*/
|
|
|
|
if (ctx->active && sc->sched.extend_absence) {
|
|
|
|
avp->noa_duration = 0;
|
|
|
|
sc->sched.extend_absence = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If at least two consecutive beacons were missed on the STA
|
|
|
|
* chanctx, stay on the STA channel for one extra beacon period,
|
|
|
|
* to resync the timer properly.
|
|
|
|
*/
|
|
|
|
if (ctx->active && sc->sched.beacon_miss >= 2) {
|
|
|
|
avp->noa_duration = 0;
|
|
|
|
sc->sched.extend_absence = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-16 19:10:23 -07:00
|
|
|
static void ath_chanctx_offchannel_noa(struct ath_softc *sc,
|
|
|
|
struct ath_chanctx *ctx,
|
|
|
|
struct ath_vif *avp,
|
|
|
|
u32 tsf_time)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
|
|
|
avp->noa_index++;
|
|
|
|
avp->offchannel_start = tsf_time;
|
|
|
|
avp->offchannel_duration = sc->sched.offchannel_duration;
|
|
|
|
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
2015-11-27 01:37:06 -07:00
|
|
|
"offchannel noa_duration: %d, noa_start: %u, noa_index: %d\n",
|
2014-10-16 19:10:23 -07:00
|
|
|
avp->offchannel_duration,
|
|
|
|
avp->offchannel_start,
|
|
|
|
avp->noa_index);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When multiple contexts are active, the NoA
|
|
|
|
* has to be recalculated and advertised after
|
|
|
|
* an offchannel operation.
|
|
|
|
*/
|
|
|
|
if (ctx->active && avp->noa_duration)
|
|
|
|
avp->noa_duration = 0;
|
|
|
|
}
|
|
|
|
|
2014-10-16 19:10:24 -07:00
|
|
|
static void ath_chanctx_set_periodic_noa(struct ath_softc *sc,
|
|
|
|
struct ath_vif *avp,
|
|
|
|
struct ath_beacon_config *cur_conf,
|
|
|
|
u32 tsf_time,
|
|
|
|
u32 beacon_int)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
|
|
|
avp->noa_index++;
|
|
|
|
avp->noa_start = tsf_time;
|
|
|
|
|
|
|
|
if (sc->sched.extend_absence)
|
|
|
|
avp->noa_duration = (3 * beacon_int / 2) +
|
|
|
|
sc->sched.channel_switch_time;
|
|
|
|
else
|
|
|
|
avp->noa_duration =
|
|
|
|
TU_TO_USEC(cur_conf->beacon_interval) / 2 +
|
|
|
|
sc->sched.channel_switch_time;
|
|
|
|
|
|
|
|
if (test_bit(ATH_OP_SCANNING, &common->op_flags) ||
|
|
|
|
sc->sched.extend_absence)
|
|
|
|
avp->periodic_noa = false;
|
|
|
|
else
|
|
|
|
avp->periodic_noa = true;
|
|
|
|
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
2015-11-27 01:37:06 -07:00
|
|
|
"noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n",
|
2014-10-16 19:10:24 -07:00
|
|
|
avp->noa_duration,
|
|
|
|
avp->noa_start,
|
|
|
|
avp->noa_index,
|
|
|
|
avp->periodic_noa);
|
|
|
|
}
|
|
|
|
|
2014-10-16 19:10:27 -07:00
|
|
|
static void ath_chanctx_set_oneshot_noa(struct ath_softc *sc,
|
|
|
|
struct ath_vif *avp,
|
|
|
|
u32 tsf_time,
|
|
|
|
u32 duration)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
|
|
|
avp->noa_index++;
|
|
|
|
avp->noa_start = tsf_time;
|
|
|
|
avp->periodic_noa = false;
|
|
|
|
avp->oneshot_noa = true;
|
|
|
|
avp->noa_duration = duration + sc->sched.channel_switch_time;
|
|
|
|
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
2015-11-27 01:37:06 -07:00
|
|
|
"oneshot noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n",
|
2014-10-16 19:10:27 -07:00
|
|
|
avp->noa_duration,
|
|
|
|
avp->noa_start,
|
|
|
|
avp->noa_index,
|
|
|
|
avp->periodic_noa);
|
|
|
|
}
|
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
|
|
|
|
enum ath_chanctx_event ev)
|
|
|
|
{
|
|
|
|
struct ath_hw *ah = sc->sc_ah;
|
2014-06-11 03:48:08 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2014-06-11 03:48:11 -07:00
|
|
|
struct ath_beacon_config *cur_conf;
|
2014-06-11 03:48:06 -07:00
|
|
|
struct ath_vif *avp = NULL;
|
2014-06-11 03:48:10 -07:00
|
|
|
struct ath_chanctx *ctx;
|
2014-06-11 03:48:04 -07:00
|
|
|
u32 tsf_time;
|
2014-06-11 03:48:12 -07:00
|
|
|
u32 beacon_int;
|
2014-06-11 03:48:06 -07:00
|
|
|
|
|
|
|
if (vif)
|
|
|
|
avp = (struct ath_vif *) vif->drv_priv;
|
2014-06-11 03:48:04 -07:00
|
|
|
|
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
|
2015-11-27 01:37:17 -07:00
|
|
|
ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s, delta: %u ms\n",
|
2014-08-26 23:37:24 -07:00
|
|
|
sc->cur_chan->chandef.center_freq1,
|
|
|
|
chanctx_event_string(ev),
|
2015-11-27 01:37:17 -07:00
|
|
|
chanctx_state_string(sc->sched.state),
|
|
|
|
chanctx_event_delta(sc));
|
2014-08-26 23:37:24 -07:00
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
switch (ev) {
|
|
|
|
case ATH_CHANCTX_EVENT_BEACON_PREPARE:
|
2014-06-11 03:48:06 -07:00
|
|
|
if (avp->offchannel_duration)
|
|
|
|
avp->offchannel_duration = 0;
|
|
|
|
|
2014-10-16 19:10:27 -07:00
|
|
|
if (avp->oneshot_noa) {
|
|
|
|
avp->noa_duration = 0;
|
|
|
|
avp->oneshot_noa = false;
|
|
|
|
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Clearing oneshot NoA\n");
|
|
|
|
}
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
if (avp->chanctx != sc->cur_chan) {
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Contexts differ, not preparing beacon\n");
|
2014-06-11 03:48:10 -07:00
|
|
|
break;
|
2014-08-26 23:37:24 -07:00
|
|
|
}
|
2014-06-11 03:48:10 -07:00
|
|
|
|
ath9k: Fix MCC scanning
Scanning is curently broken when two channel contexts
are active. For example in a P2P-GO/STA setup, the
offchannel timer allows HZ / 10 to elapse before initiating
a switch to the next scan channel from the current operating
channel, which in this case would be the P2P-GO context.
But, the channel context timer might decide to switch
to the STA context when an SWBA comes early and a beacon
is sent out. Since pending offchannel requests are processed
in EVENT_BEACON_PREPARE, this causes inconsistent scanning.
Fix this by making sure that a context switch happens
before processing the pending offchannel request. This
also makes sure that active channel contexts will always
have higher priority than offchannel operations and the
scan sequence looks like this:
p2p-go, sta, p2p-go, offchannel, p2p-go, sta, p2p-go, offchannel,.....
The oper-channel is p2p-go, so the STA context has to
switch to p2p-go again before switching offchannel.
Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2014-09-04 21:20:57 -07:00
|
|
|
if (sc->sched.offchannel_pending && !sc->sched.wait_switch) {
|
2014-06-11 03:48:10 -07:00
|
|
|
sc->sched.offchannel_pending = false;
|
|
|
|
sc->next_chan = &sc->offchannel.chan;
|
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Setting offchannel_pending to false\n");
|
2014-06-11 03:48:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx = ath_chanctx_get_next(sc, sc->cur_chan);
|
|
|
|
if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) {
|
|
|
|
sc->next_chan = ctx;
|
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Set next context, move chanctx state to WAIT_FOR_BEACON\n");
|
2014-06-11 03:48:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if the timer missed its window, use the next interval */
|
2014-08-26 23:37:24 -07:00
|
|
|
if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) {
|
2014-06-11 03:48:10 -07:00
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Move chanctx state from WAIT_FOR_TIMER to WAIT_FOR_BEACON\n");
|
|
|
|
}
|
2014-06-11 03:48:10 -07:00
|
|
|
|
2014-10-16 19:10:22 -07:00
|
|
|
if (sc->sched.mgd_prepare_tx)
|
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
|
|
|
|
|
2014-10-16 19:10:14 -07:00
|
|
|
/*
|
|
|
|
* When a context becomes inactive, for example,
|
|
|
|
* disassociation of a station context, the NoA
|
|
|
|
* attribute needs to be removed from subsequent
|
|
|
|
* beacons.
|
|
|
|
*/
|
|
|
|
if (!ctx->active && avp->noa_duration &&
|
|
|
|
sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) {
|
|
|
|
avp->noa_duration = 0;
|
|
|
|
avp->periodic_noa = false;
|
|
|
|
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Clearing NoA schedule\n");
|
|
|
|
}
|
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
|
|
|
|
break;
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX, "Preparing beacon for vif: %pM\n", vif->addr);
|
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
sc->sched.beacon_pending = true;
|
|
|
|
sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER);
|
2014-06-11 03:48:06 -07:00
|
|
|
|
2014-06-11 03:48:11 -07:00
|
|
|
cur_conf = &sc->cur_chan->beacon;
|
2014-06-11 03:48:12 -07:00
|
|
|
beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
|
|
|
|
|
2014-06-11 03:48:06 -07:00
|
|
|
/* defer channel switch by a quarter beacon interval */
|
2014-06-11 03:48:12 -07:00
|
|
|
tsf_time = sc->sched.next_tbtt + beacon_int / 4;
|
2014-06-11 03:48:06 -07:00
|
|
|
sc->sched.switch_start_time = tsf_time;
|
2014-06-11 03:48:08 -07:00
|
|
|
sc->cur_chan->last_beacon = sc->sched.next_tbtt;
|
2014-06-11 03:48:06 -07:00
|
|
|
|
2014-09-10 06:45:57 -07:00
|
|
|
/*
|
|
|
|
* If an offchannel switch is scheduled to happen after
|
|
|
|
* a beacon transmission, update the NoA with one-shot
|
|
|
|
* values and increment the index.
|
|
|
|
*/
|
|
|
|
if (sc->next_chan == &sc->offchannel.chan) {
|
2014-10-16 19:10:23 -07:00
|
|
|
ath_chanctx_offchannel_noa(sc, ctx, avp, tsf_time);
|
2014-09-10 06:45:57 -07:00
|
|
|
break;
|
2014-06-11 03:48:06 -07:00
|
|
|
}
|
|
|
|
|
2014-10-16 19:10:25 -07:00
|
|
|
ath_chanctx_handle_bmiss(sc, ctx, avp);
|
2014-09-14 22:55:50 -07:00
|
|
|
|
2014-10-16 19:10:27 -07:00
|
|
|
/*
|
|
|
|
* If a mgd_prepare_tx() has been called by mac80211,
|
|
|
|
* a one-shot NoA needs to be sent. This can happen
|
|
|
|
* with one or more active channel contexts - in both
|
|
|
|
* cases, a new NoA schedule has to be advertised.
|
|
|
|
*/
|
|
|
|
if (sc->sched.mgd_prepare_tx) {
|
|
|
|
ath_chanctx_set_oneshot_noa(sc, avp, tsf_time,
|
|
|
|
jiffies_to_usecs(HZ / 5));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-09-10 06:45:57 -07:00
|
|
|
/* Prevent wrap-around issues */
|
|
|
|
if (avp->noa_duration && tsf_time - avp->noa_start > BIT(30))
|
|
|
|
avp->noa_duration = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If multiple contexts are active, start periodic
|
|
|
|
* NoA and increment the index for the first
|
|
|
|
* announcement.
|
|
|
|
*/
|
|
|
|
if (ctx->active &&
|
2014-10-16 19:10:24 -07:00
|
|
|
(!avp->noa_duration || sc->sched.force_noa_update))
|
|
|
|
ath_chanctx_set_periodic_noa(sc, avp, cur_conf,
|
|
|
|
tsf_time, beacon_int);
|
2014-09-10 06:45:57 -07:00
|
|
|
|
|
|
|
if (ctx->active && sc->sched.force_noa_update)
|
|
|
|
sc->sched.force_noa_update = false;
|
2014-08-26 23:37:24 -07:00
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
break;
|
|
|
|
case ATH_CHANCTX_EVENT_BEACON_SENT:
|
2014-08-26 23:37:24 -07:00
|
|
|
if (!sc->sched.beacon_pending) {
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"No pending beacon\n");
|
2014-06-11 03:48:04 -07:00
|
|
|
break;
|
2014-08-26 23:37:24 -07:00
|
|
|
}
|
2014-06-11 03:48:04 -07:00
|
|
|
|
|
|
|
sc->sched.beacon_pending = false;
|
2014-10-16 19:10:22 -07:00
|
|
|
|
|
|
|
if (sc->sched.mgd_prepare_tx) {
|
|
|
|
sc->sched.mgd_prepare_tx = false;
|
|
|
|
complete(&sc->go_beacon);
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Beacon sent, complete go_beacon\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
|
|
|
|
break;
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Move chanctx state to WAIT_FOR_TIMER\n");
|
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
|
2014-06-11 03:48:14 -07:00
|
|
|
ath_chanctx_setup_timer(sc, sc->sched.switch_start_time);
|
2014-06-11 03:48:04 -07:00
|
|
|
break;
|
|
|
|
case ATH_CHANCTX_EVENT_TSF_TIMER:
|
|
|
|
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
|
|
|
|
break;
|
|
|
|
|
2014-06-11 03:48:12 -07:00
|
|
|
if (!sc->cur_chan->switch_after_beacon &&
|
|
|
|
sc->sched.beacon_pending)
|
|
|
|
sc->sched.beacon_miss++;
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Move chanctx state to SWITCH\n");
|
|
|
|
|
2014-06-11 03:48:04 -07:00
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_SWITCH;
|
|
|
|
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
|
|
|
|
break;
|
2014-06-11 03:48:08 -07:00
|
|
|
case ATH_CHANCTX_EVENT_BEACON_RECEIVED:
|
2014-06-11 03:48:10 -07:00
|
|
|
if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
|
|
|
|
sc->cur_chan == &sc->offchannel.chan)
|
2014-06-11 03:48:08 -07:00
|
|
|
break;
|
|
|
|
|
2014-06-11 03:48:12 -07:00
|
|
|
sc->sched.beacon_pending = false;
|
|
|
|
sc->sched.beacon_miss = 0;
|
2014-06-11 03:48:13 -07:00
|
|
|
|
2014-10-16 19:10:10 -07:00
|
|
|
if (sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
|
2014-11-15 17:41:09 -07:00
|
|
|
!sc->sched.beacon_adjust ||
|
2014-10-16 19:10:10 -07:00
|
|
|
!sc->cur_chan->tsf_val)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ath_chanctx_adjust_tbtt_delta(sc);
|
|
|
|
|
2014-06-11 03:48:13 -07:00
|
|
|
/* TSF time might have been updated by the incoming beacon,
|
|
|
|
* need update the channel switch timer to reflect the change.
|
|
|
|
*/
|
|
|
|
tsf_time = sc->sched.switch_start_time;
|
|
|
|
tsf_time -= (u32) sc->cur_chan->tsf_val +
|
|
|
|
ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
|
|
|
|
tsf_time += ath9k_hw_gettsf32(ah);
|
|
|
|
|
2014-11-15 17:41:09 -07:00
|
|
|
sc->sched.beacon_adjust = false;
|
2014-06-11 03:48:14 -07:00
|
|
|
ath_chanctx_setup_timer(sc, tsf_time);
|
2014-06-11 03:48:08 -07:00
|
|
|
break;
|
2014-10-16 19:10:09 -07:00
|
|
|
case ATH_CHANCTX_EVENT_AUTHORIZED:
|
2014-06-11 03:48:10 -07:00
|
|
|
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
|
|
|
|
avp->chanctx != sc->cur_chan)
|
|
|
|
break;
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Move chanctx state from FORCE_ACTIVE to IDLE\n");
|
|
|
|
|
2014-06-11 03:48:10 -07:00
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
|
2020-07-27 12:35:20 -07:00
|
|
|
fallthrough;
|
2014-06-11 03:48:10 -07:00
|
|
|
case ATH_CHANCTX_EVENT_SWITCH:
|
|
|
|
if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
|
|
|
|
sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
|
|
|
|
sc->cur_chan->switch_after_beacon ||
|
|
|
|
sc->cur_chan == &sc->offchannel.chan)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* If this is a station chanctx, stay active for a half
|
|
|
|
* beacon period (minus channel switch time)
|
|
|
|
*/
|
|
|
|
sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
|
2014-06-11 03:48:11 -07:00
|
|
|
cur_conf = &sc->cur_chan->beacon;
|
2014-06-11 03:48:10 -07:00
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Move chanctx state to WAIT_FOR_TIMER (event SWITCH)\n");
|
|
|
|
|
2014-06-11 03:48:10 -07:00
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
|
ath9k: Fix MCC scanning
Scanning is curently broken when two channel contexts
are active. For example in a P2P-GO/STA setup, the
offchannel timer allows HZ / 10 to elapse before initiating
a switch to the next scan channel from the current operating
channel, which in this case would be the P2P-GO context.
But, the channel context timer might decide to switch
to the STA context when an SWBA comes early and a beacon
is sent out. Since pending offchannel requests are processed
in EVENT_BEACON_PREPARE, this causes inconsistent scanning.
Fix this by making sure that a context switch happens
before processing the pending offchannel request. This
also makes sure that active channel contexts will always
have higher priority than offchannel operations and the
scan sequence looks like this:
p2p-go, sta, p2p-go, offchannel, p2p-go, sta, p2p-go, offchannel,.....
The oper-channel is p2p-go, so the STA context has to
switch to p2p-go again before switching offchannel.
Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2014-09-04 21:20:57 -07:00
|
|
|
sc->sched.wait_switch = false;
|
2014-06-11 03:48:12 -07:00
|
|
|
|
|
|
|
tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2;
|
2014-09-10 06:46:00 -07:00
|
|
|
|
|
|
|
if (sc->sched.extend_absence) {
|
2014-06-11 03:48:12 -07:00
|
|
|
sc->sched.beacon_miss = 0;
|
|
|
|
tsf_time *= 3;
|
|
|
|
}
|
|
|
|
|
2014-06-11 03:48:10 -07:00
|
|
|
tsf_time -= sc->sched.channel_switch_time;
|
2014-06-11 03:48:12 -07:00
|
|
|
tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
|
2014-06-11 03:48:10 -07:00
|
|
|
sc->sched.switch_start_time = tsf_time;
|
|
|
|
|
2014-06-11 03:48:14 -07:00
|
|
|
ath_chanctx_setup_timer(sc, tsf_time);
|
2014-06-11 03:48:12 -07:00
|
|
|
sc->sched.beacon_pending = true;
|
2014-11-15 17:41:09 -07:00
|
|
|
sc->sched.beacon_adjust = true;
|
2014-06-11 03:48:10 -07:00
|
|
|
break;
|
|
|
|
case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
|
|
|
|
if (sc->cur_chan == &sc->offchannel.chan ||
|
|
|
|
sc->cur_chan->switch_after_beacon)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
|
|
|
|
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
|
|
|
|
break;
|
|
|
|
case ATH_CHANCTX_EVENT_UNASSIGN:
|
|
|
|
if (sc->cur_chan->assigned) {
|
|
|
|
if (sc->next_chan && !sc->next_chan->assigned &&
|
|
|
|
sc->next_chan != &sc->offchannel.chan)
|
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx = ath_chanctx_get_next(sc, sc->cur_chan);
|
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
|
|
|
|
if (!ctx->assigned)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sc->next_chan = ctx;
|
|
|
|
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
|
|
|
|
break;
|
2014-08-24 08:46:10 -07:00
|
|
|
case ATH_CHANCTX_EVENT_ASSIGN:
|
|
|
|
break;
|
|
|
|
case ATH_CHANCTX_EVENT_CHANGE:
|
|
|
|
break;
|
2014-06-11 03:48:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
}
|
2014-08-22 08:09:26 -07:00
|
|
|
|
2014-08-23 00:59:18 -07:00
|
|
|
void ath_chanctx_beacon_sent_ev(struct ath_softc *sc,
|
|
|
|
enum ath_chanctx_event ev)
|
|
|
|
{
|
|
|
|
if (sc->sched.beacon_pending)
|
|
|
|
ath_chanctx_event(sc, NULL, ev);
|
|
|
|
}
|
|
|
|
|
2014-09-14 22:55:50 -07:00
|
|
|
void ath_chanctx_beacon_recv_ev(struct ath_softc *sc,
|
2014-08-23 00:59:18 -07:00
|
|
|
enum ath_chanctx_event ev)
|
|
|
|
{
|
|
|
|
ath_chanctx_event(sc, NULL, ev);
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
static int ath_scan_channel_duration(struct ath_softc *sc,
|
|
|
|
struct ieee80211_channel *chan)
|
|
|
|
{
|
|
|
|
struct cfg80211_scan_request *req = sc->offchannel.scan_req;
|
|
|
|
|
|
|
|
if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR))
|
|
|
|
return (HZ / 9); /* ~110 ms */
|
|
|
|
|
|
|
|
return (HZ / 16); /* ~60 ms */
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:15 -07:00
|
|
|
static void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
|
|
|
|
struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
|
|
|
|
if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) &&
|
|
|
|
(sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) {
|
2014-09-04 21:20:56 -07:00
|
|
|
if (chandef)
|
|
|
|
ctx->chandef = *chandef;
|
2014-09-10 06:45:56 -07:00
|
|
|
|
|
|
|
sc->sched.offchannel_pending = true;
|
|
|
|
sc->sched.wait_switch = true;
|
|
|
|
sc->sched.offchannel_duration =
|
|
|
|
jiffies_to_usecs(sc->offchannel.duration) +
|
|
|
|
sc->sched.channel_switch_time;
|
|
|
|
|
2014-08-23 00:59:15 -07:00
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
2014-09-04 21:20:56 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Set offchannel_pending to true\n");
|
2014-08-23 00:59:15 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->next_chan = ctx;
|
|
|
|
if (chandef) {
|
|
|
|
ctx->chandef = *chandef;
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Assigned next_chan to %d MHz\n", chandef->center_freq1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->next_chan == &sc->offchannel.chan) {
|
|
|
|
sc->sched.offchannel_duration =
|
2014-09-04 19:33:13 -07:00
|
|
|
jiffies_to_usecs(sc->offchannel.duration) +
|
2014-08-23 00:59:15 -07:00
|
|
|
sc->sched.channel_switch_time;
|
|
|
|
|
|
|
|
if (chandef) {
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Offchannel duration for chan %d MHz : %u\n",
|
|
|
|
chandef->center_freq1,
|
|
|
|
sc->sched.offchannel_duration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:13 -07:00
|
|
|
static void ath_chanctx_offchan_switch(struct ath_softc *sc,
|
|
|
|
struct ieee80211_channel *chan)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
struct cfg80211_chan_def chandef;
|
|
|
|
|
|
|
|
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Channel definition created: %d MHz\n", chandef.center_freq1);
|
|
|
|
|
|
|
|
ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef);
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:14 -07:00
|
|
|
static struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc,
|
|
|
|
bool active)
|
|
|
|
{
|
|
|
|
struct ath_chanctx *ctx;
|
|
|
|
|
|
|
|
ath_for_each_chanctx(sc, ctx) {
|
|
|
|
if (!ctx->assigned || list_empty(&ctx->vifs))
|
|
|
|
continue;
|
|
|
|
if (active && !ctx->active)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ctx->switch_after_beacon)
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &sc->chanctx[0];
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
static void
|
|
|
|
ath_scan_next_channel(struct ath_softc *sc)
|
|
|
|
{
|
2014-08-22 08:09:27 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2014-08-22 08:09:26 -07:00
|
|
|
struct cfg80211_scan_request *req = sc->offchannel.scan_req;
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
|
|
|
if (sc->offchannel.scan_idx >= req->n_channels) {
|
2014-08-22 08:09:27 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
2014-08-26 23:37:24 -07:00
|
|
|
"Moving offchannel state to ATH_OFFCHANNEL_IDLE, "
|
|
|
|
"scan_idx: %d, n_channels: %d\n",
|
2014-08-22 08:09:27 -07:00
|
|
|
sc->offchannel.scan_idx,
|
|
|
|
req->n_channels);
|
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
|
|
|
|
ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
|
|
|
|
NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:27 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
2014-08-26 23:37:24 -07:00
|
|
|
"Moving offchannel state to ATH_OFFCHANNEL_PROBE_SEND, scan_idx: %d\n",
|
2014-08-22 08:09:27 -07:00
|
|
|
sc->offchannel.scan_idx);
|
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
chan = req->channels[sc->offchannel.scan_idx++];
|
|
|
|
sc->offchannel.duration = ath_scan_channel_duration(sc, chan);
|
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND;
|
2014-08-22 08:09:27 -07:00
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
ath_chanctx_offchan_switch(sc, chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath_offchannel_next(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct ieee80211_vif *vif;
|
|
|
|
|
|
|
|
if (sc->offchannel.scan_req) {
|
|
|
|
vif = sc->offchannel.scan_vif;
|
|
|
|
sc->offchannel.chan.txpower = vif->bss_conf.txpower;
|
|
|
|
ath_scan_next_channel(sc);
|
|
|
|
} else if (sc->offchannel.roc_vif) {
|
|
|
|
vif = sc->offchannel.roc_vif;
|
|
|
|
sc->offchannel.chan.txpower = vif->bss_conf.txpower;
|
2014-09-04 19:33:13 -07:00
|
|
|
sc->offchannel.duration =
|
|
|
|
msecs_to_jiffies(sc->offchannel.roc_duration);
|
2014-08-22 08:09:26 -07:00
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
|
|
|
|
ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
|
|
|
|
} else {
|
2014-11-15 17:41:06 -07:00
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
sc->sched.offchannel_pending = false;
|
|
|
|
sc->sched.wait_switch = false;
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
|
|
|
|
NULL);
|
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
|
|
|
|
if (sc->ps_idle)
|
|
|
|
ath_cancel_work(sc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
ath9k: handle RoC cancel correctly
In case we will get ROC cancel from mac80211 we
should not call ieee80211_remain_on_channel_expired().
In other case I hit such warning on MIPS and
p2p negotiation failed (tested with use_chanctx=1).
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506632
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: Stopping current chanctx: 2412
ath: phy0: Flush timeout: 200
ath: phy0: ath_chanctx_set_next: Set channel 2412 MHz
ath: phy0: Set channel: 2412 MHz width: 0
ath: phy0: Reset to 2412 MHz, HT40: 0 fastcc: 0
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_TSF_TIMER, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: Cancel RoC
ath: phy0: RoC aborted
ath: phy0: RoC request on vif: 00:03:7f:4e:a0:cd, type: 1 duration: 500
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506705
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
------------[ cut here ]------------
WARNING: CPU: 0 PID: 3312 at drivers/net/wireless/ath/ath9k/main.c:2319
Modules linked in: ath9k ath9k_common ath9k_hw ath mac80211 cfg80211
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2015-07-21 02:11:41 -07:00
|
|
|
void ath_roc_complete(struct ath_softc *sc, enum ath_roc_complete_reason reason)
|
2014-08-22 08:09:26 -07:00
|
|
|
{
|
2014-10-01 18:03:12 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
ath9k: handle RoC cancel correctly
In case we will get ROC cancel from mac80211 we
should not call ieee80211_remain_on_channel_expired().
In other case I hit such warning on MIPS and
p2p negotiation failed (tested with use_chanctx=1).
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506632
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: Stopping current chanctx: 2412
ath: phy0: Flush timeout: 200
ath: phy0: ath_chanctx_set_next: Set channel 2412 MHz
ath: phy0: Set channel: 2412 MHz width: 0
ath: phy0: Reset to 2412 MHz, HT40: 0 fastcc: 0
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_TSF_TIMER, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: Cancel RoC
ath: phy0: RoC aborted
ath: phy0: RoC request on vif: 00:03:7f:4e:a0:cd, type: 1 duration: 500
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506705
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
------------[ cut here ]------------
WARNING: CPU: 0 PID: 3312 at drivers/net/wireless/ath/ath9k/main.c:2319
Modules linked in: ath9k ath9k_common ath9k_hw ath mac80211 cfg80211
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2015-07-21 02:11:41 -07:00
|
|
|
sc->offchannel.roc_vif = NULL;
|
|
|
|
sc->offchannel.roc_chan = NULL;
|
|
|
|
|
|
|
|
switch (reason) {
|
|
|
|
case ATH_ROC_COMPLETE_ABORT:
|
2014-10-01 18:03:12 -07:00
|
|
|
ath_dbg(common, CHAN_CTX, "RoC aborted\n");
|
ath9k: handle RoC cancel correctly
In case we will get ROC cancel from mac80211 we
should not call ieee80211_remain_on_channel_expired().
In other case I hit such warning on MIPS and
p2p negotiation failed (tested with use_chanctx=1).
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506632
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: Stopping current chanctx: 2412
ath: phy0: Flush timeout: 200
ath: phy0: ath_chanctx_set_next: Set channel 2412 MHz
ath: phy0: Set channel: 2412 MHz width: 0
ath: phy0: Reset to 2412 MHz, HT40: 0 fastcc: 0
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_TSF_TIMER, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: Cancel RoC
ath: phy0: RoC aborted
ath: phy0: RoC request on vif: 00:03:7f:4e:a0:cd, type: 1 duration: 500
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506705
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
------------[ cut here ]------------
WARNING: CPU: 0 PID: 3312 at drivers/net/wireless/ath/ath9k/main.c:2319
Modules linked in: ath9k ath9k_common ath9k_hw ath mac80211 cfg80211
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2015-07-21 02:11:41 -07:00
|
|
|
ieee80211_remain_on_channel_expired(sc->hw);
|
|
|
|
break;
|
|
|
|
case ATH_ROC_COMPLETE_EXPIRE:
|
2014-10-01 18:03:12 -07:00
|
|
|
ath_dbg(common, CHAN_CTX, "RoC expired\n");
|
ath9k: handle RoC cancel correctly
In case we will get ROC cancel from mac80211 we
should not call ieee80211_remain_on_channel_expired().
In other case I hit such warning on MIPS and
p2p negotiation failed (tested with use_chanctx=1).
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506632
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: Stopping current chanctx: 2412
ath: phy0: Flush timeout: 200
ath: phy0: ath_chanctx_set_next: Set channel 2412 MHz
ath: phy0: Set channel: 2412 MHz width: 0
ath: phy0: Reset to 2412 MHz, HT40: 0 fastcc: 0
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_TSF_TIMER, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: Cancel RoC
ath: phy0: RoC aborted
ath: phy0: RoC request on vif: 00:03:7f:4e:a0:cd, type: 1 duration: 500
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506705
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
------------[ cut here ]------------
WARNING: CPU: 0 PID: 3312 at drivers/net/wireless/ath/ath9k/main.c:2319
Modules linked in: ath9k ath9k_common ath9k_hw ath mac80211 cfg80211
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2015-07-21 02:11:41 -07:00
|
|
|
ieee80211_remain_on_channel_expired(sc->hw);
|
|
|
|
break;
|
|
|
|
case ATH_ROC_COMPLETE_CANCEL:
|
|
|
|
ath_dbg(common, CHAN_CTX, "RoC canceled\n");
|
|
|
|
break;
|
|
|
|
}
|
2014-10-01 18:03:12 -07:00
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
ath_offchannel_next(sc);
|
|
|
|
ath9k_ps_restore(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath_scan_complete(struct ath_softc *sc, bool abort)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2016-07-05 05:23:12 -07:00
|
|
|
struct cfg80211_scan_info info = {
|
|
|
|
.aborted = abort,
|
|
|
|
};
|
2014-08-22 08:09:26 -07:00
|
|
|
|
2014-08-22 08:09:27 -07:00
|
|
|
if (abort)
|
|
|
|
ath_dbg(common, CHAN_CTX, "HW scan aborted\n");
|
|
|
|
else
|
|
|
|
ath_dbg(common, CHAN_CTX, "HW scan complete\n");
|
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
sc->offchannel.scan_req = NULL;
|
|
|
|
sc->offchannel.scan_vif = NULL;
|
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
|
2016-07-05 05:23:12 -07:00
|
|
|
ieee80211_scan_completed(sc->hw, &info);
|
2014-08-22 08:09:26 -07:00
|
|
|
clear_bit(ATH_OP_SCANNING, &common->op_flags);
|
2014-09-10 06:45:57 -07:00
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
|
|
|
|
sc->sched.force_noa_update = true;
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
2014-08-22 08:09:26 -07:00
|
|
|
ath_offchannel_next(sc);
|
|
|
|
ath9k_ps_restore(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath_scan_send_probe(struct ath_softc *sc,
|
|
|
|
struct cfg80211_ssid *ssid)
|
|
|
|
{
|
|
|
|
struct cfg80211_scan_request *req = sc->offchannel.scan_req;
|
|
|
|
struct ieee80211_vif *vif = sc->offchannel.scan_vif;
|
|
|
|
struct ath_tx_control txctl = {};
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct ieee80211_tx_info *info;
|
|
|
|
int band = sc->offchannel.chan.chandef.chan->band;
|
|
|
|
|
2014-06-12 13:24:31 -07:00
|
|
|
skb = ieee80211_probereq_get(sc->hw, vif->addr,
|
2014-08-22 08:09:26 -07:00
|
|
|
ssid->ssid, ssid->ssid_len, req->ie_len);
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
info = IEEE80211_SKB_CB(skb);
|
|
|
|
if (req->no_cck)
|
|
|
|
info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
|
|
|
|
|
|
|
|
if (req->ie_len)
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 05:29:20 -07:00
|
|
|
skb_put_data(skb, req->ie, req->ie_len);
|
2014-08-22 08:09:26 -07:00
|
|
|
|
|
|
|
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
|
|
|
|
|
|
|
|
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
|
|
|
|
if (ath_tx_start(sc->hw, skb, &txctl))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
ieee80211_free_txskb(sc->hw, skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath_scan_channel_start(struct ath_softc *sc)
|
|
|
|
{
|
2014-08-22 08:09:27 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2014-08-22 08:09:26 -07:00
|
|
|
struct cfg80211_scan_request *req = sc->offchannel.scan_req;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) &&
|
|
|
|
req->n_ssids) {
|
|
|
|
for (i = 0; i < req->n_ssids; i++)
|
|
|
|
ath_scan_send_probe(sc, &req->ssids[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:27 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
2014-08-26 23:37:24 -07:00
|
|
|
"Moving offchannel state to ATH_OFFCHANNEL_PROBE_WAIT\n");
|
2014-08-22 08:09:27 -07:00
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT;
|
|
|
|
mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration);
|
|
|
|
}
|
|
|
|
|
2017-10-24 02:29:54 -07:00
|
|
|
static void ath_chanctx_timer(struct timer_list *t)
|
2014-08-23 00:59:06 -07:00
|
|
|
{
|
2017-10-24 02:29:54 -07:00
|
|
|
struct ath_softc *sc = from_timer(sc, t, sched.timer);
|
2014-08-26 23:37:24 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Channel context timer invoked\n");
|
2014-08-23 00:59:06 -07:00
|
|
|
|
|
|
|
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
|
|
|
|
}
|
|
|
|
|
2017-10-24 02:29:54 -07:00
|
|
|
static void ath_offchannel_timer(struct timer_list *t)
|
2014-08-22 08:09:26 -07:00
|
|
|
{
|
2017-10-24 02:29:54 -07:00
|
|
|
struct ath_softc *sc = from_timer(sc, t, offchannel.timer);
|
2014-08-22 08:09:26 -07:00
|
|
|
struct ath_chanctx *ctx;
|
2014-08-22 08:09:27 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n",
|
2014-08-22 08:09:27 -07:00
|
|
|
__func__, offchannel_state_string(sc->offchannel.state));
|
2014-08-22 08:09:26 -07:00
|
|
|
|
|
|
|
switch (sc->offchannel.state) {
|
|
|
|
case ATH_OFFCHANNEL_PROBE_WAIT:
|
|
|
|
if (!sc->offchannel.scan_req)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* get first active channel context */
|
|
|
|
ctx = ath_chanctx_get_oper_chan(sc, true);
|
|
|
|
if (ctx->active) {
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Switch to oper/active context, "
|
|
|
|
"move offchannel state to ATH_OFFCHANNEL_SUSPEND\n");
|
|
|
|
|
2014-08-22 08:09:26 -07:00
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
|
|
|
|
ath_chanctx_switch(sc, ctx, NULL);
|
|
|
|
mod_timer(&sc->offchannel.timer, jiffies + HZ / 10);
|
|
|
|
break;
|
|
|
|
}
|
2020-07-27 12:35:20 -07:00
|
|
|
fallthrough;
|
2014-08-22 08:09:26 -07:00
|
|
|
case ATH_OFFCHANNEL_SUSPEND:
|
|
|
|
if (!sc->offchannel.scan_req)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ath_scan_next_channel(sc);
|
|
|
|
break;
|
|
|
|
case ATH_OFFCHANNEL_ROC_START:
|
|
|
|
case ATH_OFFCHANNEL_ROC_WAIT:
|
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE;
|
ath9k: handle RoC cancel correctly
In case we will get ROC cancel from mac80211 we
should not call ieee80211_remain_on_channel_expired().
In other case I hit such warning on MIPS and
p2p negotiation failed (tested with use_chanctx=1).
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506632
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: Stopping current chanctx: 2412
ath: phy0: Flush timeout: 200
ath: phy0: ath_chanctx_set_next: Set channel 2412 MHz
ath: phy0: Set channel: 2412 MHz width: 0
ath: phy0: Reset to 2412 MHz, HT40: 0 fastcc: 0
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_TSF_TIMER, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
ath: phy0: Cancel RoC
ath: phy0: RoC aborted
ath: phy0: RoC request on vif: 00:03:7f:4e:a0:cd, type: 1 duration: 500
ath: phy0: Starting RoC period
ath: phy0: Channel definition created: 2412 MHz
ath: phy0: Assigned next_chan to 2412 MHz
ath: phy0: Offchannel duration for chan 2412 MHz : 506705
ath: phy0: ath_chanctx_set_next: current: 2412 MHz, next: 2412 MHz
ath: phy0: ath_offchannel_channel_change: offchannel state: ATH_OFFCHANNEL_ROC_START
ath: phy0: cur_chan: 2412 MHz, event: ATH_CHANCTX_EVENT_SWITCH, state: ATH_CHANCTX_STATE_IDLE
------------[ cut here ]------------
WARNING: CPU: 0 PID: 3312 at drivers/net/wireless/ath/ath9k/main.c:2319
Modules linked in: ath9k ath9k_common ath9k_hw ath mac80211 cfg80211
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2015-07-21 02:11:41 -07:00
|
|
|
ath_roc_complete(sc, ATH_ROC_COMPLETE_EXPIRE);
|
2014-08-22 08:09:26 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-08-22 08:09:29 -07:00
|
|
|
|
2014-08-23 00:59:10 -07:00
|
|
|
static bool
|
|
|
|
ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
|
|
|
|
bool powersave)
|
|
|
|
{
|
|
|
|
struct ieee80211_vif *vif = avp->vif;
|
|
|
|
struct ieee80211_sta *sta = NULL;
|
|
|
|
struct ieee80211_hdr_3addr *nullfunc;
|
|
|
|
struct ath_tx_control txctl;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int band = sc->cur_chan->chandef.chan->band;
|
|
|
|
|
|
|
|
switch (vif->type) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2014-09-17 02:15:56 -07:00
|
|
|
if (!avp->assoc)
|
2014-08-23 00:59:10 -07:00
|
|
|
return false;
|
|
|
|
|
2022-09-02 07:12:47 -07:00
|
|
|
skb = ieee80211_nullfunc_get(sc->hw, vif, -1, false);
|
2014-08-23 00:59:10 -07:00
|
|
|
if (!skb)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
|
|
|
|
if (powersave)
|
|
|
|
nullfunc->frame_control |=
|
|
|
|
cpu_to_le16(IEEE80211_FCTL_PM);
|
|
|
|
|
2015-11-27 01:37:11 -07:00
|
|
|
skb->priority = 7;
|
2014-08-23 00:59:10 -07:00
|
|
|
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
|
|
|
|
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&txctl, 0, sizeof(txctl));
|
|
|
|
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
|
|
|
|
txctl.sta = sta;
|
|
|
|
if (ath_tx_start(sc->hw, skb, &txctl)) {
|
|
|
|
ieee80211_free_txskb(sc->hw, skb);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave)
|
|
|
|
{
|
|
|
|
struct ath_vif *avp;
|
|
|
|
bool sent = false;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry(avp, &sc->cur_chan->vifs, list) {
|
|
|
|
if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave))
|
|
|
|
sent = true;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return sent;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ath_chanctx_defer_switch(struct ath_softc *sc)
|
|
|
|
{
|
2014-08-26 23:37:24 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
2014-08-23 00:59:10 -07:00
|
|
|
if (sc->cur_chan == &sc->offchannel.chan)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (sc->sched.state) {
|
|
|
|
case ATH_CHANCTX_STATE_SWITCH:
|
|
|
|
return false;
|
|
|
|
case ATH_CHANCTX_STATE_IDLE:
|
|
|
|
if (!sc->cur_chan->switch_after_beacon)
|
|
|
|
return false;
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Defer switch, set chanctx state to WAIT_FOR_BEACON\n");
|
|
|
|
|
2014-08-23 00:59:10 -07:00
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:11 -07:00
|
|
|
static void ath_offchannel_channel_change(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n",
|
2014-08-23 00:59:11 -07:00
|
|
|
__func__, offchannel_state_string(sc->offchannel.state));
|
|
|
|
|
|
|
|
switch (sc->offchannel.state) {
|
|
|
|
case ATH_OFFCHANNEL_PROBE_SEND:
|
|
|
|
if (!sc->offchannel.scan_req)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sc->cur_chan->chandef.chan !=
|
|
|
|
sc->offchannel.chan.chandef.chan)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ath_scan_channel_start(sc);
|
|
|
|
break;
|
|
|
|
case ATH_OFFCHANNEL_IDLE:
|
|
|
|
if (!sc->offchannel.scan_req)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ath_scan_complete(sc, false);
|
|
|
|
break;
|
|
|
|
case ATH_OFFCHANNEL_ROC_START:
|
|
|
|
if (sc->cur_chan != &sc->offchannel.chan)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT;
|
2014-09-04 19:33:13 -07:00
|
|
|
mod_timer(&sc->offchannel.timer,
|
|
|
|
jiffies + sc->offchannel.duration);
|
2014-08-23 00:59:11 -07:00
|
|
|
ieee80211_ready_on_channel(sc->hw);
|
|
|
|
break;
|
|
|
|
case ATH_OFFCHANNEL_ROC_DONE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:10 -07:00
|
|
|
void ath_chanctx_set_next(struct ath_softc *sc, bool force)
|
|
|
|
{
|
2014-08-26 23:37:24 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2014-10-01 18:03:20 -07:00
|
|
|
struct ath_chanctx *old_ctx;
|
2018-06-18 08:11:17 -07:00
|
|
|
struct timespec64 ts;
|
2014-08-23 00:59:10 -07:00
|
|
|
bool measure_time = false;
|
|
|
|
bool send_ps = false;
|
2014-10-01 18:03:20 -07:00
|
|
|
bool queues_stopped = false;
|
2014-08-23 00:59:10 -07:00
|
|
|
|
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
if (!sc->next_chan) {
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!force && ath_chanctx_defer_switch(sc)) {
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"%s: current: %d MHz, next: %d MHz\n",
|
|
|
|
__func__,
|
|
|
|
sc->cur_chan->chandef.center_freq1,
|
|
|
|
sc->next_chan->chandef.center_freq1);
|
|
|
|
|
2014-08-23 00:59:10 -07:00
|
|
|
if (sc->cur_chan != sc->next_chan) {
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"Stopping current chanctx: %d\n",
|
|
|
|
sc->cur_chan->chandef.center_freq1);
|
2014-08-23 00:59:10 -07:00
|
|
|
sc->cur_chan->stopped = true;
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
|
|
|
|
if (sc->next_chan == &sc->offchannel.chan) {
|
2018-06-18 08:11:17 -07:00
|
|
|
ktime_get_raw_ts64(&ts);
|
2014-08-23 00:59:10 -07:00
|
|
|
measure_time = true;
|
|
|
|
}
|
2014-10-01 18:03:20 -07:00
|
|
|
|
|
|
|
ath9k_chanctx_stop_queues(sc, sc->cur_chan);
|
|
|
|
queues_stopped = true;
|
|
|
|
|
2014-10-16 19:10:29 -07:00
|
|
|
__ath9k_flush(sc->hw, ~0, true, false, false);
|
2014-08-23 00:59:10 -07:00
|
|
|
|
|
|
|
if (ath_chanctx_send_ps_frame(sc, true))
|
2014-10-16 19:10:18 -07:00
|
|
|
__ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO),
|
2014-10-16 19:10:29 -07:00
|
|
|
false, false, false);
|
2014-08-23 00:59:10 -07:00
|
|
|
|
|
|
|
send_ps = true;
|
|
|
|
spin_lock_bh(&sc->chan_lock);
|
|
|
|
|
|
|
|
if (sc->cur_chan != &sc->offchannel.chan) {
|
2018-06-18 08:11:17 -07:00
|
|
|
ktime_get_raw_ts64(&sc->cur_chan->tsf_ts);
|
2014-08-23 00:59:10 -07:00
|
|
|
sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah);
|
|
|
|
}
|
|
|
|
}
|
2014-10-01 18:03:20 -07:00
|
|
|
old_ctx = sc->cur_chan;
|
2014-08-23 00:59:10 -07:00
|
|
|
sc->cur_chan = sc->next_chan;
|
|
|
|
sc->cur_chan->stopped = false;
|
|
|
|
sc->next_chan = NULL;
|
2014-09-10 06:45:58 -07:00
|
|
|
|
|
|
|
if (!sc->sched.offchannel_pending)
|
|
|
|
sc->sched.offchannel_duration = 0;
|
|
|
|
|
2014-08-23 00:59:10 -07:00
|
|
|
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE)
|
|
|
|
sc->sched.state = ATH_CHANCTX_STATE_IDLE;
|
|
|
|
|
|
|
|
spin_unlock_bh(&sc->chan_lock);
|
|
|
|
|
|
|
|
if (sc->sc_ah->chip_fullsleep ||
|
|
|
|
memcmp(&sc->cur_chandef, &sc->cur_chan->chandef,
|
|
|
|
sizeof(sc->cur_chandef))) {
|
2014-08-26 23:37:24 -07:00
|
|
|
ath_dbg(common, CHAN_CTX,
|
|
|
|
"%s: Set channel %d MHz\n",
|
|
|
|
__func__, sc->cur_chan->chandef.center_freq1);
|
2014-08-23 00:59:10 -07:00
|
|
|
ath_set_channel(sc);
|
|
|
|
if (measure_time)
|
|
|
|
sc->sched.channel_switch_time =
|
|
|
|
ath9k_hw_get_tsf_offset(&ts, NULL);
|
2014-10-01 18:03:20 -07:00
|
|
|
/*
|
|
|
|
* A reset will ensure that all queues are woken up,
|
|
|
|
* so there is no need to awaken them again.
|
|
|
|
*/
|
|
|
|
goto out;
|
2014-08-23 00:59:10 -07:00
|
|
|
}
|
2014-10-01 18:03:20 -07:00
|
|
|
|
|
|
|
if (queues_stopped)
|
|
|
|
ath9k_chanctx_wake_queues(sc, old_ctx);
|
|
|
|
out:
|
2014-08-23 00:59:10 -07:00
|
|
|
if (send_ps)
|
|
|
|
ath_chanctx_send_ps_frame(sc, false);
|
|
|
|
|
|
|
|
ath_offchannel_channel_change(sc);
|
|
|
|
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH);
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:08 -07:00
|
|
|
static void ath_chanctx_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = container_of(work, struct ath_softc,
|
|
|
|
chanctx_work);
|
|
|
|
mutex_lock(&sc->mutex);
|
|
|
|
ath_chanctx_set_next(sc, false);
|
|
|
|
mutex_unlock(&sc->mutex);
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:20 -07:00
|
|
|
void ath9k_offchannel_init(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
struct ath_chanctx *ctx;
|
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
int i;
|
|
|
|
|
2016-04-12 06:56:15 -07:00
|
|
|
sband = &common->sbands[NL80211_BAND_2GHZ];
|
2014-08-23 00:59:20 -07:00
|
|
|
if (!sband->n_channels)
|
2016-04-12 06:56:15 -07:00
|
|
|
sband = &common->sbands[NL80211_BAND_5GHZ];
|
2014-08-23 00:59:20 -07:00
|
|
|
|
|
|
|
chan = &sband->channels[0];
|
|
|
|
|
|
|
|
ctx = &sc->offchannel.chan;
|
|
|
|
INIT_LIST_HEAD(&ctx->vifs);
|
|
|
|
ctx->txpower = ATH_TXPOWER_MAX;
|
|
|
|
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
|
|
|
|
|
2016-12-05 04:27:37 -07:00
|
|
|
for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) {
|
|
|
|
INIT_LIST_HEAD(&ctx->acq[i].acq_new);
|
|
|
|
INIT_LIST_HEAD(&ctx->acq[i].acq_old);
|
|
|
|
spin_lock_init(&ctx->acq[i].lock);
|
|
|
|
}
|
2014-08-23 00:59:20 -07:00
|
|
|
|
|
|
|
sc->offchannel.chan.offchannel = true;
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:06 -07:00
|
|
|
void ath9k_init_channel_context(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
|
|
|
|
|
2017-10-24 02:29:54 -07:00
|
|
|
timer_setup(&sc->offchannel.timer, ath_offchannel_timer, 0);
|
|
|
|
timer_setup(&sc->sched.timer, ath_chanctx_timer, 0);
|
2014-10-16 19:10:22 -07:00
|
|
|
|
|
|
|
init_completion(&sc->go_beacon);
|
2014-08-23 00:59:06 -07:00
|
|
|
}
|
2014-08-22 08:09:30 -07:00
|
|
|
|
2014-08-23 00:59:07 -07:00
|
|
|
void ath9k_deinit_channel_context(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
cancel_work_sync(&sc->chanctx_work);
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:31 -07:00
|
|
|
bool ath9k_is_chanctx_enabled(void)
|
|
|
|
{
|
|
|
|
return (ath9k_use_chanctx == 1);
|
|
|
|
}
|
|
|
|
|
2014-08-23 00:59:19 -07:00
|
|
|
/********************/
|
|
|
|
/* Queue management */
|
|
|
|
/********************/
|
|
|
|
|
2014-10-01 18:03:18 -07:00
|
|
|
void ath9k_chanctx_stop_queues(struct ath_softc *sc, struct ath_chanctx *ctx)
|
|
|
|
{
|
|
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ctx == &sc->offchannel.chan) {
|
|
|
|
ieee80211_stop_queue(sc->hw,
|
|
|
|
sc->hw->offchannel_tx_hw_queue);
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < IEEE80211_NUM_ACS; i++)
|
|
|
|
ieee80211_stop_queue(sc->hw,
|
|
|
|
ctx->hw_queue_base + i);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ah->opmode == NL80211_IFTYPE_AP)
|
|
|
|
ieee80211_stop_queue(sc->hw, sc->hw->queues - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-01 18:03:17 -07:00
|
|
|
void ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx)
|
2014-08-23 00:59:19 -07:00
|
|
|
{
|
|
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
|
|
int i;
|
|
|
|
|
2014-10-01 18:03:17 -07:00
|
|
|
if (ctx == &sc->offchannel.chan) {
|
2014-08-23 00:59:19 -07:00
|
|
|
ieee80211_wake_queue(sc->hw,
|
|
|
|
sc->hw->offchannel_tx_hw_queue);
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < IEEE80211_NUM_ACS; i++)
|
|
|
|
ieee80211_wake_queue(sc->hw,
|
2014-10-01 18:03:17 -07:00
|
|
|
ctx->hw_queue_base + i);
|
2014-08-23 00:59:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ah->opmode == NL80211_IFTYPE_AP)
|
|
|
|
ieee80211_wake_queue(sc->hw, sc->hw->queues - 2);
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:30 -07:00
|
|
|
/*****************/
|
|
|
|
/* P2P Powersave */
|
|
|
|
/*****************/
|
|
|
|
|
|
|
|
static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
|
2014-08-22 08:09:29 -07:00
|
|
|
{
|
2015-11-27 01:37:06 -07:00
|
|
|
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
2014-08-22 08:09:29 -07:00
|
|
|
struct ath_hw *ah = sc->sc_ah;
|
2015-11-27 01:37:10 -07:00
|
|
|
u32 tsf, target_tsf;
|
2014-08-22 08:09:29 -07:00
|
|
|
|
|
|
|
if (!avp || !avp->noa.has_next_tsf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer);
|
|
|
|
|
|
|
|
tsf = ath9k_hw_gettsf32(sc->sc_ah);
|
|
|
|
|
|
|
|
target_tsf = avp->noa.next_tsf;
|
|
|
|
if (!avp->noa.absent)
|
|
|
|
target_tsf -= ATH_P2P_PS_STOP_TIME;
|
2015-11-27 01:37:09 -07:00
|
|
|
else
|
|
|
|
target_tsf += ATH_P2P_PS_STOP_TIME;
|
2014-08-22 08:09:29 -07:00
|
|
|
|
|
|
|
if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME)
|
|
|
|
target_tsf = tsf + ATH_P2P_PS_STOP_TIME;
|
|
|
|
|
2015-11-27 01:37:06 -07:00
|
|
|
ath_dbg(common, CHAN_CTX, "%s absent %d tsf 0x%08X next_tsf 0x%08X (%dms)\n",
|
|
|
|
__func__, avp->noa.absent, tsf, target_tsf,
|
|
|
|
(target_tsf - tsf) / 1000);
|
|
|
|
|
2015-11-27 01:37:10 -07:00
|
|
|
ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, target_tsf, 1000000);
|
2014-08-22 08:09:29 -07:00
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:30 -07:00
|
|
|
static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ath_vif *avp = (void *)vif->drv_priv;
|
|
|
|
u32 tsf;
|
|
|
|
|
|
|
|
if (!sc->p2p_ps_timer)
|
|
|
|
return;
|
|
|
|
|
2015-11-27 01:37:14 -07:00
|
|
|
if (vif->type != NL80211_IFTYPE_STATION)
|
2014-08-22 08:09:30 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
sc->p2p_ps_vif = avp;
|
2015-11-27 01:37:13 -07:00
|
|
|
|
|
|
|
if (sc->ps_flags & PS_BEACON_SYNC)
|
|
|
|
return;
|
|
|
|
|
2014-08-22 08:09:30 -07:00
|
|
|
tsf = ath9k_hw_gettsf32(sc->sc_ah);
|
|
|
|
ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
|
|
|
|
ath9k_update_p2p_ps_timer(sc, avp);
|
|
|
|
}
|
|
|
|
|
2014-09-04 19:33:14 -07:00
|
|
|
static u8 ath9k_get_ctwin(struct ath_softc *sc, struct ath_vif *avp)
|
|
|
|
{
|
|
|
|
struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
|
|
|
|
u8 switch_time, ctwin;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Channel switch in multi-channel mode is deferred
|
|
|
|
* by a quarter beacon interval when handling
|
|
|
|
* ATH_CHANCTX_EVENT_BEACON_PREPARE, so the P2P-GO
|
|
|
|
* interface is guaranteed to be discoverable
|
|
|
|
* for that duration after a TBTT.
|
|
|
|
*/
|
|
|
|
switch_time = cur_conf->beacon_interval / 4;
|
|
|
|
|
|
|
|
ctwin = avp->vif->bss_conf.p2p_noa_attr.oppps_ctwindow;
|
|
|
|
if (ctwin && (ctwin < switch_time))
|
|
|
|
return ctwin;
|
|
|
|
|
|
|
|
if (switch_time < P2P_DEFAULT_CTWIN)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return P2P_DEFAULT_CTWIN;
|
|
|
|
}
|
|
|
|
|
2014-08-23 06:42:15 -07:00
|
|
|
void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
static const u8 noa_ie_hdr[] = {
|
|
|
|
WLAN_EID_VENDOR_SPECIFIC, /* type */
|
|
|
|
0, /* length */
|
|
|
|
0x50, 0x6f, 0x9a, /* WFA OUI */
|
|
|
|
0x09, /* P2P subtype */
|
|
|
|
0x0c, /* Notice of Absence */
|
|
|
|
0x00, /* LSB of little-endian len */
|
|
|
|
0x00, /* MSB of little-endian len */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ieee80211_p2p_noa_attr *noa;
|
|
|
|
int noa_len, noa_desc, i = 0;
|
|
|
|
u8 *hdr;
|
|
|
|
|
2014-09-10 06:45:57 -07:00
|
|
|
if (!avp->offchannel_duration && !avp->noa_duration)
|
2014-08-23 06:42:15 -07:00
|
|
|
return;
|
|
|
|
|
2014-09-10 06:45:57 -07:00
|
|
|
noa_desc = !!avp->offchannel_duration + !!avp->noa_duration;
|
2014-08-23 06:42:15 -07:00
|
|
|
noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc;
|
|
|
|
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 05:29:20 -07:00
|
|
|
hdr = skb_put_data(skb, noa_ie_hdr, sizeof(noa_ie_hdr));
|
2014-08-23 06:42:15 -07:00
|
|
|
hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2;
|
|
|
|
hdr[7] = noa_len;
|
|
|
|
|
networking: convert many more places to skb_put_zero()
There were many places that my previous spatch didn't find,
as pointed out by yuan linyu in various patches.
The following spatch found many more and also removes the
now unnecessary casts:
@@
identifier p, p2;
expression len;
expression skb;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_zero(skb, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_zero(skb, len);
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, len);
|
-memset(p, 0, len);
)
@@
type t, t2;
identifier p, p2;
expression skb;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, sizeof(*p));
|
-memset(p, 0, sizeof(*p));
)
@@
expression skb, len;
@@
-memset(skb_put(skb, len), 0, len);
+skb_put_zero(skb, len);
Apply it to the tree (with one manual fixup to keep the
comment in vxlan.c, which spatch removed.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 05:29:19 -07:00
|
|
|
noa = skb_put_zero(skb, noa_len);
|
2014-08-23 06:42:15 -07:00
|
|
|
|
|
|
|
noa->index = avp->noa_index;
|
2014-09-04 19:33:14 -07:00
|
|
|
noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp);
|
2015-11-27 01:37:12 -07:00
|
|
|
if (noa->oppps_ctwindow)
|
|
|
|
noa->oppps_ctwindow |= BIT(7);
|
2014-09-04 19:33:14 -07:00
|
|
|
|
2014-09-10 06:45:57 -07:00
|
|
|
if (avp->noa_duration) {
|
|
|
|
if (avp->periodic_noa) {
|
|
|
|
u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
|
|
|
|
noa->desc[i].count = 255;
|
|
|
|
noa->desc[i].interval = cpu_to_le32(interval);
|
|
|
|
} else {
|
|
|
|
noa->desc[i].count = 1;
|
|
|
|
}
|
2014-08-23 06:42:15 -07:00
|
|
|
|
2014-09-10 06:45:57 -07:00
|
|
|
noa->desc[i].start_time = cpu_to_le32(avp->noa_start);
|
|
|
|
noa->desc[i].duration = cpu_to_le32(avp->noa_duration);
|
2014-08-23 06:42:15 -07:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avp->offchannel_duration) {
|
|
|
|
noa->desc[i].count = 1;
|
|
|
|
noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start);
|
|
|
|
noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:29 -07:00
|
|
|
void ath9k_p2p_ps_timer(void *priv)
|
|
|
|
{
|
|
|
|
struct ath_softc *sc = priv;
|
|
|
|
struct ath_vif *avp = sc->p2p_ps_vif;
|
|
|
|
struct ieee80211_vif *vif;
|
|
|
|
struct ieee80211_sta *sta;
|
|
|
|
struct ath_node *an;
|
|
|
|
u32 tsf;
|
|
|
|
|
|
|
|
del_timer_sync(&sc->sched.timer);
|
|
|
|
ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer);
|
|
|
|
ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
|
|
|
|
|
|
|
|
if (!avp || avp->chanctx != sc->cur_chan)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tsf = ath9k_hw_gettsf32(sc->sc_ah);
|
|
|
|
if (!avp->noa.absent)
|
|
|
|
tsf += ATH_P2P_PS_STOP_TIME;
|
2015-11-27 01:37:09 -07:00
|
|
|
else
|
|
|
|
tsf -= ATH_P2P_PS_STOP_TIME;
|
2014-08-22 08:09:29 -07:00
|
|
|
|
|
|
|
if (!avp->noa.has_next_tsf ||
|
|
|
|
avp->noa.next_tsf - tsf > BIT(31))
|
|
|
|
ieee80211_update_p2p_noa(&avp->noa, tsf);
|
|
|
|
|
|
|
|
ath9k_update_p2p_ps_timer(sc, avp);
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
vif = avp->vif;
|
2014-09-17 02:15:56 -07:00
|
|
|
sta = ieee80211_find_sta(vif, avp->bssid);
|
2014-08-22 08:09:29 -07:00
|
|
|
if (!sta)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
an = (void *) sta->drv_priv;
|
|
|
|
if (an->sleeping == !!avp->noa.absent)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
an->sleeping = avp->noa.absent;
|
|
|
|
if (an->sleeping)
|
|
|
|
ath_tx_aggr_sleep(sta, sc, an);
|
|
|
|
else
|
|
|
|
ath_tx_aggr_wakeup(sc, an);
|
|
|
|
|
|
|
|
out:
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:09:30 -07:00
|
|
|
void ath9k_p2p_bss_info_changed(struct ath_softc *sc,
|
|
|
|
struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_bh(&sc->sc_pcu_lock);
|
|
|
|
spin_lock_irqsave(&sc->sc_pm_lock, flags);
|
2015-11-27 01:37:13 -07:00
|
|
|
ath9k_update_p2p_ps(sc, vif);
|
2014-08-22 08:09:30 -07:00
|
|
|
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
|
|
|
|
spin_unlock_bh(&sc->sc_pcu_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath9k_p2p_beacon_sync(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
if (sc->p2p_ps_vif)
|
|
|
|
ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath9k_p2p_remove_vif(struct ath_softc *sc,
|
|
|
|
struct ieee80211_vif *vif)
|
2014-08-22 08:09:29 -07:00
|
|
|
{
|
|
|
|
struct ath_vif *avp = (void *)vif->drv_priv;
|
|
|
|
|
2014-08-22 08:09:30 -07:00
|
|
|
spin_lock_bh(&sc->sc_pcu_lock);
|
|
|
|
if (avp == sc->p2p_ps_vif) {
|
|
|
|
sc->p2p_ps_vif = NULL;
|
|
|
|
ath9k_update_p2p_ps_timer(sc, NULL);
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&sc->sc_pcu_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ath9k_init_p2p(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer,
|
|
|
|
NULL, sc, AR_FIRST_NDP_TIMER);
|
2014-08-22 08:09:29 -07:00
|
|
|
if (!sc->p2p_ps_timer)
|
2014-08-22 08:09:30 -07:00
|
|
|
return -ENOMEM;
|
2014-08-22 08:09:29 -07:00
|
|
|
|
2014-08-22 08:09:30 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2014-08-22 08:09:29 -07:00
|
|
|
|
2014-08-22 08:09:30 -07:00
|
|
|
void ath9k_deinit_p2p(struct ath_softc *sc)
|
|
|
|
{
|
|
|
|
if (sc->p2p_ps_timer)
|
|
|
|
ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer);
|
2014-08-22 08:09:29 -07:00
|
|
|
}
|
2014-08-22 08:09:30 -07:00
|
|
|
|
|
|
|
#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
|