* [PATCH 01/13] ath9k: Add channel context structure
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-09 11:36 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 02/13] ath9k: Move txpower limit to channel context Rajkumar Manoharan
` (12 subsequent siblings)
13 siblings, 1 reply; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
The channel context structure is defined to enable
multi-channel concurrency support.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/Makefile | 3 +-
drivers/net/wireless/ath/ath9k/ath9k.h | 18 +++++
drivers/net/wireless/ath/ath9k/channel.c | 133 +++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/init.c | 1 +
drivers/net/wireless/ath/ath9k/link.c | 2 +-
drivers/net/wireless/ath/ath9k/main.c | 111 +++-----------------------
drivers/net/wireless/ath/ath9k/recv.c | 4 +-
7 files changed, 169 insertions(+), 103 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath9k/channel.c
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 8fcd586..6b4020a 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -5,7 +5,8 @@ ath9k-y += beacon.o \
recv.o \
xmit.o \
link.o \
- antenna.o
+ antenna.o \
+ channel.o
ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
ath9k-$(CONFIG_ATH9K_PCI) += pci.o
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 2ca8f7e..cc75b9d 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -325,6 +325,16 @@ struct ath_rx {
u32 ampdu_ref;
};
+struct ath_chanctx {
+ struct cfg80211_chan_def chandef;
+ struct list_head vifs;
+ bool offchannel;
+};
+
+void ath_chanctx_init(struct ath_softc *sc);
+int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+ struct cfg80211_chan_def *chandef);
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
@@ -370,12 +380,15 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
/********/
struct ath_vif {
+ struct list_head list;
+
struct ieee80211_vif *vif;
struct ath_node mcast_node;
int av_bslot;
bool primary_sta_vif;
__le64 tsf_adjust; /* TSF adjustment for staggered beacons */
struct ath_buf *av_bcbuf;
+ struct ath_chanctx *chanctx;
/* P2P Client */
struct ieee80211_noa_data noa;
@@ -702,6 +715,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
#define PS_BEACON_SYNC BIT(4)
#define PS_WAIT_FOR_ANI BIT(5)
+#define ATH9K_NUM_CHANCTX 3 /* 2 oper_channel + 1 off_channel */
+
struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
@@ -743,6 +758,9 @@ struct ath_softc {
struct ath_tx tx;
struct ath_beacon beacon;
+ struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX];
+ struct ath_chanctx *cur_chan;
+
#ifdef CONFIG_MAC80211_LEDS
bool led_registered;
char led_name[32];
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
new file mode 100644
index 0000000..aee6cdb
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -0,0 +1,133 @@
+/*
+ * 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;
+ 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 */
+ spin_lock_bh(&common->cc_lock);
+ ath_update_survey_stats(sc);
+ spin_unlock_bh(&common->cc_lock);
+
+ 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];
+ r = ath_reset_internal(sc, hchan);
+ 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;
+
+ /* set HW specific DFS configuration */
+ ath9k_hw_set_radar_params(ah);
+ 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) &&
+ sc->spectral_mode == SPECTRAL_CHANSCAN)
+ ath9k_spectral_scan_trigger(hw);
+ }
+
+ 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;
+ int i;
+
+ sband = &common->sbands[IEEE80211_BAND_2GHZ];
+ if (!sband->n_channels)
+ sband = &common->sbands[IEEE80211_BAND_5GHZ];
+
+ 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);
+ }
+ sc->cur_chan = &sc->chanctx[0];
+}
+
+int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+ struct cfg80211_chan_def *chandef)
+{
+ memcpy(&ctx->chandef, chandef, sizeof(ctx->chandef));
+ if (ctx != sc->cur_chan)
+ return 0;
+
+ return ath_set_channel(sc);
+}
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 0246b99..32d9542 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -599,6 +599,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_init_misc(sc);
ath_fill_led_pin(sc);
+ ath_chanctx_init(sc);
if (common->bus_ops->aspm_init)
common->bus_ops->aspm_init(common);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 72a715f..6f91974 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -416,7 +416,7 @@ void ath_start_ani(struct ath_softc *sc)
if (common->disable_ani ||
!test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
- (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+ sc->cur_chan->offchannel)
return;
common->ani.longcal_timer = timestamp;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 62ac95d..2e7cce7 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -233,7 +233,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
ath9k_hw_set_interrupts(ah);
ath9k_hw_enable_interrupts(ah);
- if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) {
+ if (!sc->cur_chan->offchannel && start) {
if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
goto work;
@@ -266,7 +266,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
return true;
}
-static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -279,7 +279,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
tasklet_disable(&sc->intr_tq);
spin_lock_bh(&sc->sc_pcu_lock);
- if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+ if (!sc->cur_chan->offchannel) {
fastcc = false;
caldata = &sc->caldata;
}
@@ -307,7 +307,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
}
if (ath9k_hw_mci_is_enabled(sc->sc_ah) &&
- (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+ sc->cur_chan->offchannel)
ath9k_mci_set_txpower(sc, true, false);
if (!ath_complete_reset(sc, true))
@@ -320,98 +320,6 @@ out:
return r;
}
-
-/*
- * 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 cfg80211_chan_def *chandef)
-{
- 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 ieee80211_channel *chan = chandef->chan;
- bool offchannel;
- int pos = chan->hw_value;
- int old_pos = -1;
- int r;
-
- if (test_bit(ATH_OP_INVALID, &common->op_flags))
- return -EIO;
-
- offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
-
- 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 */
- spin_lock_bh(&common->cc_lock);
- ath_update_survey_stats(sc);
- spin_unlock_bh(&common->cc_lock);
-
- 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 (!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];
- r = ath_reset_internal(sc, hchan);
- 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;
-
- /* set HW specific DFS configuration */
- ath9k_hw_set_radar_params(ah);
- 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) &&
- sc->spectral_mode == SPECTRAL_CHANSCAN)
- ath9k_spectral_scan_trigger(hw);
- }
-
- return 0;
-}
-
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
struct ieee80211_vif *vif)
{
@@ -713,6 +621,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+ struct ath_chanctx *ctx = sc->cur_chan;
struct ath9k_channel *init_channel;
int r;
@@ -723,7 +632,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
- init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+ memcpy(&ctx->chandef, &hw->conf.chandef, sizeof(ctx->chandef));
+ init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef);
/* Reset SERDES registers */
ath9k_hw_configpcipowersave(ah, false);
@@ -934,7 +844,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
}
if (!ah->curchan)
- ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+ ah->curchan = ath9k_cmn_get_channel(hw, ah,
+ &sc->cur_chan->chandef);
ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
ath9k_hw_phy_disable(ah);
@@ -1345,6 +1256,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &hw->conf;
+ struct ath_chanctx *ctx = sc->cur_chan;
bool reset_channel = false;
ath9k_ps_wakeup(sc);
@@ -1392,7 +1304,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
}
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
- if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
+ ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL);
+ if (ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef) < 0) {
ath_err(common, "Unable to set channel\n");
mutex_unlock(&sc->mutex);
ath9k_ps_restore(sc);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 9105a92..de5684a 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -259,7 +259,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP);
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP);
ath_opmode_init(sc);
- ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+ ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel);
}
static void ath_edma_stop_recv(struct ath_softc *sc)
@@ -457,7 +457,7 @@ int ath_startrecv(struct ath_softc *sc)
start_recv:
ath_opmode_init(sc);
- ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+ ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel);
return 0;
}
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 02/13] ath9k: Move txpower limit to channel context
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 01/13] ath9k: Add channel context structure Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 03/13] ath9k: Move acq " Rajkumar Manoharan
` (11 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 6 +-----
drivers/net/wireless/ath/ath9k/channel.c | 1 +
drivers/net/wireless/ath/ath9k/init.c | 5 ++---
drivers/net/wireless/ath/ath9k/main.c | 6 +++---
drivers/net/wireless/ath/ath9k/mci.c | 2 +-
5 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index cc75b9d..840cdf9 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -36,10 +36,6 @@ extern int ath9k_modparam_nohwcrypt;
extern int led_blink;
extern bool is_ath9k_unloaded;
-struct ath_config {
- u16 txpowlimit;
-};
-
/*************************/
/* Descriptor Management */
/*************************/
@@ -328,6 +324,7 @@ struct ath_rx {
struct ath_chanctx {
struct cfg80211_chan_def chandef;
struct list_head vifs;
+ u16 txpower;
bool offchannel;
};
@@ -753,7 +750,6 @@ struct ath_softc {
short nvifs;
unsigned long ps_usecount;
- struct ath_config config;
struct ath_rx rx;
struct ath_tx tx;
struct ath_beacon beacon;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index aee6cdb..1b40cf5 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -118,6 +118,7 @@ void ath_chanctx_init(struct ath_softc *sc)
ctx = &sc->chanctx[i];
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
INIT_LIST_HEAD(&ctx->vifs);
+ ctx->txpower = ATH_TXPOWER_MAX;
}
sc->cur_chan = &sc->chanctx[0];
}
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 32d9542..ffd42bf 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -169,9 +169,9 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
/* Set tx power */
if (ah->curchan) {
- sc->config.txpowlimit = 2 * ah->curchan->chan->max_power;
+ sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
ath9k_ps_wakeup(sc);
- ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+ ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
/* synchronize DFS detector if regulatory domain changed */
if (sc->dfs_detector != NULL)
@@ -335,7 +335,6 @@ static void ath9k_init_misc(struct ath_softc *sc)
setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
- sc->config.txpowlimit = ATH_TXPOWER_MAX;
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
sc->beacon.slottime = ATH9K_SLOT_TIME_9;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 2e7cce7..5a9cee4 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -227,7 +227,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
}
ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->config.txpowlimit, &sc->curtxpow);
+ sc->cur_chan->txpower, &sc->curtxpow);
clear_bit(ATH_OP_HW_RESET, &common->op_flags);
ath9k_hw_set_interrupts(ah);
@@ -1315,9 +1315,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_POWER) {
ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
- sc->config.txpowlimit = 2 * conf->power_level;
+ sc->cur_chan->txpower = 2 * conf->power_level;
ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->config.txpowlimit, &sc->curtxpow);
+ sc->cur_chan->txpower, &sc->curtxpow);
}
mutex_unlock(&sc->mutex);
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index a0dbcc4..313ed99 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -720,7 +720,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
mci_hw->concur_tx = concur_tx;
if (old_concur_tx != mci_hw->concur_tx)
- ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+ ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
}
static void ath9k_mci_stomp_audio(struct ath_softc *sc)
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 03/13] ath9k: Move acq to channel context
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 01/13] ath9k: Add channel context structure Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 02/13] ath9k: Move txpower limit to channel context Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 04/13] ath9k: Add channel context worker thread Rajkumar Manoharan
` (10 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Add support to maintain per-channel ACs list.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++-
drivers/net/wireless/ath/ath9k/channel.c | 4 +++-
drivers/net/wireless/ath/ath9k/main.c | 14 ++++++++++-
drivers/net/wireless/ath/ath9k/xmit.c | 41 ++++++++++++++++++++------------
4 files changed, 44 insertions(+), 18 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 840cdf9..4906c55 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -163,7 +163,6 @@ struct ath_txq {
u32 axq_ampdu_depth;
bool stopped;
bool axq_tx_inprogress;
- struct list_head axq_acq;
struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
u8 txq_headidx;
u8 txq_tailidx;
@@ -324,6 +323,8 @@ struct ath_rx {
struct ath_chanctx {
struct cfg80211_chan_def chandef;
struct list_head vifs;
+ struct list_head acq[IEEE80211_NUM_ACS];
+
u16 txpower;
bool offchannel;
};
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 1b40cf5..c8d91df 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -107,7 +107,7 @@ void ath_chanctx_init(struct ath_softc *sc)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
- int i;
+ int i, j;
sband = &common->sbands[IEEE80211_BAND_2GHZ];
if (!sband->n_channels)
@@ -119,6 +119,8 @@ void ath_chanctx_init(struct ath_softc *sc)
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
INIT_LIST_HEAD(&ctx->vifs);
ctx->txpower = ATH_TXPOWER_MAX;
+ for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
+ INIT_LIST_HEAD(&ctx->acq[j]);
}
sc->cur_chan = &sc->chanctx[0];
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5a9cee4..9a2177d 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -63,9 +63,16 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
spin_lock_bh(&txq->axq_lock);
- if (txq->axq_depth || !list_empty(&txq->axq_acq))
+ if (txq->axq_depth)
pending = true;
+ if (txq->mac80211_qnum >= 0) {
+ struct list_head *list;
+
+ list = &sc->cur_chan->acq[txq->mac80211_qnum];
+ if (!list_empty(list))
+ pending = true;
+ }
spin_unlock_bh(&txq->axq_lock);
return pending;
}
@@ -1035,6 +1042,10 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
avp->vif = vif;
+ /* XXX - will be removed once chanctx ops are added */
+ avp->chanctx = sc->cur_chan;
+ list_add_tail(&avp->list, &sc->cur_chan->vifs);
+
an->sc = sc;
an->sta = NULL;
an->vif = vif;
@@ -1120,6 +1131,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
}
spin_unlock_bh(&sc->sc_pcu_lock);
+ list_del(&avp->list);
sc->nvifs--;
sc->tx99_vif = NULL;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 66acb2c..a84fe1a 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -103,9 +103,13 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
ieee80211_tx_status(sc->hw, skb);
}
-static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
+static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_atx_tid *tid)
{
+ struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv;
+ struct ath_chanctx *ctx = avp->chanctx;
struct ath_atx_ac *ac = tid->ac;
+ struct list_head *list;
if (tid->sched)
return;
@@ -117,7 +121,9 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
return;
ac->sched = true;
- list_add_tail(&ac->list, &txq->axq_acq);
+
+ list = &ctx->acq[TID_TO_WME_AC(tid->tidno)];
+ list_add_tail(&ac->list, list);
}
static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
@@ -626,7 +632,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
skb_queue_splice_tail(&bf_pending, &tid->retry_q);
if (!an->sleeping) {
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY))
tid->ac->clear_ps_filter = true;
@@ -1483,7 +1489,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
ac->clear_ps_filter = true;
if (ath_tid_has_buffered(tid)) {
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
}
@@ -1507,7 +1513,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
if (ath_tid_has_buffered(tid)) {
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
}
@@ -1642,7 +1648,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
txq->axq_link = NULL;
__skb_queue_head_init(&txq->complete_q);
INIT_LIST_HEAD(&txq->axq_q);
- INIT_LIST_HEAD(&txq->axq_acq);
spin_lock_init(&txq->axq_lock);
txq->axq_depth = 0;
txq->axq_ampdu_depth = 0;
@@ -1804,7 +1809,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
sc->tx.txqsetup &= ~(1<<txq->axq_qnum);
}
-/* For each axq_acq entry, for each tid, try to schedule packets
+/* For each acq entry, for each tid, try to schedule packets
* for transmit until ampdu_depth has reached min Q depth.
*/
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
@@ -1812,19 +1817,25 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_ac *ac, *last_ac;
struct ath_atx_tid *tid, *last_tid;
+ struct list_head *ac_list;
bool sent = false;
+ if (txq->mac80211_qnum < 0)
+ return;
+
+ ac_list = &sc->cur_chan->acq[txq->mac80211_qnum];
+
if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
- list_empty(&txq->axq_acq))
+ list_empty(ac_list))
return;
rcu_read_lock();
- last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
- while (!list_empty(&txq->axq_acq)) {
+ last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list);
+ while (!list_empty(ac_list)) {
bool stop = false;
- ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
+ ac = list_first_entry(ac_list, struct ath_atx_ac, list);
last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
list_del(&ac->list);
ac->sched = false;
@@ -1844,7 +1855,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
* are pending for the tid
*/
if (ath_tid_has_buffered(tid))
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
if (stop || tid == last_tid)
break;
@@ -1852,7 +1863,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
if (!list_empty(&ac->tid_q) && !ac->sched) {
ac->sched = true;
- list_add_tail(&ac->list, &txq->axq_acq);
+ list_add_tail(&ac->list, ac_list);
}
if (stop)
@@ -1863,7 +1874,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
break;
sent = false;
- last_ac = list_entry(txq->axq_acq.prev,
+ last_ac = list_entry(ac_list->prev,
struct ath_atx_ac, list);
}
}
@@ -2198,7 +2209,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
TX_STAT_INC(txq->axq_qnum, a_queued_sw);
__skb_queue_tail(&tid->buf_q, skb);
if (!txctl->an->sleeping)
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
goto out;
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 04/13] ath9k: Add channel context worker thread
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (2 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 03/13] ath9k: Move acq " Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 05/13] ath9k: channel context based transmission Rajkumar Manoharan
` (9 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
The channel context worker is used to switch to next requested
channel context.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 7 ++++++
drivers/net/wireless/ath/ath9k/channel.c | 41 ++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/init.c | 2 ++
drivers/net/wireless/ath/ath9k/main.c | 14 +++++++----
drivers/net/wireless/ath/ath9k/wow.c | 1 +
drivers/net/wireless/ath/ath9k/xmit.c | 7 ++++++
6 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 4906c55..052a2f9 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -327,11 +327,13 @@ struct ath_chanctx {
u16 txpower;
bool offchannel;
+ bool stopped;
};
void ath_chanctx_init(struct ath_softc *sc);
int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef);
+void ath_chanctx_switch(struct ath_softc *sc, u8 next_chanctx_idx);
int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
@@ -469,6 +471,7 @@ void ath9k_csa_update(struct ath_softc *sc);
#define ATH_PAPRD_TIMEOUT 100 /* msecs */
#define ATH_PLL_WORK_INTERVAL 100
+void ath_chanctx_work(struct work_struct *work);
void ath_tx_complete_poll_work(struct work_struct *work);
void ath_reset_work(struct work_struct *work);
bool ath_hw_check(struct ath_softc *sc);
@@ -484,6 +487,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
void ath_ps_full_sleep(unsigned long data);
void ath9k_p2p_ps_timer(void *priv);
void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif);
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
/**********/
/* BTCOEX */
@@ -733,6 +737,7 @@ struct ath_softc {
struct mutex mutex;
struct work_struct paprd_work;
struct work_struct hw_reset_work;
+ struct work_struct chanctx_work;
struct completion paprd_complete;
wait_queue_head_t tx_wait;
@@ -757,6 +762,8 @@ struct ath_softc {
struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX];
struct ath_chanctx *cur_chan;
+ spinlock_t chan_lock;
+ s8 next_chanctx_idx;
#ifdef CONFIG_MAC80211_LEDS
bool led_registered;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index c8d91df..ecc4d50 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -134,3 +134,44 @@ int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
return ath_set_channel(sc);
}
+
+void ath_chanctx_work(struct work_struct *work)
+{
+ struct ath_softc *sc = container_of(work, struct ath_softc,
+ chanctx_work);
+
+ mutex_lock(&sc->mutex);
+ spin_lock_bh(&sc->chan_lock);
+ if ((sc->next_chanctx_idx < 0) ||
+ (sc->next_chanctx_idx >= ATH9K_NUM_CHANCTX)) {
+ spin_unlock_bh(&sc->chan_lock);
+ mutex_unlock(&sc->mutex);
+ return;
+ }
+
+ if (sc->cur_chan != &sc->chanctx[sc->next_chanctx_idx]) {
+ sc->cur_chan->stopped = true;
+ spin_unlock_bh(&sc->chan_lock);
+
+ __ath9k_flush(sc->hw, ~0, true);
+
+ spin_lock_bh(&sc->chan_lock);
+ }
+ sc->cur_chan = &sc->chanctx[sc->next_chanctx_idx];
+ sc->cur_chan->stopped = false;
+ sc->next_chanctx_idx = -1;
+ spin_unlock_bh(&sc->chan_lock);
+
+ ath_set_channel(sc);
+ mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_switch(struct ath_softc *sc, u8 next_chanctx_idx)
+{
+
+ WARN_ON(next_chanctx_idx >= ATH9K_NUM_CHANCTX);
+ spin_lock_bh(&sc->chan_lock);
+ sc->next_chanctx_idx = next_chanctx_idx;
+ spin_unlock_bh(&sc->chan_lock);
+ ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+}
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index ffd42bf..8bd3e42 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -555,6 +555,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&common->cc_lock);
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
+ spin_lock_init(&sc->chan_lock);
mutex_init(&sc->mutex);
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
@@ -563,6 +564,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
INIT_WORK(&sc->hw_reset_work, ath_reset_work);
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
+ INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
/*
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 9a2177d..e4aa29b 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -803,6 +803,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
struct ath_common *common = ath9k_hw_common(ah);
bool prev_idle;
+ cancel_work_sync(&sc->chanctx_work);
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
@@ -1957,23 +1958,29 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop)
{
struct ath_softc *sc = hw->priv;
+
+ mutex_lock(&sc->mutex);
+ __ath9k_flush(hw, queues, drop);
+ mutex_unlock(&sc->mutex);
+}
+
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+ struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
int timeout = HZ / 5; /* 200 ms */
bool drain_txq;
- mutex_lock(&sc->mutex);
cancel_delayed_work_sync(&sc->tx_complete_work);
if (ah->ah_flags & AH_UNPLUGGED) {
ath_dbg(common, ANY, "Device has been unplugged!\n");
- mutex_unlock(&sc->mutex);
return;
}
if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(common, ANY, "Device not present\n");
- mutex_unlock(&sc->mutex);
return;
}
@@ -1995,7 +2002,6 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
- mutex_unlock(&sc->mutex);
}
static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
index 2879887..a4f4f0d 100644
--- a/drivers/net/wireless/ath/ath9k/wow.c
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -193,6 +193,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
u32 wow_triggers_enabled = 0;
int ret = 0;
+ cancel_work_sync(&sc->chanctx_work);
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index a84fe1a..0c9571a 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1823,18 +1823,24 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
if (txq->mac80211_qnum < 0)
return;
+ spin_lock_bh(&sc->chan_lock);
ac_list = &sc->cur_chan->acq[txq->mac80211_qnum];
+ spin_unlock_bh(&sc->chan_lock);
if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
list_empty(ac_list))
return;
+ spin_lock_bh(&sc->chan_lock);
rcu_read_lock();
last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list);
while (!list_empty(ac_list)) {
bool stop = false;
+ if (sc->cur_chan->stopped)
+ break;
+
ac = list_first_entry(ac_list, struct ath_atx_ac, list);
last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
list_del(&ac->list);
@@ -1880,6 +1886,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
}
rcu_read_unlock();
+ spin_unlock_bh(&sc->chan_lock);
}
/***********/
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 05/13] ath9k: channel context based transmission
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (3 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 04/13] ath9k: Add channel context worker thread Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 06/13] ath9k: send powersave frame on channel switch Rajkumar Manoharan
` (8 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Force queueing of all frames that belong to a virtual interface on
a different channel context, to ensure that they are sent on the
correct channel.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++-
drivers/net/wireless/ath/ath9k/xmit.c | 24 +++++++++++++++++++++---
2 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 052a2f9..3457234 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -275,8 +275,9 @@ struct ath_node {
struct ath_tx_control {
struct ath_txq *txq;
struct ath_node *an;
- u8 paprd;
struct ieee80211_sta *sta;
+ u8 paprd;
+ bool force_channel;
};
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 0c9571a..10a35cd 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2168,13 +2168,18 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sta *sta = txctl->sta;
struct ieee80211_vif *vif = info->control.vif;
+ struct ath_vif *avp = NULL;
struct ath_softc *sc = hw->priv;
struct ath_txq *txq = txctl->txq;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf;
+ bool queue;
int q;
int ret;
+ if (vif)
+ avp = (void *)vif->drv_priv;
+
ret = ath_tx_prepare(hw, skb, txctl);
if (ret)
return ret;
@@ -2195,15 +2200,28 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
txq->stopped = true;
}
- if (txctl->an && ieee80211_is_data_present(hdr->frame_control))
+ queue = ieee80211_is_data_present(hdr->frame_control);
+
+ /* Force queueing of all frames that belong to a virtual interface on
+ * a different channel context, to ensure that they are sent on the
+ * correct channel.
+ */
+ if (((avp && avp->chanctx != sc->cur_chan) ||
+ sc->cur_chan->stopped) && !txctl->force_channel) {
+ if (!txctl->an)
+ txctl->an = &avp->mcast_node;
+ info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE;
+ queue = true;
+ }
+
+ if (txctl->an && queue)
tid = ath_get_skb_tid(sc, txctl->an, skb);
if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
ath_txq_unlock(sc, txq);
txq = sc->tx.uapsdq;
ath_txq_lock(sc, txq);
- } else if (txctl->an &&
- ieee80211_is_data_present(hdr->frame_control)) {
+ } else if (txctl->an && queue) {
WARN_ON(tid->ac->txq != txctl->txq);
if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 06/13] ath9k: send powersave frame on channel switch
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (4 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 05/13] ath9k: channel context based transmission Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 07/13] ath9k: Implement hw_scan support Rajkumar Manoharan
` (7 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
While leaving from or entering to active channel context, send out
nullfunc frame to inform to the AP about the presence of station.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 3 +
drivers/net/wireless/ath/ath9k/channel.c | 96 ++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/main.c | 6 ++
3 files changed, 105 insertions(+)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 3457234..a599831 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -329,12 +329,15 @@ struct ath_chanctx {
u16 txpower;
bool offchannel;
bool stopped;
+ bool active;
};
void ath_chanctx_init(struct ath_softc *sc);
int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef);
void ath_chanctx_switch(struct ath_softc *sc, u8 next_chanctx_idx);
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
+
int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index ecc4d50..df418e5 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -135,10 +135,99 @@ int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
return ath_set_channel(sc);
}
+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:
+ if (!vif->bss_conf.assoc)
+ return false;
+
+ skb = ieee80211_nullfunc_get(sc->hw, vif);
+ if (!skb)
+ return false;
+
+ nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
+ if (powersave)
+ nullfunc->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_PM);
+
+ 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;
+ txctl.force_channel = true;
+ if (ath_tx_start(sc->hw, skb, &txctl)) {
+ ieee80211_free_txskb(sc->hw, skb);
+ return false;
+ }
+
+ return true;
+}
+
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
+{
+ struct ath_vif *avp;
+ bool active = false;
+
+ if (!ctx)
+ return;
+
+ 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:
+ if (vif->bss_conf.assoc)
+ active = true;
+ break;
+ default:
+ active = true;
+ break;
+ }
+ }
+ ctx->active = active;
+}
+
+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;
+}
+
void ath_chanctx_work(struct work_struct *work)
{
struct ath_softc *sc = container_of(work, struct ath_softc,
chanctx_work);
+ bool send_ps = false;
mutex_lock(&sc->mutex);
spin_lock_bh(&sc->chan_lock);
@@ -155,6 +244,10 @@ void ath_chanctx_work(struct work_struct *work)
__ath9k_flush(sc->hw, ~0, true);
+ if (ath_chanctx_send_ps_frame(sc, true))
+ __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false);
+
+ send_ps = true;
spin_lock_bh(&sc->chan_lock);
}
sc->cur_chan = &sc->chanctx[sc->next_chanctx_idx];
@@ -163,6 +256,9 @@ void ath_chanctx_work(struct work_struct *work)
spin_unlock_bh(&sc->chan_lock);
ath_set_channel(sc);
+ if (send_ps)
+ ath_chanctx_send_ps_frame(sc, false);
+
mutex_unlock(&sc->mutex);
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index e4aa29b..ce5484b 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1046,6 +1046,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
/* XXX - will be removed once chanctx ops are added */
avp->chanctx = sc->cur_chan;
list_add_tail(&avp->list, &sc->cur_chan->vifs);
+ ath_chanctx_check_active(sc, avp->chanctx);
an->sc = sc;
an->sta = NULL;
@@ -1064,6 +1065,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_vif *avp = (void *)vif->drv_priv;
mutex_lock(&sc->mutex);
@@ -1086,6 +1088,7 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_assign_slot(sc, vif);
+ ath_chanctx_check_active(sc, avp->chanctx);
mutex_unlock(&sc->mutex);
return 0;
@@ -1144,6 +1147,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
ath9k_ps_restore(sc);
ath_tx_node_cleanup(sc, &avp->mcast_node);
+ ath_chanctx_check_active(sc, avp->chanctx);
mutex_unlock(&sc->mutex);
}
@@ -1741,6 +1745,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
if (ath9k_hw_mci_is_enabled(sc->sc_ah))
ath9k_mci_update_wlan_channels(sc, true);
}
+
+ ath_chanctx_check_active(sc, avp->chanctx);
}
if (changed & BSS_CHANGED_IBSS) {
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 07/13] ath9k: Implement hw_scan support
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (5 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 06/13] ath9k: send powersave frame on channel switch Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 08/13] ath9k: Implement remain-on-channal support Rajkumar Manoharan
` (6 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Implement hw_scan support for enabling multi-channel cuncurrency.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 28 +++++
drivers/net/wireless/ath/ath9k/channel.c | 24 +++++
drivers/net/wireless/ath/ath9k/init.c | 9 +-
drivers/net/wireless/ath/ath9k/main.c | 179 +++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/pci.c | 4 +
drivers/net/wireless/ath/ath9k/recv.c | 5 +
6 files changed, 247 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index a599831..5b55f42 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -35,6 +35,7 @@ extern struct ieee80211_ops ath9k_ops;
extern int ath9k_modparam_nohwcrypt;
extern int led_blink;
extern bool is_ath9k_unloaded;
+extern int ath9k_use_chanctx;
/*************************/
/* Descriptor Management */
@@ -332,11 +333,37 @@ struct ath_chanctx {
bool active;
};
+enum ath_offchannel_state {
+ ATH_OFFCHANNEL_IDLE,
+ ATH_OFFCHANNEL_PROBE_SEND,
+ ATH_OFFCHANNEL_PROBE_WAIT,
+ ATH_OFFCHANNEL_SUSPEND,
+};
+
+struct ath_offchannel {
+ struct ath_chanctx *ctx;
+ struct timer_list timer;
+ struct cfg80211_scan_request *scan_req;
+ struct ieee80211_vif *scan_vif;
+ int scan_idx;
+ enum ath_offchannel_state state;
+};
+
+void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req);
+
void ath_chanctx_init(struct ath_softc *sc);
int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef);
void ath_chanctx_switch(struct ath_softc *sc, u8 next_chanctx_idx);
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
+void ath_offchannel_timer(unsigned long data);
+void ath_offchannel_channel_change(struct ath_softc *sc);
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+ struct ieee80211_channel *chan);
+u8 ath_chanctx_get_oper_chan(struct ath_softc *sc);
int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
@@ -768,6 +795,7 @@ struct ath_softc {
struct ath_chanctx *cur_chan;
spinlock_t chan_lock;
s8 next_chanctx_idx;
+ struct ath_offchannel offchannel;
#ifdef CONFIG_MAC80211_LEDS
bool led_registered;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index df418e5..02de0ab 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -123,6 +123,8 @@ void ath_chanctx_init(struct ath_softc *sc)
INIT_LIST_HEAD(&ctx->acq[j]);
}
sc->cur_chan = &sc->chanctx[0];
+ sc->offchannel.ctx = &sc->chanctx[i - 1];
+ sc->offchannel.ctx->offchannel = true;
}
int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
@@ -259,6 +261,7 @@ void ath_chanctx_work(struct work_struct *work)
if (send_ps)
ath_chanctx_send_ps_frame(sc, false);
+ ath_offchannel_channel_change(sc);
mutex_unlock(&sc->mutex);
}
@@ -271,3 +274,24 @@ void ath_chanctx_switch(struct ath_softc *sc, u8 next_chanctx_idx)
spin_unlock_bh(&sc->chan_lock);
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
}
+
+u8 ath_chanctx_get_oper_chan(struct ath_softc *sc)
+{
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(sc->chanctx); i++) {
+ if (!list_empty(&sc->chanctx[i].vifs))
+ return i;
+ }
+
+ return 0;
+}
+
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+ struct ieee80211_channel *chan)
+{
+ cfg80211_chandef_create(&sc->chanctx[ATH9K_NUM_CHANCTX-1].chandef,
+ chan, NL80211_CHAN_NO_HT);
+
+ ath_chanctx_switch(sc, ATH9K_NUM_CHANCTX - 1);
+}
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 8bd3e42..9f5e1e4 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -61,7 +61,7 @@ static int ath9k_ps_enable;
module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
-static int ath9k_use_chanctx;
+int ath9k_use_chanctx;
module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
@@ -566,6 +566,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
+ setup_timer(&sc->offchannel.timer, ath_offchannel_timer,
+ (unsigned long)sc);
/*
* Cache line size is used to size and align various
@@ -745,8 +747,11 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
if (!ath9k_use_chanctx) {
hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
- } else
+ } else {
hw->wiphy->n_iface_combinations = 1;
+ hw->wiphy->max_scan_ssids = 255;
+ hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ }
}
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index ce5484b..12a0ebd 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2167,6 +2167,185 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
clear_bit(ATH_OP_SCANNING, &common->op_flags);
}
+static void
+ath_scan_next_channel(struct ath_softc *sc)
+{
+ struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+ struct ieee80211_channel *chan;
+
+ if (sc->offchannel.scan_idx >= req->n_channels) {
+ sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc));
+ return;
+ }
+
+ chan = req->channels[sc->offchannel.scan_idx++];
+ sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND;
+ ath_chanctx_offchan_switch(sc, chan);
+}
+
+static void ath_scan_complete(struct ath_softc *sc, bool abort)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc));
+ sc->offchannel.scan_req = NULL;
+ sc->offchannel.scan_vif = NULL;
+ sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+ ieee80211_scan_completed(sc->hw, abort);
+ clear_bit(ATH_OP_SCANNING, &common->op_flags);
+ ath9k_ps_restore(sc);
+
+ if (!sc->ps_idle)
+ return;
+
+ ath_cancel_work(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.ctx->chandef.chan->band;
+
+ skb = ieee80211_probereq_get(sc->hw, vif,
+ 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)
+ memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
+
+ 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];
+ txctl.force_channel = true;
+ 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)
+{
+ struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+ int i, dwell;
+
+ if ((sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) ||
+ !req->n_ssids) {
+ dwell = HZ / 9; /* ~110 ms */
+ } else {
+ dwell = HZ / 16; /* ~60 ms */
+
+ for (i = 0; i < req->n_ssids; i++)
+ ath_scan_send_probe(sc, &req->ssids[i]);
+ }
+
+ sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT;
+ mod_timer(&sc->offchannel.timer, jiffies + dwell);
+}
+
+void ath_offchannel_channel_change(struct ath_softc *sc)
+{
+ if (!sc->offchannel.scan_req)
+ return;
+
+ switch (sc->offchannel.state) {
+ case ATH_OFFCHANNEL_PROBE_SEND:
+ if (sc->cur_chan->chandef.chan !=
+ sc->offchannel.ctx->chandef.chan)
+ return;
+
+ ath_scan_channel_start(sc);
+ break;
+ case ATH_OFFCHANNEL_IDLE:
+ ath_scan_complete(sc, false);
+ break;
+ default:
+ break;
+ }
+}
+
+void ath_offchannel_timer(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *)data;
+ u8 idx = ath_chanctx_get_oper_chan(sc);
+ struct ath_chanctx *ctx = &sc->chanctx[idx];
+
+ if (!sc->offchannel.scan_req)
+ return;
+
+ switch (sc->offchannel.state) {
+ case ATH_OFFCHANNEL_PROBE_WAIT:
+ if (ctx->active) {
+ sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
+ ath_chanctx_switch(sc, idx);
+ mod_timer(&sc->offchannel.timer, jiffies + HZ / 10);
+ break;
+ }
+ /* fall through */
+ case ATH_OFFCHANNEL_SUSPEND:
+ ath_scan_next_channel(sc);
+ break;
+ default:
+ break;
+ }
+}
+
+int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ int ret = 0;
+
+ mutex_lock(&sc->mutex);
+
+ if (WARN_ON(sc->offchannel.scan_req)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ath9k_ps_wakeup(sc);
+ set_bit(ATH_OP_SCANNING, &common->op_flags);
+ sc->offchannel.scan_vif = vif;
+ sc->offchannel.scan_req = req;
+ sc->offchannel.scan_idx = 0;
+ sc->offchannel.ctx->txpower = vif->bss_conf.txpower;
+
+ ath_scan_next_channel(sc);
+
+out:
+ mutex_unlock(&sc->mutex);
+
+ return ret;
+}
+
+void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath_softc *sc = hw->priv;
+
+ mutex_lock(&sc->mutex);
+ del_timer_sync(&sc->offchannel.timer);
+ ath_scan_complete(sc, true);
+ mutex_unlock(&sc->mutex);
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 4dec09e..448af5d 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -842,6 +842,10 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_err(&pdev->dev, "PCI memory region reserve error\n");
return -ENODEV;
}
+ if (ath9k_use_chanctx) {
+ ath9k_ops.hw_scan = ath9k_hw_scan;
+ ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
+ }
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
if (!hw) {
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index de5684a..fec9e0b 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
u32 ath_calcrxfilter(struct ath_softc *sc)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u32 rfilt;
if (config_enabled(CONFIG_ATH9K_TX99))
@@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
rfilt |= ATH9K_RX_FILTER_4ADDRESS;
+ if (ath9k_use_chanctx &&
+ test_bit(ATH_OP_SCANNING, &common->op_flags))
+ rfilt |= ATH9K_RX_FILTER_BEACON;
+
return rfilt;
}
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 08/13] ath9k: Implement remain-on-channal support
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (6 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 07/13] ath9k: Implement hw_scan support Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 09/13] ath9k: Implement channel context ops Rajkumar Manoharan
` (5 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Add remain on channel support in order to enable multi-channel
concurrency.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 11 ++++
drivers/net/wireless/ath/ath9k/init.c | 1 +
drivers/net/wireless/ath/ath9k/main.c | 110 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath9k/pci.c | 3 +
4 files changed, 124 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 5b55f42..7b4a147 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -338,6 +338,9 @@ enum ath_offchannel_state {
ATH_OFFCHANNEL_PROBE_SEND,
ATH_OFFCHANNEL_PROBE_WAIT,
ATH_OFFCHANNEL_SUSPEND,
+ ATH_OFFCHANNEL_ROC_START,
+ ATH_OFFCHANNEL_ROC_WAIT,
+ ATH_OFFCHANNEL_ROC_DONE,
};
struct ath_offchannel {
@@ -347,12 +350,20 @@ struct ath_offchannel {
struct ieee80211_vif *scan_vif;
int scan_idx;
enum ath_offchannel_state state;
+ struct ieee80211_channel *roc_chan;
+ struct ieee80211_vif *roc_vif;
+ int roc_duration;
};
void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
+int ath9k_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan, int duration,
+ enum ieee80211_roc_type type);
+int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw);
void ath_chanctx_init(struct ath_softc *sc);
int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 9f5e1e4..001b7d0 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -751,6 +751,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->n_iface_combinations = 1;
hw->wiphy->max_scan_ssids = 255;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ hw->wiphy->max_remain_on_channel_duration = 10000;
}
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 12a0ebd..c79ab6d 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2184,6 +2184,37 @@ ath_scan_next_channel(struct ath_softc *sc)
ath_chanctx_offchan_switch(sc, chan);
}
+static void ath_offchannel_next(struct ath_softc *sc)
+{
+ struct ieee80211_vif *vif;
+
+ if (sc->offchannel.scan_req) {
+ vif = sc->offchannel.scan_vif;
+ sc->offchannel.ctx->txpower = vif->bss_conf.txpower;
+ ath_scan_next_channel(sc);
+ } else if (sc->offchannel.roc_vif) {
+ vif = sc->offchannel.roc_vif;
+ sc->offchannel.ctx->txpower = vif->bss_conf.txpower;
+ sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
+ ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
+ } else {
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc));
+ sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+ if (sc->ps_idle)
+ ath_cancel_work(sc);
+ }
+}
+
+static void ath_roc_complete(struct ath_softc *sc, bool abort)
+{
+ sc->offchannel.roc_vif = NULL;
+ sc->offchannel.roc_chan = NULL;
+ if (!abort)
+ ieee80211_remain_on_channel_expired(sc->hw);
+ ath_offchannel_next(sc);
+ ath9k_ps_restore(sc);
+}
+
static void ath_scan_complete(struct ath_softc *sc, bool abort)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -2266,6 +2297,9 @@ void ath_offchannel_channel_change(struct ath_softc *sc)
switch (sc->offchannel.state) {
case ATH_OFFCHANNEL_PROBE_SEND:
+ if (!sc->offchannel.scan_req)
+ return;
+
if (sc->cur_chan->chandef.chan !=
sc->offchannel.ctx->chandef.chan)
return;
@@ -2273,8 +2307,23 @@ void ath_offchannel_channel_change(struct ath_softc *sc)
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.ctx)
+ break;
+
+ sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT;
+ mod_timer(&sc->offchannel.timer, jiffies +
+ msecs_to_jiffies(sc->offchannel.roc_duration));
+ ieee80211_ready_on_channel(sc->hw);
+ break;
+ case ATH_OFFCHANNEL_ROC_DONE:
+ ath_roc_complete(sc, false);
+ break;
default:
break;
}
@@ -2291,6 +2340,9 @@ void ath_offchannel_timer(unsigned long data)
switch (sc->offchannel.state) {
case ATH_OFFCHANNEL_PROBE_WAIT:
+ if (!sc->offchannel.scan_req)
+ return;
+
if (ctx->active) {
sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
ath_chanctx_switch(sc, idx);
@@ -2299,8 +2351,16 @@ void ath_offchannel_timer(unsigned long data)
}
/* fall through */
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;
+ ath_chanctx_switch(sc, idx);
+ break;
default:
break;
}
@@ -2327,7 +2387,8 @@ int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
sc->offchannel.scan_idx = 0;
sc->offchannel.ctx->txpower = vif->bss_conf.txpower;
- ath_scan_next_channel(sc);
+ if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+ ath_offchannel_next(sc);
out:
mutex_unlock(&sc->mutex);
@@ -2346,6 +2407,53 @@ void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
mutex_unlock(&sc->mutex);
}
+int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+ struct ath_softc *sc = hw->priv;
+
+ mutex_lock(&sc->mutex);
+
+ del_timer_sync(&sc->offchannel.timer);
+
+ if (sc->offchannel.roc_vif) {
+ if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
+ ath_roc_complete(sc, true);
+ }
+
+ mutex_unlock(&sc->mutex);
+
+ return 0;
+}
+
+int ath9k_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan, int duration,
+ enum ieee80211_roc_type type)
+{
+ struct ath_softc *sc = hw->priv;
+ int ret = 0;
+
+ mutex_lock(&sc->mutex);
+
+ if (WARN_ON(sc->offchannel.roc_vif)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ath9k_ps_wakeup(sc);
+ sc->offchannel.roc_vif = vif;
+ sc->offchannel.roc_chan = chan;
+ sc->offchannel.roc_duration = duration;
+
+ if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+ ath_offchannel_next(sc);
+
+out:
+ mutex_unlock(&sc->mutex);
+
+ return ret;
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 448af5d..48b4a72 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -845,6 +845,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (ath9k_use_chanctx) {
ath9k_ops.hw_scan = ath9k_hw_scan;
ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
+ ath9k_ops.remain_on_channel = ath9k_remain_on_channel;
+ ath9k_ops.cancel_remain_on_channel =
+ ath9k_cancel_remain_on_channel;
}
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 09/13] ath9k: Implement channel context ops
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (7 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 08/13] ath9k: Implement remain-on-channal support Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 10/13] ath9k: Fetch appropriate opereting channel context Rajkumar Manoharan
` (4 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Add channel context operations (add, remove, change, assign and
unassign) to enable support for multiple channels.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 8 +++
drivers/net/wireless/ath/ath9k/beacon.c | 2 +-
drivers/net/wireless/ath/ath9k/channel.c | 92 ++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/init.c | 1 +
drivers/net/wireless/ath/ath9k/link.c | 2 +-
drivers/net/wireless/ath/ath9k/main.c | 17 ++----
drivers/net/wireless/ath/ath9k/pci.c | 8 +--
drivers/net/wireless/ath/ath9k/tx99.c | 2 +-
8 files changed, 110 insertions(+), 22 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 7b4a147..3c7ec1c 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -331,6 +331,7 @@ struct ath_chanctx {
bool offchannel;
bool stopped;
bool active;
+ bool assigned;
};
enum ath_offchannel_state {
@@ -364,7 +365,14 @@ int ath9k_remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_channel *chan, int duration,
enum ieee80211_roc_type type);
int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw);
+void ath_chanctx_fill_ops(void);
+static inline struct ath_chanctx *
+ath_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+{
+ struct ath_chanctx **ptr = (void *) ctx->drv_priv;
+ return *ptr;
+}
void ath_chanctx_init(struct ath_softc *sc);
int ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
struct cfg80211_chan_def *chandef);
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index e387f0b..ed58a9e 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
u8 chainmask = ah->txchainmask;
u8 rate = 0;
- sband = &common->sbands[common->hw->conf.chandef.chan->band];
+ sband = &common->sbands[sc->cur_chan->chandef.chan->band];
rate = sband->bitrates[rateidx].hw_value;
if (vif->bss_conf.use_short_preamble)
rate |= sband->bitrates[rateidx].hw_value_short;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 02de0ab..099e673 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -295,3 +295,95 @@ void ath_chanctx_offchan_switch(struct ath_softc *sc,
ath_chanctx_switch(sc, ATH9K_NUM_CHANCTX - 1);
}
+
+static int ath9k_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_chanctx *ctx, **ptr;
+ int i;
+
+ for (i = 0; i < ATH9K_NUM_CHANCTX; i++) {
+ if (!sc->chanctx[i].assigned)
+ break;
+ }
+ if (i == ATH9K_NUM_CHANCTX)
+ return -ENOSPC;
+
+ ctx = &sc->chanctx[i];
+ ptr = (void *) conf->drv_priv;
+ *ptr = ctx;
+ ctx->assigned = true;
+ ath_chanctx_set_channel(sc, ctx, &conf->def);
+
+ return 0;
+}
+
+
+static void ath9k_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+ ctx->assigned = false;
+}
+
+static void ath9k_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf,
+ u32 changed)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+ mutex_lock(&sc->mutex);
+ ath_chanctx_set_channel(sc, ctx, &conf->def);
+ mutex_unlock(&sc->mutex);
+}
+
+static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+ mutex_lock(&sc->mutex);
+ avp->chanctx = ctx;
+ list_add_tail(&avp->list, &ctx->vifs);
+ ath_chanctx_check_active(sc, ctx);
+ mutex_unlock(&sc->mutex);
+
+ return 0;
+}
+
+static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+ mutex_lock(&sc->mutex);
+ avp->chanctx = NULL;
+ list_del(&avp->list);
+ ath_chanctx_check_active(sc, ctx);
+ mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_fill_ops(void)
+{
+ if (!ath9k_use_chanctx)
+ return;
+
+ ath9k_ops.hw_scan = ath9k_hw_scan;
+ ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
+ ath9k_ops.remain_on_channel = ath9k_remain_on_channel;
+ ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel;
+ ath9k_ops.add_chanctx = ath9k_add_chanctx;
+ ath9k_ops.remove_chanctx = ath9k_remove_chanctx;
+ ath9k_ops.change_chanctx = ath9k_change_chanctx;
+ ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx;
+ ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx;
+}
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 001b7d0..45f198f 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -752,6 +752,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->max_scan_ssids = 255;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
hw->wiphy->max_remain_on_channel_duration = 10000;
+ hw->chanctx_data_size = sizeof(void *);
}
}
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 6f91974..d8d87f0 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -178,7 +178,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];
memset(tx_info, 0, sizeof(*tx_info));
- tx_info->band = hw->conf.chandef.chan->band;
+ tx_info->band = sc->cur_chan->chandef.chan->band;
tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
tx_info->control.rates[0].idx = 0;
tx_info->control.rates[0].count = 1;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index c79ab6d..7326eca 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -627,7 +627,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+ struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
struct ath_chanctx *ctx = sc->cur_chan;
struct ath9k_channel *init_channel;
int r;
@@ -639,7 +639,6 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
- memcpy(&ctx->chandef, &hw->conf.chandef, sizeof(ctx->chandef));
init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef);
/* Reset SERDES registers */
@@ -1042,11 +1041,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
ath9k_beacon_assign_slot(sc, vif);
avp->vif = vif;
-
- /* XXX - will be removed once chanctx ops are added */
- avp->chanctx = sc->cur_chan;
- list_add_tail(&avp->list, &sc->cur_chan->vifs);
- ath_chanctx_check_active(sc, avp->chanctx);
+ if (!ath9k_use_chanctx)
+ avp->chanctx = sc->cur_chan;
an->sc = sc;
an->sta = NULL;
@@ -1135,7 +1131,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
}
spin_unlock_bh(&sc->sc_pcu_lock);
- list_del(&avp->list);
sc->nvifs--;
sc->tx99_vif = NULL;
@@ -1147,7 +1142,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
ath9k_ps_restore(sc);
ath_tx_node_cleanup(sc, &avp->mcast_node);
- ath_chanctx_check_active(sc, avp->chanctx);
mutex_unlock(&sc->mutex);
}
@@ -1274,7 +1268,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &hw->conf;
struct ath_chanctx *ctx = sc->cur_chan;
- bool reset_channel = false;
ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
@@ -1290,7 +1283,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
* The chip needs a reset to properly wake up from
* full sleep
*/
- reset_channel = ah->chip_fullsleep;
+ ath_chanctx_set_channel(sc, ctx, &ctx->chandef);
}
}
@@ -1320,7 +1313,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
}
}
- if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
+ if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL);
if (ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef) < 0) {
ath_err(common, "Unable to set channel\n");
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 48b4a72..1d4393f 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -842,13 +842,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_err(&pdev->dev, "PCI memory region reserve error\n");
return -ENODEV;
}
- if (ath9k_use_chanctx) {
- ath9k_ops.hw_scan = ath9k_hw_scan;
- ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
- ath9k_ops.remain_on_channel = ath9k_remain_on_channel;
- ath9k_ops.cancel_remain_on_channel =
- ath9k_cancel_remain_on_channel;
- }
+ ath_chanctx_fill_ops();
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
if (!hw) {
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index a65cfb9..2397292 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -76,7 +76,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
tx_info = IEEE80211_SKB_CB(skb);
memset(tx_info, 0, sizeof(*tx_info));
rate = &tx_info->control.rates[0];
- tx_info->band = hw->conf.chandef.chan->band;
+ tx_info->band = sc->cur_chan->chandef.chan->band;
tx_info->flags = IEEE80211_TX_CTL_NO_ACK;
tx_info->control.vif = sc->tx99_vif;
rate->count = 1;
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 10/13] ath9k: Fetch appropriate opereting channel context
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (8 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 09/13] ath9k: Implement channel context ops Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 11/13] ath9k: Move caldata into " Rajkumar Manoharan
` (3 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Rajkumar Manoharan
Retrieve appropriate operating channel context while switching
between operating and off channels.
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 2 +-
drivers/net/wireless/ath/ath9k/channel.c | 9 +++++++--
drivers/net/wireless/ath/ath9k/main.c | 11 +++++++----
3 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 3c7ec1c..7634960 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -382,7 +382,7 @@ void ath_offchannel_timer(unsigned long data);
void ath_offchannel_channel_change(struct ath_softc *sc);
void ath_chanctx_offchan_switch(struct ath_softc *sc,
struct ieee80211_channel *chan);
-u8 ath_chanctx_get_oper_chan(struct ath_softc *sc);
+u8 ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active);
int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 099e673..f7842ee 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -275,12 +275,17 @@ void ath_chanctx_switch(struct ath_softc *sc, u8 next_chanctx_idx)
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
}
-u8 ath_chanctx_get_oper_chan(struct ath_softc *sc)
+u8 ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active)
{
+ struct ath_chanctx *ctx;
u8 i;
for (i = 0; i < ARRAY_SIZE(sc->chanctx); i++) {
- if (!list_empty(&sc->chanctx[i].vifs))
+ ctx = &sc->chanctx[i];
+ if (!ctx->assigned || list_empty(&ctx->vifs))
+ continue;
+
+ if (active && ctx->active)
return i;
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 7326eca..2d619b7 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2168,7 +2168,7 @@ ath_scan_next_channel(struct ath_softc *sc)
if (sc->offchannel.scan_idx >= req->n_channels) {
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
- ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc));
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false));
return;
}
@@ -2191,7 +2191,7 @@ static void ath_offchannel_next(struct ath_softc *sc)
sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
} else {
- ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc));
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false));
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
if (sc->ps_idle)
ath_cancel_work(sc);
@@ -2212,7 +2212,7 @@ static void ath_scan_complete(struct ath_softc *sc, bool abort)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc));
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false));
sc->offchannel.scan_req = NULL;
sc->offchannel.scan_vif = NULL;
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
@@ -2325,7 +2325,7 @@ void ath_offchannel_channel_change(struct ath_softc *sc)
void ath_offchannel_timer(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
- u8 idx = ath_chanctx_get_oper_chan(sc);
+ u8 idx = ath_chanctx_get_oper_chan(sc, false);
struct ath_chanctx *ctx = &sc->chanctx[idx];
if (!sc->offchannel.scan_req)
@@ -2336,6 +2336,9 @@ void ath_offchannel_timer(unsigned long data)
if (!sc->offchannel.scan_req)
return;
+ /* get first active channel context */
+ idx = ath_chanctx_get_oper_chan(sc, true);
+ ctx = &sc->chanctx[idx];
if (ctx->active) {
sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
ath_chanctx_switch(sc, idx);
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 11/13] ath9k: Move caldata into channel context
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (9 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 10/13] ath9k: Fetch appropriate opereting channel context Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 12/13] ath9k: Add ATH_OP_MULTI_CHANNEL Rajkumar Manoharan
` (2 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 4 ++--
drivers/net/wireless/ath/ath9k/debug.c | 2 +-
drivers/net/wireless/ath/ath9k/main.c | 2 +-
drivers/net/wireless/ath/ath9k/mci.c | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 7634960..7776094 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -327,6 +327,8 @@ struct ath_chanctx {
struct list_head vifs;
struct list_head acq[IEEE80211_NUM_ACS];
+ struct ath9k_hw_cal_data caldata;
+
u16 txpower;
bool offchannel;
bool stopped;
@@ -822,8 +824,6 @@ struct ath_softc {
struct led_classdev led_cdev;
#endif
- struct ath9k_hw_cal_data caldata;
-
#ifdef CONFIG_ATH9K_DEBUGFS
struct ath9k_debug debug;
#endif
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 6cc42be..f230e2e9 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -1080,7 +1080,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
{
struct ath_softc *sc = file->private_data;
struct ath_hw *ah = sc->sc_ah;
- struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist;
+ struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
u32 len = 0, size = 1500;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 2d619b7..fa9d915 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -288,7 +288,7 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
if (!sc->cur_chan->offchannel) {
fastcc = false;
- caldata = &sc->caldata;
+ caldata = &sc->cur_chan->caldata;
}
if (!hchan) {
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index 313ed99..3f7a11e 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -706,7 +706,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
return;
if (setchannel) {
- struct ath9k_hw_cal_data *caldata = &sc->caldata;
+ struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata;
if (IS_CHAN_HT40PLUS(ah->curchan) &&
(ah->curchan->channel > caldata->channel) &&
(ah->curchan->channel <= caldata->channel + 20))
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 12/13] ath9k: Add ATH_OP_MULTI_CHANNEL
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (10 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 11/13] ath9k: Move caldata into " Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 13/13] ath9k: save tsf in channel context Rajkumar Manoharan
2014-06-04 14:00 ` [PATCH 00/13] ath9k: Channel context support Johannes Berg
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath.h | 1 +
drivers/net/wireless/ath/ath9k/channel.c | 12 ++++++++++++
2 files changed, 13 insertions(+)
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index a889fd6..fd9e530 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -63,6 +63,7 @@ enum ath_op_flags {
ATH_OP_PRIM_STA_VIF,
ATH_OP_HW_RESET,
ATH_OP_SCANNING,
+ ATH_OP_MULTI_CHANNEL,
};
enum ath_bus_type {
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index f7842ee..3cb86f4 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -186,8 +186,10 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp;
bool active = false;
+ u8 i, n_active = 0;
if (!ctx)
return;
@@ -207,6 +209,16 @@ void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
}
}
ctx->active = active;
+
+ for (i = 0; i < ATH9K_NUM_CHANCTX; i++) {
+ ctx = &sc->chanctx[i];
+ if (ctx->assigned && !list_empty(&ctx->vifs))
+ n_active++;
+ }
+ if (n_active > 1)
+ set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
+ else
+ clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
}
static bool
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 13/13] ath9k: save tsf in channel context
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (11 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 12/13] ath9k: Add ATH_OP_MULTI_CHANNEL Rajkumar Manoharan
@ 2014-06-04 13:46 ` Rajkumar Manoharan
2014-06-04 14:00 ` [PATCH 00/13] ath9k: Channel context support Johannes Berg
13 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, Felix Fietkau, Rajkumar Manoharan
From: Felix Fietkau <nbd@openwrt.org>
Save TSF in channel context for multiple operating channels.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 3 +++
drivers/net/wireless/ath/ath9k/channel.c | 5 +++++
drivers/net/wireless/ath/ath9k/hw.c | 23 ++++++++++++++++++-----
drivers/net/wireless/ath/ath9k/hw.h | 1 +
drivers/net/wireless/ath/ath9k/main.c | 10 ++++++++++
5 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 7776094..a5ff4ab 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -22,6 +22,7 @@
#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/completion.h>
+#include <linux/time.h>
#include "common.h"
#include "debug.h"
@@ -328,6 +329,8 @@ struct ath_chanctx {
struct list_head acq[IEEE80211_NUM_ACS];
struct ath9k_hw_cal_data caldata;
+ struct timespec tsf_ts;
+ u64 tsf_val;
u16 txpower;
bool offchannel;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 3cb86f4..716967c 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -263,6 +263,11 @@ void ath_chanctx_work(struct work_struct *work)
send_ps = true;
spin_lock_bh(&sc->chan_lock);
+
+ if (!sc->cur_chan->offchannel) {
+ getrawmonotonic(&sc->cur_chan->tsf_ts);
+ sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah);
+ }
}
sc->cur_chan = &sc->chanctx[sc->next_chanctx_idx];
sc->cur_chan->stopped = false;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 2a8ed83..ace4fe2 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1730,6 +1730,23 @@ fail:
return -EINVAL;
}
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur)
+{
+ struct timespec ts;
+ s64 usec;
+
+ if (!cur) {
+ getrawmonotonic(&ts);
+ cur = &ts;
+ }
+
+ usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000;
+ usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000;
+
+ return (u32) usec;
+}
+EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
+
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
struct ath9k_hw_cal_data *caldata, bool fastcc)
{
@@ -1739,7 +1756,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
u32 saveDefAntenna;
u32 macStaId1;
u64 tsf = 0;
- s64 usec = 0;
int r;
bool start_mci_reset = false;
bool save_fullsleep = ah->chip_fullsleep;
@@ -1785,7 +1801,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
/* Save TSF before chip reset, a cold reset clears it */
tsf = ath9k_hw_gettsf64(ah);
getrawmonotonic(&ts);
- usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
saveLedState = REG_READ(ah, AR_CFG_LED) &
(AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
@@ -1818,9 +1833,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
}
/* Restore TSF */
- getrawmonotonic(&ts);
- usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec;
- ath9k_hw_settsf64(ah, tsf + usec);
+ ath9k_hw_settsf64(ah, tsf + ath9k_hw_get_tsf_offset(&ts, NULL));
if (AR_SREV_9280_20_OR_LATER(ah))
REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 0acd4b5..51b4ebe 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -1000,6 +1000,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah);
u64 ath9k_hw_gettsf64(struct ath_hw *ah);
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
void ath9k_hw_reset_tsf(struct ath_hw *ah);
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur);
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
void ath9k_hw_init_global_settings(struct ath_hw *ah);
u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index fa9d915..3b6b030 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -241,6 +241,16 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
ath9k_hw_enable_interrupts(ah);
if (!sc->cur_chan->offchannel && start) {
+ /* restore per chanctx TSF timer */
+ if (sc->cur_chan->tsf_val) {
+ u32 offset;
+
+ offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts,
+ NULL);
+ ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset);
+ }
+
+
if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
goto work;
--
2.0.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH 00/13] ath9k: Channel context support
2014-06-04 13:46 [PATCH 00/13] ath9k: Channel context support Rajkumar Manoharan
` (12 preceding siblings ...)
2014-06-04 13:46 ` [PATCH 13/13] ath9k: save tsf in channel context Rajkumar Manoharan
@ 2014-06-04 14:00 ` Johannes Berg
2014-06-04 16:16 ` Rajkumar Manoharan
13 siblings, 1 reply; 17+ messages in thread
From: Johannes Berg @ 2014-06-04 14:00 UTC (permalink / raw)
To: Rajkumar Manoharan; +Cc: linville, linux-wireless
On Wed, 2014-06-04 at 19:16 +0530, Rajkumar Manoharan wrote:
> These patches adds a basic infrastruture for multi channel
> context. Currently channel context usage is restricted by
> modparam "use_chanctx" as it is not yet fully implemented.
This isn't really a review - just a note. You should probably also tell
mac80211 that you use multiple HW queues (at least per chanctx) so you
avoid a situation where stopping one channel context leads to stopping
the entire traffic for that AC even on other contexts, otherwise you
could unfairly stop the other interface?
I guess I'm also surprised by the implementation just switching from a
worker, I would have thought you'd switch from a HW timer interrupt and
use different hardware queues, but hey :)
> Rajkumar Manoharan (1):
> ath9k: Fetch appropriate opereting channel context
Also: typo: "operating"
johannes
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 00/13] ath9k: Channel context support
2014-06-04 14:00 ` [PATCH 00/13] ath9k: Channel context support Johannes Berg
@ 2014-06-04 16:16 ` Rajkumar Manoharan
0 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 16:16 UTC (permalink / raw)
To: Johannes Berg; +Cc: linville, linux-wireless
On Wed, Jun 04, 2014 at 04:00:46PM +0200, Johannes Berg wrote:
> On Wed, 2014-06-04 at 19:16 +0530, Rajkumar Manoharan wrote:
>
> > These patches adds a basic infrastruture for multi channel
> > context. Currently channel context usage is restricted by
> > modparam "use_chanctx" as it is not yet fully implemented.
>
> This isn't really a review - just a note. You should probably also tell
> mac80211 that you use multiple HW queues (at least per chanctx) so you
> avoid a situation where stopping one channel context leads to stopping
> the entire traffic for that AC even on other contexts, otherwise you
> could unfairly stop the other interface?
>
Completely agree. IEEE80211_HW_QUEUE_CONTROL support is still in-progress.
> I guess I'm also surprised by the implementation just switching from a
> worker, I would have thought you'd switch from a HW timer interrupt and
> use different hardware queues, but hey :)
>
These are just a preparatory changes for enabling MCC. The actual
scheduler is based on HW timers that will kickstart the worker thread.
Will post all of the changes after completing regression test cycle.
-Rajkumar
^ permalink raw reply [flat|nested] 17+ messages in thread