diff options
| -rw-r--r-- | package/mac80211/Makefile | 21 | ||||
| -rw-r--r-- | package/mac80211/patches-old/001-disable_drivers.patch (renamed from package/mac80211/patches/001-disable_drivers.patch) | 0 | ||||
| -rw-r--r-- | package/mac80211/patches-old/004-allow-ap-vlan-modes.patch (renamed from package/mac80211/patches/004-allow-ap-vlan-modes.patch) | 0 | ||||
| -rw-r--r-- | package/mac80211/patches/000-kconfig.patch | 201 | ||||
| -rw-r--r-- | package/mac80211/patches/100-define.patch | 14 | ||||
| -rw-r--r-- | package/mac80211/patches/110-ath5k_stat.patch | 53 | ||||
| -rw-r--r-- | package/mac80211/patches/200-hwkey_len.patch | 183 | ||||
| -rw-r--r-- | package/mac80211/patches/210-mrr.patch | 183 | ||||
| -rw-r--r-- | package/mac80211/patches/250-ath5k_mrr.patch | 208 | ||||
| -rw-r--r-- | package/mac80211/patches/299-compat_minstrel.patch | 32 | ||||
| -rw-r--r-- | package/mac80211/patches/300-minstrel.patch | 928 | 
11 files changed, 1818 insertions, 5 deletions
diff --git a/package/mac80211/Makefile b/package/mac80211/Makefile index a3b483cb5..cd1feed8c 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 index ccf040915..ccf040915 100644 --- a/package/mac80211/patches/001-disable_drivers.patch +++ b/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 index a8080b20e..a8080b20e 100644 --- a/package/mac80211/patches/004-allow-ap-vlan-modes.patch +++ b/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 000000000..bcbf8868b --- /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 000000000..b41cad694 --- /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 <linux/compat_autoconf.h> +  + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) ++#include <linux/if_ether.h> ++ ++#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 000000000..2cd954851 --- /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 <nbd@openwrt.org> + +--- 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 000000000..20deeb778 --- /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 <nbd@openwrt.org> + +--- 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 000000000..389478824 --- /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 <nbd@openwrt.org> + +--- 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 000000000..ff15fd4b7 --- /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 <nbd@openwrt.org> + +--- 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 000000000..7f5019780 --- /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 000000000..ee1887a6c --- /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 <nbd@openwrt.org> ++ * ++ * 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 <derek@indranet.co.nz> ++ *   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 <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/skbuff.h> ++#include <linux/debugfs.h> ++#include <linux/random.h> ++#include <linux/ieee80211.h> ++#include <net/mac80211.h> ++#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 <nbd@openwrt.org> ++ * ++ * 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 <nbd@openwrt.org> ++ * ++ * 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 <derek@indranet.co.nz> ++ *   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 <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/skbuff.h> ++#include <linux/debugfs.h> ++#include <linux/ieee80211.h> ++#include <net/mac80211.h> ++#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  | 
