[OpenWrt-Devel] [PATCH v3 2/2] Add kmod-ubnt-hsr tuner for the UBNT HSR filter

Stefan Rompf stefan at loplof.de
Fri Jun 12 15:38:53 EDT 2015


Add the tuner for the Ubiquiti Outdoor Plus HSR filter as a self contained
kmod package. When loaded, it registers with ath9k as a channel set helper and
tunes the HSR on every channel change.

Signed-off-by: Stefan Rompf <stefan at loplof.de>
---
Index: package/kernel/ubnt-hsr/Makefile
===================================================================
--- package/kernel/ubnt-hsr/Makefile	(Revision 0)
+++ package/kernel/ubnt-hsr/Makefile	(Arbeitskopie)
@@ -0,0 +1,56 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=ubnt_hsr
+PKG_VERSION:=0.1
+PKG_RELEASE:=1
+PKG_BUILD_DEPENDS:=mac80211
+
+include $(INCLUDE_DIR)/package.mk
+
+define KernelPackage/ubnt-hsr
+  $(call KernelPackage/mac80211/Default)
+  SUBMENU:=Wireless Drivers
+  TITLE:=Driver for Ubiquiti UniFi Outdoor Plus HSR filter
+  URL:=http://wiki.openwrt.org/toh/ubiquiti/unifi_outdoorplus
+  DEPENDS:=@PCI_SUPPORT||TARGET_ar71xx kmod-ath9k
+  FILES:= $(PKG_BUILD_DIR)/ubnt_hsr.ko
+  AUTOLOAD:=$(call AutoProbe,ubnt_hsr)
+endef
+
+define KernelPackage/ubnt-hsr/description
+ubnt-hsr adds support for the 'High-Selectivity Receiver'
+RF filter built into the receive path of the Ubiquiti
+UniFi Outdoor Plus access point. It is required for this and
+only for this access point.
+endef
+
+include $(INCLUDE_DIR)/kernel-defaults.mk
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+	cp src/Makefile src/ubnt_hsr.c $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+	$(MAKE) $(KERNEL_MAKEOPTS) SUBDIRS="$(PKG_BUILD_DIR)" \
+		LINUXINCLUDE="-I$(STAGING_DIR)/usr/include/mac80211-backport/uapi -I$(STAGING_DIR)/usr/include/mac80211-backport \
+		-I$(STAGING_DIR)/usr/include/mac80211/uapi -I$(STAGING_DIR)/usr/include/mac80211 \
+		-I$(LINUX_DIR)/include -I$(LINUX_DIR)/include/$(LINUX_UAPI_DIR) \
+		-I$(LINUX_DIR)/include/generated/uapi/ -Iarch/$(LINUX_KARCH)/include \
+		-Iarch/$(LINUX_KARCH)/include/$(LINUX_UAPI_DIR) \
+		-Iarch/$(LINUX_KARCH)/include/generated \
+		-Iarch/$(LINUX_KARCH)/include/generated/$(LINUX_UAPI_DIR) \
+		-include generated/autoconf.h \
+		-include backport/backport.h " \
+		V="${V}" modules
+endef
+
+$(eval $(call KernelPackage,ubnt-hsr))
Index: package/kernel/ubnt-hsr/src/Makefile
===================================================================
--- package/kernel/ubnt-hsr/src/Makefile	(Revision 0)
+++ package/kernel/ubnt-hsr/src/Makefile	(Arbeitskopie)
@@ -0,0 +1 @@
+obj-m   := ubnt_hsr.o
Index: package/kernel/ubnt-hsr/src/ubnt_hsr.c
===================================================================
--- package/kernel/ubnt-hsr/src/ubnt_hsr.c	(Revision 0)
+++ package/kernel/ubnt-hsr/src/ubnt_hsr.c	(Arbeitskopie)
@@ -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 <ath/ath9k/hw.h>
+#include <ath/ath9k/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) {
+	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;
+	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) {
+			printk(KERN_WARNING "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;
+}
+
+static 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));
+	/* 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;
+	}
+
+	return -1;
+}
+
+static int hsr_enable(struct ath_hw* ah, int bw, int fq) {
+	char cmd[10];
+	int ret;
+
+	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 -1;
+	}
+
+	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 -1;
+	}
+
+	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  -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)) {
+		printk(KERN_NOTICE "hsr_enable: failed set frequency -> reply (%d, %d) \n", *cmd, ret);
+		return -1;
+	}
+
+	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;
+
+	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;
+	static int last_bw, last_fq;
+
+	if (NULL == ah) {
+		return;
+	}
+
+	/* Bandwidth argument is 0 sometimes. Assume default 802.11bgn
+	   20MHz on invalid values */
+	if ( (bw != 5) && (bw != 10) && (bw != 20) && (bw != 40)) {
+		bw = 20;
+	}
+
+	if (bw == last_bw && fq == last_fq) {
+		/* Avoid tuning if nothing changes */
+		printk(KERN_NOTICE "hsr_tune: already tuned to center frequency %dMHz bandwidth %dMHz\n", fq, bw);
+		return;
+	}
+
+	if (!initialized) {
+		initialized = 1;
+		hsr_init(ah);
+	}
+
+	if (!hsr_enable(ah, bw, fq)) {
+		hsr_status(ah);
+		last_bw = bw;
+		last_fq = fq;
+	} else {
+		/* Tuning failed - make sure that we try again */
+		last_bw = -1;
+	}
+}
+
+
+static int __init hsr_mod_init(void)
+{
+	rtnl_lock(); /* Should lock against nl80211_set_channel() */
+	ath9k_register_set_channel_helper(hsr_tune);
+	rtnl_unlock();
+	return 0;
+}
+
+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("Dual MIT/GPL");
+
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list