From 354da25bbdf36b51006e6c71c4c769bc64f94f05 Mon Sep 17 00:00:00 2001 From: Bjoern Franke Date: Sat, 16 Mar 2013 14:27:25 +0100 Subject: [PATCH] backport mac80211/ath9k-patches from owrt trunk r36007 (now really ;) ) --- .../mac80211/004-use_env_for_bash.patch | 8 + .../mac80211/040-linux_3_9_compat.patch | 153 +++++++ .../mac80211/130-mesh_pathtbl_backport.patch | 212 ++++++++++ .../mac80211/150-disable_addr_notifier.patch | 67 +++ ...h9k-fix-invalid-mac-address-handling.patch | 29 ++ .../411-ath5k_allow_adhoc_and_ap.patch | 46 ++ ...add_platform_eeprom_support_to_ath5k.patch | 56 +++ .../440-ath5k_channel_bw_debugfs.patch | 113 +++++ .../513-mac80211_reduce_txqueuelen.patch | 10 + .../522-ath9k_per_chain_signal_strength.patch | 393 ++++++++++++++++++ .../523-mac80211_configure_antenna_gain.patch | 162 ++++++++ .../mac80211/530-ath9k_extra_leds.patch | 248 +++++++++++ .../552-ath9k_rx_dma_stop_check.patch | 28 ++ 13 files changed, 1525 insertions(+) create mode 100644 build_patches/mac80211/004-use_env_for_bash.patch create mode 100644 build_patches/mac80211/040-linux_3_9_compat.patch create mode 100644 build_patches/mac80211/130-mesh_pathtbl_backport.patch create mode 100644 build_patches/mac80211/150-disable_addr_notifier.patch create mode 100644 build_patches/mac80211/402-ath9k-fix-invalid-mac-address-handling.patch create mode 100644 build_patches/mac80211/411-ath5k_allow_adhoc_and_ap.patch create mode 100644 build_patches/mac80211/431-add_platform_eeprom_support_to_ath5k.patch create mode 100644 build_patches/mac80211/440-ath5k_channel_bw_debugfs.patch create mode 100644 build_patches/mac80211/513-mac80211_reduce_txqueuelen.patch create mode 100644 build_patches/mac80211/522-ath9k_per_chain_signal_strength.patch create mode 100644 build_patches/mac80211/523-mac80211_configure_antenna_gain.patch create mode 100644 build_patches/mac80211/530-ath9k_extra_leds.patch create mode 100644 build_patches/mac80211/552-ath9k_rx_dma_stop_check.patch diff --git a/build_patches/mac80211/004-use_env_for_bash.patch b/build_patches/mac80211/004-use_env_for_bash.patch new file mode 100644 index 0000000..0ae3820 --- /dev/null +++ b/build_patches/mac80211/004-use_env_for_bash.patch @@ -0,0 +1,8 @@ +--- a/compat/scripts/gen-compat-config.sh ++++ b/compat/scripts/gen-compat-config.sh +@@ -1,4 +1,4 @@ +-#!/bin/bash ++#!/usr/bin/env bash + # Copyright 2012 Luis R. Rodriguez + # Copyright 2012 Hauke Mehrtens + # diff --git a/build_patches/mac80211/040-linux_3_9_compat.patch b/build_patches/mac80211/040-linux_3_9_compat.patch new file mode 100644 index 0000000..d83c39c --- /dev/null +++ b/build_patches/mac80211/040-linux_3_9_compat.patch @@ -0,0 +1,153 @@ +--- a/include/linux/compat-2.6.h ++++ b/include/linux/compat-2.6.h +@@ -69,6 +69,7 @@ void compat_dependency_symbol(void); + #include + #include + #include ++#include + + #endif /* __ASSEMBLY__ */ + +--- /dev/null ++++ b/include/linux/compat-3.9.h +@@ -0,0 +1,140 @@ ++#ifndef LINUX_3_9_COMPAT_H ++#define LINUX_3_9_COMPAT_H ++ ++#include ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)) ++ ++#include ++#include ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)) ++#include ++#endif ++#include ++#include ++#include ++ ++/* include this before changing hlist_for_each_* to use the old versions. */ ++#include ++ ++ ++/** ++ * backport of idr idr_alloc() usage ++ * ++ * This backports a patch series send by Tejun Heo: ++ * https://lkml.org/lkml/2013/2/2/159 ++ */ ++static inline void compat_idr_destroy(struct idr *idp) ++{ ++ idr_remove_all(idp); ++ idr_destroy(idp); ++} ++#define idr_destroy(idp) compat_idr_destroy(idp) ++ ++static inline int idr_alloc(struct idr *idr, void *ptr, int start, int end, ++ gfp_t gfp_mask) ++{ ++ int id, ret; ++ ++ do { ++ if (!idr_pre_get(idr, gfp_mask)) ++ return -ENOMEM; ++ ret = idr_get_new_above(idr, ptr, start, &id); ++ if (!ret && id > end) { ++ idr_remove(idr, id); ++ ret = -ENOSPC; ++ } ++ } while (ret == -EAGAIN); ++ ++ return ret ? ret : id; ++} ++ ++static inline void idr_preload(gfp_t gfp_mask) ++{ ++} ++ ++static inline void idr_preload_end(void) ++{ ++} ++ ++ ++/** ++ * backport: ++ * ++ * commit 0bbacca7c3911451cea923b0ad6389d58e3d9ce9 ++ * Author: Sasha Levin ++ * Date: Thu Feb 7 12:32:18 2013 +1100 ++ * ++ * hlist: drop the node parameter from iterators ++ */ ++ ++#define hlist_entry_safe(ptr, type, member) \ ++ (ptr) ? hlist_entry(ptr, type, member) : NULL ++ ++#undef hlist_for_each_entry ++/** ++ * hlist_for_each_entry - iterate over list of given type ++ * @pos: the type * to use as a loop cursor. ++ * @head: the head for your list. ++ * @member: the name of the hlist_node within the struct. ++ */ ++#define hlist_for_each_entry(pos, head, member) \ ++ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member); \ ++ pos; \ ++ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) ++ ++#undef hlist_for_each_entry_safe ++/** ++ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry ++ * @pos: the type * to use as a loop cursor. ++ * @n: another &struct hlist_node to use as temporary storage ++ * @head: the head for your list. ++ * @member: the name of the hlist_node within the struct. ++ */ ++#define hlist_for_each_entry_safe(pos, n, head, member) \ ++ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member); \ ++ pos && ({ n = pos->member.next; 1; }); \ ++ pos = hlist_entry_safe(n, typeof(*pos), member)) ++ ++#undef hlist_for_each_entry_rcu ++/** ++ * hlist_for_each_entry_rcu - iterate over rcu list of given type ++ * @pos: the type * to use as a loop cursor. ++ * @head: the head for your list. ++ * @member: the name of the hlist_node within the struct. ++ * ++ * This list-traversal primitive may safely run concurrently with ++ * the _rcu list-mutation primitives such as hlist_add_head_rcu() ++ * as long as the traversal is guarded by rcu_read_lock(). ++ */ ++#define hlist_for_each_entry_rcu(pos, head, member) \ ++ for (pos = hlist_entry_safe (rcu_dereference_raw(hlist_first_rcu(head)),\ ++ typeof(*(pos)), member); \ ++ pos; \ ++ pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu( \ ++ &(pos)->member)), typeof(*(pos)), member)) ++ ++#undef sk_for_each ++#define sk_for_each(__sk, list) \ ++ hlist_for_each_entry(__sk, list, sk_node) ++ ++#define tty_flip_buffer_push(port) tty_flip_buffer_push((port)->tty) ++#define tty_insert_flip_string(port, chars, size) tty_insert_flip_string((port)->tty, chars, size) ++ ++/** ++ * backport of: ++ * ++ * commit 496ad9aa8ef448058e36ca7a787c61f2e63f0f54 ++ * Author: Al Viro ++ * Date: Wed Jan 23 17:07:38 2013 -0500 ++ * ++ * new helper: file_inode(file) ++ */ ++static inline struct inode *file_inode(struct file *f) ++{ ++ return f->f_path.dentry->d_inode; ++} ++ ++#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)) */ ++ ++#endif /* LINUX_3_9_COMPAT_H */ diff --git a/build_patches/mac80211/130-mesh_pathtbl_backport.patch b/build_patches/mac80211/130-mesh_pathtbl_backport.patch new file mode 100644 index 0000000..1322099 --- /dev/null +++ b/build_patches/mac80211/130-mesh_pathtbl_backport.patch @@ -0,0 +1,212 @@ +--- a/net/mac80211/mesh_pathtbl.c ++++ b/net/mac80211/mesh_pathtbl.c +@@ -72,9 +72,9 @@ static inline struct mesh_table *resize_ + * it's used twice. So it is illegal to do + * for_each_mesh_entry(rcu_dereference(...), ...) + */ +-#define for_each_mesh_entry(tbl, p, node, i) \ ++#define for_each_mesh_entry(tbl, node, i) \ + for (i = 0; i <= tbl->hash_mask; i++) \ +- hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list) ++ hlist_for_each_entry_rcu(node, &tbl->hash_buckets[i], list) + + + static struct mesh_table *mesh_table_alloc(int size_order) +@@ -139,7 +139,7 @@ static void mesh_table_free(struct mesh_ + } + if (free_leafs) { + spin_lock_bh(&tbl->gates_lock); +- hlist_for_each_entry_safe(gate, p, q, ++ hlist_for_each_entry_safe(gate, q, + tbl->known_gates, list) { + hlist_del(&gate->list); + kfree(gate); +@@ -333,12 +333,11 @@ static struct mesh_path *mpath_lookup(st + struct ieee80211_sub_if_data *sdata) + { + struct mesh_path *mpath; +- struct hlist_node *n; + struct hlist_head *bucket; + struct mpath_node *node; + + bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; +- hlist_for_each_entry_rcu(node, n, bucket, list) { ++ hlist_for_each_entry_rcu(node, bucket, list) { + mpath = node->mpath; + if (mpath->sdata == sdata && + ether_addr_equal(dst, mpath->dst)) { +@@ -389,11 +388,10 @@ mesh_path_lookup_by_idx(struct ieee80211 + { + struct mesh_table *tbl = rcu_dereference(mesh_paths); + struct mpath_node *node; +- struct hlist_node *p; + int i; + int j = 0; + +- for_each_mesh_entry(tbl, p, node, i) { ++ for_each_mesh_entry(tbl, node, i) { + if (sdata && node->mpath->sdata != sdata) + continue; + if (j++ == idx) { +@@ -417,13 +415,12 @@ int mesh_path_add_gate(struct mesh_path + { + struct mesh_table *tbl; + struct mpath_node *gate, *new_gate; +- struct hlist_node *n; + int err; + + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + +- hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list) ++ hlist_for_each_entry_rcu(gate, tbl->known_gates, list) + if (gate->mpath == mpath) { + err = -EEXIST; + goto err_rcu; +@@ -460,9 +457,9 @@ err_rcu: + static void mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) + { + struct mpath_node *gate; +- struct hlist_node *p, *q; ++ struct hlist_node *q; + +- hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) { ++ hlist_for_each_entry_safe(gate, q, tbl->known_gates, list) { + if (gate->mpath != mpath) + continue; + spin_lock_bh(&tbl->gates_lock); +@@ -504,7 +501,6 @@ int mesh_path_add(struct ieee80211_sub_i + struct mesh_path *mpath, *new_mpath; + struct mpath_node *node, *new_node; + struct hlist_head *bucket; +- struct hlist_node *n; + int grow = 0; + int err = 0; + u32 hash_idx; +@@ -550,7 +546,7 @@ int mesh_path_add(struct ieee80211_sub_i + spin_lock(&tbl->hashwlock[hash_idx]); + + err = -EEXIST; +- hlist_for_each_entry(node, n, bucket, list) { ++ hlist_for_each_entry(node, bucket, list) { + mpath = node->mpath; + if (mpath->sdata == sdata && + ether_addr_equal(dst, mpath->dst)) +@@ -640,7 +636,6 @@ int mpp_path_add(struct ieee80211_sub_if + struct mesh_path *mpath, *new_mpath; + struct mpath_node *node, *new_node; + struct hlist_head *bucket; +- struct hlist_node *n; + int grow = 0; + int err = 0; + u32 hash_idx; +@@ -680,7 +675,7 @@ int mpp_path_add(struct ieee80211_sub_if + spin_lock(&tbl->hashwlock[hash_idx]); + + err = -EEXIST; +- hlist_for_each_entry(node, n, bucket, list) { ++ hlist_for_each_entry(node, bucket, list) { + mpath = node->mpath; + if (mpath->sdata == sdata && + ether_addr_equal(dst, mpath->dst)) +@@ -725,14 +720,13 @@ void mesh_plink_broken(struct sta_info * + static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct mesh_path *mpath; + struct mpath_node *node; +- struct hlist_node *p; + struct ieee80211_sub_if_data *sdata = sta->sdata; + int i; + __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE); + + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); +- for_each_mesh_entry(tbl, p, node, i) { ++ for_each_mesh_entry(tbl, node, i) { + mpath = node->mpath; + if (rcu_dereference(mpath->next_hop) == sta && + mpath->flags & MESH_PATH_ACTIVE && +@@ -792,13 +786,12 @@ void mesh_path_flush_by_nexthop(struct s + struct mesh_table *tbl; + struct mesh_path *mpath; + struct mpath_node *node; +- struct hlist_node *p; + int i; + + rcu_read_lock(); + read_lock_bh(&pathtbl_resize_lock); + tbl = resize_dereference_mesh_paths(); +- for_each_mesh_entry(tbl, p, node, i) { ++ for_each_mesh_entry(tbl, node, i) { + mpath = node->mpath; + if (rcu_dereference(mpath->next_hop) == sta) { + spin_lock(&tbl->hashwlock[i]); +@@ -815,11 +808,9 @@ static void table_flush_by_iface(struct + { + struct mesh_path *mpath; + struct mpath_node *node; +- struct hlist_node *p; + int i; + +- WARN_ON(!rcu_read_lock_held()); +- for_each_mesh_entry(tbl, p, node, i) { ++ for_each_mesh_entry(tbl, node, i) { + mpath = node->mpath; + if (mpath->sdata != sdata) + continue; +@@ -865,7 +856,6 @@ int mesh_path_del(struct ieee80211_sub_i + struct mesh_path *mpath; + struct mpath_node *node; + struct hlist_head *bucket; +- struct hlist_node *n; + int hash_idx; + int err = 0; + +@@ -875,7 +865,7 @@ int mesh_path_del(struct ieee80211_sub_i + bucket = &tbl->hash_buckets[hash_idx]; + + spin_lock(&tbl->hashwlock[hash_idx]); +- hlist_for_each_entry(node, n, bucket, list) { ++ hlist_for_each_entry(node, bucket, list) { + mpath = node->mpath; + if (mpath->sdata == sdata && + ether_addr_equal(addr, mpath->dst)) { +@@ -920,7 +910,6 @@ void mesh_path_tx_pending(struct mesh_pa + int mesh_path_send_to_gates(struct mesh_path *mpath) + { + struct ieee80211_sub_if_data *sdata = mpath->sdata; +- struct hlist_node *n; + struct mesh_table *tbl; + struct mesh_path *from_mpath = mpath; + struct mpath_node *gate = NULL; +@@ -935,7 +924,7 @@ int mesh_path_send_to_gates(struct mesh_ + if (!known_gates) + return -EHOSTUNREACH; + +- hlist_for_each_entry_rcu(gate, n, known_gates, list) { ++ hlist_for_each_entry_rcu(gate, known_gates, list) { + if (gate->mpath->sdata != sdata) + continue; + +@@ -951,7 +940,7 @@ int mesh_path_send_to_gates(struct mesh_ + } + } + +- hlist_for_each_entry_rcu(gate, n, known_gates, list) ++ hlist_for_each_entry_rcu(gate, known_gates, list) + if (gate->mpath->sdata == sdata) { + mpath_dbg(sdata, "Sending to %pM\n", gate->mpath->dst); + mesh_path_tx_pending(gate->mpath); +@@ -1096,12 +1085,11 @@ void mesh_path_expire(struct ieee80211_s + struct mesh_table *tbl; + struct mesh_path *mpath; + struct mpath_node *node; +- struct hlist_node *p; + int i; + + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); +- for_each_mesh_entry(tbl, p, node, i) { ++ for_each_mesh_entry(tbl, node, i) { + if (node->mpath->sdata != sdata) + continue; + mpath = node->mpath; diff --git a/build_patches/mac80211/150-disable_addr_notifier.patch b/build_patches/mac80211/150-disable_addr_notifier.patch new file mode 100644 index 0000000..9714696 --- /dev/null +++ b/build_patches/mac80211/150-disable_addr_notifier.patch @@ -0,0 +1,67 @@ +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -313,7 +313,7 @@ void ieee80211_restart_hw(struct ieee802 + } + EXPORT_SYMBOL(ieee80211_restart_hw); + +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + static int ieee80211_ifa_changed(struct notifier_block *nb, + unsigned long data, void *arg) + { +@@ -372,7 +372,7 @@ static int ieee80211_ifa_changed(struct + } + #endif + +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + static int ieee80211_ifa6_changed(struct notifier_block *nb, + unsigned long data, void *arg) + { +@@ -1015,14 +1015,14 @@ int ieee80211_register_hw(struct ieee802 + goto fail_pm_qos; + } + +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + local->ifa_notifier.notifier_call = ieee80211_ifa_changed; + result = register_inetaddr_notifier(&local->ifa_notifier); + if (result) + goto fail_ifa; + #endif + +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; + result = register_inet6addr_notifier(&local->ifa6_notifier); + if (result) +@@ -1034,13 +1034,13 @@ int ieee80211_register_hw(struct ieee802 + + return 0; + +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + fail_ifa6: +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + unregister_inetaddr_notifier(&local->ifa_notifier); + #endif + #endif +-#if defined(CONFIG_INET) || defined(CONFIG_IPV6) ++#if defined(__disabled__CONFIG_INET) || defined(__disabled__CONFIG_IPV6) + fail_ifa: + pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, + &local->network_latency_notifier); +@@ -1073,10 +1073,10 @@ void ieee80211_unregister_hw(struct ieee + + pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, + &local->network_latency_notifier); +-#ifdef CONFIG_INET ++#ifdef __disabled__CONFIG_INET + unregister_inetaddr_notifier(&local->ifa_notifier); + #endif +-#if IS_ENABLED(CONFIG_IPV6) ++#if IS_ENABLED(__disabled__CONFIG_IPV6) + unregister_inet6addr_notifier(&local->ifa6_notifier); + #endif + diff --git a/build_patches/mac80211/402-ath9k-fix-invalid-mac-address-handling.patch b/build_patches/mac80211/402-ath9k-fix-invalid-mac-address-handling.patch new file mode 100644 index 0000000..3dbdba0 --- /dev/null +++ b/build_patches/mac80211/402-ath9k-fix-invalid-mac-address-handling.patch @@ -0,0 +1,29 @@ +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + + #include "hw.h" +@@ -519,8 +520,16 @@ static int ath9k_hw_init_macaddr(struct + common->macaddr[2 * i] = eeval >> 8; + common->macaddr[2 * i + 1] = eeval & 0xff; + } +- if (sum == 0 || sum == 0xffff * 3) +- return -EADDRNOTAVAIL; ++ if (!is_valid_ether_addr(common->macaddr)) { ++ ath_err(common, ++ "eeprom contains invalid mac address: %pM\n", ++ common->macaddr); ++ ++ random_ether_addr(common->macaddr); ++ ath_err(common, ++ "random mac address will be used: %pM\n", ++ common->macaddr); ++ } + + return 0; + } diff --git a/build_patches/mac80211/411-ath5k_allow_adhoc_and_ap.patch b/build_patches/mac80211/411-ath5k_allow_adhoc_and_ap.patch new file mode 100644 index 0000000..3d885bb --- /dev/null +++ b/build_patches/mac80211/411-ath5k_allow_adhoc_and_ap.patch @@ -0,0 +1,46 @@ +--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c ++++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c +@@ -89,13 +89,8 @@ ath5k_add_interface(struct ieee80211_hw + goto end; + } + +- /* Don't allow other interfaces if one ad-hoc is configured. +- * TODO: Fix the problems with ad-hoc and multiple other interfaces. +- * We would need to operate the HW in ad-hoc mode to allow TSF updates +- * for the IBSS, but this breaks with additional AP or STA interfaces +- * at the moment. */ +- if (ah->num_adhoc_vifs || +- (ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { ++ /* Don't allow more than one ad-hoc interface */ ++ if (ah->num_adhoc_vifs && vif->type == NL80211_IFTYPE_ADHOC) { + ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n"); + ret = -ELNRNG; + goto end; +--- a/drivers/net/wireless/ath/ath5k/base.c ++++ b/drivers/net/wireless/ath/ath5k/base.c +@@ -1868,7 +1868,7 @@ ath5k_beacon_send(struct ath5k_hw *ah) + } + + if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs + +- ah->num_mesh_vifs > 1) || ++ ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) || + ah->opmode == NL80211_IFTYPE_MESH_POINT) { + u64 tsf = ath5k_hw_get_tsf64(ah); + u32 tsftu = TSF_TO_TU(tsf); +@@ -1954,7 +1954,7 @@ ath5k_beacon_update_timers(struct ath5k_ + + intval = ah->bintval & AR5K_BEACON_PERIOD; + if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs +- + ah->num_mesh_vifs > 1) { ++ + ah->num_adhoc_vifs + ah->num_mesh_vifs > 1) { + intval /= ATH_BCBUF; /* staggered multi-bss beacons */ + if (intval < 15) + ATH5K_WARN(ah, "intval %u is too low, min 15\n", +@@ -2418,6 +2418,7 @@ static const struct ieee80211_iface_limi + BIT(NL80211_IFTYPE_MESH_POINT) | + #endif + BIT(NL80211_IFTYPE_AP) }, ++ { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, + }; + + static const struct ieee80211_iface_combination if_comb = { diff --git a/build_patches/mac80211/431-add_platform_eeprom_support_to_ath5k.patch b/build_patches/mac80211/431-add_platform_eeprom_support_to_ath5k.patch new file mode 100644 index 0000000..d4967b6 --- /dev/null +++ b/build_patches/mac80211/431-add_platform_eeprom_support_to_ath5k.patch @@ -0,0 +1,56 @@ +--- a/drivers/net/wireless/ath/ath5k/pci.c ++++ b/drivers/net/wireless/ath/ath5k/pci.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include "../ath.h" + #include "ath5k.h" + #include "debug.h" +@@ -74,7 +75,7 @@ static void ath5k_pci_read_cachesize(str + } + + /* +- * Read from eeprom ++ * Read from eeprom or platform_data + */ + static bool + ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data) +@@ -82,6 +83,19 @@ ath5k_pci_eeprom_read(struct ath_common + struct ath5k_hw *ah = (struct ath5k_hw *) common->ah; + u32 status, timeout; + ++ struct ath5k_platform_data *pdata = NULL; ++ ++ if (ah->pdev) ++ pdata = ah->pdev->dev.platform_data; ++ ++ if (pdata && pdata->eeprom_data && pdata->eeprom_data[61] == AR5K_EEPROM_MAGIC_VALUE) { ++ if (offset >= ATH5K_PLAT_EEP_MAX_WORDS) ++ return false; ++ ++ *data = pdata->eeprom_data[offset]; ++ return true; ++ } ++ + /* + * Initialize EEPROM access + */ +@@ -125,6 +139,16 @@ static int ath5k_pci_eeprom_read_mac(str + u16 data; + int octet; + ++ struct ath5k_platform_data *pdata = NULL; ++ ++ if (ah->pdev) ++ pdata = ah->pdev->dev.platform_data; ++ ++ if (pdata && pdata->macaddr) { ++ memcpy(mac, pdata->macaddr, ETH_ALEN); ++ return 0; ++ } ++ + AR5K_EEPROM_READ(0x20, data); + + for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { diff --git a/build_patches/mac80211/440-ath5k_channel_bw_debugfs.patch b/build_patches/mac80211/440-ath5k_channel_bw_debugfs.patch new file mode 100644 index 0000000..f9c2a09 --- /dev/null +++ b/build_patches/mac80211/440-ath5k_channel_bw_debugfs.patch @@ -0,0 +1,113 @@ +This adds a bwmode debugfs file which can be used to set alternate +channel operating bandwidths. Only tested with AR5413 and only at +5 and 20 mhz channels. + +Signed-off-by: Pat Erley +--- +Other devices will need to be added to the switch in write_file_bwmode + +drivers/net/wireless/ath/ath5k/debug.c | 86 ++++++++++++++++++++++++++++++++ + 1 files changed, 86 insertions(+), 0 deletions(-) + +--- a/drivers/net/wireless/ath/ath5k/debug.c ++++ b/drivers/net/wireless/ath/ath5k/debug.c +@@ -813,6 +813,89 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++/* debugfs: bwmode */ ++ ++static ssize_t read_file_bwmode(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath5k_hw *ah = file->private_data; ++ char buf[15]; ++ unsigned int len = 0; ++ ++ int cur_ah_bwmode = ah->ah_bwmode; ++ ++#define print_selected(MODE, LABEL) \ ++ if (cur_ah_bwmode == MODE) \ ++ len += snprintf(buf+len, sizeof(buf)-len, "[%s]", LABEL); \ ++ else \ ++ len += snprintf(buf+len, sizeof(buf)-len, "%s", LABEL); \ ++ len += snprintf(buf+len, sizeof(buf)-len, " "); ++ ++ print_selected(AR5K_BWMODE_5MHZ, "5"); ++ print_selected(AR5K_BWMODE_10MHZ, "10"); ++ print_selected(AR5K_BWMODE_DEFAULT, "20"); ++ print_selected(AR5K_BWMODE_40MHZ, "40"); ++#undef print_selected ++ ++ len += snprintf(buf+len, sizeof(buf)-len, "\n"); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, len); ++} ++ ++static ssize_t write_file_bwmode(struct file *file, ++ const char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath5k_hw *ah = file->private_data; ++ char buf[3]; ++ int bw = 20; ++ int tobwmode = AR5K_BWMODE_DEFAULT; ++ ++ if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) ++ return -EFAULT; ++ ++ /* TODO: Add check for active interface */ ++ ++ if(strncmp(buf, "5", 1) == 0 ) { ++ tobwmode = AR5K_BWMODE_5MHZ; ++ bw = 5; ++ } else if ( strncmp(buf, "10", 2) == 0 ) { ++ tobwmode = AR5K_BWMODE_10MHZ; ++ bw = 10; ++ } else if ( strncmp(buf, "20", 2) == 0 ) { ++ tobwmode = AR5K_BWMODE_DEFAULT; ++ bw = 20; ++ } else if ( strncmp(buf, "40", 2) == 0 ) { ++ tobwmode = AR5K_BWMODE_40MHZ; ++ bw = 40; ++ } else ++ return -EINVAL; ++ ++ ATH5K_INFO(ah, "Changing to %imhz channel width[%i]\n", ++ bw, tobwmode); ++ ++ switch (ah->ah_radio) { ++ /* TODO: only define radios that actually support 5/10mhz channels */ ++ case AR5K_RF5413: case AR5K_RF5110: case AR5K_RF5111: case AR5K_RF5112: case AR5K_RF2413: case AR5K_RF2316: case AR5K_RF2317: case AR5K_RF2425: ++ if(ah->ah_bwmode != tobwmode) { ++ mutex_lock(&ah->lock); ++ ah->ah_bwmode = tobwmode; ++ mutex_unlock(&ah->lock); ++ } ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ return count; ++} ++ ++static const struct file_operations fops_bwmode = { ++ .read = read_file_bwmode, ++ .write = write_file_bwmode, ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; + + /* debugfs: queues etc */ + +@@ -904,6 +987,9 @@ ath5k_debug_init_device(struct ath5k_hw + debugfs_create_file("beacon", S_IWUSR | S_IRUSR, phydir, ah, + &fops_beacon); + ++ debugfs_create_file("bwmode", S_IWUSR | S_IRUSR, phydir, ah, ++ &fops_bwmode); ++ + debugfs_create_file("reset", S_IWUSR, phydir, ah, &fops_reset); + + debugfs_create_file("antenna", S_IWUSR | S_IRUSR, phydir, ah, diff --git a/build_patches/mac80211/513-mac80211_reduce_txqueuelen.patch b/build_patches/mac80211/513-mac80211_reduce_txqueuelen.patch new file mode 100644 index 0000000..fe44cdb --- /dev/null +++ b/build_patches/mac80211/513-mac80211_reduce_txqueuelen.patch @@ -0,0 +1,10 @@ +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -995,6 +995,7 @@ static const struct net_device_ops ieee8 + static void ieee80211_if_setup(struct net_device *dev) + { + ether_setup(dev); ++ dev->tx_queue_len = 32; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + netdev_attach_ops(dev, &ieee80211_dataif_ops); + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)) diff --git a/build_patches/mac80211/522-ath9k_per_chain_signal_strength.patch b/build_patches/mac80211/522-ath9k_per_chain_signal_strength.patch new file mode 100644 index 0000000..c0feedd --- /dev/null +++ b/build_patches/mac80211/522-ath9k_per_chain_signal_strength.patch @@ -0,0 +1,393 @@ +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -839,6 +839,9 @@ enum mac80211_rx_flags { + * @signal: signal strength when receiving this frame, either in dBm, in dB or + * unspecified depending on the hardware capabilities flags + * @IEEE80211_HW_SIGNAL_* ++ * @chains: bitmask of receive chains for which separate signal strength ++ * values were filled. ++ * @chain_signal: per-chain signal strength, same format as @signal + * @antenna: antenna used + * @rate_idx: index of data rate into band's supported rates or MCS index if + * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) +@@ -870,6 +873,8 @@ struct ieee80211_rx_status { + u8 band; + u8 antenna; + s8 signal; ++ u8 chains; ++ s8 chain_signal[4]; + u8 ampdu_delimiter_crc; + u8 vendor_radiotap_align; + u8 vendor_radiotap_oui[3]; +--- a/net/mac80211/sta_info.h ++++ b/net/mac80211/sta_info.h +@@ -342,6 +342,11 @@ struct sta_info { + int last_signal; + struct ewma avg_signal; + int last_ack_signal; ++ ++ u8 chains; ++ s8 chain_signal_last[4]; ++ struct ewma chain_signal_avg[4]; ++ + /* Plus 1 for non-QoS frames */ + __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -1383,6 +1383,7 @@ ieee80211_rx_h_sta_process(struct ieee80 + struct sk_buff *skb = rx->skb; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; ++ int i; + + if (!sta) + return RX_CONTINUE; +@@ -1433,6 +1434,19 @@ ieee80211_rx_h_sta_process(struct ieee80 + ewma_add(&sta->avg_signal, -status->signal); + } + ++ if (status->chains) { ++ sta->chains = status->chains; ++ for (i = 0; i < 4; i++) { ++ int signal = status->chain_signal[i]; ++ ++ if (!(status->chains & BIT(i))) ++ continue; ++ ++ sta->chain_signal_last[i] = signal; ++ ewma_add(&sta->chain_signal_avg[i], -signal); ++ } ++ } ++ + /* + * Change STA power saving mode only at the end of a frame + * exchange sequence. +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -353,6 +353,8 @@ struct sta_info *sta_info_alloc(struct i + do_posix_clock_monotonic_gettime(&uptime); + sta->last_connected = uptime.tv_sec; + ewma_init(&sta->avg_signal, 1024, 8); ++ for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) ++ ewma_init(&sta->chain_signal_avg[i], 1024, 8); + + if (sta_prepare_rate_control(local, sta, gfp)) { + kfree(sta); +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -721,6 +721,8 @@ struct station_parameters { + * @STATION_INFO_LOCAL_PM: @local_pm filled + * @STATION_INFO_PEER_PM: @peer_pm filled + * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled ++ * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled ++ * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled + */ + enum station_info_flags { + STATION_INFO_INACTIVE_TIME = 1<<0, +@@ -749,6 +751,8 @@ enum station_info_flags { + STATION_INFO_NONPEER_PM = 1<<23, + STATION_INFO_RX_BYTES64 = 1<<24, + STATION_INFO_TX_BYTES64 = 1<<25, ++ STATION_INFO_CHAIN_SIGNAL = 1<<26, ++ STATION_INFO_CHAIN_SIGNAL_AVG = 1<<27, + }; + + /** +@@ -842,6 +846,9 @@ struct sta_bss_parameters { + * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. + * @signal_avg: Average signal strength, type depends on the wiphy's signal_type. + * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. ++ * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg ++ * @chain_signal: per-chain signal strength of last received packet in dBm ++ * @chain_signal_avg: per-chain signal strength average in dBm + * @txrate: current unicast bitrate from this station + * @rxrate: current unicast bitrate to this station + * @rx_packets: packets received from this station +@@ -877,6 +884,11 @@ struct station_info { + u8 plink_state; + s8 signal; + s8 signal_avg; ++ ++ u8 chains; ++ s8 chain_signal[4]; ++ s8 chain_signal_avg[4]; ++ + struct rate_info txrate; + struct rate_info rxrate; + u32 rx_packets; +--- a/drivers/net/wireless/ath/ath9k/mac.h ++++ b/drivers/net/wireless/ath/ath9k/mac.h +@@ -133,12 +133,8 @@ struct ath_rx_status { + u8 rs_rate; + u8 rs_antenna; + u8 rs_more; +- int8_t rs_rssi_ctl0; +- int8_t rs_rssi_ctl1; +- int8_t rs_rssi_ctl2; +- int8_t rs_rssi_ext0; +- int8_t rs_rssi_ext1; +- int8_t rs_rssi_ext2; ++ int8_t rs_rssi_ctl[3]; ++ int8_t rs_rssi_ext[3]; + u8 rs_isaggr; + u8 rs_moreaggr; + u8 rs_num_delims; +--- a/drivers/net/wireless/ath/ath9k/recv.c ++++ b/drivers/net/wireless/ath/ath9k/recv.c +@@ -937,6 +937,7 @@ static int ath9k_rx_skb_preprocess(struc + bool *decrypt_error) + { + struct ath_hw *ah = common->ah; ++ int i, j; + + /* + * everything but the rate is checked here, the rate check is done +@@ -962,6 +963,20 @@ static int ath9k_rx_skb_preprocess(struc + if (rx_stats->rs_moreaggr) + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + ++ for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) { ++ s8 rssi; ++ ++ if (!(ah->rxchainmask & BIT(i))) ++ continue; ++ ++ rssi = rx_stats->rs_rssi_ctl[i]; ++ if (rssi != ATH9K_RSSI_BAD) { ++ rx_status->chains |= BIT(j); ++ rx_status->chain_signal[j] = ah->noise + rssi; ++ } ++ j++; ++ } ++ + return 0; + } + +@@ -1070,7 +1085,7 @@ static int ath_process_fft(struct ath_so + fft_sample.tlv.length = __cpu_to_be16(length); + + fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq); +- fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); ++ fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); + fft_sample.noise = ah->noise; + + switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) { +--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c +@@ -475,12 +475,12 @@ int ath9k_hw_process_rxdesc_edma(struct + + /* XXX: Keycache */ + rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined); +- rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00); +- rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01); +- rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02); +- rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10); +- rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11); +- rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12); ++ rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_RxRSSIAnt00); ++ rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_RxRSSIAnt01); ++ rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_RxRSSIAnt02); ++ rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_RxRSSIAnt10); ++ rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_RxRSSIAnt11); ++ rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_RxRSSIAnt12); + + if (rxsp->status11 & AR_RxKeyIdxValid) + rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx); +--- a/drivers/net/wireless/ath/ath9k/mac.c ++++ b/drivers/net/wireless/ath/ath9k/mac.c +@@ -553,25 +553,25 @@ int ath9k_hw_rxprocdesc(struct ath_hw *a + + if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) { + rs->rs_rssi = ATH9K_RSSI_BAD; +- rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD; +- rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD; +- rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD; +- rs->rs_rssi_ext0 = ATH9K_RSSI_BAD; +- rs->rs_rssi_ext1 = ATH9K_RSSI_BAD; +- rs->rs_rssi_ext2 = ATH9K_RSSI_BAD; ++ rs->rs_rssi_ctl[0] = ATH9K_RSSI_BAD; ++ rs->rs_rssi_ctl[1] = ATH9K_RSSI_BAD; ++ rs->rs_rssi_ctl[2] = ATH9K_RSSI_BAD; ++ rs->rs_rssi_ext[0] = ATH9K_RSSI_BAD; ++ rs->rs_rssi_ext[1] = ATH9K_RSSI_BAD; ++ rs->rs_rssi_ext[2] = ATH9K_RSSI_BAD; + } else { + rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); +- rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0, ++ rs->rs_rssi_ctl[0] = MS(ads.ds_rxstatus0, + AR_RxRSSIAnt00); +- rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0, ++ rs->rs_rssi_ctl[1] = MS(ads.ds_rxstatus0, + AR_RxRSSIAnt01); +- rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0, ++ rs->rs_rssi_ctl[2] = MS(ads.ds_rxstatus0, + AR_RxRSSIAnt02); +- rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4, ++ rs->rs_rssi_ext[0] = MS(ads.ds_rxstatus4, + AR_RxRSSIAnt10); +- rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4, ++ rs->rs_rssi_ext[1] = MS(ads.ds_rxstatus4, + AR_RxRSSIAnt11); +- rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4, ++ rs->rs_rssi_ext[2] = MS(ads.ds_rxstatus4, + AR_RxRSSIAnt12); + } + if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) +--- a/drivers/net/wireless/ath/ath9k/debug.c ++++ b/drivers/net/wireless/ath/ath9k/debug.c +@@ -940,12 +940,12 @@ void ath_debug_stat_rx(struct ath_softc + #ifdef CONFIG_ATH9K_MAC_DEBUG + spin_lock(&sc->debug.samp_lock); + RX_SAMP_DBG(jiffies) = jiffies; +- RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0; +- RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1; +- RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2; +- RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0; +- RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1; +- RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2; ++ RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl[0]; ++ RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl[1]; ++ RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl[2]; ++ RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext[0]; ++ RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext[1]; ++ RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext[2]; + RX_SAMP_DBG(antenna) = rs->rs_antenna; + RX_SAMP_DBG(rssi) = rs->rs_rssi; + RX_SAMP_DBG(rate) = rs->rs_rate; +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -1918,6 +1918,8 @@ enum nl80211_sta_bss_param { + * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode + * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards + * non-peer STA ++ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU ++ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + */ +@@ -1947,6 +1949,8 @@ enum nl80211_sta_info { + NL80211_STA_INFO_NONPEER_PM, + NL80211_STA_INFO_RX_BYTES64, + NL80211_STA_INFO_TX_BYTES64, ++ NL80211_STA_INFO_CHAIN_SIGNAL, ++ NL80211_STA_INFO_CHAIN_SIGNAL_AVG, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -3082,6 +3082,32 @@ static bool nl80211_put_sta_rate(struct + return true; + } + ++static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal, ++ int id) ++{ ++ void *attr; ++ int i = 0; ++ ++ if (!mask) ++ return true; ++ ++ attr = nla_nest_start(msg, id); ++ if (!attr) ++ return false; ++ ++ for (i = 0; i < 4; i++) { ++ if (!(mask & BIT(i))) ++ continue; ++ ++ if (nla_put_u8(msg, i, signal[i])) ++ return false; ++ } ++ ++ nla_nest_end(msg, attr); ++ ++ return true; ++} ++ + static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, + int flags, + struct cfg80211_registered_device *rdev, +@@ -3153,6 +3179,18 @@ static int nl80211_send_station(struct s + default: + break; + } ++ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) { ++ if (!nl80211_put_signal(msg, sinfo->chains, ++ sinfo->chain_signal, ++ NL80211_STA_INFO_CHAIN_SIGNAL)) ++ goto nla_put_failure; ++ } ++ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) { ++ if (!nl80211_put_signal(msg, sinfo->chains, ++ sinfo->chain_signal_avg, ++ NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) ++ goto nla_put_failure; ++ } + if (sinfo->filled & STATION_INFO_TX_BITRATE) { + if (!nl80211_put_sta_rate(msg, &sinfo->txrate, + NL80211_STA_INFO_TX_BITRATE)) +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -445,6 +445,7 @@ static void sta_set_sinfo(struct sta_inf + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; + struct timespec uptime; ++ int i; + + sinfo->generation = sdata->local->sta_generation; + +@@ -484,6 +485,17 @@ static void sta_set_sinfo(struct sta_inf + sinfo->signal = (s8)sta->last_signal; + sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); + } ++ if (sta->chains) { ++ sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | ++ STATION_INFO_CHAIN_SIGNAL_AVG; ++ ++ sinfo->chains = sta->chains; ++ for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { ++ sinfo->chain_signal[i] = sta->chain_signal_last[i]; ++ sinfo->chain_signal_avg[i] = ++ (s8) -ewma_read(&sta->chain_signal_avg[i]); ++ } ++ } + + sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); + sta_set_rate_info_rx(sta, &sinfo->rxrate); +--- a/drivers/net/wireless/ath/ath9k/dfs.c ++++ b/drivers/net/wireless/ath/ath9k/dfs.c +@@ -164,8 +164,8 @@ void ath9k_dfs_process_phyerr(struct ath + return; + } + +- ard.rssi = rs->rs_rssi_ctl0; +- ard.ext_rssi = rs->rs_rssi_ext0; ++ ard.rssi = rs->rs_rssi_ctl[0]; ++ ard.ext_rssi = rs->rs_rssi_ext[0]; + + /* + * hardware stores this as 8 bit signed value. +--- a/drivers/net/wireless/ath/ath9k/antenna.c ++++ b/drivers/net/wireless/ath/ath9k/antenna.c +@@ -546,14 +546,14 @@ void ath_ant_comb_scan(struct ath_softc + struct ath_ant_comb *antcomb = &sc->ant_comb; + int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; + int curr_main_set; +- int main_rssi = rs->rs_rssi_ctl0; +- int alt_rssi = rs->rs_rssi_ctl1; ++ int main_rssi = rs->rs_rssi_ctl[0]; ++ int alt_rssi = rs->rs_rssi_ctl[1]; + int rx_ant_conf, main_ant_conf; + bool short_scan = false; + +- rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & ++ rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) & + ATH_ANT_RX_MASK; +- main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & ++ main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) & + ATH_ANT_RX_MASK; + + /* Record packet only when both main_rssi and alt_rssi is positive */ diff --git a/build_patches/mac80211/523-mac80211_configure_antenna_gain.patch b/build_patches/mac80211/523-mac80211_configure_antenna_gain.patch new file mode 100644 index 0000000..8c13f32 --- /dev/null +++ b/build_patches/mac80211/523-mac80211_configure_antenna_gain.patch @@ -0,0 +1,162 @@ +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -978,6 +978,7 @@ enum ieee80211_smps_mode { + * + * @power_level: requested transmit power (in dBm), backward compatibility + * value only that is set to the minimum of all interfaces ++ * @max_antenna_gain: maximum antenna gain adjusted by user config (in dBi) + * + * @channel: the channel to tune to + * @channel_type: the channel (HT) type +@@ -1000,6 +1001,7 @@ struct ieee80211_conf { + u32 flags; + int power_level, dynamic_ps_timeout; + int max_sleep_period; ++ int max_antenna_gain; + + u16 listen_interval; + u8 ps_dtim_period; +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -1116,6 +1116,7 @@ struct ieee80211_local { + int dynamic_ps_forced_timeout; + + int user_power_level; /* in dBm, for all interfaces */ ++ int user_antenna_gain; /* in dBi */ + + enum ieee80211_smps_mode smps_mode; + +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -1654,6 +1654,8 @@ enum nl80211_attrs { + NL80211_ATTR_STA_CAPABILITY, + NL80211_ATTR_STA_EXT_CAPABILITY, + ++ NL80211_ATTR_WIPHY_ANTENNA_GAIN, ++ + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -370,6 +370,7 @@ static const struct nla_policy nl80211_p + [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, + [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, }, ++ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 }, + }; + + /* policy for the key attributes */ +@@ -1705,6 +1706,22 @@ static int nl80211_set_wiphy(struct sk_b + if (result) + goto bad_res; + } ++ ++ if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_GAIN]) { ++ int idx, dbi = 0; ++ ++ if (!rdev->ops->set_antenna_gain) { ++ result = -EOPNOTSUPP; ++ goto bad_res; ++ } ++ ++ idx = NL80211_ATTR_WIPHY_ANTENNA_GAIN; ++ dbi = nla_get_u32(info->attrs[idx]); ++ ++ result = rdev->ops->set_antenna_gain(&rdev->wiphy, dbi); ++ if (result) ++ goto bad_res; ++ } + + if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && + info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -2212,6 +2212,19 @@ static int ieee80211_get_tx_power(struct + return 0; + } + ++static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi) ++{ ++ struct ieee80211_local *local = wiphy_priv(wiphy); ++ ++ if (dbi < 0) ++ return -EINVAL; ++ ++ local->user_antenna_gain = dbi; ++ ieee80211_hw_config(local, 0); ++ ++ return 0; ++} ++ + static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr) + { +@@ -3375,6 +3388,7 @@ struct cfg80211_ops mac80211_config_ops + .set_wiphy_params = ieee80211_set_wiphy_params, + .set_tx_power = ieee80211_set_tx_power, + .get_tx_power = ieee80211_get_tx_power, ++ .set_antenna_gain = ieee80211_set_antenna_gain, + .set_wds_peer = ieee80211_set_wds_peer, + .rfkill_poll = ieee80211_rfkill_poll, + CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -1862,6 +1862,7 @@ struct cfg80211_gtk_rekey_data { + * (as advertised by the nl80211 feature flag.) + * @get_tx_power: store the current TX power into the dbm variable; + * return 0 if successful ++ * @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary + * + * @set_wds_peer: set the WDS peer for a WDS interface + * +@@ -2071,6 +2072,7 @@ struct cfg80211_ops { + enum nl80211_tx_power_setting type, int mbm); + int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, + int *dbm); ++ int (*set_antenna_gain)(struct wiphy *wiphy, int dbi); + + int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr); +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -101,7 +101,7 @@ static u32 ieee80211_hw_conf_chan(struct + struct ieee80211_sub_if_data *sdata; + struct ieee80211_channel *chan; + u32 changed = 0; +- int power; ++ int power, ant_gain, max_power; + enum nl80211_channel_type channel_type; + u32 offchannel_flag; + bool scanning = false; +@@ -164,8 +164,21 @@ static u32 ieee80211_hw_conf_chan(struct + } + rcu_read_unlock(); + +- if (local->hw.conf.power_level != power) { ++ max_power = chan->max_reg_power; ++ ant_gain = chan->max_antenna_gain; ++ if (local->user_antenna_gain > 0) { ++ if (local->user_antenna_gain > ant_gain) { ++ max_power -= local->user_antenna_gain - ant_gain; ++ ant_gain = 0; ++ } else ++ ant_gain -= local->user_antenna_gain; ++ power = min(power, max_power); ++ } ++ ++ if (local->hw.conf.power_level != power || ++ local->hw.conf.max_antenna_gain != ant_gain) { + changed |= IEEE80211_CONF_CHANGE_POWER; ++ local->hw.conf.max_antenna_gain = ant_gain; + local->hw.cur_power_level = power; + local->hw.conf.power_level = power; + } +@@ -612,6 +625,7 @@ struct ieee80211_hw *ieee80211_alloc_hw( + IEEE80211_RADIOTAP_MCS_HAVE_BW; + local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI | + IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; ++ local->user_antenna_gain = 0; + local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; + wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; + diff --git a/build_patches/mac80211/530-ath9k_extra_leds.patch b/build_patches/mac80211/530-ath9k_extra_leds.patch new file mode 100644 index 0000000..a13cb41 --- /dev/null +++ b/build_patches/mac80211/530-ath9k_extra_leds.patch @@ -0,0 +1,248 @@ +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -551,6 +551,9 @@ struct ath9k_wow_pattern { + void ath_init_leds(struct ath_softc *sc); + void ath_deinit_leds(struct ath_softc *sc); + void ath_fill_led_pin(struct ath_softc *sc); ++int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name, ++ const char *trigger, bool active_low); ++ + #else + static inline void ath_init_leds(struct ath_softc *sc) + { +@@ -686,6 +689,13 @@ enum spectral_mode { + SPECTRAL_CHANSCAN, + }; + ++struct ath_led { ++ struct list_head list; ++ struct ath_softc *sc; ++ const struct gpio_led *gpio; ++ struct led_classdev cdev; ++}; ++ + struct ath_softc { + struct ieee80211_hw *hw; + struct device *dev; +@@ -727,9 +737,8 @@ struct ath_softc { + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + + #ifdef CONFIG_MAC80211_LEDS +- bool led_registered; +- char led_name[32]; +- struct led_classdev led_cdev; ++ const char *led_default_trigger; ++ struct list_head leds; + #endif + + struct ath9k_hw_cal_data caldata; +--- a/drivers/net/wireless/ath/ath9k/gpio.c ++++ b/drivers/net/wireless/ath/ath9k/gpio.c +@@ -24,40 +24,102 @@ + static void ath_led_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) + { +- struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev); +- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF)); ++ struct ath_led *led = container_of(led_cdev, struct ath_led, cdev); ++ struct ath_softc *sc = led->sc; ++ ++ ath9k_ps_wakeup(sc); ++ ath9k_hw_set_gpio(sc->sc_ah, led->gpio->gpio, ++ (brightness != LED_OFF) ^ led->gpio->active_low); ++ ath9k_ps_restore(sc); ++} ++ ++static int ath_add_led(struct ath_softc *sc, struct ath_led *led) ++{ ++ const struct gpio_led *gpio = led->gpio; ++ int ret; ++ ++ led->cdev.name = gpio->name; ++ led->cdev.default_trigger = gpio->default_trigger; ++ led->cdev.brightness_set = ath_led_brightness; ++ ++ ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->cdev); ++ if (ret < 0) ++ return ret; ++ ++ led->sc = sc; ++ list_add(&led->list, &sc->leds); ++ ++ /* Configure gpio for output */ ++ ath9k_hw_cfg_output(sc->sc_ah, gpio->gpio, ++ AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ++ ++ /* LED off */ ++ ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low); ++ ++ return 0; ++} ++ ++int ath_create_gpio_led(struct ath_softc *sc, int gpio_num, const char *name, ++ const char *trigger, bool active_low) ++{ ++ struct ath_led *led; ++ struct gpio_led *gpio; ++ char *_name; ++ int ret; ++ ++ led = kzalloc(sizeof(*led) + sizeof(*gpio) + strlen(name) + 1, ++ GFP_KERNEL); ++ if (!led) ++ return -ENOMEM; ++ ++ led->gpio = gpio = (struct gpio_led *) (led + 1); ++ _name = (char *) (led->gpio + 1); ++ ++ strcpy(_name, name); ++ gpio->name = _name; ++ gpio->gpio = gpio_num; ++ gpio->active_low = active_low; ++ gpio->default_trigger = trigger; ++ ++ ret = ath_add_led(sc, led); ++ if (unlikely(ret < 0)) ++ kfree(led); ++ ++ return ret; + } + + void ath_deinit_leds(struct ath_softc *sc) + { +- if (!sc->led_registered) +- return; ++ struct ath_led *led; + +- ath_led_brightness(&sc->led_cdev, LED_OFF); +- led_classdev_unregister(&sc->led_cdev); ++ while (!list_empty(&sc->leds)) { ++ led = list_first_entry(&sc->leds, struct ath_led, list); ++ list_del(&led->list); ++ ath_led_brightness(&led->cdev, LED_OFF); ++ led_classdev_unregister(&led->cdev); ++ kfree(led); ++ } + } + + void ath_init_leds(struct ath_softc *sc) + { +- int ret; ++ char led_name[32]; ++ const char *trigger; ++ ++ INIT_LIST_HEAD(&sc->leds); + + if (AR_SREV_9100(sc->sc_ah)) + return; + +- if (!led_blink) +- sc->led_cdev.default_trigger = +- ieee80211_get_radio_led_name(sc->hw); +- +- snprintf(sc->led_name, sizeof(sc->led_name), +- "ath9k-%s", wiphy_name(sc->hw->wiphy)); +- sc->led_cdev.name = sc->led_name; +- sc->led_cdev.brightness_set = ath_led_brightness; ++ snprintf(led_name, sizeof(led_name), "ath9k-%s", ++ wiphy_name(sc->hw->wiphy)); + +- ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev); +- if (ret < 0) +- return; ++ if (led_blink) ++ trigger = sc->led_default_trigger; ++ else ++ trigger = ieee80211_get_radio_led_name(sc->hw); + +- sc->led_registered = true; ++ ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger, 1); + } + + void ath_fill_led_pin(struct ath_softc *sc) +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -870,7 +870,7 @@ int ath9k_init_device(u16 devid, struct + + #ifdef CONFIG_MAC80211_LEDS + /* must be initialized before ieee80211_register_hw */ +- sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw, ++ sc->led_default_trigger = ieee80211_create_tpt_led_trigger(sc->hw, + IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink, + ARRAY_SIZE(ath9k_tpt_blink)); + #endif +--- a/drivers/net/wireless/ath/ath9k/debug.c ++++ b/drivers/net/wireless/ath/ath9k/debug.c +@@ -1482,6 +1482,61 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++#ifdef CONFIG_MAC80211_LEDS ++ ++static ssize_t write_file_gpio_led(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath_softc *sc = file->private_data; ++ char buf[32], *str, *name, *c; ++ ssize_t len; ++ unsigned int gpio; ++ bool active_low = false; ++ ++ len = min(count, sizeof(buf) - 1); ++ if (copy_from_user(buf, ubuf, len)) ++ return -EFAULT; ++ ++ buf[len] = '\0'; ++ name = strchr(buf, ','); ++ if (!name) ++ return -EINVAL; ++ ++ *(name++) = 0; ++ if (!*name) ++ return -EINVAL; ++ ++ c = strchr(name, '\n'); ++ if (c) ++ *c = 0; ++ ++ str = buf; ++ if (*str == '!') { ++ str++; ++ active_low = true; ++ } ++ ++ if (kstrtouint(str, 0, &gpio) < 0) ++ return -EINVAL; ++ ++ if (gpio >= sc->sc_ah->caps.num_gpio_pins) ++ return -EINVAL; ++ ++ if (ath_create_gpio_led(sc, gpio, name, NULL, active_low) < 0) ++ return -EINVAL; ++ ++ return count; ++} ++ ++static const struct file_operations fops_gpio_led = { ++ .write = write_file_gpio_led, ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++#endif ++ + #ifdef CONFIG_ATH9K_MAC_DEBUG + + void ath9k_debug_samp_bb_mac(struct ath_softc *sc) +@@ -2115,6 +2170,10 @@ int ath9k_init_debug(struct ath_hw *ah) + &fops_eeprom); + debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, + sc, &fops_chanbw); ++#ifdef CONFIG_MAC80211_LEDS ++ debugfs_create_file("gpio_led", S_IWUSR, ++ sc->debug.debugfs_phy, sc, &fops_gpio_led); ++#endif + debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, + &fops_dma); + debugfs_create_file("interrupt", S_IRUSR, sc->debug.debugfs_phy, sc, diff --git a/build_patches/mac80211/552-ath9k_rx_dma_stop_check.patch b/build_patches/mac80211/552-ath9k_rx_dma_stop_check.patch new file mode 100644 index 0000000..1da7207 --- /dev/null +++ b/build_patches/mac80211/552-ath9k_rx_dma_stop_check.patch @@ -0,0 +1,28 @@ +--- a/drivers/net/wireless/ath/ath9k/mac.c ++++ b/drivers/net/wireless/ath/ath9k/mac.c +@@ -689,7 +689,7 @@ bool ath9k_hw_stopdmarecv(struct ath_hw + { + #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ + struct ath_common *common = ath9k_hw_common(ah); +- u32 mac_status, last_mac_status = 0; ++ u32 mac_status = 0, last_mac_status = 0; + int i; + + /* Enable access to the DMA observation bus */ +@@ -719,6 +719,16 @@ bool ath9k_hw_stopdmarecv(struct ath_hw + } + + if (i == 0) { ++ if (!AR_SREV_9300_20_OR_LATER(ah) && ++ (mac_status & 0x700) == 0) { ++ /* ++ * DMA is idle but the MAC is still stuck ++ * processing events ++ */ ++ *reset = true; ++ return true; ++ } ++ + ath_err(common, + "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x DMADBG_7=0x%08x\n", + AH_RX_STOP_DMA_TIMEOUT / 1000,