Hi Felix,
> I think deferring this to user space might mess up timings during fast
> active scans. I'd prefer to have a callback in ath9k_platform.h that
> allows the platform code to provide a hook which implements this.
> That way the changes to ath9k stay very small, and the code is still
> quite simple.
this would move the hsr driver into the kernel image, wouldn't it? That's IMHO
a maintenance nightmare as it would force us to flash a new image whenever we
want to update or even debug the driver.
What about this solution:
-Add the possibility to register a channel change helper in ath9k.ko. As with
your idea, this is only a very small patch that all ar71xx targets would
have to carry.
-Make the hsr driver a channel change helper .ko
-Install the hsr kernel module only into the UniFi Outdoor Plus image.
Like in the attached RFC patch (that has be applied manually).
Stefan
diff -Nur linux/drivers/net/wireless/ath9k.orig/ath/ath9k.h linux/drivers/net/wireless/ath9k/ath/ath9k.h
--- linux/drivers/net/wireless/ath/ath9k.orig/ath9k.h 2015-04-04 04:46:37.000000000 +0200
+++ linux/drivers/net/wireless/ath/ath9k/ath9k.h 2015-06-03 18:37:29.000000000 +0200
@@ -1098,4 +1098,10 @@
static inline void ath_ahb_exit(void) {};
#endif
+/*
+ * OpenWrt UBNT HSR filter support
+ */
+typedef void (set_channel_helper_fn)(struct ath_hw* ah, int bw, int fq);
+void ath9k_register_set_channel_helper(set_channel_helper_fn *);
+
#endif /* ATH9K_H */
diff -Nur linux/drivers/net/wireless/ath/ath9k.orig/channel.c linux/drivers/net/wireless/ath/ath9k/channel.c
--- linux/drivers/net/wireless/ath/ath9k.orig/channel.c 2015-04-04 04:46:37.000000000 +0200
+++ linux/drivers/net/wireless/ath/ath9k/channel.c 2015-06-03 18:42:28.000000000 +0200
@@ -16,6 +16,18 @@
#include "ath9k.h"
+/*
+ * OpenWrt UBNT HSR filter support
+ */
+static set_channel_helper_fn *ath9k_set_channel_helper;
+
+void ath9k_register_set_channel_helper(set_channel_helper_fn *chanfn)
+{
+ ath9k_set_channel_helper = chanfn;
+}
+EXPORT_SYMBOL(ath9k_register_set_channel_helper);
+
+
/* 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.
@@ -41,6 +53,9 @@
ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
chan->center_freq, chandef->width);
+ if (ath9k_set_channel_helper)
+ ath9k_set_channel_helper(ah, chandef->width, chan->center_freq);
+
/* update survey stats for the old channel before switching */
spin_lock_bh(&common->cc_lock);
ath_update_survey_stats(sc);
diff -Nur linux/drivers/net/wireless/ath/ath9k.orig/hsr.c linux/drivers/net/wireless/ath/ath9k/hsr.c
--- linux/drivers/net/wireless/ath/ath9k.orig/hsr.c 1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wireless/ath/ath9k/hsr.c 2015-06-03 18:45:04.000000000 +0200
@@ -0,0 +1,283 @@
+/*
+ *
+ * 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 "hw-ops.h"
+#include "ar9003_mac.h"
+#include "ar9003_mci.h"
+#include "ar9003_phy.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
+
+static void hsr_init(struct ath_hw* ah);
+static int hsr_disable(struct ath_hw* ah);
+static int hsr_enable(struct ath_hw* ah, int bw, int fq);
+static int hsr_status(struct ath_hw* ah);
+
+static void hsr_init(struct ath_hw* ah) {
+
+ if ( NULL == ah) {
+ return;
+ }
+
+ 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);
+
+ printk(KERN_NOTICE "hsr_init: done");
+
+}
+
+static u32 hsr_write_byte(struct ath_hw* ah, int delay, u32 value){
+ 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);
+
+ /* printk(KERN_NOTICE "hsr_write_byte: write byte %d return value is %x %d %c \n", value, rval, rval, rval > 32 ? rval : '-'); */
+
+ return rval & 0xff;
+}
+
+static int hsr_write_a_chain(struct ath_hw* ah, char* chain, int items) {
+ int i = 0, j;
+ int status = 0;
+ int loops = 0;
+ // a preabmle
+ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+
+ status = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+
+ if ( status) {
+ int loop = 2;
+ ++ loops;
+ if ( loops > 42) {
+ printk(KERN_NOTICE "hsr_write_a_chain: too many loops in preamble. giving up.\n");
+ return 0;
+ }
+ do {
+ status = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+ loop = (loop + 1) & 0xffff;
+ if ( loop < 2) {
+ continue;
+ }
+ } while(status);
+ }
+
+ 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);
+
+ memset(chain, 0, items);
+
+ for ( j = 0, i = 0; (i < 7) && (j < (items - 1)) ; ++i) {
+ u32 ret;
+ if ( 31 < (ret = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0))) {
+ chain[j] = (char)ret;
+ ++ j;
+ }
+ udelay(HSR_DELAY_TRAILING);
+ }
+ /* printk(KERN_NOTICE "hsr_write_a_chain: j %d \n", j); */
+ return j > 1 ? simple_strtol(chain + 1, NULL, 10) : 0;
+}
+
+static int hsr_disable(struct ath_hw* ah) {
+ char cmd[10] = {'b', '4', '0', 0, 0, 0, 0, 0, 0, 0};
+ int ret;
+
+ if ( NULL == ah) {
+ return 0;
+ }
+
+ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
+ /* printk(KERN_NOTICE "hsr_disable: return %d \n", ret); */
+ if ( (ret > 0) && (*cmd == 'B')) {
+ printk(KERN_NOTICE "hsr_disable: bandwidth set %d \n", ret);
+ }
+
+ return 0;
+}
+
+static int hsr_enable(struct ath_hw* ah, int bw, int fq) {
+ char cmd[10];
+ int ret;
+
+ if ( NULL == ah) {
+ return 0;
+ }
+
+
+ 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)) {
+ printk(KERN_NOTICE "hsr_enable: failed changing bandwidth -> set (%d,%d) reply (%d, %d) \n", 'b', bw, *cmd, ret);
+ return 0;
+ }
+
+ memset(cmd, 0, sizeof(cmd));
+ *cmd = 'x'; // 120
+ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
+ if ( *cmd != 'X') {
+ printk(KERN_NOTICE "hsr_enable: failed 'x' command -> reply (%d, %d) \n", *cmd, ret);
+ return 0;
+ }
+
+ memset(cmd, 0, sizeof(cmd));
+ *cmd = 'm'; // 109
+ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
+ if ( *cmd != 'M') {
+ printk(KERN_NOTICE "hsr_enable: failed 'm' command -> reply (%d, %d) \n", *cmd, ret);
+ return 0;
+ }
+
+ 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)) {
+ printk(KERN_NOTICE "hsr_enable: failed set frequency -> reply (%d, %d) \n", *cmd, ret);
+ return 0;
+ }
+
+ printk(KERN_NOTICE "hsr_enable: center frequency %dMHz bandwidth %dMHz \n", fq, bw);
+
+ return 0;
+}
+
+static int hsr_status(struct ath_hw* ah) {
+ char cmd[10] = {'s', 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 115
+ int ret;
+ if ( NULL == ah) {
+ return 0;
+ }
+
+ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
+ if ( (*cmd != 'S')) {
+ printk(KERN_NOTICE "hsr_status: returned %d,%d \n", *cmd, ret);
+ return -1;
+ }
+
+ printk(KERN_NOTICE "hsr_status: current status is %d \n", ret);
+
+ return 0;
+}
+
+static void hsr_tune(struct ath_hw* ah, int bw, int fq) {
+ static int initialized;
+
+ if (!initialized) {
+ initialized = 1;
+ hsr_init(ah);
+ }
+ hsr_enable(ah, bw, fq);
+ hsr_status(ah);
+}
+
+
+
+static int __init hsr_mod_init(void)
+{
+ rtnl_lock(); /* Should lock against nl80211_set_channel() */
+ ath9k_register_set_channel_helper(hsr_tune);
+ rtnl_unlock();
+}
+
+static void __exit hsr_mod_exit(void)
+{
+ rtnl_lock();
+ ath9k_register_set_channel_helper(NULL);
+ rtnl_unlock();
+}
+
+module_init(hsr_mod_init);
+module_exit(hsr_mod_exit);
+
+
+MODULE_AUTHOR("Kirill Berezin, Stefan Rompf");
+MODULE_DESCRIPTION("Support for Ubiquiti Outdoor Plus HSR filter.");
+MODULE_SUPPORTED_DEVICE("Ubiquiti Outdoor Plus");
+MODULE_LICENSE("GPL");
+
diff -Nur linux/drivers/net/wireless/ath/ath9k.orig/Makefile linux/drivers/net/wireless/ath/ath9k/Makefile
--- linux/drivers/net/wireless/ath/ath9k.orig/Makefile 2015-04-04 04:46:37.000000000 +0200
+++ linux/drivers/net/wireless/ath/ath9k/Makefile 2015-06-03 18:37:29.000000000 +0200
@@ -23,6 +23,10 @@
obj-$(CPTCFG_ATH9K) += ath9k.o
+ath9k_hsr-y := hsr.o
+
+obj-$(CPTCFG_ATH9K) += ath9k_hsr.o
+
ath9k_hw-y:= \
ar9002_hw.o \
ar9003_hw.o \
_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel