diff options
| author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2011-08-28 18:38:24 +0000 | 
|---|---|---|
| committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2011-08-28 18:38:24 +0000 | 
| commit | 9176f7d5a8c221823de2930b07cc1b109de23f7c (patch) | |
| tree | e021e922696045c1b0670b2813193c97c89ee969 /package | |
| parent | 2ce07e9d02f539ae5dbeed3cb228a8846b6deb78 (diff) | |
ath9k: implement rx/tx antenna control
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@28107 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package')
6 files changed, 893 insertions, 31 deletions
diff --git a/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch b/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch index 6ae1323d2..fedd17f51 100644 --- a/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch +++ b/package/mac80211/patches/513-ath9k_channelbw_debugfs.patch @@ -22,36 +22,53 @@   }  --- a/drivers/net/wireless/ath/ath9k/main.c  +++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -226,6 +226,7 @@ static int ath_set_channel(struct ath_so - 	bool fastcc = true, stopped; - 	struct ieee80211_channel *channel = hw->conf.channel; - 	struct ath9k_hw_cal_data *caldata = NULL; -+	u32 oldflags; - 	int r; +@@ -1654,9 +1654,10 @@ static int ath9k_config(struct ieee80211 - 	if (sc->sc_flags & SC_OP_INVALID) -@@ -268,6 +269,24 @@ static int ath_set_channel(struct ath_so - 	if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL)) - 		fastcc = false; + 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + 		struct ieee80211_channel *curchan = hw->conf.channel; +-		struct ath9k_channel old_chan; ++		struct ath9k_channel old_chan, *hchan; + 		int pos = curchan->hw_value; + 		int old_pos = -1; ++		u32 oldflags; + 		unsigned long flags; -+	oldflags = hchan->channelFlags; -+	switch (sc->chan_bw) { -+	case 5: -+		hchan->channelFlags &= ~CHANNEL_HALF; -+		hchan->channelFlags |= CHANNEL_QUARTER; -+		break; -+	case 10: -+		hchan->channelFlags &= ~CHANNEL_QUARTER; -+		hchan->channelFlags |= CHANNEL_HALF; -+		break; -+	default: -+		hchan->channelFlags &= ~(CHANNEL_HALF | CHANNEL_QUARTER); -+		break; -+	} -+ -+	if (oldflags != hchan->channelFlags) -+		fastcc = false; + 		if (ah->curchan) +@@ -1709,7 +1710,23 @@ static int ath9k_config(struct ieee80211 + 			memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + 		} +  +-		if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { ++		hchan = &sc->sc_ah->channels[pos]; ++		oldflags = hchan->channelFlags; ++		switch (sc->chan_bw) { ++		case 5: ++			hchan->channelFlags &= ~CHANNEL_HALF; ++			hchan->channelFlags |= CHANNEL_QUARTER; ++			break; ++		case 10: ++			hchan->channelFlags &= ~CHANNEL_QUARTER; ++			hchan->channelFlags |= CHANNEL_HALF; ++			break; ++		default: ++			hchan->channelFlags &= ~(CHANNEL_HALF | CHANNEL_QUARTER); ++			break; ++		}  + - 	if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) - 		caldata = &sc->caldata; ++		if (ath_set_channel(sc, hw, hchan) < 0) { + 			ath_err(common, "Unable to set channel\n"); + 			mutex_unlock(&sc->mutex); + 			return -EINVAL; +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -1504,6 +1504,10 @@ int ath9k_hw_reset(struct ath_hw *ah, st + 	} + 	ah->noise = ath9k_hw_getchan_noise(ah, chan); ++	if (!ah->curchan || ((ah->curchan->channelFlags ^ chan->channelFlags) & ++	    (CHANNEL_HALF | CHANNEL_QUARTER))) ++		bChannelChange = false; ++ + 	if (bChannelChange && + 	    (ah->chip_fullsleep != true) && + 	    (ah->curchan != NULL) && diff --git a/package/mac80211/patches/531-ath9k_cur_txpower.patch b/package/mac80211/patches/531-ath9k_cur_txpower.patch index f0de43eb4..8e104d9fe 100644 --- a/package/mac80211/patches/531-ath9k_cur_txpower.patch +++ b/package/mac80211/patches/531-ath9k_cur_txpower.patch @@ -1,6 +1,6 @@  --- a/drivers/net/wireless/ath/ath9k/main.c  +++ b/drivers/net/wireless/ath/ath9k/main.c -@@ -1734,6 +1734,8 @@ static int ath9k_config(struct ieee80211 +@@ -1732,6 +1732,8 @@ static int ath9k_config(struct ieee80211   			return -EINVAL;   		} @@ -9,7 +9,7 @@   		/*   		 * The most recent snapshot of channel->noisefloor for the old   		 * channel is only available after the hardware reset. Copy it to -@@ -1751,6 +1753,7 @@ static int ath9k_config(struct ieee80211 +@@ -1749,6 +1751,7 @@ static int ath9k_config(struct ieee80211   		ath9k_cmn_update_txpow(ah, sc->curtxpow,   				       sc->config.txpowlimit, &sc->curtxpow);   		ath9k_ps_restore(sc); diff --git a/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch b/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch new file mode 100644 index 000000000..1164fc2ac --- /dev/null +++ b/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch @@ -0,0 +1,273 @@ +--- a/drivers/net/wireless/ath/ath.h ++++ b/drivers/net/wireless/ath/ath.h +@@ -140,9 +140,6 @@ struct ath_common { + 	u8 curbssid[ETH_ALEN]; + 	u8 bssidmask[ETH_ALEN]; +  +-	u8 tx_chainmask; +-	u8 rx_chainmask; +- + 	u32 rx_bufsize; +  + 	u32 keymax; +--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +@@ -113,7 +113,7 @@ static int ar9003_get_training_power_5g( + 	if (delta > scale) + 		return -1; +  +-	switch (get_streams(common->tx_chainmask)) { ++	switch (get_streams(ah->txchainmask)) { + 	case 1: + 		delta = 6; + 		break; +@@ -126,7 +126,7 @@ static int ar9003_get_training_power_5g( + 	default: + 		delta = 0; + 		ath_dbg(common, ATH_DBG_CALIBRATE, +-		"Invalid tx-chainmask: %u\n", common->tx_chainmask); ++		"Invalid tx-chainmask: %u\n", ah->txchainmask); + 	} +  + 	power += delta; +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -318,7 +318,6 @@ static void ath_paprd_activate(struct at + { + 	struct ath_hw *ah = sc->sc_ah; + 	struct ath9k_hw_cal_data *caldata = ah->caldata; +-	struct ath_common *common = ath9k_hw_common(ah); + 	int chain; +  + 	if (!caldata || !caldata->paprd_done) +@@ -327,7 +326,7 @@ static void ath_paprd_activate(struct at + 	ath9k_ps_wakeup(sc); + 	ar9003_paprd_enable(ah, false); + 	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { +-		if (!(common->tx_chainmask & BIT(chain))) ++		if (!(ah->txchainmask & BIT(chain))) + 			continue; +  + 		ar9003_paprd_populate_single_table(ah, caldata, chain); +@@ -414,7 +413,7 @@ void ath_paprd_calibrate(struct work_str + 	memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); +  + 	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { +-		if (!(common->tx_chainmask & BIT(chain))) ++		if (!(ah->txchainmask & BIT(chain))) + 			continue; +  + 		chain_ok = 0; +@@ -535,7 +534,7 @@ void ath_ani_calibrate(unsigned long dat + 	if (longcal || shortcal) { + 		common->ani.caldone = + 			ath9k_hw_calibrate(ah, ah->curchan, +-						common->rx_chainmask, longcal); ++						ah->rxchainmask, longcal); + 	} +  + 	ath9k_ps_restore(sc); +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -1485,9 +1485,6 @@ int ath9k_hw_reset(struct ath_hw *ah, st + 	u64 tsf = 0; + 	int i, r; +  +-	ah->txchainmask = common->tx_chainmask; +-	ah->rxchainmask = common->rx_chainmask; +- + 	if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + 		return -EIO; +  +@@ -2105,6 +2102,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw +  + 	pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask); + 	pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask); ++	ah->txchainmask = pCap->tx_chainmask; ++	ah->rxchainmask = pCap->rx_chainmask; +  + 	ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; +  +--- a/drivers/net/wireless/ath/ath9k/xmit.c ++++ b/drivers/net/wireless/ath/ath9k/xmit.c +@@ -1655,7 +1655,7 @@ u8 ath_txchainmask_reduction(struct ath_ +  + static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, int len) + { +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++	struct ath_hw *ah = sc->sc_ah; + 	struct ath9k_11n_rate_series series[4]; + 	struct sk_buff *skb; + 	struct ieee80211_tx_info *tx_info; +@@ -1715,7 +1715,7 @@ static void ath_buf_set_rate(struct ath_ + 			/* MCS rates */ + 			series[i].Rate = rix | 0x80; + 			series[i].ChSel = ath_txchainmask_reduction(sc, +-					common->tx_chainmask, series[i].Rate); ++					ah->txchainmask, series[i].Rate); + 			series[i].PktDuration = ath_pkt_duration(sc, rix, len, + 				 is_40, is_sgi, is_sp); + 			if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC)) +@@ -1740,10 +1740,10 @@ static void ath_buf_set_rate(struct ath_ + 		} +  + 		if (bf->bf_state.bfs_paprd) +-			series[i].ChSel = common->tx_chainmask; ++			series[i].ChSel = ah->txchainmask; + 		else + 			series[i].ChSel = ath_txchainmask_reduction(sc, +-					common->tx_chainmask, series[i].Rate); ++					ah->txchainmask, series[i].Rate); +  + 		series[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah, + 			phy, rate->bitrate * 100, len, rix, is_sp); +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -107,7 +107,7 @@ static void ath_beacon_setup(struct ath_ + 	series[0].Tries = 1; + 	series[0].Rate = rate; + 	series[0].ChSel = ath_txchainmask_reduction(sc, +-			common->tx_chainmask, series[0].Rate); ++			ah->txchainmask, series[0].Rate); + 	series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; + 	ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration, + 				     series, 4, 0); +--- a/drivers/net/wireless/ath/ath9k/debug.c ++++ b/drivers/net/wireless/ath/ath9k/debug.c +@@ -95,11 +95,11 @@ static ssize_t read_file_tx_chainmask(st + 			     size_t count, loff_t *ppos) + { + 	struct ath_softc *sc = file->private_data; +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++	struct ath_hw *ah = sc->sc_ah; + 	char buf[32]; + 	unsigned int len; +  +-	len = sprintf(buf, "0x%08x\n", common->tx_chainmask); ++	len = sprintf(buf, "0x%08x\n", ah->txchainmask); + 	return simple_read_from_buffer(user_buf, count, ppos, buf, len); + } +  +@@ -107,7 +107,7 @@ static ssize_t write_file_tx_chainmask(s + 			     size_t count, loff_t *ppos) + { + 	struct ath_softc *sc = file->private_data; +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++	struct ath_hw *ah = sc->sc_ah; + 	unsigned long mask; + 	char buf[32]; + 	ssize_t len; +@@ -120,8 +120,8 @@ static ssize_t write_file_tx_chainmask(s + 	if (strict_strtoul(buf, 0, &mask)) + 		return -EINVAL; +  +-	common->tx_chainmask = mask; +-	sc->sc_ah->caps.tx_chainmask = mask; ++	ah->txchainmask = mask; ++	ah->caps.tx_chainmask = mask; + 	return count; + } +  +@@ -138,11 +138,11 @@ static ssize_t read_file_rx_chainmask(st + 			     size_t count, loff_t *ppos) + { + 	struct ath_softc *sc = file->private_data; +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++	struct ath_hw *ah = sc->sc_ah; + 	char buf[32]; + 	unsigned int len; +  +-	len = sprintf(buf, "0x%08x\n", common->rx_chainmask); ++	len = sprintf(buf, "0x%08x\n", ah->rxchainmask); + 	return simple_read_from_buffer(user_buf, count, ppos, buf, len); + } +  +@@ -150,7 +150,7 @@ static ssize_t write_file_rx_chainmask(s + 			     size_t count, loff_t *ppos) + { + 	struct ath_softc *sc = file->private_data; +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++	struct ath_hw *ah = sc->sc_ah; + 	unsigned long mask; + 	char buf[32]; + 	ssize_t len; +@@ -163,8 +163,8 @@ static ssize_t write_file_rx_chainmask(s + 	if (strict_strtoul(buf, 0, &mask)) + 		return -EINVAL; +  +-	common->rx_chainmask = mask; +-	sc->sc_ah->caps.rx_chainmask = mask; ++	ah->rxchainmask = mask; ++	ah->caps.rx_chainmask = mask; + 	return count; + } +  +--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c +@@ -509,8 +509,8 @@ static void setup_ht_cap(struct ath9k_ht + 	memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); +  + 	/* ath9k_htc supports only 1 or 2 stream devices */ +-	tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, 2); +-	rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, 2); ++	tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2); ++	rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2); +  + 	ath_dbg(common, ATH_DBG_CONFIG, + 		"TX streams %d, RX streams: %d\n", +@@ -601,9 +601,6 @@ static void ath9k_init_misc(struct ath9k + { + 	struct ath_common *common = ath9k_hw_common(priv->ah); +  +-	common->tx_chainmask = priv->ah->caps.tx_chainmask; +-	common->rx_chainmask = priv->ah->caps.rx_chainmask; +- + 	memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); +  + 	priv->ah->opmode = NL80211_IFTYPE_STATION; +--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c +@@ -826,8 +826,7 @@ void ath9k_htc_ani_work(struct work_stru + 		if (longcal || shortcal) + 			common->ani.caldone = + 				ath9k_hw_calibrate(ah, ah->curchan, +-						   common->rx_chainmask, +-						   longcal); ++						   ah->rxchainmask, longcal); +  + 		ath9k_htc_ps_restore(priv); + 	} +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -270,8 +270,8 @@ static void setup_ht_cap(struct ath_soft +  + 	/* set up supported mcs set */ + 	memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); +-	tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, max_streams); +-	rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, max_streams); ++	tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams); ++	rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams); +  + 	ath_dbg(common, ATH_DBG_CONFIG, + 		"TX streams %d, RX streams: %d\n", +@@ -506,9 +506,6 @@ static void ath9k_init_misc(struct ath_s + 		sc->sc_flags |= SC_OP_RXAGGR; + 	} +  +-	common->tx_chainmask = sc->sc_ah->caps.tx_chainmask; +-	common->rx_chainmask = sc->sc_ah->caps.rx_chainmask; +- + 	ath9k_hw_set_diversity(sc->sc_ah, true); + 	sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); +  +@@ -645,10 +642,8 @@ static void ath9k_init_band_txpower(stru + static void ath9k_init_txpower_limits(struct ath_softc *sc) + { + 	struct ath_hw *ah = sc->sc_ah; +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); + 	struct ath9k_channel *curchan = ah->curchan; +  +-	ah->txchainmask = common->tx_chainmask; + 	if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) + 		ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ); + 	if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) diff --git a/package/mac80211/patches/581-ath9k_merge_reset_functions.patch b/package/mac80211/patches/581-ath9k_merge_reset_functions.patch new file mode 100644 index 000000000..201994db4 --- /dev/null +++ b/package/mac80211/patches/581-ath9k_merge_reset_functions.patch @@ -0,0 +1,343 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -212,83 +212,39 @@ static int ath_update_survey_stats(struc + 	return ret; + } +  +-/* +- * 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 ieee80211_hw *hw, +-		    struct ath9k_channel *hchan) ++static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx) + { + 	struct ath_hw *ah = sc->sc_ah; +-	struct ath_common *common = ath9k_hw_common(ah); +-	struct ieee80211_conf *conf = &common->hw->conf; +-	bool fastcc = true, stopped; +-	struct ieee80211_channel *channel = hw->conf.channel; +-	struct ath9k_hw_cal_data *caldata = NULL; +-	int r; ++	bool ret; +  +-	if (sc->sc_flags & SC_OP_INVALID) +-		return -EIO; ++	ieee80211_stop_queues(sc->hw); +  + 	sc->hw_busy_count = 0; +- +-	del_timer_sync(&common->ani.timer); + 	cancel_work_sync(&sc->paprd_work); + 	cancel_work_sync(&sc->hw_check_work); + 	cancel_delayed_work_sync(&sc->tx_complete_work); + 	cancel_delayed_work_sync(&sc->hw_pll_work); +  +-	ath9k_ps_wakeup(sc); +- +-	spin_lock_bh(&sc->sc_pcu_lock); +- +-	/* +-	 * This is only performed if the channel settings have +-	 * actually changed. +-	 * +-	 * To switch channels clear any pending DMA operations; +-	 * wait long enough for the RX fifo to drain, reset the +-	 * hardware at the new frequency, and then re-enable +-	 * the relevant bits of the h/w. +-	 */ + 	ath9k_hw_disable_interrupts(ah); +-	stopped = ath_drain_all_txq(sc, false); +  +-	if (!ath_stoprecv(sc)) +-		stopped = false; +- +-	if (!ath9k_hw_check_alive(ah)) +-		stopped = false; +- +-	/* XXX: do not flush receive queue here. We don't want +-	 * to flush data frames already in queue because of +-	 * changing channel. */ ++	ret = ath_drain_all_txq(sc, retry_tx); +  +-	if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL)) +-		fastcc = false; ++	if (!ath_stoprecv(sc)) ++		ret = false; +  +-	if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) +-		caldata = &sc->caldata; ++	ath_flushrecv(sc); +  +-	ath_dbg(common, ATH_DBG_CONFIG, +-		"(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n", +-		sc->sc_ah->curchan->channel, +-		channel->center_freq, conf_is_ht40(conf), +-		fastcc); ++	return ret; ++} +  +-	r = ath9k_hw_reset(ah, hchan, caldata, fastcc); +-	if (r) { +-		ath_err(common, +-			"Unable to reset channel (%u MHz), reset status %d\n", +-			channel->center_freq, r); +-		goto ps_restore; +-	} ++static bool ath_complete_reset(struct ath_softc *sc) ++{ ++	struct ath_hw *ah = sc->sc_ah; ++	struct ath_common *common = ath9k_hw_common(ah); +  + 	if (ath_startrecv(sc) != 0) { + 		ath_err(common, "Unable to restart recv logic\n"); +-		r = -EIO; +-		goto ps_restore; ++		return false; + 	} +  + 	ath9k_cmn_update_txpow(ah, sc->curtxpow, +@@ -299,18 +255,84 @@ static int ath_set_channel(struct ath_so + 	if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) { + 		if (sc->sc_flags & SC_OP_BEACONS) + 			ath_set_beacon(sc); ++ + 		ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); + 		ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2); + 		if (!common->disable_ani) + 			ath_start_ani(common); + 	} +  +- ps_restore: +-	ieee80211_wake_queues(hw); ++	ieee80211_wake_queues(sc->hw); ++ ++	return true; ++} ++ ++static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, ++			      bool retry_tx) ++{ ++	struct ath_hw *ah = sc->sc_ah; ++	struct ath_common *common = ath9k_hw_common(ah); ++	struct ath9k_hw_cal_data *caldata = NULL; ++	bool fastcc = true; ++	int r; ++ ++	if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) { ++		fastcc = false; ++		caldata = &sc->caldata; ++	} +  ++	if (!hchan) { ++		fastcc = false; ++		hchan = ah->curchan; ++	} ++ ++	if (fastcc && !ath9k_hw_check_alive(ah)) ++		fastcc = false; ++ ++	if (!ath_prepare_reset(sc, retry_tx)) ++		fastcc = false; ++ ++	ath_dbg(common, ATH_DBG_CONFIG, ++		"Reset to %u MHz, HT40: %d fastcc: %d\n", ++		hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS | ++							  CHANNEL_HT40PLUS)), ++		fastcc); ++ ++	r = ath9k_hw_reset(ah, hchan, caldata, fastcc); ++	if (r) { ++		ath_err(common, ++			"Unable to reset channel, reset status %d\n", r); ++		return r; ++	} ++ ++	if (!ath_complete_reset(sc)) ++		return -EIO; ++ ++	return 0; ++} ++ ++ ++/* ++ * 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 ieee80211_hw *hw, ++		    struct ath9k_channel *hchan) ++{ ++	int r; ++ ++	if (sc->sc_flags & SC_OP_INVALID) ++		return -EIO; ++ ++	ath9k_ps_wakeup(sc); ++ ++	spin_lock_bh(&sc->sc_pcu_lock); ++	r = ath_reset_internal(sc, hchan, false); + 	spin_unlock_bh(&sc->sc_pcu_lock); +  + 	ath9k_ps_restore(sc); ++ + 	return r; + } +  +@@ -893,28 +915,13 @@ static void ath_radio_enable(struct ath_ + 			channel->center_freq, r); + 	} +  +-	ath9k_cmn_update_txpow(ah, sc->curtxpow, +-			       sc->config.txpowlimit, &sc->curtxpow); +-	if (ath_startrecv(sc) != 0) { +-		ath_err(common, "Unable to restart recv logic\n"); +-		goto out; +-	} +-	if (sc->sc_flags & SC_OP_BEACONS) +-		ath_set_beacon(sc);	/* restart beacons */ +- +-	/* Re-Enable  interrupts */ +-	ath9k_hw_set_interrupts(ah, ah->imask); +-	ath9k_hw_enable_interrupts(ah); ++	ath_complete_reset(sc); +  + 	/* Enable LED */ + 	ath9k_hw_cfg_output(ah, ah->led_pin, + 			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + 	ath9k_hw_set_gpio(ah, ah->led_pin, 0); +  +-	ieee80211_wake_queues(hw); +-	ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2); +- +-out: + 	spin_unlock_bh(&sc->sc_pcu_lock); +  + 	ath9k_ps_restore(sc); +@@ -942,13 +949,7 @@ void ath_radio_disable(struct ath_softc  + 		ath9k_hw_cfg_gpio_input(ah, ah->led_pin); + 	} +  +-	/* Disable interrupts */ +-	ath9k_hw_disable_interrupts(ah); +- +-	ath_drain_all_txq(sc, false);	/* clear pending tx frames */ +- +-	ath_stoprecv(sc);		/* turn off frame recv */ +-	ath_flushrecv(sc);		/* flush recv queue */ ++	ath_prepare_reset(sc, false); +  + 	if (!ah->curchan) + 		ah->curchan = ath9k_cmn_get_curchannel(hw, ah); +@@ -970,48 +971,11 @@ void ath_radio_disable(struct ath_softc  +  + int ath_reset(struct ath_softc *sc, bool retry_tx) + { +-	struct ath_hw *ah = sc->sc_ah; +-	struct ath_common *common = ath9k_hw_common(ah); +-	struct ieee80211_hw *hw = sc->hw; + 	int r; +  +-	sc->hw_busy_count = 0; +- +-	/* Stop ANI */ +- +-	del_timer_sync(&common->ani.timer); +- + 	ath9k_ps_wakeup(sc); +  +-	ieee80211_stop_queues(hw); +- +-	ath9k_hw_disable_interrupts(ah); +-	ath_drain_all_txq(sc, retry_tx); +- +-	ath_stoprecv(sc); +-	ath_flushrecv(sc); +- +-	r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false); +-	if (r) +-		ath_err(common, +-			"Unable to reset hardware; reset status %d\n", r); +- +-	if (ath_startrecv(sc) != 0) +-		ath_err(common, "Unable to start recv logic\n"); +- +-	/* +-	 * We may be doing a reset in response to a request +-	 * that changes the channel so update any state that +-	 * might change as a result. +-	 */ +-	ath9k_cmn_update_txpow(ah, sc->curtxpow, +-			       sc->config.txpowlimit, &sc->curtxpow); +- +-	if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL))) +-		ath_set_beacon(sc);	/* restart beacons */ +- +-	ath9k_hw_set_interrupts(ah, ah->imask); +-	ath9k_hw_enable_interrupts(ah); ++	r = ath_reset_internal(sc, NULL, retry_tx); +  + 	if (retry_tx) { + 		int i; +@@ -1024,12 +988,6 @@ int ath_reset(struct ath_softc *sc, bool + 		} + 	} +  +-	ieee80211_wake_queues(hw); +- +-	/* Start ANI */ +-	if (!common->disable_ani) +-		ath_start_ani(common); +- + 	ath9k_ps_restore(sc); +  + 	return r; +@@ -1081,28 +1039,6 @@ static int ath9k_start(struct ieee80211_ + 		goto mutex_unlock; + 	} +  +-	/* +-	 * This is needed only to setup initial state +-	 * but it's best done after a reset. +-	 */ +-	ath9k_cmn_update_txpow(ah, sc->curtxpow, +-			sc->config.txpowlimit, &sc->curtxpow); +- +-	/* +-	 * Setup the hardware after reset: +-	 * The receive engine is set going. +-	 * Frame transmit is handled entirely +-	 * in the frame output path; there's nothing to do +-	 * here except setup the interrupt mask. +-	 */ +-	if (ath_startrecv(sc) != 0) { +-		ath_err(common, "Unable to start recv logic\n"); +-		r = -EIO; +-		spin_unlock_bh(&sc->sc_pcu_lock); +-		goto mutex_unlock; +-	} +-	spin_unlock_bh(&sc->sc_pcu_lock); +- + 	/* Setup our intr mask. */ + 	ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL | + 		    ATH9K_INT_RXORN | ATH9K_INT_FATAL | +@@ -1125,12 +1061,14 @@ static int ath9k_start(struct ieee80211_ +  + 	/* Disable BMISS interrupt when we're not associated */ + 	ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); +-	ath9k_hw_set_interrupts(ah, ah->imask); +-	ath9k_hw_enable_interrupts(ah); +  +-	ieee80211_wake_queues(hw); ++	if (!ath_complete_reset(sc)) { ++		r = -EIO; ++		spin_unlock_bh(&sc->sc_pcu_lock); ++		goto mutex_unlock; ++	} +  +-	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); ++	spin_unlock_bh(&sc->sc_pcu_lock); +  + 	if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) && + 	    !ah->btcoex_hw.enabled) { diff --git a/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch b/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch new file mode 100644 index 000000000..92c8ea4b8 --- /dev/null +++ b/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch @@ -0,0 +1,50 @@ +--- a/drivers/net/wireless/ath/ath9k/hw-ops.h ++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h +@@ -121,13 +121,15 @@ static inline void ath9k_hw_set_clrdmask + static inline void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah, + 		struct ath_hw_antcomb_conf *antconf) + { +-	ath9k_hw_ops(ah)->antdiv_comb_conf_get(ah, antconf); ++	if (ath9k_hw_ops(ah)->antdiv_comb_conf_get) ++		ath9k_hw_ops(ah)->antdiv_comb_conf_get(ah, antconf); + } +  + static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, + 		struct ath_hw_antcomb_conf *antconf) + { +-	ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); ++	if (ath9k_hw_ops(ah)->antdiv_comb_conf_set) ++		ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); + } +  + /* Private hardware call ops */ +--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c ++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c +@@ -570,8 +570,10 @@ void ar9002_hw_attach_phy_ops(struct ath + 	priv_ops->compute_pll_control = ar9002_hw_compute_pll_control; + 	priv_ops->do_getnf = ar9002_hw_do_getnf; +  +-	ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get; +-	ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set; ++	if (AR_SREV_9285(ah)) { ++		ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get; ++		ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set; ++	} +  + 	ar9002_hw_set_nf_limits(ah); + } +--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c +@@ -1283,8 +1283,10 @@ void ar9003_hw_attach_phy_ops(struct ath + 	priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs; + 	priv_ops->set_radar_params = ar9003_hw_set_radar_params; +  +-	ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; +-	ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; ++	if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) { ++		ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; ++		ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; ++	} +  + 	ar9003_hw_set_nf_limits(ah); + 	ar9003_hw_set_radar_conf(ah); diff --git a/package/mac80211/patches/583-ath9k_antenna_control.patch b/package/mac80211/patches/583-ath9k_antenna_control.patch new file mode 100644 index 000000000..a1caa8aaa --- /dev/null +++ b/package/mac80211/patches/583-ath9k_antenna_control.patch @@ -0,0 +1,179 @@ +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -652,9 +652,22 @@ static void ath9k_init_txpower_limits(st + 	ah->curchan = curchan; + } +  ++void ath9k_reload_chainmask_settings(struct ath_softc *sc) ++{ ++	if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)) ++		return; ++ ++	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) ++		setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); ++	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) ++		setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); ++} ++ ++ + void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) + { +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++	struct ath_hw *ah = sc->sc_ah; ++	struct ath_common *common = ath9k_hw_common(ah); +  + 	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + 		IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | +@@ -692,6 +705,17 @@ void ath9k_set_hw_capab(struct ath_softc + 	hw->sta_data_size = sizeof(struct ath_node); + 	hw->vif_data_size = sizeof(struct ath_vif); +  ++	hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; ++	hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; ++ ++	/* single chain devices with rx diversity */ ++	if (ah->caps.max_rxchains == 1 && ++	    ath9k_hw_ops(ah)->antdiv_comb_conf_get) ++		hw->wiphy->available_antennas_rx = 3; ++ ++	sc->ant_rx = hw->wiphy->available_antennas_rx; ++	sc->ant_tx = hw->wiphy->available_antennas_tx; ++ + #ifdef CONFIG_ATH9K_RATE_CONTROL + 	hw->rate_control_algorithm = "ath9k_rate_control"; + #endif +@@ -703,12 +727,7 @@ void ath9k_set_hw_capab(struct ath_softc + 		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + 			&sc->sbands[IEEE80211_BAND_5GHZ]; +  +-	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { +-		if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) +-			setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); +-		if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) +-			setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); +-	} ++	ath9k_reload_chainmask_settings(sc); +  + 	SET_IEEE80211_PERM_ADDR(hw, common->macaddr); + } +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -652,6 +652,7 @@ struct ath_softc { + 	struct ath_descdma txsdma; +  + 	struct ath_ant_comb ant_comb; ++	u32 ant_tx, ant_rx; + }; +  + void ath9k_tasklet(unsigned long data); +@@ -673,6 +674,7 @@ int ath9k_init_device(u16 devid, struct  + 		    const struct ath_bus_ops *bus_ops); + void ath9k_deinit_device(struct ath_softc *sc); + void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); ++void ath9k_reload_chainmask_settings(struct ath_softc *sc); +  + void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); + bool ath9k_uses_beacons(int type); +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -262,6 +262,22 @@ static bool ath_complete_reset(struct at + 			ath_start_ani(common); + 	} +  ++	if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) { ++		struct ath_hw_antcomb_conf div_ant_conf; ++		u8 lna_conf; ++ ++		ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); ++ ++		if (sc->ant_rx == 1) ++			lna_conf = ATH_ANT_DIV_COMB_LNA1; ++		else ++			lna_conf = ATH_ANT_DIV_COMB_LNA2; ++		div_ant_conf.main_lna_conf = lna_conf; ++		div_ant_conf.alt_lna_conf = lna_conf; ++ ++		ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); ++	} ++ + 	ieee80211_wake_queues(sc->hw); +  + 	return true; +@@ -2360,6 +2376,59 @@ static int ath9k_get_stats(struct ieee80 + 	return 0; + } +  ++static u32 fill_chainmask(u32 cap, u32 new) ++{ ++	u32 filled = 0; ++	int i; ++ ++	for (i = 0; cap && new; i++, cap >>= 1) { ++		if (!(cap & BIT(0))) ++			continue; ++ ++		if (new & BIT(0)) ++			filled |= BIT(i); ++ ++		new >>= 1; ++	} ++ ++	return filled; ++} ++ ++static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) ++{ ++	struct ath_softc *sc = hw->priv; ++	struct ath_hw *ah = sc->sc_ah; ++ ++	if (!rx_ant || !tx_ant) ++		return -EINVAL; ++ ++	sc->ant_rx = rx_ant; ++	sc->ant_tx = tx_ant; ++ ++	if (ah->caps.rx_chainmask == 1) ++		return 0; ++ ++	/* AR9100 runs into calibration issues if not all rx chains are enabled */ ++	if (AR_SREV_9100(ah)) ++		ah->rxchainmask = 0x7; ++	else ++		ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant); ++ ++	ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant); ++	ath9k_reload_chainmask_settings(sc); ++ ++	return 0; ++} ++ ++static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) ++{ ++	struct ath_softc *sc = hw->priv; ++ ++	*tx_ant = sc->ant_tx; ++	*rx_ant = sc->ant_rx; ++	return 0; ++} ++ + struct ieee80211_ops ath9k_ops = { + 	.tx 		    = ath9k_tx, + 	.start 		    = ath9k_start, +@@ -2386,4 +2455,6 @@ struct ieee80211_ops ath9k_ops = { + 	.tx_frames_pending  = ath9k_tx_frames_pending, + 	.tx_last_beacon     = ath9k_tx_last_beacon, + 	.get_stats	    = ath9k_get_stats, ++	.set_antenna	    = ath9k_set_antenna, ++	.get_antenna	    = ath9k_get_antenna, + }; +--- a/drivers/net/wireless/ath/ath9k/recv.c ++++ b/drivers/net/wireless/ath/ath9k/recv.c +@@ -1956,7 +1956,7 @@ int ath_rx_tasklet(struct ath_softc *sc, + 			ath_rx_ps(sc, skb); + 		spin_unlock_irqrestore(&sc->sc_pm_lock, flags); +  +-		if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) ++		if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3) + 			ath_ant_comb_scan(sc, &rs); +  + 		ieee80211_rx(hw, skb);  | 
