Add tuner for the HSR filter of the UniFi Outdoor Plus access point. Usage of the tuner is controlled at runtime by ath9k_platform_data. The code can be enabled or disabled by a compile time option.
Signed-off-by: Stefan Rompf <[email protected]> --- Index: package/kernel/mac80211/Makefile =================================================================== --- package/kernel/mac80211/Makefile (Revision 46000) +++ package/kernel/mac80211/Makefile (Arbeitskopie) @@ -587,6 +587,11 @@ bool "Support chips used in PC OEM cards" depends on PACKAGE_kmod-ath9k + config ATH9K_UBNTHSR + bool "Support for Ubiquiti UniFi Outdoor Plus access point" + depends on PACKAGE_kmod-ath9k + default y + endef define KernelPackage/ath9k-htc @@ -1558,6 +1563,7 @@ config-$(CONFIG_PCI) += ATH9K_PCI config-$(CONFIG_ATH_USER_REGD) += ATH_USER_REGD config-$(CONFIG_ATH9K_SUPPORT_PCOEM) += ATH9K_PCOEM +config-$(CONFIG_ATH9K_UBNTHSR) += ATH9K_UBNTHSR config-$(call config_package,ath9k-htc) += ATH9K_HTC config-$(call config_package,ath10k) += ATH10K ATH10K_PCI Index: package/kernel/mac80211/patches/930-ubnt-uap-plus-hsr.patch =================================================================== --- package/kernel/mac80211/patches/930-ubnt-uap-plus-hsr.patch (Revision 0) +++ package/kernel/mac80211/patches/930-ubnt-uap-plus-hsr.patch (Arbeitskopie) @@ -0,0 +1,373 @@ +--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/channel.c 2015-06-16 09:28:34.000000000 +0200 ++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/channel.c 2015-06-16 09:49:53.000000000 +0200 +@@ -15,6 +15,8 @@ + */ + + #include "ath9k.h" ++#include <linux/ath9k_platform.h> ++#include "hsr.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 +@@ -22,6 +24,7 @@ + */ + static int ath_set_channel(struct ath_softc *sc) + { ++ struct ath9k_platform_data *pdata = sc->dev->platform_data; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hw *hw = sc->hw; +@@ -41,6 +44,11 @@ + ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", + chan->center_freq, chandef->width); + ++ if (pdata && pdata->ubnt_hsr) { ++ hsr_enable(ah, chandef->width, chan->center_freq); ++ hsr_status(ah); ++ } ++ + /* update survey stats for the old channel before switching */ + spin_lock_bh(&common->cc_lock); + ath_update_survey_stats(sc); +--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/hsr.c 1970-01-01 01:00:00.000000000 +0100 ++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/hsr.c 2015-06-16 09:41:24.000000000 +0200 +@@ -0,0 +1,220 @@ ++/* ++ * ++ * The MIT License (MIT) ++ * ++ * Copyright (c) 2015 Kirill Berezin ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in all ++ * copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/time.h> ++#include <linux/bitops.h> ++#include <linux/etherdevice.h> ++#include <linux/rtnetlink.h> ++#include <asm/unaligned.h> ++ ++#include "hw.h" ++#include "ath9k.h" ++ ++#define HSR_GPIO_CSN 8 ++#define HSR_GPIO_CLK 6 ++#define HSR_GPIO_DOUT 7 ++#define HSR_GPIO_DIN 5 ++ ++/* delays are in useconds */ ++#define HSR_DELAY_HALF_TICK 100 ++#define HSR_DELAY_PRE_WRITE 75 ++#define HSR_DELAY_FINAL 20000 ++#define HSR_DELAY_TRAILING 200 ++ ++ ++void hsr_init(struct ath_hw* ah) { ++ ath9k_hw_cfg_gpio_input(ah, HSR_GPIO_DIN); ++ ath9k_hw_cfg_output(ah, HSR_GPIO_CSN, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ++ ath9k_hw_cfg_output(ah, HSR_GPIO_CLK, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ++ ath9k_hw_cfg_output(ah, HSR_GPIO_DOUT, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ++ ++ ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1); ++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0); ++ ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, 0); ++ ++ udelay(HSR_DELAY_TRAILING); ++} ++ ++static u32 hsr_write_byte(struct ath_hw* ah, int delay, u32 value){ ++ struct ath_common *common = ath9k_hw_common(ah); ++ int i; ++ u32 rval = 0; ++ ++ udelay(delay); ++ ++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0); ++ udelay(HSR_DELAY_HALF_TICK); ++ ++ ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 0); ++ udelay(HSR_DELAY_HALF_TICK); ++ ++ for( i = 0; i < 8; ++i) { ++ rval = rval << 1; ++ ++ // pattern is left to right, that is 7-th bit runs first ++ ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, (value >> (7 - i)) & 0x1); ++ udelay(HSR_DELAY_HALF_TICK); ++ ++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 1); ++ udelay(HSR_DELAY_HALF_TICK); ++ ++ rval |= ath9k_hw_gpio_get(ah, HSR_GPIO_DIN); ++ ++ ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0); ++ udelay(HSR_DELAY_HALF_TICK); ++ } ++ ++ ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1); ++ udelay(HSR_DELAY_HALF_TICK); ++ ++ ath_dbg(common, CONFIG, "hsr_write_byte: write byte %d return value is %d %c\n", ++ value, rval, rval > 32 ? rval : '-'); ++ ++ return rval & 0xff; ++} ++ ++static int hsr_write_a_chain(struct ath_hw* ah, char* chain, int items) { ++ int i = 0; ++ int status = 0; ++ ++ // a preamble ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0); ++ status = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0); ++ ++ // clear HSR's reply buffer ++ if (status) { ++ int loop = 0; ++ for ( loop = 0; (loop < 42) && status; ++loop) { ++ status = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0); ++ } ++ if ( loop >= 42) { ++ ATH_DBG_WARN("hsr_write_a_chain: can't clear an output buffer after a 42 cycles.\n"); ++ return -1; ++ } ++ } ++ ++ for ( i =0; (i < items) && ( 0 != chain[i]); ++i) { ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, (u32)chain[i]); ++ } ++ ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0); ++ mdelay(HSR_DELAY_FINAL / 1000); ++ ++ // reply ++ memset(chain, 0, items); ++ ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0); ++ udelay(HSR_DELAY_TRAILING); ++ ++ for ( i = 0; i < (items - 1); ++i) { ++ u32 ret; ++ if ( 0 != (ret = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0))) { ++ chain[i] = (char)ret; ++ } else { ++ break; ++ } ++ udelay(HSR_DELAY_TRAILING); ++ } ++ ++ return (1 < i) ? simple_strtol(chain + 1, NULL, 10) : 0; ++} ++ ++int hsr_disable(struct ath_hw* ah) { ++ char cmd[10] = {'b', '4', '0', 0, 0, 0, 0, 0, 0, 0}; ++ int ret; ++ ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd)); ++ if ( (ret > 0) && (*cmd == 'B')) { ++ return 0; ++ } ++ ++ return -1; ++} ++ ++int hsr_enable(struct ath_hw* ah, int bw, int fq) { ++ char cmd[10]; ++ int ret; ++ ++ /* Bandwidth argument is 0 sometimes. Assume default 802.11bgn ++ 20MHz on invalid values */ ++ if ( (bw != 5) && (bw != 10) && (bw != 20) && (bw != 40)) { ++ bw = 20; ++ } ++ ++ memset(cmd, 0, sizeof(cmd)); ++ *cmd = 'b'; // 98 ++ snprintf(cmd + 1, 3, "%02d", bw); ++ ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd)); ++ if ( (*cmd != 'B') || (ret != bw)) { ++ ATH_DBG_WARN("hsr_enable: failed changing bandwidth -> set (%d,%d) reply (%d, %d) \n", 'b', bw, *cmd, ret); ++ return -1; ++ } ++ ++ memset(cmd, 0, sizeof(cmd)); ++ *cmd = 'x'; // 120 ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd)); ++ if ( *cmd != 'X') { ++ ATH_DBG_WARN("hsr_enable: failed 'x' command -> reply (%d, %d) \n", *cmd, ret); ++ return -1; ++ } ++ ++ memset(cmd, 0, sizeof(cmd)); ++ *cmd = 'm'; // 109 ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd)); ++ if ( *cmd != 'M') { ++ ATH_DBG_WARN("hsr_enable: failed 'm' command -> reply (%d, %d) \n", *cmd, ret); ++ return -1; ++ } ++ ++ memset(cmd, 0, sizeof(cmd)); ++ *cmd = 'f'; // 102 ++ snprintf(cmd + 1, 6, "%05d", fq); ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd)); ++ if ( (*cmd != 'F') && (ret != fq)) { ++ ATH_DBG_WARN("hsr_enable: failed set frequency -> reply (%d, %d) \n", *cmd, ret); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int hsr_status(struct ath_hw* ah) { ++ char cmd[10] = {'s', 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 115 ++ int ret; ++ ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd)); ++ if ( (*cmd != 'S')) { ++ ATH_DBG_WARN("hsr_status: returned %d,%d \n", *cmd, ret); ++ return -1; ++ } ++ ++ return 0; ++} ++ +--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/hsr.h 1970-01-01 01:00:00.000000000 +0100 ++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/hsr.h 2015-06-16 09:17:52.000000000 +0200 +@@ -0,0 +1,40 @@ ++/* ++ * The MIT License (MIT) ++ * ++ * Copyright (c) 2015 Kirill Berezin ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in all ++ * copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ */ ++ ++#ifndef HSR_H_ ++#define HSR_H_ ++ ++#ifdef CPTCFG_ATH9K_UBNTHSR ++void hsr_init(struct ath_hw* ah); ++int hsr_disable(struct ath_hw* ah); ++int hsr_enable(struct ath_hw* ah, int bw, int fq); ++int hsr_status(struct ath_hw* ah); ++#else ++static inline void hsr_init(struct ath_hw* ah) {} ++static inline int hsr_disable(struct ath_hw* ah) { return 0; } ++static inline int hsr_enable(struct ath_hw* ah, int bw, int fq) { return 0; } ++static inline int hsr_status(struct ath_hw* ah) { return 0; } ++#endif ++ ++#endif /* HSR_H_ */ +--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/Kconfig 2015-03-10 04:37:16.000000000 +0100 ++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/Kconfig 2015-06-16 09:13:53.000000000 +0200 +@@ -59,6 +59,20 @@ + Say Y, if you have a SoC with a compatible built-in + wireless MAC. Say N if unsure. + ++config ATH9K_UBNTHSR ++ bool "Ubiquiti UniFi Outdoor Plus HSR support" ++ depends on ATH9K ++ default y ++ ---help--- ++ This options enables code to control the HSR RF ++ filter in the receive path of the Ubiquiti UniFi ++ Outdoor Plus access point. ++ ++ Say Y, if you want to use the access point. The ++ code will only be used if the device is detected, ++ so it does not harm other setup other than occupying ++ a bit of memory. ++ + config ATH9K_DEBUGFS + bool "Atheros ath9k debugging" + depends on ATH9K && DEBUG_FS +--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/main.c 2015-06-16 08:45:50.000000000 +0200 ++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/main.c 2015-06-16 09:49:43.000000000 +0200 +@@ -16,8 +16,10 @@ + + #include <linux/nl80211.h> + #include <linux/delay.h> ++#include <linux/ath9k_platform.h> + #include "ath9k.h" + #include "btcoex.h" ++#include "hsr.h" + + u8 ath9k_parse_mpdudensity(u8 mpdudensity) + { +@@ -652,6 +654,7 @@ + static int ath9k_start(struct ieee80211_hw *hw) + { + struct ath_softc *sc = hw->priv; ++ struct ath9k_platform_data *pdata = sc->dev->platform_data; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan; +@@ -730,6 +733,11 @@ + (ah->config.led_active_high) ? 1 : 0); + } + ++ if (pdata && pdata->ubnt_hsr) { ++ hsr_init(ah); ++ hsr_disable(ah); ++ } ++ + /* + * Reset key cache to sane defaults (all entries cleared) instead of + * semi-random values after suspend/resume. +--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/Makefile 2015-03-10 04:37:16.000000000 +0100 ++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/Makefile 2015-06-16 09:14:59.000000000 +0200 +@@ -15,6 +15,7 @@ + ath9k-$(CPTCFG_ATH9K_DFS_CERTIFIED) += dfs.o + ath9k-$(CPTCFG_ATH9K_TX99) += tx99.o + ath9k-$(CPTCFG_ATH9K_WOW) += wow.o ++ath9k-$(CPTCFG_ATH9K_UBNTHSR) += hsr.o + + ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o + +--- compat-wireless-2015-03-09/.local-symbols.orig 2015-06-16 14:54:43.000000000 +0200 ++++ compat-wireless-2015-03-09/.local-symbols 2015-06-16 14:54:45.000000000 +0200 +@@ -115,6 +115,7 @@ + ATH9K_RFKILL= + ATH9K_CHANNEL_CONTEXT= + ATH9K_PCOEM= ++ATH9K_UBNTHSR= + ATH9K_HTC= + ATH9K_HTC_DEBUGFS= + CARL9170= _______________________________________________ openwrt-devel mailing list [email protected] https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
