backport mac80211/ath9k-patches from owrt trunk r36007 (now really ;) )

This commit is contained in:
Bjoern Franke 2013-03-16 14:27:25 +01:00
parent f245986bc8
commit 354da25bbd
13 changed files with 1525 additions and 0 deletions

View File

@ -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 <mcgrof@frijolero.org>
# Copyright 2012 Hauke Mehrtens <hauke@hauke-m.de>
#

View File

@ -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 <linux/compat-3.6.h>
#include <linux/compat-3.7.h>
#include <linux/compat-3.8.h>
+#include <linux/compat-3.9.h>
#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 <linux/version.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0))
+
+#include <linux/idr.h>
+#include <linux/list.h>
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25))
+#include <linux/rculist.h>
+#endif
+#include <net/sock.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/* include this before changing hlist_for_each_* to use the old versions. */
+#include <net/sch_generic.h>
+
+
+/**
+ * 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 <sasha.levin@oracle.com>
+ * 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 <viro@zeniv.linux.org.uk>
+ * 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 */

View File

@ -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;

View File

@ -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

View File

@ -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 <linux/io.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/etherdevice.h>
#include <asm/unaligned.h>
#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;
}

View File

@ -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 = {

View File

@ -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 <linux/pci-aspm.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
+#include <linux/ath5k_platform.h>
#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--) {

View File

@ -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 <pat-lkml at erley.org>
---
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,

View File

@ -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))

View File

@ -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 */

View File

@ -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;

View File

@ -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,

View File

@ -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,