diff --git a/patches/openwrt/0024-ath9k-add-HSR-tuner-support-for-UniFi-Outdoor-Plus.patch b/patches/openwrt/0024-ath9k-add-HSR-tuner-support-for-UniFi-Outdoor-Plus.patch new file mode 100644 index 00000000..98a85aaf --- /dev/null +++ b/patches/openwrt/0024-ath9k-add-HSR-tuner-support-for-UniFi-Outdoor-Plus.patch @@ -0,0 +1,399 @@ +From: Matthias Schiffer +Date: Thu, 3 Sep 2015 23:50:51 +0200 +Subject: ath9k: add HSR tuner support for UniFi Outdoor Plus + +Patch-by: Stefan Rompf + +diff --git a/package/kernel/mac80211/patches/931-ubnt-uap-plus-hsr.patch b/package/kernel/mac80211/patches/931-ubnt-uap-plus-hsr.patch +new file mode 100644 +index 0000000..4cd6faf +--- /dev/null ++++ b/package/kernel/mac80211/patches/931-ubnt-uap-plus-hsr.patch +@@ -0,0 +1,353 @@ ++--- a/drivers/net/wireless/ath/ath9k/channel.c +++++ b/drivers/net/wireless/ath/ath9k/channel.c ++@@ -15,6 +15,8 @@ ++ */ ++ ++ #include "ath9k.h" +++#include +++#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 @@ static int ath_set_channel(struct ath_so ++ 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); ++--- /dev/null +++++ b/drivers/net/wireless/ath/ath9k/hsr.c ++@@ -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 +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++#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(1, "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(1, "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(1, "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(1, "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(1, "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(1, "hsr_status: returned %d,%d \n", *cmd, ret); +++ return -1; +++ } +++ +++ return 0; +++} +++ ++--- /dev/null +++++ b/drivers/net/wireless/ath/ath9k/hsr.h ++@@ -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_ */ ++--- a/drivers/net/wireless/ath/ath9k/main.c +++++ b/drivers/net/wireless/ath/ath9k/main.c ++@@ -16,8 +16,10 @@ ++ ++ #include ++ #include +++#include ++ #include "ath9k.h" ++ #include "btcoex.h" +++#include "hsr.h" ++ ++ u8 ath9k_parse_mpdudensity(u8 mpdudensity) ++ { ++@@ -652,6 +654,7 @@ void ath_reset_work(struct work_struct * ++ 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 @@ static int ath9k_start(struct ieee80211_ ++ (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. ++--- a/drivers/net/wireless/ath/ath9k/Makefile +++++ b/drivers/net/wireless/ath/ath9k/Makefile ++@@ -6,7 +6,8 @@ ath9k-y += beacon.o \ ++ xmit.o \ ++ link.o \ ++ antenna.o \ ++- channel.o +++ channel.o \ +++ hsr.o ++ ++ ath9k-$(CPTCFG_ATH9K_BTCOEX_SUPPORT) += mci.o ++ ath9k-$(CPTCFG_ATH9K_PCI) += pci.o ++--- a/include/linux/ath9k_platform.h +++++ b/include/linux/ath9k_platform.h ++@@ -44,6 +44,8 @@ struct ath9k_platform_data { ++ ++ int num_leds; ++ const struct gpio_led *leds; +++ +++ bool ubnt_hsr; ++ }; ++ ++ #endif /* _LINUX_ATH9K_PLATFORM_H */ +diff --git a/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch b/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch +index 7803513..d865ed2 100644 +--- a/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch ++++ b/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch +@@ -254,6 +254,7 @@ + + ath79_register_eth(0); + + ath79_register_eth(1); + + +++ ap9x_pci_get_wmac_data(0)->ubnt_hsr = true; + + ap91_pci_init(ee, NULL); + + + + ath79_register_leds_gpio(-1, ARRAY_SIZE(ubnt_unifi_outdoor_plus_leds_gpio), +diff --git a/target/linux/generic/patches-3.18/150-ath9k_ubnt_hsr_filter.patch b/target/linux/generic/patches-3.18/150-ath9k_ubnt_hsr_filter.patch +new file mode 100644 +index 0000000..a0ebbc7 +--- /dev/null ++++ b/target/linux/generic/patches-3.18/150-ath9k_ubnt_hsr_filter.patch +@@ -0,0 +1,16 @@ ++Flag that this platform is an Ubiquiti UniFi Outdoor Plus ++containing a RF filter in ath9k's receive path. ++ ++Signed-off-by: Stefan Rompf ++ ++--- a/include/linux/ath9k_platform.h +++++ b/include/linux/ath9k_platform.h ++@@ -44,6 +44,8 @@ struct ath9k_platform_data { ++ ++ int num_leds; ++ const struct gpio_led *leds; +++ +++ bool ubnt_hsr; ++ }; ++ ++ #endif /* _LINUX_ATH9K_PLATFORM_H */