All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/13] ath9k: Channel context support
@ 2014-06-04 13:46 Rajkumar Manoharan
  2014-06-04 13:46 ` [PATCH 01/13] ath9k: Add channel context structure Rajkumar Manoharan
                   ` (13 more replies)
  0 siblings, 14 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-04 13:46 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Rajkumar Manoharan

Hi,

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 is based on Felix's initial patches for MCC.

Felix Fietkau (12):
  ath9k: Add channel context structure
  ath9k: Move txpower limit to channel context
  ath9k: Move acq to channel context
  ath9k: Add channel context worker thread
  ath9k: channel context based transmission
  ath9k: send powersave frame on channel switch
  ath9k: Implement hw_scan support
  ath9k: Implement remain-on-channal support
  ath9k: Implement channel context ops
  ath9k: Move caldata into channel context
  ath9k: Add ATH_OP_MULTI_CHANNEL
  ath9k: save tsf in channel context

Rajkumar Manoharan (1):
  ath9k: Fetch appropriate opereting channel context

 drivers/net/wireless/ath/ath.h           |   1 +
 drivers/net/wireless/ath/ath9k/Makefile  |   3 +-
 drivers/net/wireless/ath/ath9k/ath9k.h   |  94 ++++++-
 drivers/net/wireless/ath/ath9k/beacon.c  |   2 +-
 drivers/net/wireless/ath/ath9k/channel.c | 411 ++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath9k/debug.c   |   2 +-
 drivers/net/wireless/ath/ath9k/hw.c      |  23 +-
 drivers/net/wireless/ath/ath9k/hw.h      |   1 +
 drivers/net/wireless/ath/ath9k/init.c    |  19 +-
 drivers/net/wireless/ath/ath9k/link.c    |   4 +-
 drivers/net/wireless/ath/ath9k/main.c    | 454 +++++++++++++++++++++++--------
 drivers/net/wireless/ath/ath9k/mci.c     |   4 +-
 drivers/net/wireless/ath/ath9k/pci.c     |   1 +
 drivers/net/wireless/ath/ath9k/recv.c    |   9 +-
 drivers/net/wireless/ath/ath9k/tx99.c    |   2 +-
 drivers/net/wireless/ath/ath9k/wow.c     |   1 +
 drivers/net/wireless/ath/ath9k/xmit.c    |  72 +++--
 17 files changed, 944 insertions(+), 159 deletions(-)
 create mode 100644 drivers/net/wireless/ath/ath9k/channel.c

-- 
2.0.0


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [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

* Re: [PATCH 01/13] ath9k: Add channel context structure
  2014-06-04 13:46 ` [PATCH 01/13] ath9k: Add channel context structure Rajkumar Manoharan
@ 2014-06-09 11:36   ` Rajkumar Manoharan
  0 siblings, 0 replies; 17+ messages in thread
From: Rajkumar Manoharan @ 2014-06-09 11:36 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, Felix Fietkau

On Wed, Jun 04, 2014 at 07:16:27PM +0530, Rajkumar Manoharan wrote:
> From: Felix Fietkau <nbd@openwrt.org>
> 
> The channel context structure is defined to enable
> multi-channel concurrency support.
> 
John,

Can you please ignore this patch set. I will send out complete patch
set. Sorry for the inconvenience.

-Rajkumar

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2014-06-09 11:36 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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-09 11:36   ` Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 02/13] ath9k: Move txpower limit to channel context Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 03/13] ath9k: Move acq " Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 04/13] ath9k: Add channel context worker thread Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 05/13] ath9k: channel context based transmission Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 06/13] ath9k: send powersave frame on channel switch Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 07/13] ath9k: Implement hw_scan support Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 08/13] ath9k: Implement remain-on-channal support Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 09/13] ath9k: Implement channel context ops Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 10/13] ath9k: Fetch appropriate opereting channel context Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 11/13] ath9k: Move caldata into " Rajkumar Manoharan
2014-06-04 13:46 ` [PATCH 12/13] ath9k: Add ATH_OP_MULTI_CHANNEL 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
2014-06-04 16:16   ` Rajkumar Manoharan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.