From 7e0e4fb486f80de36c04c58f3052862d895062e8 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 6 Oct 2008 12:04:04 +0000 Subject: [PATCH] add the new compat-wireless for 2.6.27 + multi-rate retry and minstrel patches, rename patches/ for old compat-wireless to patches-old/ SVN-Revision: 12865 --- package/mac80211/Makefile | 21 +- .../001-disable_drivers.patch | 0 .../004-allow-ap-vlan-modes.patch | 0 package/mac80211/patches/000-kconfig.patch | 201 ++++ package/mac80211/patches/100-define.patch | 14 + package/mac80211/patches/110-ath5k_stat.patch | 53 + package/mac80211/patches/200-hwkey_len.patch | 183 ++++ package/mac80211/patches/210-mrr.patch | 183 ++++ package/mac80211/patches/250-ath5k_mrr.patch | 208 ++++ .../patches/299-compat_minstrel.patch | 32 + package/mac80211/patches/300-minstrel.patch | 928 ++++++++++++++++++ 11 files changed, 1818 insertions(+), 5 deletions(-) rename package/mac80211/{patches => patches-old}/001-disable_drivers.patch (100%) rename package/mac80211/{patches => patches-old}/004-allow-ap-vlan-modes.patch (100%) create mode 100644 package/mac80211/patches/000-kconfig.patch create mode 100644 package/mac80211/patches/100-define.patch create mode 100644 package/mac80211/patches/110-ath5k_stat.patch create mode 100644 package/mac80211/patches/200-hwkey_len.patch create mode 100644 package/mac80211/patches/210-mrr.patch create mode 100644 package/mac80211/patches/250-ath5k_mrr.patch create mode 100644 package/mac80211/patches/299-compat_minstrel.patch create mode 100644 package/mac80211/patches/300-minstrel.patch diff --git a/package/mac80211/Makefile b/package/mac80211/Makefile index a3b483cb52..cd1feed8c2 100644 --- a/package/mac80211/Makefile +++ b/package/mac80211/Makefile @@ -12,9 +12,16 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=mac80211 PKG_RELEASE:=1 -PKG_VERSION:=2008-08-06 +ifneq ($(CONFIG_LINUX_2_6_27),) + PKG_VERSION:=2008-09-28 + PKG_SOURCE_URL:=http://www.orbit-lab.org/kernel/compat-wireless-2.6/2008/09 +else + PKG_VERSION:=2008-08-06 + PKG_SOURCE_URL:=http://www.orbit-lab.org/kernel/compat-wireless-2.6/2008/08 + PATCH_DIR:=./patches-old +endif + PKG_SOURCE:=compat-wireless-$(PKG_VERSION).tar.bz2 -PKG_SOURCE_URL:=http://www.orbit-lab.org/kernel/compat-wireless-2.6/2008/08/ PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/compat-wireless-$(PKG_VERSION) include $(INCLUDE_DIR)/package.mk @@ -137,9 +144,11 @@ define KernelPackage/ath5k endef BUILDFLAGS:= \ + -I$(PKG_BUILD_DIR)/include \ $(foreach opt,$(CONFOPTS),-DCONFIG_$(opt)) \ $(if $(CONFIG_LEDS_TRIGGERS), -DCONFIG_MAC80211_LEDS -DCONFIG_LEDS_TRIGGERS) \ - -D__CONFIG_MAC80211_RC_DEFAULT=pid \ + $(if $(CONFIG_DEBUG_FS), -DCONFIG_MAC80211_DEBUGFS) \ + -D__CONFIG_MAC80211_RC_DEFAULT=minstrel \ MAKE_OPTS:= \ CROSS_COMPILE="$(TARGET_CROSS)" \ @@ -148,9 +157,11 @@ MAKE_OPTS:= \ $(foreach opt,$(CONFOPTS),CONFIG_$(opt)=m) \ CONFIG_NL80211=y \ CONFIG_MAC80211_RC_PID=y \ + CONFIG_MAC80211_RC_MINSTREL=y \ CONFIG_MAC80211_LEDS=$(CONFIG_LEDS_TRIGGERS) \ - LINUXINCLUDE="-I$(PKG_BUILD_DIR)/include -I$(LINUX_DIR)/include -include linux/autoconf.h" \ + CONFIG_MAC80211_DEBUGFS=$(CONFIG_DEBUG_FS) \ KLIB_BUILD="$(LINUX_DIR)" \ + MODPROBE=: define Build/Prepare rm -rf $(PKG_BUILD_DIR) @@ -160,7 +171,7 @@ define Build/Prepare $(if $(QUILT),touch $(PKG_BUILD_DIR)/.quilt_used) unzip -jod $(PKG_BUILD_DIR) $(DL_DIR)/$(RT61FW) unzip -jod $(PKG_BUILD_DIR) $(DL_DIR)/$(RT71FW) - rm -rf $(PKG_BUILD_DIR)/include/linux/ssb + $(if $(CONFIG_TARGET_brcm47xx),rm -rf $(PKG_BUILD_DIR)/include/linux/ssb) endef ifneq ($(CONFIG_PACKAGE_kmod-mac80211),) diff --git a/package/mac80211/patches/001-disable_drivers.patch b/package/mac80211/patches-old/001-disable_drivers.patch similarity index 100% rename from package/mac80211/patches/001-disable_drivers.patch rename to package/mac80211/patches-old/001-disable_drivers.patch diff --git a/package/mac80211/patches/004-allow-ap-vlan-modes.patch b/package/mac80211/patches-old/004-allow-ap-vlan-modes.patch similarity index 100% rename from package/mac80211/patches/004-allow-ap-vlan-modes.patch rename to package/mac80211/patches-old/004-allow-ap-vlan-modes.patch diff --git a/package/mac80211/patches/000-kconfig.patch b/package/mac80211/patches/000-kconfig.patch new file mode 100644 index 0000000000..bcbf8868b5 --- /dev/null +++ b/package/mac80211/patches/000-kconfig.patch @@ -0,0 +1,201 @@ +--- /dev/null ++++ b/net/mac80211/Kconfig +@@ -0,0 +1,198 @@ ++config MAC80211 ++ tristate "Generic IEEE 802.11 Networking Stack (mac80211)" ++ select CRYPTO ++ select CRYPTO_ECB ++ select CRYPTO_ARC4 ++ select CRYPTO_AES ++ select CRC32 ++ select WIRELESS_EXT ++ select CFG80211 ++ ---help--- ++ This option enables the hardware independent IEEE 802.11 ++ networking stack. ++ ++menu "Rate control algorithm selection" ++ depends on MAC80211 != n ++ ++config MAC80211_RC_PID ++ bool "PID controller based rate control algorithm" if EMBEDDED ++ default y ++ ---help--- ++ This option enables a TX rate control algorithm for ++ mac80211 that uses a PID controller to select the TX ++ rate. ++ ++choice ++ prompt "Default rate control algorithm" ++ default MAC80211_RC_DEFAULT_PID ++ ---help--- ++ This option selects the default rate control algorithm ++ mac80211 will use. Note that this default can still be ++ overriden through the ieee80211_default_rc_algo module ++ parameter if different algorithms are available. ++ ++config MAC80211_RC_DEFAULT_PID ++ bool "PID controller based rate control algorithm" ++ depends on MAC80211_RC_PID ++ ---help--- ++ Select the PID controller based rate control as the ++ default rate control algorithm. You should choose ++ this unless you know what you are doing. ++ ++endchoice ++ ++config MAC80211_RC_DEFAULT ++ string ++ default "pid" if MAC80211_RC_DEFAULT_PID ++ default "" ++ ++endmenu ++ ++config MAC80211_MESH ++ bool "Enable mac80211 mesh networking (pre-802.11s) support" ++ depends on MAC80211 && EXPERIMENTAL ++ ---help--- ++ This options enables support of Draft 802.11s mesh networking. ++ The implementation is based on Draft 1.08 of the Mesh Networking ++ amendment. For more information visit http://o11s.org/. ++ ++ ++config MAC80211_LEDS ++ bool "Enable LED triggers" ++ depends on MAC80211 ++ select NEW_LEDS ++ select LEDS_TRIGGERS ++ ---help--- ++ This option enables a few LED triggers for different ++ packet receive/transmit events. ++ ++config MAC80211_DEBUGFS ++ bool "Export mac80211 internals in DebugFS" ++ depends on MAC80211 && DEBUG_FS ++ ---help--- ++ Select this to see extensive information about ++ the internal state of mac80211 in debugfs. ++ ++ Say N unless you know you need this. ++ ++menuconfig MAC80211_DEBUG_MENU ++ bool "Select mac80211 debugging features" ++ depends on MAC80211 ++ ---help--- ++ This option collects various mac80211 debug settings. ++ ++config MAC80211_DEBUG_PACKET_ALIGNMENT ++ bool "Enable packet alignment debugging" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ This option is recommended for driver authors and strongly ++ discouraged for everybody else, it will trigger a warning ++ when a driver hands mac80211 a buffer that is aligned in ++ a way that will cause problems with the IP stack on some ++ architectures. ++ ++ Say N unless you're writing a mac80211 based driver. ++ ++config MAC80211_NOINLINE ++ bool "Do not inline TX/RX handlers" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ This option affects code generation in mac80211, when ++ selected some functions are marked "noinline" to allow ++ easier debugging of problems in the transmit and receive ++ paths. ++ ++ This option increases code size a bit and inserts a lot ++ of function calls in the code, but is otherwise safe to ++ enable. ++ ++ If unsure, say N unless you expect to be finding problems ++ in mac80211. ++ ++config MAC80211_VERBOSE_DEBUG ++ bool "Verbose debugging output" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ Selecting this option causes mac80211 to print out ++ many debugging messages. It should not be selected ++ on production systems as some of the messages are ++ remotely triggerable. ++ ++ Do not select this option. ++ ++config MAC80211_HT_DEBUG ++ bool "Verbose HT debugging" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ This option enables 802.11n High Throughput features ++ debug tracing output. ++ ++ It should not be selected on production systems as some ++ of the messages are remotely triggerable. ++ ++ Do not select this option. ++ ++config MAC80211_TKIP_DEBUG ++ bool "Verbose TKIP debugging" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ Selecting this option causes mac80211 to print out ++ very verbose TKIP debugging messages. It should not ++ be selected on production systems as those messages ++ are remotely triggerable. ++ ++ Do not select this option. ++ ++config MAC80211_IBSS_DEBUG ++ bool "Verbose IBSS debugging" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ Selecting this option causes mac80211 to print out ++ very verbose IBSS debugging messages. It should not ++ be selected on production systems as those messages ++ are remotely triggerable. ++ ++ Do not select this option. ++ ++config MAC80211_VERBOSE_PS_DEBUG ++ bool "Verbose powersave mode debugging" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ Selecting this option causes mac80211 to print out very ++ verbose power save mode debugging messages (when mac80211 ++ is an AP and has power saving stations.) ++ It should not be selected on production systems as those ++ messages are remotely triggerable. ++ ++ Do not select this option. ++ ++config MAC80211_VERBOSE_MPL_DEBUG ++ bool "Verbose mesh peer link debugging" ++ depends on MAC80211_DEBUG_MENU ++ depends on MAC80211_MESH ++ ---help--- ++ Selecting this option causes mac80211 to print out very ++ verbose mesh peer link debugging messages (when mac80211 ++ is taking part in a mesh network). ++ It should not be selected on production systems as those ++ messages are remotely triggerable. ++ ++ Do not select this option. ++ ++config MAC80211_DEBUG_COUNTERS ++ bool "Extra statistics for TX/RX debugging" ++ depends on MAC80211_DEBUG_MENU ++ depends on MAC80211_DEBUGFS ++ ---help--- ++ Selecting this option causes mac80211 to keep additional ++ and very verbose statistics about TX and RX handler use ++ and show them in debugfs. ++ ++ If unsure, say N. ++ ++config MAC80211_VERBOSE_SPECT_MGMT_DEBUG ++ bool "Verbose Spectrum Management (IEEE 802.11h)debugging" ++ depends on MAC80211_DEBUG_MENU ++ ---help--- ++ Say Y here to print out verbose Spectrum Management (IEEE 802.11h) ++ debug messages. diff --git a/package/mac80211/patches/100-define.patch b/package/mac80211/patches/100-define.patch new file mode 100644 index 0000000000..b41cad6943 --- /dev/null +++ b/package/mac80211/patches/100-define.patch @@ -0,0 +1,14 @@ +--- a/include/net/compat.h ++++ b/include/net/compat.h +@@ -6,6 +6,11 @@ + #include + + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) ++#include ++ ++#ifndef ETH_P_PAE ++#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ ++#endif + #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) */ + + #endif /* LINUX_26_COMPAT_H */ diff --git a/package/mac80211/patches/110-ath5k_stat.patch b/package/mac80211/patches/110-ath5k_stat.patch new file mode 100644 index 0000000000..2cd9548518 --- /dev/null +++ b/package/mac80211/patches/110-ath5k_stat.patch @@ -0,0 +1,53 @@ +This patch removes the unnecessary ATH_STAT_MRRETRY flag and turns +other ATH_STAT_* defines into an enum + +Signed-off-by: Felix Fietkau + +--- a/drivers/net/wireless/ath5k/base.c ++++ b/drivers/net/wireless/ath5k/base.c +@@ -707,19 +707,6 @@ + ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device); + + /* +- * Check if the MAC has multi-rate retry support. +- * We do this by trying to setup a fake extended +- * descriptor. MAC's that don't have support will +- * return false w/o doing anything. MAC's that do +- * support it will return true w/o doing anything. +- */ +- ret = ah->ah_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0); +- if (ret < 0) +- goto err; +- if (ret > 0) +- __set_bit(ATH_STAT_MRRETRY, sc->status); +- +- /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ +--- a/drivers/net/wireless/ath5k/base.h ++++ b/drivers/net/wireless/ath5k/base.h +@@ -99,6 +99,12 @@ + #define ATH_CHAN_MAX (14+14+14+252+20) + #endif + ++enum { ++ ATH_STAT_INVALID, /* disable hardware accesses */ ++ ATH_STAT_PROMISC, ++ ATH_STAT_LEDSOFT /* enable LED gpio status */ ++}; ++ + /* Software Carrier, keeps track of the driver state + * associated with an instance of a device */ + struct ath5k_softc { +@@ -129,10 +135,6 @@ + u16 cachelsz; /* cache line size */ + + DECLARE_BITMAP(status, 4); +-#define ATH_STAT_INVALID 0 /* disable hardware accesses */ +-#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ +-#define ATH_STAT_PROMISC 2 +-#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ + + unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ + unsigned int curmode; /* current phy mode */ diff --git a/package/mac80211/patches/200-hwkey_len.patch b/package/mac80211/patches/200-hwkey_len.patch new file mode 100644 index 0000000000..20deeb778b --- /dev/null +++ b/package/mac80211/patches/200-hwkey_len.patch @@ -0,0 +1,183 @@ +Free up 2 bytes in skb->cb to be used for multi-rate retry later. +Move iv_len and icv_len initialization into key alloc. + +Signed-off-by: Felix Fietkau + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -337,8 +337,6 @@ + unsigned long jiffies; + s8 rts_cts_rate_idx, alt_retry_rate_idx; + u8 retry_limit; +- u8 icv_len; +- u8 iv_len; + } control; + struct { + u64 ampdu_ack_map; +@@ -635,6 +633,8 @@ + */ + struct ieee80211_key_conf { + enum ieee80211_key_alg alg; ++ u8 icv_len; ++ u8 iv_len; + u8 hw_key_idx; + u8 flags; + s8 keyidx; +--- a/drivers/net/wireless/b43/xmit.c ++++ b/drivers/net/wireless/b43/xmit.c +@@ -252,7 +252,7 @@ + } + + /* Hardware appends ICV. */ +- plcp_fragment_len += info->control.icv_len; ++ plcp_fragment_len += info->control.hw_key->icv_len; + + key_idx = b43_kidx_to_fw(dev, key_idx); + mac_ctl |= (key_idx << B43_TXH_MAC_KEYIDX_SHIFT) & +@@ -260,7 +260,7 @@ + mac_ctl |= (key->algorithm << B43_TXH_MAC_KEYALG_SHIFT) & + B43_TXH_MAC_KEYALG; + wlhdr_len = ieee80211_hdrlen(fctl); +- iv_len = min((size_t) info->control.iv_len, ++ iv_len = min((size_t) info->control.hw_key->iv_len, + ARRAY_SIZE(txhdr->iv)); + memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len); + } +--- a/drivers/net/wireless/b43legacy/xmit.c ++++ b/drivers/net/wireless/b43legacy/xmit.c +@@ -243,7 +243,7 @@ + + if (key->enabled) { + /* Hardware appends ICV. */ +- plcp_fragment_len += info->control.icv_len; ++ plcp_fragment_len += info->control.hw_key->icv_len; + + key_idx = b43legacy_kidx_to_fw(dev, key_idx); + mac_ctl |= (key_idx << B43legacy_TX4_MAC_KEYIDX_SHIFT) & +@@ -252,7 +252,7 @@ + B43legacy_TX4_MAC_KEYALG_SHIFT) & + B43legacy_TX4_MAC_KEYALG; + wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control); +- iv_len = min((size_t)info->control.iv_len, ++ iv_len = min((size_t)info->control.hw_key->iv_len, + ARRAY_SIZE(txhdr->iv)); + memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); + } else { +--- a/net/mac80211/wep.c ++++ b/net/mac80211/wep.c +@@ -313,9 +313,6 @@ + { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + +- info->control.iv_len = WEP_IV_LEN; +- info->control.icv_len = WEP_ICV_LEN; +- + if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) { + if (ieee80211_wep_encrypt(tx->local, skb, tx->key)) + return -1; +--- a/net/mac80211/wpa.c ++++ b/net/mac80211/wpa.c +@@ -152,9 +152,6 @@ + int len, tail; + u8 *pos; + +- info->control.icv_len = TKIP_ICV_LEN; +- info->control.iv_len = TKIP_IV_LEN; +- + if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) && + !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { + /* hwaccel - with no need for preallocated room for IV/ICV */ +@@ -374,9 +371,6 @@ + u8 *pos, *pn; + int i; + +- info->control.icv_len = CCMP_MIC_LEN; +- info->control.iv_len = CCMP_HDR_LEN; +- + if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) && + !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { + /* hwaccel - with no need for preallocated room for CCMP " +--- a/drivers/net/wireless/ath5k/base.c ++++ b/drivers/net/wireless/ath5k/base.c +@@ -1164,7 +1164,7 @@ + + if (info->control.hw_key) { + keyidx = info->control.hw_key->hw_key_idx; +- pktlen += info->control.icv_len; ++ pktlen += info->control.hw_key->icv_len; + } + ret = ah->ah_setup_tx_desc(ah, ds, pktlen, + ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, +--- a/drivers/net/wireless/ath9k/xmit.c ++++ b/drivers/net/wireless/ath9k/xmit.c +@@ -237,7 +237,7 @@ + + if (tx_info->control.hw_key) { + txctl->keyix = tx_info->control.hw_key->hw_key_idx; +- txctl->frmlen += tx_info->control.icv_len; ++ txctl->frmlen += tx_info->control.hw_key->icv_len; + + if (tx_info->control.hw_key->alg == ALG_WEP) + txctl->keytype = ATH9K_KEY_TYPE_WEP; +--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c ++++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c +@@ -56,10 +56,10 @@ + * note that these lengths should only be added when + * mac80211 does not generate it. + */ +- overhead += tx_info->control.icv_len; ++ overhead += key->icv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) +- overhead += tx_info->control.iv_len; ++ overhead += key->iv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { + if (key->alg == ALG_TKIP) +--- a/drivers/net/wireless/rt2x00/rt2x00queue.c ++++ b/drivers/net/wireless/rt2x00/rt2x00queue.c +@@ -374,7 +374,7 @@ + struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); + struct txentry_desc txdesc; + struct skb_frame_desc *skbdesc; +- unsigned int iv_len = IEEE80211_SKB_CB(skb)->control.iv_len; ++ unsigned int iv_len; + + if (unlikely(rt2x00queue_full(queue))) + return -EINVAL; +@@ -410,8 +410,11 @@ + * the frame so we can provide it to the driver seperately. + */ + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && +- !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) ++ !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags) && ++ (IEEE80211_SKB_CB(skb)->control.hw_key != NULL)) { ++ iv_len = IEEE80211_SKB_CB(skb)->control.hw_key->iv_len; + rt2x00crypto_tx_remove_iv(skb, iv_len); ++ } + + /* + * It could be possible that the queue was corrupted and this +--- a/net/mac80211/key.c ++++ b/net/mac80211/key.c +@@ -281,6 +281,20 @@ + key->conf.alg = alg; + key->conf.keyidx = idx; + key->conf.keylen = key_len; ++ switch (alg) { ++ case ALG_WEP: ++ key->conf.iv_len = WEP_IV_LEN; ++ key->conf.icv_len = WEP_ICV_LEN; ++ break; ++ case ALG_TKIP: ++ key->conf.iv_len = TKIP_IV_LEN; ++ key->conf.icv_len = TKIP_ICV_LEN; ++ break; ++ case ALG_CCMP: ++ key->conf.iv_len = CCMP_HDR_LEN; ++ key->conf.icv_len = CCMP_MIC_LEN; ++ break; ++ } + memcpy(key->conf.key, key_data, key_len); + INIT_LIST_HEAD(&key->list); + INIT_LIST_HEAD(&key->todo); diff --git a/package/mac80211/patches/210-mrr.patch b/package/mac80211/patches/210-mrr.patch new file mode 100644 index 0000000000..3894788245 --- /dev/null +++ b/package/mac80211/patches/210-mrr.patch @@ -0,0 +1,183 @@ +This patch adjusts the rate control API to allow multi-rate retry +if supported by the driver. The ieee80211_hw struct specifies how +many alternate rate selections the driver supports. + +Signed-off-by: Felix Fietkau + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -292,6 +292,20 @@ + #define IEEE80211_TX_INFO_DRIVER_DATA_PTRS \ + (IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)) + ++/* maximum number of alternate rate retry stages */ ++#define IEEE80211_TX_MAX_ALTRATE 3 ++ ++/** ++ * struct ieee80211_tx_altrate - alternate rate selection/status ++ * ++ * @rate_idx: rate index to attempt to send with ++ * @limit: number of retries before fallback ++ */ ++struct ieee80211_tx_altrate { ++ s8 rate_idx; ++ u8 limit; ++}; ++ + /** + * struct ieee80211_tx_info - skb transmit information + * +@@ -335,12 +349,14 @@ + struct ieee80211_key_conf *hw_key; + struct ieee80211_sta *sta; + unsigned long jiffies; +- s8 rts_cts_rate_idx, alt_retry_rate_idx; ++ s8 rts_cts_rate_idx; + u8 retry_limit; ++ struct ieee80211_tx_altrate retries[IEEE80211_TX_MAX_ALTRATE]; + } control; + struct { + u64 ampdu_ack_map; + int ack_signal; ++ struct ieee80211_tx_altrate retries[IEEE80211_TX_MAX_ALTRATE + 1]; + u8 retry_count; + bool excessive_retries; + u8 ampdu_ack_len; +@@ -828,6 +844,9 @@ + * within &struct ieee80211_vif. + * @sta_data_size: size (in bytes) of the drv_priv data area + * within &struct ieee80211_sta. ++ * ++ * @max_altrates: maximum number of alternate rate retry stages ++ * @max_altrate_tries: maximum number of tries for each stage + */ + struct ieee80211_hw { + struct ieee80211_conf conf; +@@ -844,6 +863,8 @@ + u16 ampdu_queues; + u16 max_listen_interval; + s8 max_signal; ++ u8 max_altrates; ++ u8 max_altrate_tries; + }; + + struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy); +@@ -900,11 +921,11 @@ + + static inline struct ieee80211_rate * + ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, +- const struct ieee80211_tx_info *c) ++ const struct ieee80211_tx_info *c, int idx) + { +- if (c->control.alt_retry_rate_idx < 0) ++ if (c->control.retries[idx].rate_idx < 0) + return NULL; +- return &hw->wiphy->bands[c->band]->bitrates[c->control.alt_retry_rate_idx]; ++ return &hw->wiphy->bands[c->band]->bitrates[c->control.retries[idx].rate_idx]; + } + + /** +--- a/drivers/net/wireless/b43/xmit.c ++++ b/drivers/net/wireless/b43/xmit.c +@@ -208,7 +208,7 @@ + txrate = ieee80211_get_tx_rate(dev->wl->hw, info); + rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB; + rate_ofdm = b43_is_ofdm_rate(rate); +- fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, info) ? : txrate; ++ fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : txrate; + rate_fb = fbrate->hw_value; + rate_fb_ofdm = b43_is_ofdm_rate(rate_fb); + +--- a/drivers/net/wireless/b43legacy/xmit.c ++++ b/drivers/net/wireless/b43legacy/xmit.c +@@ -210,7 +210,7 @@ + + rate = tx_rate->hw_value; + rate_ofdm = b43legacy_is_ofdm_rate(rate); +- rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, info) ? : tx_rate; ++ rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : tx_rate; + rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value); + + txhdr->mac_frame_ctl = wlhdr->frame_control; +--- a/drivers/net/wireless/rtl8180_dev.c ++++ b/drivers/net/wireless/rtl8180_dev.c +@@ -292,8 +292,8 @@ + entry->plcp_len = cpu_to_le16(plcp_len); + entry->tx_buf = cpu_to_le32(mapping); + entry->frame_len = cpu_to_le32(skb->len); +- entry->flags2 = info->control.alt_retry_rate_idx >= 0 ? +- ieee80211_get_alt_retry_rate(dev, info)->bitrate << 4 : 0; ++ entry->flags2 = info->control.retries[0].rate_idx >= 0 ? ++ ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; + entry->retry_limit = info->control.retry_limit; + entry->flags = cpu_to_le32(tx_flags); + __skb_queue_tail(&ring->queue, skb); +@@ -855,6 +855,7 @@ + priv = dev->priv; + priv->pdev = pdev; + ++ dev->max_altrates = 1; + SET_IEEE80211_DEV(dev, &pdev->dev); + pci_set_drvdata(pdev, dev); + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -454,15 +454,16 @@ + if (unlikely(rsel.probe_idx >= 0)) { + info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; + tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG; +- info->control.alt_retry_rate_idx = tx->rate_idx; ++ info->control.retries[0].rate_idx = tx->rate_idx; ++ info->control.retries[0].limit = tx->local->hw.max_altrate_tries; + tx->rate_idx = rsel.probe_idx; +- } else +- info->control.alt_retry_rate_idx = -1; ++ } else if (info->control.retries[0].limit == 0) ++ info->control.retries[0].rate_idx = -1; + + if (unlikely(tx->rate_idx < 0)) + return TX_DROP; + } else +- info->control.alt_retry_rate_idx = -1; ++ info->control.retries[0].rate_idx = -1; + + if (tx->sdata->bss_conf.use_cts_prot && + (tx->flags & IEEE80211_TX_FRAGMENTED) && (rsel.nonerp_idx >= 0)) { +@@ -521,7 +522,7 @@ + * frames. + * TODO: The last fragment could still use multiple retry + * rates. */ +- info->control.alt_retry_rate_idx = -1; ++ info->control.retries[0].rate_idx = -1; + } + + /* Use CTS protection for unicast frames sent using extended rates if +@@ -551,7 +552,7 @@ + int idx; + + /* Do not use multiple retry rates when using RTS/CTS */ +- info->control.alt_retry_rate_idx = -1; ++ info->control.retries[0].rate_idx = -1; + + /* Use min(data rate, max base rate) as CTS/RTS rate */ + rate = &sband->bitrates[tx->rate_idx]; +--- a/drivers/net/wireless/b43/main.c ++++ b/drivers/net/wireless/b43/main.c +@@ -4588,6 +4588,7 @@ + BIT(NL80211_IFTYPE_ADHOC); + + hw->queues = b43_modparam_qos ? 4 : 1; ++ hw->max_altrates = 1; + SET_IEEE80211_DEV(hw, dev->dev); + if (is_valid_ether_addr(sprom->et1mac)) + SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); +--- a/drivers/net/wireless/b43legacy/main.c ++++ b/drivers/net/wireless/b43legacy/main.c +@@ -3710,6 +3710,7 @@ + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_ADHOC); + hw->queues = 1; /* FIXME: hardware has more queues */ ++ hw->max_altrates = 1; + SET_IEEE80211_DEV(hw, dev->dev); + if (is_valid_ether_addr(sprom->et1mac)) + SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); diff --git a/package/mac80211/patches/250-ath5k_mrr.patch b/package/mac80211/patches/250-ath5k_mrr.patch new file mode 100644 index 0000000000..ff15fd4b70 --- /dev/null +++ b/package/mac80211/patches/250-ath5k_mrr.patch @@ -0,0 +1,208 @@ +Clean up the tx status reporting, fix retry counters (short retries are +virtual collisions, not actual retries). Implement multi-rate retry +support. +This also fixes strong throughput fluctuations with rc80211_pid + +Signed-off-by: Felix Fietkau + +--- a/drivers/net/wireless/ath5k/base.c ++++ b/drivers/net/wireless/ath5k/base.c +@@ -530,6 +530,12 @@ + goto err_irq; + } + ++ /* set up multi-rate retry capabilities */ ++ if (sc->ah->ah_version == AR5K_AR5212) { ++ hw->max_altrates = 3; ++ hw->max_altrate_tries = 11; ++ } ++ + /* Finish private driver data initialization */ + ret = ath5k_attach(pdev, hw); + if (ret) +@@ -1149,7 +1155,9 @@ + struct sk_buff *skb = bf->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; +- int ret; ++ struct ieee80211_rate *rate; ++ unsigned int mrr_rate[3], mrr_tries[3]; ++ int i, ret; + + flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; + +@@ -1174,6 +1182,22 @@ + if (ret) + goto err_unmap; + ++ memset(mrr_rate, 0, sizeof(mrr_rate)); ++ memset(mrr_tries, 0, sizeof(mrr_tries)); ++ for (i = 0; i < 3; i++) { ++ rate = ieee80211_get_alt_retry_rate(sc->hw, info, i); ++ if (!rate) ++ break; ++ ++ mrr_rate[i] = rate->hw_value; ++ mrr_tries[i] = info->control.retries[i].limit; ++ } ++ ++ ah->ah_setup_mrr_tx_desc(ah, ds, ++ mrr_rate[0], mrr_tries[0], ++ mrr_rate[1], mrr_tries[1], ++ mrr_rate[2], mrr_tries[2]); ++ + ds->ds_link = 0; + ds->ds_data = bf->skbaddr; + +@@ -1790,7 +1814,7 @@ + struct ath5k_desc *ds; + struct sk_buff *skb; + struct ieee80211_tx_info *info; +- int ret; ++ int i, ret; + + spin_lock(&txq->lock); + list_for_each_entry_safe(bf, bf0, &txq->q, list) { +@@ -1812,7 +1836,25 @@ + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, + PCI_DMA_TODEVICE); + +- info->status.retry_count = ts.ts_shortretry + ts.ts_longretry / 6; ++ memset(&info->status, 0, sizeof(info->status)); ++ info->tx_rate_idx = ath5k_hw_to_driver_rix(sc, ++ ts.ts_rate[ts.ts_final_idx]); ++ info->status.retry_count = ts.ts_longretry; ++ ++ for (i = 0; i < 4; i++) { ++ struct ieee80211_tx_altrate *r = ++ &info->status.retries[i]; ++ ++ if (ts.ts_rate[i]) { ++ r->rate_idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]); ++ r->limit = ts.ts_retry[i]; ++ } else { ++ r->rate_idx = -1; ++ r->limit = 0; ++ } ++ } ++ ++ info->status.excessive_retries = 0; + if (unlikely(ts.ts_status)) { + sc->ll_stats.dot11ACKFailureCount++; + if (ts.ts_status & AR5K_TXERR_XRETRY) +--- a/drivers/net/wireless/ath5k/desc.c ++++ b/drivers/net/wireless/ath5k/desc.c +@@ -318,6 +318,15 @@ + return 0; + } + ++/* no mrr support for cards older than 5212 */ ++static int ++ath5k_hw_setup_no_mrr(struct ath5k_hw *ah, struct ath5k_desc *desc, ++ unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, ++ u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3) ++{ ++ return 0; ++} ++ + /* + * Proccess the tx status descriptor on 5210/5211 + */ +@@ -352,8 +361,10 @@ + AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); + ts->ts_antenna = 1; + ts->ts_status = 0; +- ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_0, ++ ts->ts_rate[0] = AR5K_REG_MS(tx_ctl->tx_control_0, + AR5K_2W_TX_DESC_CTL0_XMIT_RATE); ++ ts->ts_retry[0] = ts->ts_longretry; ++ ts->ts_final_idx = 0; + + if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) { + if (tx_status->tx_status_0 & +@@ -405,29 +416,43 @@ + AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1; + ts->ts_status = 0; + +- switch (AR5K_REG_MS(tx_status->tx_status_1, +- AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) { +- case 0: +- ts->ts_rate = tx_ctl->tx_control_3 & +- AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; +- break; ++ ts->ts_final_idx = AR5K_REG_MS(tx_status->tx_status_1, ++ AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX); ++ ++ /* The longretry counter has the number of un-acked retries ++ * for the final rate. To get the total number of retries ++ * we have to add the retry counters for the other rates ++ * as well ++ */ ++ ts->ts_retry[ts->ts_final_idx] = ts->ts_longretry; ++ switch (ts->ts_final_idx) { ++ case 3: ++ ts->ts_rate[3] = AR5K_REG_MS(tx_ctl->tx_control_3, ++ AR5K_4W_TX_DESC_CTL3_XMIT_RATE3); ++ ++ ts->ts_retry[2] = AR5K_REG_MS(tx_ctl->tx_control_2, ++ AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2); ++ ts->ts_longretry += ts->ts_retry[2]; ++ /* fall through */ ++ case 2: ++ ts->ts_rate[2] = AR5K_REG_MS(tx_ctl->tx_control_3, ++ AR5K_4W_TX_DESC_CTL3_XMIT_RATE2); ++ ++ ts->ts_retry[1] = AR5K_REG_MS(tx_ctl->tx_control_2, ++ AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1); ++ ts->ts_longretry += ts->ts_retry[1]; ++ /* fall through */ + case 1: +- ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3, ++ ts->ts_rate[1] = AR5K_REG_MS(tx_ctl->tx_control_3, + AR5K_4W_TX_DESC_CTL3_XMIT_RATE1); +- ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2, ++ ++ ts->ts_retry[0] = AR5K_REG_MS(tx_ctl->tx_control_2, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1); +- break; +- case 2: +- ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3, +- AR5K_4W_TX_DESC_CTL3_XMIT_RATE2); +- ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2, +- AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2); +- break; +- case 3: +- ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3, +- AR5K_4W_TX_DESC_CTL3_XMIT_RATE3); +- ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2, +- AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3); ++ ts->ts_longretry += ts->ts_retry[0]; ++ /* fall through */ ++ case 0: ++ ts->ts_rate[0] = tx_ctl->tx_control_3 & ++ AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; + break; + } + +@@ -653,7 +678,7 @@ + } else { + ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc; + ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc; +- ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_mrr_tx_desc; ++ ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_no_mrr; + ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status; + } + +--- a/drivers/net/wireless/ath5k/ath5k.h ++++ b/drivers/net/wireless/ath5k/ath5k.h +@@ -418,7 +418,9 @@ + u16 ts_seqnum; + u16 ts_tstamp; + u8 ts_status; +- u8 ts_rate; ++ u8 ts_rate[4]; ++ u8 ts_retry[4]; ++ u8 ts_final_idx; + s8 ts_rssi; + u8 ts_shortretry; + u8 ts_longretry; diff --git a/package/mac80211/patches/299-compat_minstrel.patch b/package/mac80211/patches/299-compat_minstrel.patch new file mode 100644 index 0000000000..7f50197806 --- /dev/null +++ b/package/mac80211/patches/299-compat_minstrel.patch @@ -0,0 +1,32 @@ +--- a/config.mk ++++ b/config.mk +@@ -47,8 +47,9 @@ + endif # build check + endif # kernel Makefile check + +-CONFIG_MAC80211_RC_DEFAULT=pid ++CONFIG_MAC80211_RC_DEFAULT=minstrel + CONFIG_MAC80211_RC_PID=y ++CONFIG_MAC80211_RC_MINSTREL=y + + # enable mesh networking too + CONFIG_MAC80211_MESH=y +--- a/include/linux/compat_autoconf.h ++++ b/include/linux/compat_autoconf.h +@@ -18,11 +18,14 @@ + #define CONFIG_MAC80211 1 + #endif /* CONFIG_MAC80211 */ + #ifndef CONFIG_MAC80211_RC_DEFAULT +-#define CONFIG_MAC80211_RC_DEFAULT "pid" +-#endif /* CONFIG_MAC80211_RC_DEFAULT */ ++#define CONFIG_MAC80211_RC_DEFAULT "minstrel" ++#endif /* CONFIG_MAC80211_RC_DEFAULT */ + #ifndef CONFIG_MAC80211_RC_PID + #define CONFIG_MAC80211_RC_PID 1 + #endif /* CONFIG_MAC80211_RC_PID */ ++#ifndef CONFIG_MAC80211_RC_MINSTREL ++#define CONFIG_MAC80211_RC_MINSTREL 1 ++#endif /* CONFIG_MAC80211_RC_MINSTREL */ + #ifndef CONFIG_MAC80211_MESH + #define CONFIG_MAC80211_MESH 1 + #endif /* CONFIG_MAC80211_MESH */ diff --git a/package/mac80211/patches/300-minstrel.patch b/package/mac80211/patches/300-minstrel.patch new file mode 100644 index 0000000000..ee1887a6ce --- /dev/null +++ b/package/mac80211/patches/300-minstrel.patch @@ -0,0 +1,928 @@ +--- /dev/null ++++ b/net/mac80211/rc80211_minstrel.c +@@ -0,0 +1,583 @@ ++/* ++ * Copyright (C) 2008 Felix Fietkau ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Based on minstrel.c: ++ * Copyright (C) 2005-2007 Derek Smithies ++ * Sponsored by Indranet Technologies Ltd ++ * ++ * Based on sample.c: ++ * Copyright (c) 2005 John Bicket ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce at minimum a disclaimer ++ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any ++ * redistribution must be conditioned upon including a substantially ++ * similar Disclaimer requirement for further binary redistribution. ++ * 3. Neither the names of the above-listed copyright holders nor the names ++ * of any contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * Alternatively, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2 as published by the Free ++ * Software Foundation. ++ * ++ * NO WARRANTY ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY ++ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, ++ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ++ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++ * THE POSSIBILITY OF SUCH DAMAGES. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rate.h" ++#include "rc80211_minstrel.h" ++ ++#define SAMPLE_COLUMNS 10 ++#define SAMPLE_TBL(_mi, _idx, _col) \ ++ _mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col] ++ ++/* convert mac80211 rate index to local array index */ ++static inline int ++rix_to_ndx(struct minstrel_sta_info *mi, int rix) ++{ ++ int i = rix; ++ for (i = rix; i >= 0; i--) ++ if (mi->r[i].rix == rix) ++ break; ++ WARN_ON(mi->r[i].rix != rix); ++ return i; ++} ++ ++static inline bool ++use_low_rate(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ u16 fc; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || ++ (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || ++ is_multicast_ether_addr(hdr->addr1)); ++} ++ ++ ++static void ++minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) ++{ ++ u32 max_tp = 0, index_max_tp = 0, index_max_tp2 = 0; ++ u32 max_prob = 0, index_max_prob = 0; ++ u32 usecs; ++ u32 p; ++ int i; ++ ++ mi->stats_update = jiffies; ++ for (i = 0; i < mi->n_rates; i++) { ++ struct minstrel_rate *mr = &mi->r[i]; ++ ++ usecs = mr->perfect_tx_time; ++ if (!usecs) ++ usecs = 1000000; ++ ++ /* To avoid rounding issues, probabilities scale from 0 (0%) ++ * to 18000 (100%) */ ++ if (mr->attempts) { ++ p = (mr->success * 18000) / mr->attempts; ++ mr->succ_hist += mr->success; ++ mr->att_hist += mr->attempts; ++ mr->cur_prob = p; ++ p = ((p * (100 - mp->ewma_level)) + (mr->probability * ++ mp->ewma_level)) / 100; ++ mr->probability = p; ++ mr->cur_tp = p * (1000000 / usecs); ++ } ++ ++ mr->last_success = mr->success; ++ mr->last_attempts = mr->attempts; ++ mr->success = 0; ++ mr->attempts = 0; ++ ++ /* Sample less often below the 10% chance of success. ++ * Sample less often above the 95% chance of success. */ ++ if ((mr->probability > 17100) || (mr->probability < 1800)) { ++ mr->adjusted_retry_count = mr->retry_count >> 1; ++ if (mr->adjusted_retry_count > 2) ++ mr->adjusted_retry_count = 2; ++ } else { ++ mr->adjusted_retry_count = mr->retry_count; ++ } ++ if (!mr->adjusted_retry_count) ++ mr->adjusted_retry_count = 2; ++ } ++ ++ for (i = 0; i < mi->n_rates; i++) { ++ struct minstrel_rate *mr = &mi->r[i]; ++ if (max_tp < mr->cur_tp) { ++ index_max_tp = i; ++ max_tp = mr->cur_tp; ++ } ++ if (max_prob < mr->probability) { ++ index_max_prob = i; ++ max_prob = mr->probability; ++ } ++ } ++ ++ max_tp = 0; ++ for (i = 0; i < mi->n_rates; i++) { ++ struct minstrel_rate *mr = &mi->r[i]; ++ ++ if (i == index_max_tp) ++ continue; ++ ++ if (max_tp < mr->cur_tp) { ++ index_max_tp2 = i; ++ max_tp = mr->cur_tp; ++ } ++ } ++ mi->max_tp_rate = index_max_tp; ++ mi->max_tp_rate2 = index_max_tp2; ++ mi->max_prob_rate = index_max_prob; ++} ++ ++static void ++minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta, void *priv_sta, ++ struct sk_buff *skb) ++{ ++ struct minstrel_sta_info *mi = priv_sta; ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ struct ieee80211_tx_altrate *ar = info->status.retries; ++ struct minstrel_priv *mp = priv; ++ int i, ndx, tries; ++ int success = 0; ++ ++ if (!info->status.excessive_retries) ++ success = 1; ++ ++ if (!mp->has_mrr || (ar[0].rate_idx < 0)) { ++ ndx = rix_to_ndx(mi, info->tx_rate_idx); ++ tries = info->status.retry_count + 1; ++ mi->r[ndx].success += success; ++ mi->r[ndx].attempts += tries; ++ return; ++ } ++ ++ for (i = 0; i < 4; i++) { ++ if (ar[i].rate_idx < 0) ++ break; ++ ++ ndx = rix_to_ndx(mi, ar[i].rate_idx); ++ mi->r[ndx].attempts += ar[i].limit + 1; ++ ++ if ((i != 3) && (ar[i + 1].rate_idx < 0)) ++ mi->r[ndx].success += success; ++ } ++ ++ if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) ++ mi->sample_count++; ++ ++ if (mi->sample_deferred > 0) ++ mi->sample_deferred--; ++} ++ ++ ++static inline unsigned int ++minstrel_get_retry_count(struct minstrel_rate *mr, ++ struct ieee80211_tx_info *info) ++{ ++ unsigned int retry = mr->adjusted_retry_count; ++ ++ if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ++ retry = max(2U, min(mr->retry_count_rtscts, retry)); ++ else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) ++ retry = max(2U, min(mr->retry_count_cts, retry)); ++ return retry; ++} ++ ++ ++static int ++minstrel_get_next_sample(struct minstrel_sta_info *mi) ++{ ++ unsigned int sample_ndx; ++ sample_ndx = SAMPLE_TBL(mi, mi->sample_idx, mi->sample_column); ++ mi->sample_idx++; ++ if (mi->sample_idx > (mi->n_rates - 2)) { ++ mi->sample_idx = 0; ++ mi->sample_column++; ++ if (mi->sample_column >= SAMPLE_COLUMNS) ++ mi->sample_column = 0; ++ } ++ return sample_ndx; ++} ++ ++void ++minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta, void *priv_sta, ++ struct sk_buff *skb, struct rate_selection *sel) ++{ ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ struct minstrel_sta_info *mi = priv_sta; ++ struct minstrel_priv *mp = priv; ++ struct ieee80211_tx_altrate *ar = info->control.retries; ++ unsigned int ndx, sample_ndx = 0; ++ bool mrr; ++ bool sample_slower = false; ++ bool sample = false; ++ int i, delta; ++ int mrr_ndx[3]; ++ int sample_rate; ++ ++ if (!sta || !mi || use_low_rate(skb)) { ++ sel->rate_idx = rate_lowest_index(sband, sta); ++ return; ++ } ++ ++ mrr = mp->has_mrr; ++ ++ /* mac80211 does not allow mrr for RTS/CTS */ ++ if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) || ++ (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) ++ mrr = false; ++ ++ if (time_after(jiffies, mi->stats_update + (mp->update_interval * ++ HZ) / 1000)) ++ minstrel_update_stats(mp, mi); ++ ++ ndx = mi->max_tp_rate; ++ ++ if (mrr) ++ sample_rate = mp->lookaround_rate_mrr; ++ else ++ sample_rate = mp->lookaround_rate; ++ ++ mi->packet_count++; ++ delta = (mi->packet_count * sample_rate / 100) - ++ (mi->sample_count + mi->sample_deferred / 2); ++ ++ /* delta > 0: sampling required */ ++ if (delta > 0) { ++ if (mi->packet_count >= 10000) { ++ mi->sample_deferred = 0; ++ mi->sample_count = 0; ++ mi->packet_count = 0; ++ } else if (delta > mi->n_rates * 2) { ++ /* With multi-rate retry, not every planned sample ++ * attempt actually gets used, due to the way the retry ++ * chain is set up - [max_tp,sample,prob,lowest] for ++ * sample_rate < max_tp. ++ * ++ * If there's too much sampling backlog and the link ++ * starts getting worse, minstrel would start bursting ++ * out lots of sampling frames, which would result ++ * in a large throughput loss. */ ++ mi->sample_count += (delta - mi->n_rates * 2); ++ } ++ ++ sample_ndx = minstrel_get_next_sample(mi); ++ sample = true; ++ sample_slower = mrr && (mi->r[sample_ndx].perfect_tx_time > ++ mi->r[ndx].perfect_tx_time); ++ ++ if (!sample_slower) { ++ ndx = sample_ndx; ++ mi->sample_count++; ++ } else { ++ /* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark ++ * packets that have the sampling rate deferred to the ++ * second MRR stage. Increase the sample counter only ++ * if the deferred sample rate was actually used. ++ * Use the sample_deferred counter to make sure that ++ * the sampling is not done in large bursts */ ++ info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; ++ mi->sample_deferred++; ++ } ++ } ++ sel->rate_idx = mi->r[ndx].rix; ++ info->control.retry_limit = minstrel_get_retry_count(&mi->r[ndx], info); ++ ++ if (!mrr) { ++ ar[0].rate_idx = mi->lowest_rix; ++ ar[0].limit = mp->max_retry; ++ ar[1].rate_idx = -1; ++ return; ++ } ++ ++ /* MRR setup */ ++ if (sample) { ++ if (sample_slower) ++ mrr_ndx[0] = sample_ndx; ++ else ++ mrr_ndx[0] = mi->max_tp_rate; ++ } else { ++ mrr_ndx[0] = mi->max_tp_rate2; ++ } ++ mrr_ndx[1] = mi->max_prob_rate; ++ mrr_ndx[2] = 0; ++ for (i = 0; i < 3; i++) { ++ ar[i].rate_idx = mi->r[mrr_ndx[i]].rix; ++ ar[i].limit = mi->r[mrr_ndx[i]].adjusted_retry_count; ++ } ++} ++ ++ ++static void ++calc_rate_durations(struct minstrel_sta_info *mi, struct ieee80211_local *local, ++ struct minstrel_rate *d, struct ieee80211_rate *rate) ++{ ++ int erp = !!(rate->flags & IEEE80211_RATE_ERP_G); ++ ++ d->perfect_tx_time = ieee80211_frame_duration(local, 1200, ++ rate->bitrate, erp, 1); ++ d->ack_time = ieee80211_frame_duration(local, 10, ++ rate->bitrate, erp, 1); ++} ++ ++static void ++init_sample_table(struct minstrel_sta_info *mi) ++{ ++ unsigned int i, col, new_idx; ++ unsigned int n_srates = mi->n_rates - 1; ++ u8 rnd[8]; ++ ++ mi->sample_column = 0; ++ mi->sample_idx = 0; ++ memset(mi->sample_table, 0, SAMPLE_COLUMNS * mi->n_rates); ++ ++ for (col = 0; col < SAMPLE_COLUMNS; col++) { ++ for (i = 0; i < n_srates; i++) { ++ get_random_bytes(rnd, sizeof(rnd)); ++ new_idx = (i + rnd[i & 7]) % n_srates; ++ ++ while (SAMPLE_TBL(mi, new_idx, col) != 0) ++ new_idx = (new_idx + 1) % n_srates; ++ ++ /* Don't sample the slowest rate (i.e. slowest base ++ * rate). We must presume that the slowest rate works ++ * fine, or else other management frames will also be ++ * failing and the link will break */ ++ SAMPLE_TBL(mi, new_idx, col) = i + 1; ++ } ++ } ++} ++ ++static void ++minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta, void *priv_sta) ++{ ++ struct minstrel_sta_info *mi = priv_sta; ++ struct minstrel_priv *mp = priv; ++ struct minstrel_rate *mr_ctl; ++ unsigned int i, n = 0; ++ unsigned int t_slot = 9; /* FIXME: get real slot time */ ++ ++ mi->lowest_rix = rate_lowest_index(sband, sta); ++ mr_ctl = &mi->r[rix_to_ndx(mi, mi->lowest_rix)]; ++ mi->sp_ack_dur = mr_ctl->ack_time; ++ ++ for (i = 0; i < sband->n_bitrates; i++) { ++ struct minstrel_rate *mr = &mi->r[n]; ++ unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; ++ unsigned int tx_time_single; ++ unsigned int cw = mp->cw_min; ++ ++ if (!rate_supported(sta, sband->band, i)) ++ continue; ++ n++; ++ memset(mr, 0, sizeof(*mr)); ++ ++ mr->rix = i; ++ mr->bitrate = sband->bitrates[i].bitrate / 5; ++ calc_rate_durations(mi, hw_to_local(mp->hw), mr, ++ &sband->bitrates[i]); ++ ++ /* calculate maximum number of retransmissions before ++ * fallback (based on maximum segment size) */ ++ mr->retry_count = 1; ++ mr->retry_count_cts = 1; ++ mr->retry_count_rtscts = 1; ++ tx_time = mr->perfect_tx_time + mi->sp_ack_dur; ++ do { ++ /* add one retransmission */ ++ tx_time_single = mr->ack_time + mr->perfect_tx_time; ++ ++ /* contention window */ ++ tx_time_single += t_slot + min(cw, mp->cw_max); ++ cw = (cw + 1) << 1; ++ ++ tx_time += tx_time_single; ++ tx_time_cts += tx_time_single + mi->sp_ack_dur; ++ tx_time_rtscts += tx_time_single + 2 * mi->sp_ack_dur; ++ if ((tx_time_cts < mp->segment_size) && ++ (mr->retry_count_cts < mp->max_retry)) ++ mr->retry_count_cts++; ++ if ((tx_time_rtscts < mp->segment_size) && ++ (mr->retry_count_rtscts < mp->max_retry)) ++ mr->retry_count_rtscts++; ++ } while ((tx_time < mp->segment_size) && ++ (++mr->retry_count < mp->max_retry)); ++ mr->adjusted_retry_count = mr->retry_count; ++ } ++ ++ for (i = n; i < sband->n_bitrates; i++) { ++ struct minstrel_rate *mr = &mi->r[i]; ++ mr->rix = -1; ++ } ++ ++ mi->n_rates = n; ++ mi->stats_update = jiffies; ++ ++ init_sample_table(mi); ++} ++ ++static void * ++minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) ++{ ++ struct ieee80211_supported_band *sband; ++ struct minstrel_sta_info *mi; ++ struct minstrel_priv *mp = priv; ++ struct ieee80211_hw *hw = mp->hw; ++ int max_rates = 0; ++ int i; ++ ++ mi = kzalloc(sizeof(struct minstrel_sta_info), gfp); ++ if (!mi) ++ return NULL; ++ ++ for (i = 0; i < IEEE80211_NUM_BANDS; i++) { ++ sband = hw->wiphy->bands[hw->conf.channel->band]; ++ if (sband->n_bitrates > max_rates) ++ max_rates = sband->n_bitrates; ++ } ++ ++ mi->r = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp); ++ if (!mi->r) ++ goto error; ++ ++ mi->sample_table = kmalloc(SAMPLE_COLUMNS * max_rates, gfp); ++ if (!mi->sample_table) ++ goto error1; ++ ++ mi->stats_update = jiffies; ++ return mi; ++ ++error1: ++ kfree(mi->r); ++error: ++ kfree(mi); ++ return NULL; ++} ++ ++static void ++minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) ++{ ++ struct minstrel_sta_info *mi = priv_sta; ++ ++ kfree(mi->sample_table); ++ kfree(mi->r); ++ kfree(mi); ++} ++ ++static void ++minstrel_clear(void *priv) ++{ ++} ++ ++static void * ++minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) ++{ ++ struct minstrel_priv *mp; ++ ++ mp = kzalloc(sizeof(struct minstrel_priv), GFP_ATOMIC); ++ if (!mp) ++ return NULL; ++ ++ /* contention window settings ++ * Just an approximation. Using the per-queue values would complicate ++ * the calculations and is probably unnecessary */ ++ mp->cw_min = 15; ++ mp->cw_max = 1023; ++ ++ /* number of packets (in %) to use for sampling other rates ++ * sample less often for non-mrr packets, because the overhead ++ * is much higher than with mrr */ ++ mp->lookaround_rate = 5; ++ mp->lookaround_rate_mrr = 10; ++ ++ /* moving average weight for EWMA */ ++ mp->ewma_level = 75; ++ ++ /* maximum time that the hw is allowed to stay in one MRR segment */ ++ mp->segment_size = 6000; ++ ++ if (hw->max_altrate_tries > 0) ++ mp->max_retry = hw->max_altrate_tries; ++ else ++ /* safe default, does not necessarily have to match hw properties */ ++ mp->max_retry = 7; ++ ++ if (hw->max_altrates >= 3) ++ mp->has_mrr = true; ++ ++ mp->hw = hw; ++ mp->update_interval = 100; ++ ++ return mp; ++} ++ ++static void ++minstrel_free(void *priv) ++{ ++ kfree(priv); ++} ++ ++static struct rate_control_ops mac80211_minstrel = { ++ .name = "minstrel", ++ .tx_status = minstrel_tx_status, ++ .get_rate = minstrel_get_rate, ++ .rate_init = minstrel_rate_init, ++ .clear = minstrel_clear, ++ .alloc = minstrel_alloc, ++ .free = minstrel_free, ++ .alloc_sta = minstrel_alloc_sta, ++ .free_sta = minstrel_free_sta, ++#ifdef CONFIG_MAC80211_DEBUGFS ++ .add_sta_debugfs = minstrel_add_sta_debugfs, ++ .remove_sta_debugfs = minstrel_remove_sta_debugfs, ++#endif ++}; ++ ++int __init ++rc80211_minstrel_init(void) ++{ ++ return ieee80211_rate_control_register(&mac80211_minstrel); ++} ++ ++void ++rc80211_minstrel_exit(void) ++{ ++ ieee80211_rate_control_unregister(&mac80211_minstrel); ++} ++ +--- a/net/mac80211/Makefile ++++ b/net/mac80211/Makefile +@@ -41,4 +41,8 @@ + rc80211_pid-y := rc80211_pid_algo.o + rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o + ++rc80211_minstrel-y := rc80211_minstrel.o ++rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o ++ + mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y) ++mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -1015,6 +1015,10 @@ + BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) + + IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb)); + ++ ret = rc80211_minstrel_init(); ++ if (ret) ++ return ret; ++ + ret = rc80211_pid_init(); + if (ret) + return ret; +@@ -1027,6 +1031,7 @@ + static void __exit ieee80211_exit(void) + { + rc80211_pid_exit(); ++ rc80211_minstrel_exit(); + + /* + * For key todo, it'll be empty by now but the work +--- a/net/mac80211/rate.h ++++ b/net/mac80211/rate.h +@@ -125,4 +125,18 @@ + } + #endif + ++#ifdef CONFIG_MAC80211_RC_MINSTREL ++extern int rc80211_minstrel_init(void); ++extern void rc80211_minstrel_exit(void); ++#else ++static inline int rc80211_minstrel_init(void) ++{ ++ return 0; ++} ++static inline void rc80211_minstrel_exit(void) ++{ ++} ++#endif ++ ++ + #endif /* IEEE80211_RATE_H */ +--- /dev/null ++++ b/net/mac80211/rc80211_minstrel.h +@@ -0,0 +1,85 @@ ++/* ++ * Copyright (C) 2008 Felix Fietkau ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __RC_MINSTREL_H ++#define __RC_MINSTREL_H ++ ++struct minstrel_rate { ++ int bitrate; ++ int rix; ++ ++ unsigned int perfect_tx_time; ++ unsigned int ack_time; ++ ++ unsigned int retry_count; ++ unsigned int retry_count_cts; ++ unsigned int retry_count_rtscts; ++ unsigned int adjusted_retry_count; ++ ++ u32 success; ++ u32 attempts; ++ u32 last_attempts; ++ u32 last_success; ++ ++ /* parts per thousand */ ++ u32 cur_prob; ++ u32 probability; ++ ++ /* per-rate throughput */ ++ u32 cur_tp; ++ u32 throughput; ++ ++ u64 succ_hist; ++ u64 att_hist; ++}; ++ ++struct minstrel_sta_info { ++ unsigned long stats_update; ++ unsigned int sp_ack_dur; ++ unsigned int rate_avg; ++ ++ unsigned int lowest_rix; ++ ++ unsigned int max_tp_rate; ++ unsigned int max_tp_rate2; ++ unsigned int max_prob_rate; ++ unsigned int packet_count; ++ unsigned int sample_count; ++ int sample_deferred; ++ ++ unsigned int sample_idx; ++ unsigned int sample_column; ++ ++ int n_rates; ++ struct minstrel_rate *r; ++ ++ /* sampling table */ ++ u8 *sample_table; ++ ++#ifdef CONFIG_MAC80211_DEBUGFS ++ struct dentry *dbg_stats; ++#endif ++}; ++ ++struct minstrel_priv { ++ struct ieee80211_hw *hw; ++ bool has_mrr; ++ unsigned int cw_min; ++ unsigned int cw_max; ++ unsigned int max_retry; ++ unsigned int ewma_level; ++ unsigned int segment_size; ++ unsigned int update_interval; ++ unsigned int lookaround_rate; ++ unsigned int lookaround_rate_mrr; ++}; ++ ++void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); ++void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); ++ ++#endif +--- /dev/null ++++ b/net/mac80211/rc80211_minstrel_debugfs.c +@@ -0,0 +1,164 @@ ++/* ++ * Copyright (C) 2008 Felix Fietkau ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Based on minstrel.c: ++ * Copyright (C) 2005-2007 Derek Smithies ++ * Sponsored by Indranet Technologies Ltd ++ * ++ * Based on sample.c: ++ * Copyright (c) 2005 John Bicket ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce at minimum a disclaimer ++ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any ++ * redistribution must be conditioned upon including a substantially ++ * similar Disclaimer requirement for further binary redistribution. ++ * 3. Neither the names of the above-listed copyright holders nor the names ++ * of any contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * Alternatively, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2 as published by the Free ++ * Software Foundation. ++ * ++ * NO WARRANTY ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY ++ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, ++ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER ++ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF ++ * THE POSSIBILITY OF SUCH DAMAGES. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rc80211_minstrel.h" ++ ++struct minstrel_stats_info { ++ struct minstrel_sta_info *mi; ++ char buf[4096]; ++ size_t len; ++}; ++ ++static int ++minstrel_stats_open(struct inode *inode, struct file *file) ++{ ++ struct minstrel_sta_info *mi = inode->i_private; ++ struct minstrel_stats_info *ms; ++ unsigned int i, tp, prob, eprob; ++ char *p; ++ ++ ms = kmalloc(sizeof(*ms), GFP_KERNEL); ++ if (!ms) ++ return -ENOMEM; ++ ++ file->private_data = ms; ++ p = ms->buf; ++ p += sprintf(p, "rate throughput ewma prob this prob " ++ "this succ/attempt success attempts\n"); ++ for (i = 0; i < mi->n_rates; i++) { ++ struct minstrel_rate *mr = &mi->r[i]; ++ ++ *(p++) = (i == mi->max_tp_rate) ? 'T' : ' '; ++ *(p++) = (i == mi->max_tp_rate2) ? 't' : ' '; ++ *(p++) = (i == mi->max_prob_rate) ? 'P' : ' '; ++ p += sprintf(p, "%3u%s", mr->bitrate / 2, ++ (mr->bitrate & 1 ? ".5" : " ")); ++ ++ tp = ((mr->cur_tp * 96) / 18000) >> 10; ++ prob = mr->cur_prob / 18; ++ eprob = mr->probability / 18; ++ ++ p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " ++ "%3u(%3u) %8llu %8llu\n", ++ tp / 10, tp % 10, ++ eprob / 10, eprob % 10, ++ prob / 10, prob % 10, ++ mr->last_success, ++ mr->last_attempts, ++ mr->succ_hist, ++ mr->att_hist); ++ } ++ p += sprintf(p, "\nTotal packet count:: ideal %d " ++ "lookaround %d\n\n", ++ mi->packet_count - mi->sample_count, ++ mi->sample_count); ++ ms->len = p - ms->buf; ++ ++ return 0; ++} ++ ++static int ++minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *o) ++{ ++ struct minstrel_stats_info *ms; ++ char *src; ++ ++ ms = file->private_data; ++ src = ms->buf; ++ ++ len = min(len, ms->len); ++ if (len <= *o) ++ return 0; ++ ++ src += *o; ++ len -= *o; ++ *o += len; ++ ++ if (copy_to_user(buf, src, len)) ++ return -EFAULT; ++ ++ return len; ++} ++ ++static int ++minstrel_stats_release(struct inode *inode, struct file *file) ++{ ++ struct minstrel_stats_info *ms = file->private_data; ++ ++ kfree(ms); ++ ++ return 0; ++} ++ ++static struct file_operations minstrel_stat_fops = { ++ .owner = THIS_MODULE, ++ .open = minstrel_stats_open, ++ .read = minstrel_stats_read, ++ .release = minstrel_stats_release, ++}; ++ ++void ++minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) ++{ ++ struct minstrel_sta_info *mi = priv_sta; ++ ++ mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi, ++ &minstrel_stat_fops); ++} ++ ++void ++minstrel_remove_sta_debugfs(void *priv, void *priv_sta) ++{ ++ struct minstrel_sta_info *mi = priv_sta; ++ ++ debugfs_remove(mi->dbg_stats); ++} +--- a/net/mac80211/Kconfig ++++ b/net/mac80211/Kconfig +@@ -22,6 +22,11 @@ + mac80211 that uses a PID controller to select the TX + rate. + ++config MAC80211_RC_MINSTREL ++ bool "Minstrel" ++ ---help--- ++ This option enables the 'minstrel' TX rate control algorithm ++ + choice + prompt "Default rate control algorithm" + default MAC80211_RC_DEFAULT_PID +@@ -39,11 +44,19 @@ + default rate control algorithm. You should choose + this unless you know what you are doing. + ++config MAC80211_RC_DEFAULT_MINSTREL ++ bool "Minstrel" ++ depends on MAC80211_RC_MINSTREL ++ ---help--- ++ Select Minstrel as the default rate control algorithm. ++ ++ + endchoice + + config MAC80211_RC_DEFAULT + string + default "pid" if MAC80211_RC_DEFAULT_PID ++ default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL + default "" + + endmenu