aboutsummaryrefslogtreecommitdiffstats
path: root/package/madwifi/patches
diff options
context:
space:
mode:
authorblogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-10-05 10:12:53 +0000
committerblogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-10-05 10:12:53 +0000
commit5c105d9f3fd086aff195d3849dcf847d6b0bd927 (patch)
tree1229a11f725bfa58aa7c57a76898553bb5f6654a /package/madwifi/patches
downloadopenwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.tar.gz
openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.zip
branch Attitude Adjustment
git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/madwifi/patches')
-rw-r--r--package/madwifi/patches/102-multicall_binary.patch315
-rw-r--r--package/madwifi/patches/104-autocreate_none.patch11
-rw-r--r--package/madwifi/patches/105-ratectl_attach.patch23
-rw-r--r--package/madwifi/patches/106-get_arch.patch21
-rw-r--r--package/madwifi/patches/111-minstrel_crash.patch12
-rw-r--r--package/madwifi/patches/113-no_ibss_pwrsave.patch12
-rw-r--r--package/madwifi/patches/122-replayfail_workaround.patch12
-rw-r--r--package/madwifi/patches/123-ccmp_checks.patch95
-rw-r--r--package/madwifi/patches/124-linux24_compat.patch202
-rw-r--r--package/madwifi/patches/126-rxerr_frames.patch13
-rw-r--r--package/madwifi/patches/200-no_debug.patch408
-rw-r--r--package/madwifi/patches/201-debug_fix.patch20
-rw-r--r--package/madwifi/patches/202-debug_variables.patch204
-rw-r--r--package/madwifi/patches/300-napi_polling.patch536
-rw-r--r--package/madwifi/patches/305-pureg_fix.patch168
-rw-r--r--package/madwifi/patches/309-micfail_detect.patch321
-rw-r--r--package/madwifi/patches/310-noise_get.patch55
-rw-r--r--package/madwifi/patches/311-bssid_alloc.patch11
-rw-r--r--package/madwifi/patches/312-erpupdate.patch68
-rw-r--r--package/madwifi/patches/317-bmask.patch13
-rw-r--r--package/madwifi/patches/323-dfs_optional.patch38
-rw-r--r--package/madwifi/patches/324-alignment.patch19
-rw-r--r--package/madwifi/patches/325-channel_spam.patch28
-rw-r--r--package/madwifi/patches/327-queue.patch40
-rw-r--r--package/madwifi/patches/330-beaconcal.patch166
-rw-r--r--package/madwifi/patches/331-memory_alloc.patch36
-rw-r--r--package/madwifi/patches/332-reset_beacons.patch11
-rw-r--r--package/madwifi/patches/333-apscan_mode.patch15
-rw-r--r--package/madwifi/patches/334-input.patch12
-rw-r--r--package/madwifi/patches/340-maxrate.patch98
-rw-r--r--package/madwifi/patches/341-minrate.patch114
-rw-r--r--package/madwifi/patches/342-performance.patch263
-rw-r--r--package/madwifi/patches/343-txqueue_races.patch34
-rw-r--r--package/madwifi/patches/344-minstrel_failcnt.patch11
-rw-r--r--package/madwifi/patches/345-minstrel_sampling.patch80
-rw-r--r--package/madwifi/patches/346-protmode_trig.patch135
-rw-r--r--package/madwifi/patches/347-tuning.patch99
-rw-r--r--package/madwifi/patches/348-ackcts.patch38
-rw-r--r--package/madwifi/patches/349-reset.patch12
-rw-r--r--package/madwifi/patches/350-wisoc_softled.patch11
-rw-r--r--package/madwifi/patches/351-scanlist.patch904
-rw-r--r--package/madwifi/patches/352-ani_fix.patch265
-rw-r--r--package/madwifi/patches/353-devid.patch19
-rw-r--r--package/madwifi/patches/354-lantiq_eeprom.patch95
-rw-r--r--package/madwifi/patches/355-eap_auth_disassoc.patch77
-rw-r--r--package/madwifi/patches/356-hidden_ssid.patch49
-rw-r--r--package/madwifi/patches/357-bgscan_thresh.patch160
-rw-r--r--package/madwifi/patches/358-ignore_broken_bssid.patch18
-rw-r--r--package/madwifi/patches/359-disable_reassoc.patch31
-rw-r--r--package/madwifi/patches/360-sta_nodes.patch242
-rw-r--r--package/madwifi/patches/361-bmiss_handling.patch102
-rw-r--r--package/madwifi/patches/362-rssithr.patch93
-rw-r--r--package/madwifi/patches/363-fix_turbo.patch11
-rw-r--r--package/madwifi/patches/364-memory_alloc.patch13
-rw-r--r--package/madwifi/patches/365-turbo_channelsearch.patch10
-rw-r--r--package/madwifi/patches/366-bstuck_thresh.patch52
-rw-r--r--package/madwifi/patches/367-roaming.patch77
-rw-r--r--package/madwifi/patches/368-sta_ie_preserve.patch49
-rw-r--r--package/madwifi/patches/369-mlme_assoc.patch10
-rw-r--r--package/madwifi/patches/370-wdsvap.patch1665
-rw-r--r--package/madwifi/patches/372-queue_vif.patch39
-rw-r--r--package/madwifi/patches/373-sanity_check.patch12
-rw-r--r--package/madwifi/patches/374-nbtt_fix.patch22
-rw-r--r--package/madwifi/patches/375-atim_tsf_update.patch141
-rw-r--r--package/madwifi/patches/377-disable_vlan_code.patch25
-rw-r--r--package/madwifi/patches/378-adhoc_crash_fix.patch14
-rw-r--r--package/madwifi/patches/379-invalid_rate_fix.patch405
-rw-r--r--package/madwifi/patches/380-noderef_hack.patch13
-rw-r--r--package/madwifi/patches/381-ibss_modes.patch23
-rw-r--r--package/madwifi/patches/382-relax_bintval.patch13
-rw-r--r--package/madwifi/patches/383-ibss_hostap.patch105
-rw-r--r--package/madwifi/patches/384-hwdetect.patch325
-rw-r--r--package/madwifi/patches/385-antenna_fix.patch10
-rw-r--r--package/madwifi/patches/386-acl_crashfix.patch116
-rw-r--r--package/madwifi/patches/387-maxassoc.patch85
-rw-r--r--package/madwifi/patches/388-apsta_fix.patch60
-rw-r--r--package/madwifi/patches/389-autochannel.patch249
-rw-r--r--package/madwifi/patches/390-frame_type.patch13
-rw-r--r--package/madwifi/patches/391-vap_auth.patch29
-rw-r--r--package/madwifi/patches/392-remove_wds_nodetracking.patch388
-rw-r--r--package/madwifi/patches/393-mbss_vap_auth.patch491
-rw-r--r--package/madwifi/patches/394-probereq.patch64
-rw-r--r--package/madwifi/patches/395-ath_ff_unmap.patch11
-rw-r--r--package/madwifi/patches/396-napi_ff_fix.patch65
-rw-r--r--package/madwifi/patches/400-new_hal.patch153
-rw-r--r--package/madwifi/patches/401-changeset_r3602.patch11
-rw-r--r--package/madwifi/patches/402-changeset_r3603.patch176
-rw-r--r--package/madwifi/patches/403-changeset_r3605.patch70
-rw-r--r--package/madwifi/patches/404-linux24_fix.patch15
-rw-r--r--package/madwifi/patches/405-retransmit_check.patch22
-rw-r--r--package/madwifi/patches/406-monitor_r3711.patch20
-rw-r--r--package/madwifi/patches/407-new_athinfo.patch2352
-rw-r--r--package/madwifi/patches/408-changeset_r3337.patch34
-rw-r--r--package/madwifi/patches/409-wext_compat.patch133
-rw-r--r--package/madwifi/patches/410-ar231x_2.6.28.patch281
-rw-r--r--package/madwifi/patches/411-autochannel_multi.patch347
-rw-r--r--package/madwifi/patches/412-fragmentation_fix.patch10
-rw-r--r--package/madwifi/patches/413-rxorn.patch31
-rw-r--r--package/madwifi/patches/414-txpower.patch262
-rw-r--r--package/madwifi/patches/415-chan_switch.patch187
-rw-r--r--package/madwifi/patches/416-wprobe.patch549
-rw-r--r--package/madwifi/patches/417-beacon_txpower.patch81
-rw-r--r--package/madwifi/patches/419-skb_unmap_crash.patch20
-rw-r--r--package/madwifi/patches/420-diversity_fix.patch86
-rw-r--r--package/madwifi/patches/421-channel_handling.patch1351
-rw-r--r--package/madwifi/patches/422-confchange_reset.patch31
-rw-r--r--package/madwifi/patches/423-phyerr_handling.patch28
-rw-r--r--package/madwifi/patches/424-timing.patch764
-rw-r--r--package/madwifi/patches/425-rc_rexmit.patch506
-rw-r--r--package/madwifi/patches/426-header_len.patch12
-rw-r--r--package/madwifi/patches/427-ignore_eeprom_ff.patch11
-rw-r--r--package/madwifi/patches/430-use_netdev_priv.patch1936
-rw-r--r--package/madwifi/patches/431-compile_fixes.patch35
-rw-r--r--package/madwifi/patches/432-netdev_ops.patch184
-rw-r--r--package/madwifi/patches/433-backport_remove_irq_none.patch21
-rw-r--r--package/madwifi/patches/434-name-alloc-fix.patch28
-rw-r--r--package/madwifi/patches/435-ibss_neighbor_fix.patch11
-rw-r--r--package/madwifi/patches/436-injection_checks.patch26
-rw-r--r--package/madwifi/patches/437-sysctl_cleanup.patch73
-rw-r--r--package/madwifi/patches/438-poweroffset_sysctl.patch59
-rw-r--r--package/madwifi/patches/439-wlanconfig_stack_usage.patch20
-rw-r--r--package/madwifi/patches/440-wme_cleanup.patch136
-rw-r--r--package/madwifi/patches/441-fix_ibss_node_handling.patch91
-rw-r--r--package/madwifi/patches/442-ibss_rx_filter.patch20
-rw-r--r--package/madwifi/patches/443-tx_drop_counter.patch25
-rw-r--r--package/madwifi/patches/444-beacon_update_war.patch17
-rw-r--r--package/madwifi/patches/445-fix_ps_sta_count.patch18
-rw-r--r--package/madwifi/patches/446-single_module.patch778
-rw-r--r--package/madwifi/patches/447-sta_reconnect.patch25
-rw-r--r--package/madwifi/patches/448-beacon_handling_fixes.patch407
-rw-r--r--package/madwifi/patches/449-fix_txbuf_leak.patch10
-rw-r--r--package/madwifi/patches/450-calibration.patch177
-rw-r--r--package/madwifi/patches/451-ibss_race_fix.patch342
-rw-r--r--package/madwifi/patches/452-minstrel_no_timer.patch134
-rw-r--r--package/madwifi/patches/453-procps.patch55
-rw-r--r--package/madwifi/patches/454-cca.patch186
-rw-r--r--package/madwifi/patches/455-beacon_watchdog.patch95
-rw-r--r--package/madwifi/patches/456-rfsilent.patch85
-rw-r--r--package/madwifi/patches/457-idletime.patch151
-rw-r--r--package/madwifi/patches/458-ibss_wpa_none.patch13
-rw-r--r--package/madwifi/patches/459-2.6.33_compile.patch524
-rw-r--r--package/madwifi/patches/460-pci_softled_disable.patch18
-rw-r--r--package/madwifi/patches/461-rx_stats_count_fix.patch23
-rw-r--r--package/madwifi/patches/462-fix_ap_scan.patch26
-rw-r--r--package/madwifi/patches/463-fix_txrate_display.patch10
-rw-r--r--package/madwifi/patches/464-0dbm_txpower_fix.patch30
-rw-r--r--package/madwifi/patches/465-mc_list-2.6.35.patch40
-rw-r--r--package/madwifi/patches/466-2.6.38_compile.patch14
-rw-r--r--package/madwifi/patches/470-mac_addresss_from_ath5k_platform_data.patch36
-rw-r--r--package/madwifi/patches/471-netdev_ops_mac_mtu.patch30
-rw-r--r--package/madwifi/patches/472-remove_11n_devids.patch20
-rw-r--r--package/madwifi/patches/473-mutex_fix.patch11
-rw-r--r--package/madwifi/patches/474_fix_ssid_scan_length.patch29
-rw-r--r--package/madwifi/patches/475-2.6.39-compile.patch11
-rw-r--r--package/madwifi/patches/476-3.0_detection_fix.patch25
-rw-r--r--package/madwifi/patches/477-3.2_fixes.patch45
-rw-r--r--package/madwifi/patches/478-remove_vlan_code.patch21
157 files changed, 24054 insertions, 0 deletions
diff --git a/package/madwifi/patches/102-multicall_binary.patch b/package/madwifi/patches/102-multicall_binary.patch
new file mode 100644
index 000000000..887a462e9
--- /dev/null
+++ b/package/madwifi/patches/102-multicall_binary.patch
@@ -0,0 +1,315 @@
+--- a/tools/80211debug.c
++++ b/tools/80211debug.c
+@@ -48,6 +48,7 @@
+ #include <ctype.h>
+ #include <getopt.h>
+ #include <err.h>
++#include "do_multi.h"
+
+ #undef ARRAY_SIZE
+ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+@@ -185,7 +186,7 @@ sysctlbyname(const char *oid0, void *old
+ #endif /* __linux__ */
+
+ int
+-main(int argc, char *argv[])
++CMD(a80211debug)(int argc, char *argv[])
+ {
+ const char *ifname = "ath0";
+ const char *cp, *tp;
+--- a/tools/80211stats.c
++++ b/tools/80211stats.c
+@@ -59,6 +59,7 @@
+ #include "net80211/ieee80211.h"
+ #include "net80211/ieee80211_crypto.h"
+ #include "net80211/ieee80211_ioctl.h"
++#include "do_multi.h"
+
+ #ifndef SIOCG80211STATS
+ #define SIOCG80211STATS (SIOCDEVPRIVATE + 2)
+@@ -240,7 +241,7 @@ print_sta_stats(FILE *fd, const u_int8_t
+ }
+
+ int
+-main(int argc, char *argv[])
++CMD(a80211stats)(int argc, char *argv[])
+ {
+ int c, len;
+ struct ieee80211req_sta_info *si;
+--- a/tools/athchans.c
++++ b/tools/athchans.c
+@@ -58,6 +58,7 @@
+ #include "net80211/ieee80211.h"
+ #include "net80211/ieee80211_crypto.h"
+ #include "net80211/ieee80211_ioctl.h"
++#include "do_multi.h"
+
+ static int s = -1;
+ static const char *progname;
+@@ -140,8 +141,9 @@ usage(void)
+ }
+
+ #define MAXCHAN ((int)(sizeof(struct ieee80211req_chanlist) * NBBY))
++
+ int
+-main(int argc, char *argv[])
++CMD(athchans)(int argc, char *argv[])
+ {
+ const char *ifname = "wifi0";
+ struct ieee80211req_chanlist chanlist;
+--- a/tools/athctrl.c
++++ b/tools/athctrl.c
+@@ -52,6 +52,7 @@
+ #include <err.h>
+
+ #include <net/if.h>
++#include "do_multi.h"
+
+ static int
+ setsysctrl(const char *dev, const char *control , u_long value)
+@@ -88,7 +89,7 @@ static void usage(void)
+ }
+
+ int
+-main(int argc, char *argv[])
++CMD(athctrl)(int argc, char *argv[])
+ {
+ char device[IFNAMSIZ + 1];
+ int distance = -1;
+--- a/tools/athdebug.c
++++ b/tools/athdebug.c
+@@ -51,6 +51,7 @@
+ #include <ctype.h>
+ #include <getopt.h>
+ #include <err.h>
++#include "do_multi.h"
+
+ #undef ARRAY_SIZE
+ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+@@ -194,7 +195,7 @@ sysctlbyname(const char *oid0, void *old
+ #endif /* __linux__ */
+
+ int
+-main(int argc, char *argv[])
++CMD(athdebug)(int argc, char *argv[])
+ {
+ #ifdef __linux__
+ const char *ifname = "wifi0";
+--- a/tools/athkey.c
++++ b/tools/athkey.c
+@@ -58,6 +58,7 @@
+ #include "net80211/ieee80211.h"
+ #include "net80211/ieee80211_crypto.h"
+ #include "net80211/ieee80211_ioctl.h"
++#include "do_multi.h"
+
+ static int s = -1;
+ static const char *progname;
+@@ -213,8 +214,7 @@ usage(void)
+ exit(-1);
+ }
+
+-int
+-main(int argc, char *argv[])
++int CMD(athkey)(int argc, char *argv[])
+ {
+ const char *ifname = "wifi0";
+ struct ieee80211req_key setkey;
+--- a/tools/athstats.c
++++ b/tools/athstats.c
+@@ -65,6 +65,7 @@
+
+ #undef ARRAY_SIZE
+ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
++#include "do_multi.h"
+
+ static const struct {
+ u_int phyerr;
+@@ -228,7 +229,7 @@ catchalarm(int signo)
+ }
+
+ int
+-main(int argc, char *argv[])
++CMD(athstats)(int argc, char *argv[])
+ {
+ #ifdef __linux__
+ const char *ifname = "wifi0";
+--- /dev/null
++++ b/tools/do_multi.c
+@@ -0,0 +1,33 @@
++#include <string.h>
++#include <libgen.h>
++#include "do_multi.h"
++
++int
++main(int argc, char *argv[])
++{
++ char *progname;
++ int ret = 0;
++
++ progname = basename(argv[0]);
++
++ if(strcmp(progname, "80211debug") == 0)
++ ret = a80211debug_init(argc, argv);
++ if(strcmp(progname, "80211stats") == 0)
++ ret = a80211stats_init(argc, argv);
++ if(strcmp(progname, "athchans") == 0)
++ ret = athchans_init(argc, argv);
++ if(strcmp(progname, "athctrl") == 0)
++ ret = athctrl_init(argc, argv);
++ if(strcmp(progname, "athdebug") == 0)
++ ret = athdebug_init(argc, argv);
++ if(strcmp(progname, "athkey") == 0)
++ ret = athkey_init(argc, argv);
++ if(strcmp(progname, "athstats") == 0)
++ ret = athstats_init(argc, argv);
++ if(strcmp(progname, "wlanconfig") == 0)
++ ret = wlanconfig_init(argc, argv);
++ if(strcmp(progname, "ath_info") == 0)
++ ret = athinfo_init(argc, argv);
++
++ return ret;
++}
+--- /dev/null
++++ b/tools/do_multi.h
+@@ -0,0 +1,15 @@
++#ifdef DO_MULTI
++int a80211debug_init(int argc, char *argv[]);
++int a80211stats_init(int argc, char *argv[]);
++int athchans_init(int argc, char *argv[]);
++int athctrl_init(int argc, char *argv[]);
++int athdebug_init(int argc, char *argv[]);
++int athkey_init(int argc, char *argv[]);
++int athstats_init(int argc, char *argv[]);
++int wlanconfig_init(int argc, char *argv[]);
++int athinfo_init(int argc, char *argv[]);
++
++#define CMD(name) name##_init
++#else
++#define CMD(name) main
++#endif
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -46,56 +46,55 @@ ifeq ($(HAL),)
+ HAL= $(TOP)/hal
+ endif
+
++all: compile
+
+-ALL= athstats 80211stats athkey athchans athctrl \
++ALLPROGS= athstats 80211stats athkey athchans athctrl \
+ athdebug 80211debug wlanconfig ath_info
+
+-all: $(ALL)
++OBJS= $(patsubst %,%.o,$(ALLPROGS))
+
+-INCS= -I. -I$(HAL) -I$(TOP) -I$(ATH_HAL)
++INCS= -I. -I../ath -I$(HAL) -I$(TOP) -I$(ATH_HAL)
+ CFLAGS= -g -O2 -Wall
+ ALL_CFLAGS= $(CFLAGS) $(INCS)
+ LDFLAGS=
+
+-all: $(ALL)
+
+-athstats: athstats.c
+- $(CC) -o athstats $(ALL_CFLAGS) -I$(TOP)/ath $(LDFLAGS) athstats.c
+-80211stats: 80211stats.c
+- $(CC) -o 80211stats $(ALL_CFLAGS) $(LDFLAGS) 80211stats.c
+-athkey: athkey.c
+- $(CC) -o athkey $(ALL_CFLAGS) $(LDFLAGS) athkey.c
+-athchans: athchans.c
+- $(CC) -o athchans $(ALL_CFLAGS) $(LDFLAGS) athchans.c
+-athctrl: athctrl.c
+- $(CC) -o athctrl $(ALL_CFLAGS) $(LDFLAGS) athctrl.c
+-athdebug: athdebug.c
+- $(CC) -o athdebug $(ALL_CFLAGS) $(LDFLAGS) athdebug.c
+-wlanconfig: wlanconfig.c
+- $(CC) -o wlanconfig $(ALL_CFLAGS) $(LDFLAGS) wlanconfig.c
+-80211debug: 80211debug.c
+- $(CC) -o 80211debug $(ALL_CFLAGS) $(LDFLAGS) 80211debug.c
+-ath_info: ath_info.c
+- $(CC) -o ath_info $(CFLAGS) ath_info.c
++ifneq ($(DO_MULTI),)
++ALL_CFLAGS += -DDO_MULTI=1
++%.o: %.c
++ ${CC} $(ALL_CFLAGS) -c -o $@ $<
++
++madwifi_multi: $(OBJS) do_multi.o
++ $(CC) -o $@ $^
++
++compile: madwifi_multi
++ for i in $(ALLPROGS); do \
++ ln -s -f madwifi_multi $$i; \
++ done
++else
++$(ALLPROGS):
++ $(CC) $(ALL_CFLAGS) -o $@ $@.c
++
++compile: $(ALLPROGS)
++endif
+
+
+ install: $(ALL)
+ install -d $(DESTDIR)$(BINDIR)
+- for i in $(ALL); do \
++ for i in $(ALLPROGS) $(if $(DO_MULTI),madwifi_multi); do \
+ install $$i $(DESTDIR)$(BINDIR)/$$i; \
+- $(STRIP) $(DESTDIR)$(BINDIR)/$$i; \
+ done
+ install -d $(DESTDIR)$(MANDIR)/man8
+ install -m 0644 man/*.8 $(DESTDIR)$(MANDIR)/man8
+ install $(TOP)/scripts/madwifi-unload $(DESTDIR)$(BINDIR)/madwifi-unload
+
+ uninstall:
+- for i in $(ALL); do \
++ for i in $(ALLPROGS) $(if $(DO_MULTI),madwifi_multi); do \
+ rm -f $(DESTDIR)$(BINDIR)/$$i; \
+ done
+- for i in $(ALL:=.8); do \
+- rm -f $(DESTDIR)$(MANDIR)/man8/$$i; \
++ for i in $(ALLPROGS); do \
++ rm -f $(DESTDIR)$(MANDIR)/man8/$$i.8; \
+ done
+
+ clean:
+- rm -f $(ALL) core a.out
++ rm -f $(ALLPROGS) madwifi_multi *.o core a.out
+--- a/tools/wlanconfig.c
++++ b/tools/wlanconfig.c
+@@ -61,6 +61,7 @@
+ #include "net80211/ieee80211.h"
+ #include "net80211/ieee80211_crypto.h"
+ #include "net80211/ieee80211_ioctl.h"
++#include "do_multi.h"
+
+ /*
+ * These are taken from ieee80211_node.h
+@@ -100,7 +101,7 @@ size_t strlcat(char *, const char *, siz
+ static int verbose = 0;
+
+ int
+-main(int argc, char *argv[])
++CMD(wlanconfig)(int argc, char *argv[])
+ {
+ const char *ifname, *cmd;
+ unsigned char bnounit = 0;
+--- a/tools/ath_info.c
++++ b/tools/ath_info.c
+@@ -98,6 +98,7 @@
+ #include <sys/mman.h>
+ #include <endian.h>
+ #include <byteswap.h>
++#include "do_multi.h"
+
+ #undef ARRAY_SIZE
+ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+@@ -738,7 +739,8 @@ static void usage(const char *n)
+ "unlawful radio transmissions!\n\n");
+ }
+
+-int main(int argc, char *argv[])
++int
++CMD(athinfo)(int argc, char *argv[])
+ {
+ u_int32_t dev_addr;
+ u_int16_t eeprom_header, srev, phy_rev_5ghz, phy_rev_2ghz;
diff --git a/package/madwifi/patches/104-autocreate_none.patch b/package/madwifi/patches/104-autocreate_none.patch
new file mode 100644
index 000000000..b2181b68e
--- /dev/null
+++ b/package/madwifi/patches/104-autocreate_none.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -516,7 +516,7 @@ ath_attach(u_int16_t devid, struct net_d
+ HAL_STATUS status;
+ int error = 0;
+ unsigned int i;
+- int autocreatemode = IEEE80211_M_STA;
++ int autocreatemode = -1;
+ u_int8_t csz;
+
+ sc->devid = devid;
diff --git a/package/madwifi/patches/105-ratectl_attach.patch b/package/madwifi/patches/105-ratectl_attach.patch
new file mode 100644
index 000000000..80aad835e
--- /dev/null
+++ b/package/madwifi/patches/105-ratectl_attach.patch
@@ -0,0 +1,23 @@
+--- a/net80211/ieee80211_rate.c
++++ b/net80211/ieee80211_rate.c
+@@ -100,8 +100,18 @@ struct ath_ratectrl *ieee80211_rate_atta
+ ieee80211_load_module(buf);
+
+ if (!ratectls[id].attach) {
+- printk(KERN_ERR "Error loading module \"%s\"\n", buf);
+- return NULL;
++ /* pick the first available rate control module */
++ printk(KERN_INFO "Rate control module \"%s\" not available\n", buf);
++ for (id = 0; id < IEEE80211_RATE_MAX - 1; id++) {
++ if (ratectls[id].attach)
++ break;
++ }
++ if (!ratectls[id].attach) {
++ printk(KERN_ERR "No rate control module available");
++ return NULL;
++ } else {
++ printk(KERN_INFO "Using \"%s\" instead.\n", module_names[id]);
++ }
+ }
+
+ ctl = ratectls[id].attach(sc);
diff --git a/package/madwifi/patches/106-get_arch.patch b/package/madwifi/patches/106-get_arch.patch
new file mode 100644
index 000000000..3140f3679
--- /dev/null
+++ b/package/madwifi/patches/106-get_arch.patch
@@ -0,0 +1,21 @@
+--- a/scripts/get_arch.mk
++++ b/scripts/get_arch.mk
+@@ -36,11 +36,14 @@ ifeq (,$(ARCH-y))
+ $(Cannot determine ARCH)
+ endif
+
++# Allow ARCH to be x86
++ifneq (,$(CONFIG_X86))
++ifeq (x86,$(ARCH))
++ARCH-y = $(ARCH)
++endif
++endif
++
+ # Don't allow ARCH to be overridden by a different value.
+ ifeq (,$(ARCH))
+ ARCH = $(ARCH-y)
+-else
+-ifneq ($(ARCH),$(ARCH-y))
+-$(error ARCH mismatch: supplied "$(ARCH)", determined "$(ARCH-y)")
+-endif
+ endif
diff --git a/package/madwifi/patches/111-minstrel_crash.patch b/package/madwifi/patches/111-minstrel_crash.patch
new file mode 100644
index 000000000..975bc4e94
--- /dev/null
+++ b/package/madwifi/patches/111-minstrel_crash.patch
@@ -0,0 +1,12 @@
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -393,6 +393,9 @@ ath_rate_get_mrr(struct ath_softc *sc, s
+ struct minstrel_node *sn = ATH_NODE_MINSTREL(an);
+ int rc1, rc2, rc3; /* Index into the rate table, so for example, it is 0..11 */
+
++ if (sn->num_rates <= 0)
++ return;
++
+ if (sn->is_sampling) {
+ sn->is_sampling = 0;
+ if (sn->rs_sample_rate_slower)
diff --git a/package/madwifi/patches/113-no_ibss_pwrsave.patch b/package/madwifi/patches/113-no_ibss_pwrsave.patch
new file mode 100644
index 000000000..af23ceb7a
--- /dev/null
+++ b/package/madwifi/patches/113-no_ibss_pwrsave.patch
@@ -0,0 +1,12 @@
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -291,7 +291,8 @@ scan_restart_pwrsav(unsigned long arg)
+ struct ieee80211com *ic = vap->iv_ic;
+ int delay;
+
+- ieee80211_sta_pwrsave(vap, 1);
++ if (vap->iv_opmode != IEEE80211_M_IBSS)
++ ieee80211_sta_pwrsave(vap, 1);
+ /*
+ * Use an initial 1ms delay to ensure the null
+ * data frame has a chance to go out.
diff --git a/package/madwifi/patches/122-replayfail_workaround.patch b/package/madwifi/patches/122-replayfail_workaround.patch
new file mode 100644
index 000000000..c4eb28c94
--- /dev/null
+++ b/package/madwifi/patches/122-replayfail_workaround.patch
@@ -0,0 +1,12 @@
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -331,6 +331,9 @@ ieee80211_notify_replay_failure(struct i
+ k->wk_cipher->ic_name, k->wk_keyix,
+ (unsigned long long)rsc);
+
++ /* disabled for now due to bogus events for unknown reasons */
++ return;
++
+ /* TODO: needed parameters: count, keyid, key type, src address, TSC */
+ snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=" MAC_FMT ")", tag,
+ k->wk_keyix,
diff --git a/package/madwifi/patches/123-ccmp_checks.patch b/package/madwifi/patches/123-ccmp_checks.patch
new file mode 100644
index 000000000..6178a3f78
--- /dev/null
+++ b/package/madwifi/patches/123-ccmp_checks.patch
@@ -0,0 +1,95 @@
+--- a/net80211/ieee80211_crypto_ccmp.c
++++ b/net80211/ieee80211_crypto_ccmp.c
+@@ -115,6 +115,7 @@ ccmp_attach(struct ieee80211vap *vap, st
+ /* This function (crypto_alloc_foo might sleep. Therefore:
+ * Context: process
+ */
++#ifdef CONFIG_CRYPTO
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ ctx->cc_tfm = crypto_alloc_tfm("aes", 0);
+ #else
+@@ -123,7 +124,8 @@ ccmp_attach(struct ieee80211vap *vap, st
+ if (IS_ERR(ctx->cc_tfm))
+ ctx->cc_tfm = NULL;
+ #endif
+-
++#endif
++
+ if (ctx->cc_tfm == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: unable to load kernel AES crypto support\n",
+@@ -138,12 +140,14 @@ ccmp_detach(struct ieee80211_key *k)
+ {
+ struct ccmp_ctx *ctx = k->wk_private;
+
++#ifdef CONFIG_CRYPTO
+ if (ctx->cc_tfm != NULL)
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ crypto_free_tfm(ctx->cc_tfm);
+ #else
+ crypto_free_cipher(ctx->cc_tfm);
+ #endif
++#endif
+ FREE(ctx, M_DEVBUF);
+
+ _MOD_DEC_USE(THIS_MODULE);
+@@ -169,7 +173,9 @@ ccmp_setkey(struct ieee80211_key *k)
+ return 0;
+ }
+
++#ifdef CONFIG_CRYPTO
+ crypto_cipher_setkey(ctx->cc_tfm, k->wk_key, k->wk_keylen);
++#endif
+ }
+
+ return 1;
+@@ -324,6 +330,7 @@ xor_block(u8 *b, const u8 *a, size_t len
+ static void
+ rijndael_encrypt(struct crypto_cipher *tfm, const void *src, void *dst)
+ {
++#ifdef CONFIG_CRYPTO
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+ crypto_cipher_encrypt_one(tfm, dst, src);
+ #else
+@@ -339,6 +346,7 @@ rijndael_encrypt(struct crypto_cipher *t
+ sg_dst.length = AES_BLOCK_LEN;
+ crypto_cipher_encrypt(tfm, &sg_dst, &sg_src, AES_BLOCK_LEN);
+ #endif
++#endif
+ }
+
+ /*
+@@ -475,6 +483,9 @@ ccmp_encrypt(struct ieee80211_key *key,
+ uint8_t *mic, *pos;
+ u_int space;
+
++ if (ctx->cc_tfm == NULL)
++ return 0;
++
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
+
+ skb = skb0;
+@@ -589,6 +600,9 @@ ccmp_decrypt(struct ieee80211_key *key,
+ uint8_t *pos, *mic;
+ u_int space;
+
++ if (ctx->cc_tfm == NULL)
++ return 0;
++
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
+
+ skb = skb0;
+--- a/Makefile
++++ b/Makefile
+@@ -192,11 +192,4 @@ endif
+ exit 1; \
+ fi
+
+- @# check crypto support is enabled
+- @if [ -z "$(CONFIG_CRYPTO)" ]; then \
+- echo "FAILED"; \
+- echo "Please enable crypto API."; \
+- exit 1; \
+- fi
+-
+ @echo "ok."
diff --git a/package/madwifi/patches/124-linux24_compat.patch b/package/madwifi/patches/124-linux24_compat.patch
new file mode 100644
index 000000000..88601a4c7
--- /dev/null
+++ b/package/madwifi/patches/124-linux24_compat.patch
@@ -0,0 +1,202 @@
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -126,6 +126,11 @@ typedef void irqreturn_t;
+ #define ATH_GET_NETDEV_DEV(ndev) ((ndev)->class_dev.dev)
+ #endif
+
++#ifndef NETDEV_TX_OK
++#define NETDEV_TX_OK 0
++#define NETDEV_TX_BUSY 1
++#endif
++
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)
+ static inline struct net_device *_alloc_netdev(int sizeof_priv, const char *mask,
+ void (*setup)(struct net_device *))
+--- a/ath/if_ath_radar.c
++++ b/ath/if_ath_radar.c
+@@ -92,6 +92,13 @@
+ #define nofloat_pct(_value, _pct) \
+ ( (_value * (1000 + _pct)) / 1000 )
+
++#ifndef list_for_each_entry_reverse
++#define list_for_each_entry_reverse(pos, head, member) \
++ for (pos = list_entry((head)->prev, typeof(*pos), member); \
++ prefetch(pos->member.prev), &pos->member != (head); \
++ pos = list_entry(pos->member.prev, typeof(*pos), member))
++#endif
++
+ struct radar_pattern_specification {
+ /* The name of the rule/specification (i.e. what did we detect) */
+ const char *name;
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -4705,6 +4705,46 @@ ath_beacon_setup(struct ath_softc *sc, s
+ #undef USE_SHPREAMBLE
+ }
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
++static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
++{
++ int ret;
++ unsigned long flags;
++
++ local_irq_save(flags);
++ ret = v->counter;
++ if (likely(ret == old))
++ v->counter = new;
++ local_irq_restore(flags);
++
++ return ret;
++}
++
++/**
++ * atomic_add_unless - add unless the number is a given value
++ * @v: pointer of type atomic_t
++ * @a: the amount to add to v...
++ * @u: ...unless v is equal to u.
++ *
++ * Atomically adds @a to @v, so long as it was not @u.
++ * Returns non-zero if @v was not @u, and zero otherwise.
++ */
++static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
++{
++ int c, old;
++ c = atomic_read(v);
++ for (;;) {
++ if (unlikely(c == (u)))
++ break;
++ old = atomic_cmpxchg((v), c, c + (a));
++ if (likely(old == c))
++ break;
++ c = old;
++ }
++ return c != (u);
++}
++#endif
++
+ /*
+ * Generate beacon frame and queue cab data for a VAP.
+ */
+--- /dev/null
++++ b/net80211/sort.c
+@@ -0,0 +1,120 @@
++/*
++ * A fast, small, non-recursive O(nlog n) sort for the Linux kernel
++ *
++ * Jan 23 2005 Matt Mackall <mpm@selenic.com>
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++static void u32_swap(void *a, void *b, int size)
++{
++ u32 t = *(u32 *)a;
++ *(u32 *)a = *(u32 *)b;
++ *(u32 *)b = t;
++}
++
++static void generic_swap(void *a, void *b, int size)
++{
++ char t;
++
++ do {
++ t = *(char *)a;
++ *(char *)a++ = *(char *)b;
++ *(char *)b++ = t;
++ } while (--size > 0);
++}
++
++/**
++ * sort - sort an array of elements
++ * @base: pointer to data to sort
++ * @num: number of elements
++ * @size: size of each element
++ * @cmp: pointer to comparison function
++ * @swap: pointer to swap function or NULL
++ *
++ * This function does a heapsort on the given array. You may provide a
++ * swap function optimized to your element type.
++ *
++ * Sorting time is O(n log n) both on average and worst-case. While
++ * qsort is about 20% faster on average, it suffers from exploitable
++ * O(n*n) worst-case behavior and extra memory requirements that make
++ * it less suitable for kernel use.
++ */
++
++static void sort(void *base, size_t num, size_t size,
++ int (*cmp)(const void *, const void *),
++ void (*swap)(void *, void *, int size))
++{
++ /* pre-scale counters for performance */
++ int i = (num/2 - 1) * size, n = num * size, c, r;
++
++ if (!swap)
++ swap = (size == 4 ? u32_swap : generic_swap);
++
++ /* heapify */
++ for ( ; i >= 0; i -= size) {
++ for (r = i; r * 2 + size < n; r = c) {
++ c = r * 2 + size;
++ if (c < n - size && cmp(base + c, base + c + size) < 0)
++ c += size;
++ if (cmp(base + r, base + c) >= 0)
++ break;
++ swap(base + r, base + c, size);
++ }
++ }
++
++ /* sort */
++ for (i = n - size; i >= 0; i -= size) {
++ swap(base, base + i, size);
++ for (r = 0; r * 2 + size < i; r = c) {
++ c = r * 2 + size;
++ if (c < i - size && cmp(base + c, base + c + size) < 0)
++ c += size;
++ if (cmp(base + r, base + c) >= 0)
++ break;
++ swap(base + r, base + c, size);
++ }
++ }
++}
++
++EXPORT_SYMBOL(sort);
++
++#if 0
++/* a simple boot-time regression test */
++
++int cmpint(const void *a, const void *b)
++{
++ return *(int *)a - *(int *)b;
++}
++
++static int sort_test(void)
++{
++ int *a, i, r = 1;
++
++ a = kmalloc(1000 * sizeof(int), GFP_KERNEL);
++ BUG_ON(!a);
++
++ printk("testing sort()\n");
++
++ for (i = 0; i < 1000; i++) {
++ r = (r * 725861) % 6599;
++ a[i] = r;
++ }
++
++ sort(a, 1000, sizeof(int), cmpint, NULL);
++
++ for (i = 0; i < 999; i++)
++ if (a[i] > a[i+1]) {
++ printk("sort() failed!\n");
++ break;
++ }
++
++ kfree(a);
++
++ return 0;
++}
++
++module_init(sort_test);
++#endif
diff --git a/package/madwifi/patches/126-rxerr_frames.patch b/package/madwifi/patches/126-rxerr_frames.patch
new file mode 100644
index 000000000..762a7bc81
--- /dev/null
+++ b/package/madwifi/patches/126-rxerr_frames.patch
@@ -0,0 +1,13 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6474,8 +6474,9 @@ ath_rx_tasklet(TQUEUE_ARG data)
+ /*
+ * Reject error frames if we have no vaps that
+ * are operating in monitor mode.
++ * Reject empty frames as well
+ */
+- if (sc->sc_nmonvaps == 0)
++ if ((sc->sc_nmonvaps == 0) || (rs->rs_datalen == 0))
+ goto rx_next;
+ }
+ rx_accept:
diff --git a/package/madwifi/patches/200-no_debug.patch b/package/madwifi/patches/200-no_debug.patch
new file mode 100644
index 000000000..3f46ec88b
--- /dev/null
+++ b/package/madwifi/patches/200-no_debug.patch
@@ -0,0 +1,408 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -42,7 +42,6 @@
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+-#define AR_DEBUG
+ #include "if_ath_debug.h"
+ #include "opt_ah.h"
+
+@@ -368,8 +367,10 @@ static unsigned int ath_get_dfs_cac_time
+ static void ath_set_dfs_cac_time(struct ieee80211com *, unsigned int seconds);
+
+ static unsigned int ath_test_radar(struct ieee80211com *);
+-static unsigned int ath_dump_hal_map(struct ieee80211com *ic);
++#ifdef AR_DEBUG
+
++static unsigned int ath_dump_hal_map(struct ieee80211com *ic);
++#endif
+ static u_int32_t ath_get_clamped_maxtxpower(struct ath_softc *sc);
+ static u_int32_t ath_set_clamped_maxtxpower(struct ath_softc *sc,
+ u_int32_t new_clamped_maxtxpower);
+@@ -520,9 +521,11 @@ ath_attach(u_int16_t devid, struct net_d
+ u_int8_t csz;
+
+ sc->devid = devid;
++#ifdef AR_DEBUG
+ ath_debug_global = (ath_debug & ATH_DEBUG_GLOBAL);
+ sc->sc_debug = (ath_debug & ~ATH_DEBUG_GLOBAL);
+ DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
++#endif
+
+ /* Allocate space for dynamically determined maximum VAP count */
+ sc->sc_bslot =
+@@ -1038,8 +1041,9 @@ ath_attach(u_int16_t devid, struct net_d
+ ic->ic_vap_delete = ath_vap_delete;
+
+ ic->ic_test_radar = ath_test_radar;
++#ifdef AR_DEBUG
+ ic->ic_dump_hal_map = ath_dump_hal_map;
+-
++#endif
+ ic->ic_set_dfs_testmode = ath_set_dfs_testmode;
+ ic->ic_get_dfs_testmode = ath_get_dfs_testmode;
+
+@@ -1297,12 +1301,14 @@ ath_vap_create(struct ieee80211com *ic,
+ /* If no default VAP debug flags are passed, allow a few to
+ * transfer down from the driver to new VAPs so we can have load
+ * time debugging for VAPs too. */
++#ifdef AR_DEBUG
+ vap->iv_debug = 0 |
+ ((sc->sc_debug & ATH_DEBUG_RATE) ? IEEE80211_MSG_XRATE : 0) |
+ ((sc->sc_debug & ATH_DEBUG_XMIT) ? IEEE80211_MSG_OUTPUT : 0) |
+ ((sc->sc_debug & ATH_DEBUG_RECV) ? IEEE80211_MSG_INPUT : 0) |
+ 0
+ ;
++#endif
+ }
+ ic->ic_debug = (sc->sc_default_ieee80211_debug & IEEE80211_MSG_IC);
+
+@@ -10496,9 +10502,11 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ /* XXX validate? */
+ sc->sc_ledpin = val;
+ break;
++#ifdef AR_DEBUG
+ case ATH_DEBUG:
+ sc->sc_debug = (val & ~ATH_DEBUG_GLOBAL);
+ ath_debug_global = (val & ATH_DEBUG_GLOBAL);
++#endif
+ break;
+ case ATH_TXANTENNA:
+ /*
+@@ -10918,9 +10926,11 @@ ath_dynamic_sysctl_register(struct ath_s
+ }
+
+ /* initialize values */
++#ifdef AR_DEBUG
+ ath_debug_global = (ath_debug & ATH_DEBUG_GLOBAL);
+ sc->sc_debug = (ath_debug & ~ATH_DEBUG_GLOBAL);
+ sc->sc_default_ieee80211_debug = ieee80211_debug;
++#endif
+ sc->sc_txantenna = 0; /* default to auto-selection */
+ sc->sc_txintrperiod = ATH_TXQ_INTR_PERIOD;
+ }
+@@ -11762,6 +11772,7 @@ ath_test_radar(struct ieee80211com *ic)
+ }
+
+ /* This is called by a private ioctl (iwpriv) to dump the HAL obfuscation table */
++#ifdef AR_DEBUG
+ static unsigned int
+ ath_dump_hal_map(struct ieee80211com *ic)
+ {
+@@ -11770,7 +11781,7 @@ ath_dump_hal_map(struct ieee80211com *ic
+ ath_hal_dump_map(sc->sc_ah);
+ return 0;
+ }
+-
++#endif
+ /* If we are shutting down or blowing off the DFS channel availability check
+ * then we call this to stop the behavior before we take the rest of the
+ * necessary actions (such as a DFS reaction to radar). */
+--- a/ath_rate/amrr/amrr.c
++++ b/ath_rate/amrr/amrr.c
+@@ -70,7 +70,9 @@
+
+ #include "amrr.h"
+
++#ifdef AR_DEBUG
+ #define AMRR_DEBUG
++#endif
+ #ifdef AMRR_DEBUG
+ #define DPRINTF(sc, _fmt, ...) do { \
+ if (sc->sc_debug & 0x10) \
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -117,7 +117,9 @@
+
+ #include "minstrel.h"
+
++#ifdef AR_DEBUG
+ #define MINSTREL_DEBUG
++#endif
+ #ifdef MINSTREL_DEBUG
+ enum {
+ ATH_DEBUG_RATE = 0x00000010 /* rate control */
+--- a/ath_rate/onoe/onoe.c
++++ b/ath_rate/onoe/onoe.c
+@@ -66,7 +66,9 @@
+
+ #include "onoe.h"
+
++#ifdef AR_DEBUG
+ #define ONOE_DEBUG
++#endif
+ #ifdef ONOE_DEBUG
+ enum {
+ ATH_DEBUG_RATE = 0x00000010, /* rate control */
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -68,7 +68,9 @@
+
+ #include "sample.h"
+
+-#define SAMPLE_DEBUG
++#ifdef AR_DEBUG
++#define SAMPLE_DEBUG
++#endif
+ #ifdef SAMPLE_DEBUG
+ enum {
+ ATH_DEBUG_RATE = 0x00000010, /* rate control */
+--- a/tools/do_multi.c
++++ b/tools/do_multi.c
+@@ -10,16 +10,20 @@ main(int argc, char *argv[])
+
+ progname = basename(argv[0]);
+
++#ifdef AR_DEBUG
+ if(strcmp(progname, "80211debug") == 0)
+ ret = a80211debug_init(argc, argv);
++#endif
+ if(strcmp(progname, "80211stats") == 0)
+ ret = a80211stats_init(argc, argv);
+ if(strcmp(progname, "athchans") == 0)
+ ret = athchans_init(argc, argv);
+ if(strcmp(progname, "athctrl") == 0)
+ ret = athctrl_init(argc, argv);
++#ifdef AR_DEBUG
+ if(strcmp(progname, "athdebug") == 0)
+ ret = athdebug_init(argc, argv);
++#endif
+ if(strcmp(progname, "athkey") == 0)
+ ret = athkey_init(argc, argv);
+ if(strcmp(progname, "athstats") == 0)
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -48,14 +48,16 @@ endif
+
+ all: compile
+
++DEBUG = -DAR_DEBUG
++
+ ALLPROGS= athstats 80211stats athkey athchans athctrl \
+- athdebug 80211debug wlanconfig ath_info
++ $(if $(DEBUG),athdebug 80211debug) wlanconfig ath_info
+
+ OBJS= $(patsubst %,%.o,$(ALLPROGS))
+
+ INCS= -I. -I../ath -I$(HAL) -I$(TOP) -I$(ATH_HAL)
+ CFLAGS= -g -O2 -Wall
+-ALL_CFLAGS= $(CFLAGS) $(INCS)
++ALL_CFLAGS= $(CFLAGS) $(INCS) $(DEBUG)
+ LDFLAGS=
+
+
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -29,8 +29,6 @@
+ #ifndef _NET80211_IEEE80211_LINUX_H_
+ #define _NET80211_IEEE80211_LINUX_H_
+
+-#define IEEE80211_DEBUG
+-#define IEEE80211_DEBUG_REFCNT /* Node reference count debugging */
+ /* #define ATH_DEBUG_SPINLOCKS */ /* announce before spinlocking */
+
+ #include <linux/wireless.h>
+--- a/Makefile.inc
++++ b/Makefile.inc
+@@ -147,8 +147,9 @@ ATH_RATE= $(TOP)/ath_rate
+ #
+ TOOLS= $(TOP)/tools
+
+-WARNINGS = -Werror
+-COPTS+= $(WARNINGS)
++WARNINGS = -Wno-unused
++# DEBUG = -DAR_DEBUG -DIEEE80211_DEBUG
++COPTS+= $(WARNINGS) $(DEBUG)
+ INCS= -include $(TOP)/include/compat.h -I$(TOP)/include
+
+ # TARGET defines the target platform architecture. It must match one of
+--- a/ath/if_ath_radar.c
++++ b/ath/if_ath_radar.c
+@@ -19,8 +19,6 @@
+ * $Id: if_ath_radar.c 2464 2007-06-15 22:51:56Z mtaylor $
+ */
+ #include "opt_ah.h"
+-
+-#define AR_DEBUG
+ #include "if_ath_debug.h"
+
+ #ifndef AUTOCONF_INCLUDED
+@@ -56,8 +54,6 @@
+ #include <net80211/if_llc.h>
+ #endif
+
+-#define AR_DEBUG
+-
+ #include "net80211/if_athproto.h"
+ #include "if_athvar.h"
+
+--- a/ath/if_ath_hal.h
++++ b/ath/if_ath_hal.h
+@@ -1081,6 +1081,7 @@ static inline HAL_BOOL ath_hal_disable(s
+
+ tail -f /var/log/messages | sed -f hal_unmangle.sed
+ */
++#ifdef AR_DEBUG
+ static inline void ath_hal_dump_map(struct ath_hal *ah)
+ {
+ #ifdef CONFIG_KALLSYMS
+@@ -1345,7 +1346,7 @@ static inline void ath_hal_dump_map(stru
+ #endif /* #ifndef CONFIG_KALLSYMS */
+
+ }
+-
++#endif
+ #include "if_ath_hal_wrappers.h"
+
+ #endif /* #ifndef _IF_ATH_HAL_H_ */
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -492,9 +492,10 @@ struct ieee80211com {
+ /* inject a fake radar signal -- used while on a 802.11h DFS channels */
+ unsigned int (*ic_test_radar)(struct ieee80211com *);
+
++#ifdef AR_DEBUG
+ /* dump HAL */
+ unsigned int (*ic_dump_hal_map)(struct ieee80211com *);
+-
++#endif
+ /* DFS channel availability check time (in seconds) */
+ void (*ic_set_dfs_cac_time)(struct ieee80211com *, unsigned int);
+ unsigned int (*ic_get_dfs_cac_time)(struct ieee80211com *);
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -1548,6 +1548,7 @@ ieee80211_get_txcont_power(struct net_de
+ return 0;
+ }
+
++#ifdef AR_DEBUG
+ static int
+ ieee80211_ioctl_hal_map(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+@@ -1558,7 +1559,7 @@ ieee80211_ioctl_hal_map(struct net_devic
+ params[0] = ic->ic_dump_hal_map(ic);
+ return 0;
+ }
+-
++#endif
+
+ static int
+ ieee80211_ioctl_radar(struct net_device *dev, struct iw_request_info *info,
+@@ -5258,8 +5259,10 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwmmparams" },
+ { IEEE80211_IOCTL_RADAR,
+ 0, 0, "doth_radar" },
++#ifdef AR_DEBUG
+ { IEEE80211_IOCTL_HALMAP,
+ 0, 0, "dump_hal_map" },
++#endif
+ /*
+ * These depends on sub-ioctl support which added in version 12.
+ */
+@@ -5695,7 +5698,9 @@ static const iw_handler ieee80211_priv_h
+ set_priv(IEEE80211_IOCTL_SETMLME, ieee80211_ioctl_setmlme),
+ set_priv(IEEE80211_IOCTL_SETKEY, ieee80211_ioctl_setkey),
+ set_priv(IEEE80211_IOCTL_DELKEY, ieee80211_ioctl_delkey),
++#ifdef AR_DEBUG
+ set_priv(IEEE80211_IOCTL_HALMAP, ieee80211_ioctl_hal_map),
++#endif
+ set_priv(IEEE80211_IOCTL_ADDMAC, ieee80211_ioctl_addmac),
+ set_priv(IEEE80211_IOCTL_DELMAC, ieee80211_ioctl_delmac),
+ set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsmac),
+--- a/ath/if_ath_debug.h
++++ b/ath/if_ath_debug.h
+@@ -54,6 +54,10 @@ enum {
+ ATH_DEBUG_GLOBAL = (ATH_DEBUG_SKB|ATH_DEBUG_SKB_REF)
+ };
+
++#define EPRINTF(_sc, _fmt, ...) \
++ printk(KERN_ERR "%s: %s: " _fmt, \
++ SC_DEV_NAME(_sc), __func__, ## __VA_ARGS__)
++
+ #ifdef AR_DEBUG
+
+ /* DEBUG-ONLY DEFINITIONS */
+@@ -68,20 +72,9 @@ enum {
+ ath_keyprint((_sc), __func__, _ix, _hk, _mac); \
+ } while (0)
+
+-#else /* #ifdef AR_DEBUG */
+-
+-#define DFLAG_ISSET(sc, _m) 0
+-#define DPRINTF(sc, _m, _fmt, ...)
+-#define KEYPRINTF(sc, k, ix, mac)
+-
+-#endif /* #ifdef AR_DEBUG */
+
+ #define IFF_DUMPPKTS(_sc, _m) DFLAG_ISSET((_sc), (_m))
+
+-#define EPRINTF(_sc, _fmt, ...) \
+- printk(KERN_ERR "%s: %s: " _fmt, \
+- SC_DEV_NAME(_sc), __func__, ## __VA_ARGS__)
+-
+ #define WPRINTF(_sc, _fmt, ...) \
+ printk(KERN_WARNING "%s: %s: " _fmt, \
+ SC_DEV_NAME(_sc), __func__, ## __VA_ARGS__)
+@@ -89,5 +82,14 @@ enum {
+ #define IPRINTF(_sc, _fmt, ...) \
+ printk(KERN_INFO "%s: %s: " _fmt, \
+ SC_DEV_NAME(_sc), __func__, ## __VA_ARGS__)
++#else
++#define DFLAG_ISSET(sc, _m) 0
++#define DPRINTF(sc, _m, _fmt, ...)
++#define KEYPRINTF(sc, k, ix, mac)
++#define WPRINTF(...)
++#define IPRINTF(...)
++#define IFF_DUMPPKTS(...) 0
++
++#endif
+
+ #endif /* #ifndef _IF_ATH_DEBUG_H_ */
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -920,6 +920,9 @@ node_cleanup(struct ieee80211_node *ni)
+ ni->ni_rxkeyoff = 0;
+ }
+
++#ifndef IEEE80211_DEBUG
++#define node_print_message(...) do {} while(0)
++#else
+ static void node_print_message(
+ u_int32_t flags,
+ int show_counter,
+@@ -972,7 +975,7 @@ static void node_print_message(
+ adjusted_refcount);
+ va_end(args);
+ }
+-EXPORT_SYMBOL(node_print_message);
++#endif
+
+ static void
+ #ifdef IEEE80211_DEBUG_REFCNT
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -134,8 +134,10 @@ ath_pci_probe(struct pci_dev *pdev, cons
+ u16 vdevice;
+ int i;
+
+- if (pci_enable_device(pdev))
++ if (pci_enable_device(pdev)) {
++ printk(KERN_ERR "%s: failed to enable PCI device\n", dev_info);
+ return -EIO;
++ }
+
+ /* XXX 32-bit addressing only */
+ if (pci_set_dma_mask(pdev, 0xffffffff)) {
+@@ -244,8 +246,10 @@ ath_pci_probe(struct pci_dev *pdev, cons
+ sc->aps_sc.sc_ledpin = 1;
+ }
+
+- if (ath_attach(vdevice, dev, NULL) != 0)
++ if ((i = ath_attach(vdevice, dev, NULL)) != 0) {
++ printk(KERN_ERR "%s: ath_attach failed: %d\n", dev_info, i);
+ goto bad4;
++ }
+
+ athname = ath_hal_probe(id->vendor, vdevice);
+ printk(KERN_INFO "%s: %s: %s: mem=0x%lx, irq=%d\n",
diff --git a/package/madwifi/patches/201-debug_fix.patch b/package/madwifi/patches/201-debug_fix.patch
new file mode 100644
index 000000000..bcfbba82c
--- /dev/null
+++ b/package/madwifi/patches/201-debug_fix.patch
@@ -0,0 +1,20 @@
+--- a/ath_hal/ah_os.c
++++ b/ath_hal/ah_os.c
+@@ -65,7 +65,7 @@
+ #include <ah_os.h>
+
+ #ifdef AH_DEBUG
+-static int ath_hal_debug = 0;
++static int ath_hal_debug = 99;
+ #endif
+
+ int ath_hal_dma_beacon_response_time = 2; /* in TUs */
+@@ -327,6 +327,8 @@ EXPORT_SYMBOL(OS_MARK);
+ * useful for debugging and figuring out, which hal function sets which
+ * registers */
+ char *ath_hal_func = NULL;
++EXPORT_SYMBOL(ath_hal_func);
++
+ #endif
+
+ /*
diff --git a/package/madwifi/patches/202-debug_variables.patch b/package/madwifi/patches/202-debug_variables.patch
new file mode 100644
index 000000000..33e6efad1
--- /dev/null
+++ b/package/madwifi/patches/202-debug_variables.patch
@@ -0,0 +1,204 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -453,8 +453,8 @@ MODULE_PARM_DESC(autocreate, "Create ath
+ MODULE_PARM_DESC(ratectl, "Rate control algorithm [amrr|minstrel|onoe|sample], "
+ "defaults to '" DEF_RATE_CTL "'");
+
+-static int ath_debug = 0;
+ #ifdef AR_DEBUG
++static int ath_debug = 0;
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+ MODULE_PARM(ath_debug, "i");
+ #else
+@@ -465,8 +465,8 @@ static void ath_printrxbuf(const struct
+ static void ath_printtxbuf(const struct ath_buf *, int);
+ #endif /* defined(AR_DEBUG) */
+
+-static int ieee80211_debug = 0;
+ #ifdef AR_DEBUG
++static int ieee80211_debug = 0;
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+ MODULE_PARM(ieee80211_debug, "i");
+ #else
+@@ -1565,7 +1565,9 @@ ath_vap_delete(struct ieee80211vap *vap)
+ void
+ ath_suspend(struct net_device *dev)
+ {
++#ifdef AR_DEBUG
+ struct ath_softc *sc = dev->priv;
++#endif
+
+ DPRINTF(sc, ATH_DEBUG_ANY, "flags=%x\n", dev->flags);
+ ath_stop(dev);
+@@ -1574,7 +1576,9 @@ ath_suspend(struct net_device *dev)
+ void
+ ath_resume(struct net_device *dev)
+ {
++#ifdef AR_DEBUG
+ struct ath_softc *sc = dev->priv;
++#endif
+
+ DPRINTF(sc, ATH_DEBUG_ANY, "flags=%x\n", dev->flags);
+ ath_init(dev);
+@@ -4019,7 +4023,9 @@ static void
+ ath_key_update_begin(struct ieee80211vap *vap)
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
++#ifdef AR_DEBUG
+ struct ath_softc *sc = dev->priv;
++#endif
+
+ DPRINTF(sc, ATH_DEBUG_KEYCACHE, "Begin\n");
+ /*
+@@ -4040,7 +4046,9 @@ static void
+ ath_key_update_end(struct ieee80211vap *vap)
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
++#ifdef AR_DEBUG
+ struct ath_softc *sc = dev->priv;
++#endif
+
+ DPRINTF(sc, ATH_DEBUG_KEYCACHE, "End\n");
+ netif_wake_queue(dev);
+@@ -6218,7 +6226,9 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ struct sk_buff *skb, int subtype, int rssi, u_int64_t rtsf)
+ {
+ struct ath_softc *sc = vap->iv_ic->ic_dev->priv;
++#ifdef AR_DEBUG
+ struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
++#endif
+ struct ieee80211_node * ni = ni_or_null;
+ u_int64_t hw_tsf, beacon_tsf;
+ u_int32_t hw_tu, beacon_tu, intval;
+@@ -8382,7 +8392,9 @@ ath_tx_timeout(struct net_device *dev)
+ static void
+ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
+ {
++#ifdef AR_DEBUG
+ struct ath_hal *ah = sc->sc_ah;
++#endif
+ struct ath_buf *bf;
+ /*
+ * NB: this assumes output has been stopped and
+@@ -11002,6 +11014,7 @@ ath_announce(struct net_device *dev)
+ strncat(m, b, MLEN);
+ }
+ strncat(m, "\n", MLEN);
++#ifdef AR_DEBUG
+ if (1 /* bootverbose */) {
+ unsigned int i;
+ for (i = 0; i <= WME_AC_VO; i++) {
+@@ -11014,6 +11027,7 @@ ath_announce(struct net_device *dev)
+ sc->sc_cabq->axq_qnum);
+ IPRINTF(sc, "Use hw queue %u for beacons\n", sc->sc_bhalq);
+ }
++#endif
+ #undef HAL_MODE_DUALBAND
+ }
+
+--- a/ath/if_ath_radar.c
++++ b/ath/if_ath_radar.c
+@@ -156,7 +156,9 @@ static struct radar_pattern_specificatio
+ #endif
+ };
+
++#ifdef AR_DEBUG
+ static u_int32_t interval_to_frequency(u_int32_t pri);
++#endif
+
+ /* Returns true if radar detection is enabled. */
+ int ath_radar_is_enabled(struct ath_softc *sc)
+@@ -229,7 +231,9 @@ int ath_radar_update(struct ath_softc *s
+ {
+
+ struct ath_hal *ah = sc->sc_ah;
++#ifdef AR_DEBUG
+ struct net_device *dev = sc->sc_dev;
++#endif
+ struct ieee80211com *ic = &sc->sc_ic;
+ int required = 0;
+
+@@ -366,6 +370,7 @@ static struct ath_rp *pulse_prev(struct
+ #define MR_FAIL_MIN_PERIOD 4
+ #define MR_FAIL_MAX_PERIOD 5
+
++#ifdef AR_DEBUG
+ static const char* get_match_result_desc(u_int32_t code) {
+ switch (code) {
+ case MR_MATCH:
+@@ -384,6 +389,7 @@ static const char* get_match_result_desc
+ return "unknown";
+ }
+ }
++#endif
+
+ static int32_t match_radar(
+ u_int32_t matched,
+@@ -775,7 +781,10 @@ static HAL_BOOL rp_analyse_short_pulse(
+ struct ath_softc *sc, struct ath_rp *last_pulse,
+ u_int32_t *index, u_int32_t *pri, u_int32_t *matching_pulses,
+ u_int32_t *missed_pulses, u_int32_t *noise_pulses)
+-{ struct net_device *dev = sc->sc_dev;
++{
++#ifdef AR_DEBUG
++ struct net_device *dev = sc->sc_dev;
++#endif
+ int i;
+ int best_index = -1;
+ unsigned int best_matched = 0;
+@@ -1217,6 +1226,7 @@ static HAL_BOOL rp_analyse_short_pulse(
+ return (-1 != best_index) ? AH_TRUE : AH_FALSE;
+ }
+
++#ifdef AR_DEBUG
+ static u_int32_t interval_to_frequency(u_int32_t interval)
+ {
+ /* Calculate BRI from PRI */
+@@ -1224,6 +1234,7 @@ static u_int32_t interval_to_frequency(u
+ /* Round to nearest multiple of 50 */
+ return frequency + ((frequency % 50) >= 25 ? 50 : 0) - (frequency % 50);
+ }
++#endif
+
+ #ifdef ATH_RADAR_LONG_PULSE
+ static const char* get_longpulse_desc(int lp) {
+@@ -1580,7 +1591,9 @@ void ath_rp_done(struct ath_softc *sc)
+ void ath_rp_record(struct ath_softc *sc, u_int64_t tsf, u_int8_t rssi,
+ u_int8_t width, HAL_BOOL is_simulated)
+ {
++#ifdef AR_DEBUG
+ struct net_device *dev = sc->sc_dev;
++#endif
+ struct ath_rp *pulse;
+
+ DPRINTF(sc, ATH_DEBUG_DOTHPULSES, "%s: ath_rp_record: "
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -931,7 +931,9 @@ ath_proc_read_nodes(struct ieee80211vap
+ (struct ieee80211_node_table *) &vap->iv_ic->ic_sta;
+ unsigned int x = 0;
+ unsigned int this_tp, this_prob, this_eprob;
++#ifdef AR_DEBUG
+ struct ath_softc *sc = vap->iv_ic->ic_dev->priv;;
++#endif
+
+ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -731,6 +731,7 @@ pick_channel(struct ieee80211_scan_state
+
+ sort(chans, ss_last, sizeof(*chans), pc_cmp, pc_swap);
+
++#ifdef IEEE80211_DEBUG
+ for (i = 0; i < ss_last; i++) {
+ int chan = ieee80211_chan2ieee(ic, chans[i].chan);
+
+@@ -742,6 +743,7 @@ pick_channel(struct ieee80211_scan_state
+ !!IEEE80211_ARE_CHANS_SAME_MODE(chans[i].chan,
+ ic->ic_bsschan));
+ }
++#endif
+
+ best = NULL;
+ best_rssi = 0xff; /* If signal is bigger than 0xff, we'd be melting. */
diff --git a/package/madwifi/patches/300-napi_polling.patch b/package/madwifi/patches/300-napi_polling.patch
new file mode 100644
index 000000000..bde768494
--- /dev/null
+++ b/package/madwifi/patches/300-napi_polling.patch
@@ -0,0 +1,536 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -184,7 +184,11 @@ static void ath_recv_mgmt(struct ieee802
+ struct sk_buff *, int, int, u_int64_t);
+ static void ath_setdefantenna(struct ath_softc *, u_int);
+ static struct ath_txq *ath_txq_setup(struct ath_softc *, int, int);
+-static void ath_rx_tasklet(TQUEUE_ARG);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++static int ath_rx_poll(struct napi_struct *napi, int budget);
++#else
++static int ath_rx_poll(struct net_device *dev, int *budget);
++#endif
+ static int ath_hardstart(struct sk_buff *, struct net_device *);
+ static int ath_mgtstart(struct ieee80211com *, struct sk_buff *);
+ #ifdef ATH_SUPERG_COMP
+@@ -376,6 +380,9 @@ static u_int32_t ath_set_clamped_maxtxpo
+ u_int32_t new_clamped_maxtxpower);
+ static u_int32_t ath_get_real_maxtxpower(struct ath_softc *sc);
+
++static void ath_poll_disable(struct net_device *dev);
++static void ath_poll_enable(struct net_device *dev);
++
+ /* calibrate every 30 secs in steady state but check every second at first. */
+ static int ath_calinterval = ATH_SHORT_CALINTERVAL;
+ static int ath_countrycode = CTRY_DEFAULT; /* country code */
+@@ -547,7 +554,6 @@ ath_attach(u_int16_t devid, struct net_d
+
+ atomic_set(&sc->sc_txbuf_counter, 0);
+
+- ATH_INIT_TQUEUE(&sc->sc_rxtq, ath_rx_tasklet, dev);
+ ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet, dev);
+ ATH_INIT_TQUEUE(&sc->sc_bmisstq, ath_bmiss_tasklet, dev);
+ ATH_INIT_TQUEUE(&sc->sc_bstucktq, ath_bstuck_tasklet, dev);
+@@ -821,6 +827,12 @@ ath_attach(u_int16_t devid, struct net_d
+ dev->set_mac_address = ath_set_mac_address;
+ dev->change_mtu = ath_change_mtu;
+ dev->tx_queue_len = ATH_TXBUF - ATH_TXBUF_MGT_RESERVED;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ netif_napi_add(dev, &sc->sc_napi, ath_rx_poll, 64);
++#else
++ dev->poll = ath_rx_poll;
++ dev->weight = 64;
++#endif
+ #ifdef USE_HEADERLEN_RESV
+ dev->hard_header_len += sizeof(struct ieee80211_qosframe) +
+ sizeof(struct llc) +
+@@ -2220,6 +2232,7 @@ ath_intr(int irq, void *dev_id, struct p
+ (status & HAL_INT_GLOBAL) ? " HAL_INT_GLOBAL" : ""
+ );
+
++ sc->sc_isr = status;
+ status &= sc->sc_imask; /* discard unasked for bits */
+ /* As soon as we know we have a real interrupt we intend to service,
+ * we will check to see if we need an initial hardware TSF reading.
+@@ -2277,7 +2290,21 @@ ath_intr(int irq, void *dev_id, struct p
+ }
+ if (status & (HAL_INT_RX | HAL_INT_RXPHY)) {
+ ath_uapsd_processtriggers(sc, hw_tsf);
+- ATH_SCHEDULE_TQUEUE(&sc->sc_rxtq, &needmark);
++ sc->sc_isr &= ~HAL_INT_RX;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ if (netif_rx_schedule_prep(dev, &sc->sc_napi))
++#else
++ if (netif_rx_schedule_prep(dev))
++#endif
++ {
++ sc->sc_imask &= ~HAL_INT_RX;
++ ath_hal_intrset(ah, sc->sc_imask);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ __netif_rx_schedule(dev, &sc->sc_napi);
++#else
++ __netif_rx_schedule(dev);
++#endif
++ }
+ }
+ if (status & HAL_INT_TX) {
+ #ifdef ATH_SUPERG_DYNTURBO
+@@ -2303,6 +2330,11 @@ ath_intr(int irq, void *dev_id, struct p
+ }
+ }
+ #endif
++ /* disable transmit interrupt */
++ sc->sc_isr &= ~HAL_INT_TX;
++ ath_hal_intrset(ah, sc->sc_imask & ~HAL_INT_TX);
++ sc->sc_imask &= ~HAL_INT_TX;
++
+ ATH_SCHEDULE_TQUEUE(&sc->sc_txtq, &needmark);
+ }
+ if (status & HAL_INT_BMISS) {
+@@ -2515,6 +2547,7 @@ ath_init(struct net_device *dev)
+ if (sc->sc_tx99 != NULL)
+ sc->sc_tx99->start(sc->sc_tx99);
+ #endif
++ ath_poll_enable(dev);
+
+ done:
+ ATH_UNLOCK(sc);
+@@ -2555,6 +2588,9 @@ ath_stop_locked(struct net_device *dev)
+ if (sc->sc_tx99 != NULL)
+ sc->sc_tx99->stop(sc->sc_tx99);
+ #endif
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ ath_poll_disable(dev);
++#endif
+ netif_stop_queue(dev); /* XXX re-enabled by ath_newstate */
+ dev->flags &= ~IFF_RUNNING; /* NB: avoid recursion */
+ ieee80211_stop_running(ic); /* stop all VAPs */
+@@ -4013,12 +4049,47 @@ ath_key_set(struct ieee80211vap *vap, co
+ return ath_keyset(sc, k, mac, vap->iv_bss);
+ }
+
++static void ath_poll_disable(struct net_device *dev)
++{
++ struct ath_softc *sc = dev->priv;
++
++ /*
++ * XXX Using in_softirq is not right since we might
++ * be called from other soft irq contexts than
++ * ath_rx_poll
++ */
++ if (!in_softirq()) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ napi_disable(&sc->sc_napi);
++#else
++ netif_poll_disable(dev);
++#endif
++ }
++}
++
++static void ath_poll_enable(struct net_device *dev)
++{
++ struct ath_softc *sc = dev->priv;
++
++ /* NB: see above */
++ if (!in_softirq()) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ napi_enable(&sc->sc_napi);
++#else
++ netif_poll_enable(dev);
++#endif
++ }
++}
++
++
+ /*
+ * Block/unblock tx+rx processing while a key change is done.
+ * We assume the caller serializes key management operations
+ * so we only need to worry about synchronization with other
+ * uses that originate in the driver.
+ */
++#define IS_UP(_dev) \
++ (((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+ static void
+ ath_key_update_begin(struct ieee80211vap *vap)
+ {
+@@ -4032,14 +4103,9 @@ ath_key_update_begin(struct ieee80211vap
+ * When called from the rx tasklet we cannot use
+ * tasklet_disable because it will block waiting
+ * for us to complete execution.
+- *
+- * XXX Using in_softirq is not right since we might
+- * be called from other soft irq contexts than
+- * ath_rx_tasklet.
+ */
+- if (!in_softirq())
+- tasklet_disable(&sc->sc_rxtq);
+- netif_stop_queue(dev);
++ if (IS_UP(vap->iv_dev))
++ netif_stop_queue(dev);
+ }
+
+ static void
+@@ -4051,9 +4117,9 @@ ath_key_update_end(struct ieee80211vap *
+ #endif
+
+ DPRINTF(sc, ATH_DEBUG_KEYCACHE, "End\n");
+- netif_wake_queue(dev);
+- if (!in_softirq()) /* NB: see above */
+- tasklet_enable(&sc->sc_rxtq);
++
++ if (IS_UP(vap->iv_dev))
++ netif_wake_queue(dev);
+ }
+
+ /*
+@@ -6360,15 +6426,25 @@ ath_setdefantenna(struct ath_softc *sc,
+ sc->sc_rxotherant = 0;
+ }
+
+-static void
+-ath_rx_tasklet(TQUEUE_ARG data)
++static int
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ath_rx_poll(struct napi_struct *napi, int budget)
++#else
++ath_rx_poll(struct net_device *dev, int *budget)
++#endif
+ {
+ #define PA2DESC(_sc, _pa) \
+ ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+ ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+- struct net_device *dev = (struct net_device *)data;
+- struct ath_buf *bf;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ struct ath_softc *sc = container_of(napi, struct ath_softc, sc_napi);
++ struct net_device *dev = sc->sc_dev;
++ u_int rx_limit = budget;
++#else
+ struct ath_softc *sc = dev->priv;
++ u_int rx_limit = min(dev->quota, *budget);
++#endif
++ struct ath_buf *bf;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc ? sc->sc_ah : NULL;
+ struct ath_desc *ds;
+@@ -6378,8 +6454,10 @@ ath_rx_tasklet(TQUEUE_ARG data)
+ unsigned int len;
+ int type;
+ u_int phyerr;
++ u_int processed = 0, early_stop = 0;
+
+ DPRINTF(sc, ATH_DEBUG_RX_PROC, "invoked\n");
++process_rx_again:
+ do {
+ bf = STAILQ_FIRST(&sc->sc_rxbuf);
+ if (bf == NULL) { /* XXX ??? can this happen */
+@@ -6403,6 +6481,15 @@ ath_rx_tasklet(TQUEUE_ARG data)
+ /* NB: never process the self-linked entry at the end */
+ break;
+ }
++
++ if (rx_limit-- < 2) {
++ early_stop = 1;
++ break;
++ }
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ processed++;
++#endif
++
+ skb = bf->bf_skb;
+ if (skb == NULL) {
+ EPRINTF(sc, "Dropping; buffer contains NULL skbuff.\n");
+@@ -6450,6 +6537,7 @@ ath_rx_tasklet(TQUEUE_ARG data)
+ sc->sc_stats.ast_rx_phyerr++;
+ phyerr = rs->rs_phyerr & 0x1f;
+ sc->sc_stats.ast_rx_phy[phyerr]++;
++ goto rx_next;
+ }
+ if (rs->rs_status & HAL_RXERR_DECRYPT) {
+ /*
+@@ -6645,9 +6733,39 @@ rx_next:
+ STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+ ATH_RXBUF_UNLOCK_IRQ(sc);
+ } while (ath_rxbuf_init(sc, bf) == 0);
++ if (!early_stop) {
++ unsigned long flags;
++ /* Check if more data is received while we were
++ * processing the descriptor chain.
++ */
++ local_irq_save(flags);
++ if (sc->sc_isr & HAL_INT_RX) {
++ u_int64_t hw_tsf = ath_hal_gettsf64(ah);
++ sc->sc_isr &= ~HAL_INT_RX;
++ local_irq_restore(flags);
++ ath_uapsd_processtriggers(sc, hw_tsf);
++ goto process_rx_again;
++ }
++ local_irq_restore(flags);
++ }
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ netif_rx_complete(dev, napi);
++#else
++ netif_rx_complete(dev);
++ *budget -= processed;
++ dev->quota -= processed;
++#endif
++ sc->sc_imask |= HAL_INT_RX;
++ ath_hal_intrset(ah, sc->sc_imask);
+
+ /* rx signal state monitoring */
+ ath_hal_rxmonitor(ah, &sc->sc_halstats, &sc->sc_curchan);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ return processed;
++#else
++ return early_stop;
++#endif
+ #undef PA2DESC
+ }
+
+@@ -8298,12 +8416,24 @@ ath_tx_tasklet_q0(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+ struct ath_softc *sc = dev->priv;
++ unsigned long flags;
+
++process_tx_again:
+ if (txqactive(sc->sc_ah, 0))
+ ath_tx_processq(sc, &sc->sc_txq[0]);
+ if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
+ ath_tx_processq(sc, sc->sc_cabq);
+
++ local_irq_save(flags);
++ if (sc->sc_isr & HAL_INT_TX) {
++ sc->sc_isr &= ~HAL_INT_TX;
++ local_irq_restore(flags);
++ goto process_tx_again;
++ }
++ sc->sc_imask |= HAL_INT_TX;
++ ath_hal_intrset(sc->sc_ah, sc->sc_imask);
++ local_irq_restore(flags);
++
+ netif_wake_queue(dev);
+
+ if (sc->sc_softled)
+@@ -8319,7 +8449,9 @@ ath_tx_tasklet_q0123(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+ struct ath_softc *sc = dev->priv;
++ unsigned long flags;
+
++process_tx_again:
+ /*
+ * Process each active queue.
+ */
+@@ -8340,6 +8472,16 @@ ath_tx_tasklet_q0123(TQUEUE_ARG data)
+ if (sc->sc_uapsdq && txqactive(sc->sc_ah, sc->sc_uapsdq->axq_qnum))
+ ath_tx_processq(sc, sc->sc_uapsdq);
+
++ local_irq_save(flags);
++ if (sc->sc_isr & HAL_INT_TX) {
++ sc->sc_isr &= ~HAL_INT_TX;
++ local_irq_restore(flags);
++ goto process_tx_again;
++ }
++ sc->sc_imask |= HAL_INT_TX;
++ ath_hal_intrset(sc->sc_ah, sc->sc_imask);
++ local_irq_restore(flags);
++
+ netif_wake_queue(dev);
+
+ if (sc->sc_softled)
+@@ -8355,13 +8497,25 @@ ath_tx_tasklet(TQUEUE_ARG data)
+ struct net_device *dev = (struct net_device *)data;
+ struct ath_softc *sc = dev->priv;
+ unsigned int i;
++ unsigned long flags;
+
+ /* Process each active queue. This includes sc_cabq, sc_xrtq and
+ * sc_uapsdq */
++process_tx_again:
+ for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+ if (ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i))
+ ath_tx_processq(sc, &sc->sc_txq[i]);
+
++ local_irq_save(flags);
++ if (sc->sc_isr & HAL_INT_TX) {
++ sc->sc_isr &= ~HAL_INT_TX;
++ local_irq_restore(flags);
++ goto process_tx_again;
++ }
++ sc->sc_imask |= HAL_INT_TX;
++ ath_hal_intrset(sc->sc_ah, sc->sc_imask);
++ local_irq_restore(flags);
++
+ netif_wake_queue(dev);
+
+ if (sc->sc_softled)
+@@ -10296,9 +10450,9 @@ ath_change_mtu(struct net_device *dev, i
+ dev->mtu = mtu;
+ if ((dev->flags & IFF_RUNNING) && !sc->sc_invalid) {
+ /* NB: the rx buffers may need to be reallocated */
+- tasklet_disable(&sc->sc_rxtq);
++ ath_poll_disable(dev);
+ error = ath_reset(dev);
+- tasklet_enable(&sc->sc_rxtq);
++ ath_poll_enable(dev);
+ }
+ ATH_UNLOCK(sc);
+
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -53,6 +53,10 @@
+ # include <asm/bitops.h>
+ #endif
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
++#define irqs_disabled() 0
++#endif
++
+ /*
+ * Deduce if tasklets are available. If not then
+ * fall back to using the immediate work queue.
+@@ -616,6 +620,9 @@ struct ath_rp {
+ struct ath_softc {
+ struct ieee80211com sc_ic; /* NB: must be first */
+ struct net_device *sc_dev;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++ struct napi_struct sc_napi;
++#endif
+ void __iomem *sc_iobase; /* address of the device */
+ struct semaphore sc_lock; /* dev-level lock */
+ struct net_device_stats sc_devstats; /* device statistics */
+@@ -730,7 +737,6 @@ struct ath_softc {
+ struct ath_buf *sc_rxbufcur; /* current rx buffer */
+ u_int32_t *sc_rxlink; /* link ptr in last RX desc */
+ spinlock_t sc_rxbuflock;
+- struct ATH_TQ_STRUCT sc_rxtq; /* rx intr tasklet */
+ struct ATH_TQ_STRUCT sc_rxorntq; /* rxorn intr tasklet */
+ u_int8_t sc_defant; /* current default antenna */
+ u_int8_t sc_rxotherant; /* RXs on non-default antenna */
+@@ -745,6 +751,7 @@ struct ath_softc {
+ u_int sc_txintrperiod; /* tx interrupt batching */
+ struct ath_txq sc_txq[HAL_NUM_TX_QUEUES];
+ struct ath_txq *sc_ac2q[WME_NUM_AC]; /* WME AC -> h/w qnum */
++ HAL_INT sc_isr; /* unmasked ISR state */
+ struct ATH_TQ_STRUCT sc_txtq; /* tx intr tasklet */
+ u_int8_t sc_grppoll_str[GRPPOLL_RATE_STR_LEN];
+ struct ath_descdma sc_bdma; /* beacon descriptors */
+@@ -858,6 +865,8 @@ typedef void (*ath_callback) (struct ath
+ #define ATH_TXBUF_LOCK_CHECK(_sc)
+ #endif
+
++#define ATH_DISABLE_INTR local_irq_disable
++#define ATH_ENABLE_INTR local_irq_enable
+
+ #define ATH_RXBUF_LOCK_INIT(_sc) spin_lock_init(&(_sc)->sc_rxbuflock)
+ #define ATH_RXBUF_LOCK_DESTROY(_sc)
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1198,7 +1198,7 @@ ieee80211_deliver_data(struct ieee80211_
+ /* attach vlan tag */
+ struct ieee80211_node *ni_tmp = SKB_CB(skb)->ni;
+ if (vlan_hwaccel_receive_skb(skb, vap->iv_vlgrp, ni->ni_vlan) == NET_RX_DROP) {
+- /* If netif_rx dropped the packet because
++ /* If netif_receive_skb dropped the packet because
+ * device was too busy */
+ if (ni_tmp != NULL) {
+ /* node reference was leaked */
+@@ -1209,8 +1209,8 @@ ieee80211_deliver_data(struct ieee80211_
+ skb = NULL; /* SKB is no longer ours */
+ } else {
+ struct ieee80211_node *ni_tmp = SKB_CB(skb)->ni;
+- if (netif_rx(skb) == NET_RX_DROP) {
+- /* If netif_rx dropped the packet because
++ if (netif_receive_skb(skb) == NET_RX_DROP) {
++ /* If netif_receive_skb dropped the packet because
+ * device was too busy */
+ if (ni_tmp != NULL) {
+ /* node reference was leaked */
+@@ -2322,8 +2322,8 @@ forward_mgmt_to_app(struct ieee80211vap
+ skb1->protocol = __constant_htons(0x0019); /* ETH_P_80211_RAW */
+
+ ni_tmp = SKB_CB(skb1)->ni;
+- if (netif_rx(skb1) == NET_RX_DROP) {
+- /* If netif_rx dropped the packet because
++ if (netif_receive_skb(skb1) == NET_RX_DROP) {
++ /* If netif_receive_skb dropped the packet because
+ * device was too busy */
+ if (ni_tmp != NULL) {
+ /* node reference was leaked */
+--- a/net80211/ieee80211_monitor.c
++++ b/net80211/ieee80211_monitor.c
+@@ -584,8 +584,8 @@ ieee80211_input_monitor(struct ieee80211
+ skb1->protocol =
+ __constant_htons(0x0019); /* ETH_P_80211_RAW */
+
+- if (netif_rx(skb1) == NET_RX_DROP) {
+- /* If netif_rx dropped the packet because
++ if (netif_receive_skb(skb1) == NET_RX_DROP) {
++ /* If netif_receive_skb dropped the packet because
+ * device was too busy, reclaim the ref. in
+ * the skb. */
+ if (SKB_CB(skb1)->ni != NULL)
+--- a/net80211/ieee80211_skb.c
++++ b/net80211/ieee80211_skb.c
+@@ -73,7 +73,7 @@
+ #undef dev_queue_xmit
+ #undef kfree_skb
+ #undef kfree_skb_fast
+-#undef netif_rx
++#undef netif_receive_skb
+ #undef pskb_copy
+ #undef skb_clone
+ #undef skb_copy
+@@ -638,8 +638,8 @@ int vlan_hwaccel_receive_skb_debug(stru
+ grp, vlan_tag);
+ }
+
+-int netif_rx_debug(struct sk_buff *skb, const char* func, int line) {
+- return netif_rx(untrack_skb(skb, 0, func, line, __func__, __LINE__));
++int netif_receive_skb_debug(struct sk_buff *skb, const char* func, int line) {
++ return netif_receive_skb(untrack_skb(skb, 0, func, line, __func__, __LINE__));
+ }
+
+ struct sk_buff * alloc_skb_debug(unsigned int length, gfp_t gfp_mask,
+@@ -760,7 +760,7 @@ struct sk_buff * skb_copy_expand_debug(c
+ }
+
+ EXPORT_SYMBOL(vlan_hwaccel_receive_skb_debug);
+-EXPORT_SYMBOL(netif_rx_debug);
++EXPORT_SYMBOL(netif_receive_skb_debug);
+ EXPORT_SYMBOL(alloc_skb_debug);
+ EXPORT_SYMBOL(dev_alloc_skb_debug);
+ EXPORT_SYMBOL(skb_clone_debug);
+--- a/net80211/ieee80211_skb.h
++++ b/net80211/ieee80211_skb.h
+@@ -116,7 +116,7 @@ int ieee80211_skb_references(void);
+ int vlan_hwaccel_receive_skb_debug(struct sk_buff *skb,
+ struct vlan_group *grp, unsigned short vlan_tag,
+ const char* func, int line);
+-int netif_rx_debug(struct sk_buff *skb, const char* func, int line);
++int netif_receive_skb_debug(struct sk_buff *skb, const char* func, int line);
+ struct sk_buff * alloc_skb_debug(unsigned int length, gfp_t gfp_mask,
+ const char *func, int line);
+ struct sk_buff * dev_alloc_skb_debug(unsigned int length,
+@@ -151,7 +151,7 @@ struct sk_buff * skb_copy_expand_debug(c
+ #undef dev_queue_xmit
+ #undef kfree_skb
+ #undef kfree_skb_fast
+-#undef netif_rx
++#undef netif_receive_skb
+ #undef pskb_copy
+ #undef skb_clone
+ #undef skb_copy
+@@ -168,8 +168,8 @@ struct sk_buff * skb_copy_expand_debug(c
+ skb_copy_expand_debug(_skb, _newheadroom, _newtailroom, _gfp_mask, __func__, __LINE__)
+ #define vlan_hwaccel_receive_skb(_skb, _grp, _tag) \
+ vlan_hwaccel_receive_skb_debug(_skb, _grp, _tag, __func__, __LINE__)
+-#define netif_rx(_skb) \
+- netif_rx_debug(_skb, __func__, __LINE__)
++#define netif_receive_skb(_skb) \
++ netif_receive_skb_debug(_skb, __func__, __LINE__)
+ #define alloc_skb(_length, _gfp_mask) \
+ alloc_skb_debug(_length, _gfp_mask, __func__, __LINE__)
+ #define dev_alloc_skb(_length) \
diff --git a/package/madwifi/patches/305-pureg_fix.patch b/package/madwifi/patches/305-pureg_fix.patch
new file mode 100644
index 000000000..8adb8a716
--- /dev/null
+++ b/package/madwifi/patches/305-pureg_fix.patch
@@ -0,0 +1,168 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -4158,7 +4158,9 @@ ath_calcrxfilter(struct ath_softc *sc)
+ rfilt |= HAL_RX_FILTER_PROM;
+ if (ic->ic_opmode == IEEE80211_M_STA ||
+ sc->sc_opmode == HAL_M_IBSS || /* NB: AHDEMO too */
+- (sc->sc_nostabeacons) || sc->sc_scanning)
++ (sc->sc_nostabeacons) || sc->sc_scanning ||
++ ((ic->ic_opmode == IEEE80211_M_HOSTAP) &&
++ (ic->ic_protmode != IEEE80211_PROT_NONE)))
+ rfilt |= HAL_RX_FILTER_BEACON;
+ if (sc->sc_nmonvaps > 0)
+ rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON |
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -346,11 +346,12 @@ ieee80211_input(struct ieee80211vap * va
+ bssid = wh->i_addr3;
+ }
+ /*
+- * Validate the bssid.
++ * Validate the bssid. Let beacons get through though for 11g protection mode.
+ */
+-#ifdef ATH_SUPERG_XR
+ if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bssid) &&
+- !IEEE80211_ADDR_EQ(bssid, dev->broadcast)) {
++ !IEEE80211_ADDR_EQ(bssid, dev->broadcast) &&
++ (subtype != IEEE80211_FC0_SUBTYPE_BEACON)) {
++#ifdef ATH_SUPERG_XR
+ /*
+ * allow MGT frames to vap->iv_xrvap.
+ * this will allow roaming between XR and normal vaps
+@@ -366,18 +367,14 @@ ieee80211_input(struct ieee80211vap * va
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+- }
+ #else
+- if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bssid) &&
+- !IEEE80211_ADDR_EQ(bssid, dev->broadcast)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+- }
+-
+ #endif
++ }
+ break;
+ case IEEE80211_M_WDS:
+ if (skb->len < sizeof(struct ieee80211_frame_addr4)) {
+@@ -3066,7 +3063,7 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ u_int8_t *frm, *efrm;
+ u_int8_t *ssid, *rates, *xrates, *suppchan, *wpa, *rsn, *wme, *ath;
+ u_int8_t rate;
+- int reassoc, resp, allocbs = 0;
++ int reassoc, resp, allocbs = 0, has_erp = 0;
+ u_int8_t qosinfo;
+
+ if (ni_or_null == NULL)
+@@ -3096,11 +3093,15 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ * o station mode when associated (to collect state
+ * updates such as 802.11g slot time), or
+ * o adhoc mode (to discover neighbors)
++ * o ap mode in protection mode (beacons only)
+ * Frames otherwise received are discarded.
+ */
+ if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
+ (vap->iv_opmode == IEEE80211_M_STA && ni->ni_associd) ||
+- vap->iv_opmode == IEEE80211_M_IBSS)) {
++ (vap->iv_opmode == IEEE80211_M_IBSS) ||
++ ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
++ (vap->iv_opmode == IEEE80211_M_HOSTAP) &&
++ (ic->ic_protmode != IEEE80211_PROT_NONE)))) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+@@ -3184,6 +3185,7 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ break;
+ }
+ scan.erp = frm[2];
++ has_erp = 1;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ scan.rsn = frm;
+@@ -3421,6 +3423,20 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ ieee80211_bg_scan(vap);
+ return;
+ }
++
++ /* Update AP protection mode when in 11G mode */
++ if ((vap->iv_opmode == IEEE80211_M_HOSTAP) &&
++ IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
++
++ /* Assume no ERP IE == 11b AP */
++ if ((!has_erp || (has_erp && (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) &&
++ !(ic->ic_flags & IEEE80211_F_USEPROT)) {
++
++ ic->ic_flags |= IEEE80211_F_USEPROT;
++ ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ }
++ }
++
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -383,10 +383,16 @@ ieee80211_create_ibss(struct ieee80211va
+ /* Update country ie information */
+ ieee80211_build_countryie(ic);
+
+- if (IEEE80211_IS_CHAN_HALF(chan))
++ if (IEEE80211_IS_CHAN_HALF(chan)) {
+ ni->ni_rates = ic->ic_sup_half_rates;
+- else if (IEEE80211_IS_CHAN_QUARTER(chan))
++ } else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
+ ni->ni_rates = ic->ic_sup_quarter_rates;
++ }
++
++ if ((vap->iv_flags & IEEE80211_F_PUREG) &&
++ IEEE80211_IS_CHAN_ANYG(chan)) {
++ ieee80211_setpuregbasicrates(&ni->ni_rates);
++ }
+
+ (void) ieee80211_sta_join1(PASS_NODE(ni));
+ }
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -595,6 +595,28 @@ static const struct ieee80211_rateset ba
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_TURBO_G (mixed b/g) */
+ };
+
++static const struct ieee80211_rateset basicpureg[] = {
++ { 7, {2, 4, 11, 22, 12, 24, 48 } },
++};
++
++/*
++ * Mark basic rates for the 11g rate table based on the pureg setting
++ */
++void
++ieee80211_setpuregbasicrates(struct ieee80211_rateset *rs)
++{
++ int i, j;
++
++ for (i = 0; i < rs->rs_nrates; i++) {
++ rs->rs_rates[i] &= IEEE80211_RATE_VAL;
++ for (j = 0; j < basicpureg[0].rs_nrates; j++)
++ if (basicpureg[0].rs_rates[j] == rs->rs_rates[i]) {
++ rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
++ break;
++ }
++ }
++}
++
+ /*
+ * Mark the basic rates for the 11g rate table based on the
+ * specified mode. For 11b compatibility we mark only 11b
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -708,6 +708,7 @@ int ieee80211_media_setup(struct ieee802
+ void ieee80211_build_sc_ie(struct ieee80211com *);
+ void ieee80211_dfs_action(struct ieee80211com *);
+ void ieee80211_expire_channel_excl_restrictions(struct ieee80211com *);
++void ieee80211_setpuregbasicrates(struct ieee80211_rateset *rs);
+
+ /*
+ * Iterate through ic_channels to enumerate all distinct ic_ieee channel numbers.
diff --git a/package/madwifi/patches/309-micfail_detect.patch b/package/madwifi/patches/309-micfail_detect.patch
new file mode 100644
index 000000000..ca4103a20
--- /dev/null
+++ b/package/madwifi/patches/309-micfail_detect.patch
@@ -0,0 +1,321 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6457,6 +6457,7 @@ ath_rx_poll(struct net_device *dev, int
+ int type;
+ u_int phyerr;
+ u_int processed = 0, early_stop = 0;
++ u_int mic_fail = 0;
+
+ DPRINTF(sc, ATH_DEBUG_RX_PROC, "invoked\n");
+ process_rx_again:
+@@ -6558,24 +6559,8 @@ process_rx_again:
+ }
+ if (rs->rs_status & HAL_RXERR_MIC) {
+ sc->sc_stats.ast_rx_badmic++;
+- /*
+- * Do minimal work required to hand off
+- * the 802.11 header for notification.
+- */
+- /* XXX frag's and QoS frames */
+- if (len >= sizeof (struct ieee80211_frame)) {
+- bus_dma_sync_single(sc->sc_bdev,
+- bf->bf_skbaddr, len,
+- BUS_DMA_FROMDEVICE);
+-#if 0
+-/* XXX revalidate MIC, lookup ni to find VAP */
+- ieee80211_notify_michael_failure(ic,
+- (struct ieee80211_frame *)skb->data,
+- sc->sc_splitmic ?
+- rs->rs_keyix - 32 : rs->rs_keyix
+- );
+-#endif
+- }
++ mic_fail = 1;
++ goto rx_accept;
+ }
+ /*
+ * Reject error frames if we have no vaps that
+@@ -6614,8 +6599,9 @@ rx_accept:
+ /*
+ * Finished monitor mode handling, now reject
+ * error frames before passing to other vaps
++ * Ignore MIC failures here, as we need to recheck them
+ */
+- if (rs->rs_status != 0) {
++ if (rs->rs_status & ~(HAL_RXERR_MIC | HAL_RXERR_DECRYPT)) {
+ ieee80211_dev_kfree_skb(&skb);
+ goto rx_next;
+ }
+@@ -6623,6 +6609,26 @@ rx_accept:
+ /* remove the CRC */
+ skb_trim(skb, skb->len - IEEE80211_CRC_LEN);
+
++ if (mic_fail) {
++ /* Ignore control frames which are reported with mic error */
++ if ((((struct ieee80211_frame *)skb->data)->i_fc[0] &
++ IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
++ goto drop_micfail;
++
++ ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) skb->data);
++
++ if (ni && ni->ni_table) {
++ ieee80211_check_mic(ni, skb);
++ ieee80211_unref_node(&ni);
++ }
++
++drop_micfail:
++ dev_kfree_skb_any(skb);
++ skb = NULL;
++ mic_fail = 0;
++ goto rx_next;
++ }
++
+ /*
+ * From this point on we assume the frame is at least
+ * as large as ieee80211_frame_min; verify that.
+@@ -6635,6 +6641,7 @@ rx_accept:
+ goto rx_next;
+ }
+
++ /* MIC failure. Drop the packet in any case */
+ /*
+ * Normal receive.
+ */
+--- a/net80211/ieee80211_crypto_ccmp.c
++++ b/net80211/ieee80211_crypto_ccmp.c
+@@ -73,7 +73,7 @@ static int ccmp_setkey(struct ieee80211_
+ static int ccmp_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
+ static int ccmp_decap(struct ieee80211_key *, struct sk_buff *, int);
+ static int ccmp_enmic(struct ieee80211_key *, struct sk_buff *, int);
+-static int ccmp_demic(struct ieee80211_key *, struct sk_buff *, int);
++static int ccmp_demic(struct ieee80211_key *, struct sk_buff *, int, int);
+
+ static const struct ieee80211_cipher ccmp = {
+ .ic_name = "AES-CCM",
+@@ -314,7 +314,7 @@ ccmp_decap(struct ieee80211_key *k, stru
+ * Verify and strip MIC from the frame.
+ */
+ static int
+-ccmp_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen)
++ccmp_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen, int force)
+ {
+ return 1;
+ }
+--- a/net80211/ieee80211_crypto.h
++++ b/net80211/ieee80211_crypto.h
+@@ -145,7 +145,7 @@ struct ieee80211_cipher {
+ int (*ic_encap)(struct ieee80211_key *, struct sk_buff *, u_int8_t);
+ int (*ic_decap)(struct ieee80211_key *, struct sk_buff *, int);
+ int (*ic_enmic)(struct ieee80211_key *, struct sk_buff *, int);
+- int (*ic_demic)(struct ieee80211_key *, struct sk_buff *, int);
++ int (*ic_demic)(struct ieee80211_key *, struct sk_buff *, int, int);
+ };
+ extern const struct ieee80211_cipher ieee80211_cipher_none;
+
+@@ -163,10 +163,10 @@ struct ieee80211_key *ieee80211_crypto_d
+ */
+ static __inline int
+ ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
+- struct sk_buff *skb, int hdrlen)
++ struct sk_buff *skb, int hdrlen, int force)
+ {
+ const struct ieee80211_cipher *cip = k->wk_cipher;
+- return (cip->ic_miclen > 0 ? cip->ic_demic(k, skb, hdrlen) : 1);
++ return (cip->ic_miclen > 0 ? cip->ic_demic(k, skb, hdrlen, force) : 1);
+ }
+
+ /*
+--- a/net80211/ieee80211_crypto_none.c
++++ b/net80211/ieee80211_crypto_none.c
+@@ -52,7 +52,7 @@ static int none_setkey(struct ieee80211_
+ static int none_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
+ static int none_decap(struct ieee80211_key *, struct sk_buff *, int);
+ static int none_enmic(struct ieee80211_key *, struct sk_buff *, int);
+-static int none_demic(struct ieee80211_key *, struct sk_buff *, int);
++static int none_demic(struct ieee80211_key *, struct sk_buff *, int, int);
+
+ const struct ieee80211_cipher ieee80211_cipher_none = {
+ .ic_name = "NONE",
+@@ -137,7 +137,7 @@ none_enmic(struct ieee80211_key *k, stru
+ }
+
+ static int
+-none_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen)
++none_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen, int force)
+ {
+ struct ieee80211vap *vap = k->wk_private;
+
+--- a/net80211/ieee80211_crypto_tkip.c
++++ b/net80211/ieee80211_crypto_tkip.c
+@@ -57,7 +57,7 @@ static int tkip_setkey(struct ieee80211_
+ static int tkip_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
+ static int tkip_enmic(struct ieee80211_key *, struct sk_buff *, int);
+ static int tkip_decap(struct ieee80211_key *, struct sk_buff *, int);
+-static int tkip_demic(struct ieee80211_key *, struct sk_buff *, int);
++static int tkip_demic(struct ieee80211_key *, struct sk_buff *, int, int);
+
+ static const struct ieee80211_cipher tkip = {
+ .ic_name = "TKIP",
+@@ -339,7 +339,7 @@ tkip_decap(struct ieee80211_key *k, stru
+ * Verify and strip MIC from the frame.
+ */
+ static int
+-tkip_demic(struct ieee80211_key *k, struct sk_buff *skb0, int hdrlen)
++tkip_demic(struct ieee80211_key *k, struct sk_buff *skb0, int hdrlen, int force)
+ {
+ struct tkip_ctx *ctx = k->wk_private;
+ struct sk_buff *skb;
+@@ -355,7 +355,7 @@ tkip_demic(struct ieee80211_key *k, stru
+ }
+ wh = (struct ieee80211_frame *) skb0->data;
+ /* NB: skb left pointing at last in chain */
+- if (k->wk_flags & IEEE80211_KEY_SWMIC) {
++ if ((k->wk_flags & IEEE80211_KEY_SWMIC) || force) {
+ struct ieee80211vap *vap = ctx->tc_vap;
+ u8 mic[IEEE80211_WEP_MICLEN];
+ u8 mic0[IEEE80211_WEP_MICLEN];
+--- a/net80211/ieee80211_crypto_wep.c
++++ b/net80211/ieee80211_crypto_wep.c
+@@ -54,7 +54,7 @@ static int wep_setkey(struct ieee80211_k
+ static int wep_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t);
+ static int wep_decap(struct ieee80211_key *, struct sk_buff *, int);
+ static int wep_enmic(struct ieee80211_key *, struct sk_buff *, int);
+-static int wep_demic(struct ieee80211_key *, struct sk_buff *, int);
++static int wep_demic(struct ieee80211_key *, struct sk_buff *, int, int);
+
+ static const struct ieee80211_cipher wep = {
+ .ic_name = "WEP",
+@@ -244,7 +244,7 @@ wep_decap(struct ieee80211_key *k, struc
+ * Verify and strip MIC from the frame.
+ */
+ static int
+-wep_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen)
++wep_demic(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen, int force)
+ {
+ return 1;
+ }
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -669,7 +669,7 @@ ieee80211_input(struct ieee80211vap * va
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL &&
+- !ieee80211_crypto_demic(vap, key, skb, hdrspace)) {
++ !ieee80211_crypto_demic(vap, key, skb, hdrspace, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+@@ -4293,6 +4293,47 @@ ath_eth_type_trans(struct sk_buff *skb,
+ }
+ #endif
+
++/*
++ * Process a frame w/ hw detected MIC failure.
++ * The frame will be dropped in any case.
++ */
++void
++ieee80211_check_mic(struct ieee80211_node *ni, struct sk_buff *skb)
++{
++ struct ieee80211vap *vap = ni->ni_vap;
++
++ struct ieee80211_frame *wh;
++ struct ieee80211_key *key;
++ int hdrspace;
++ struct ieee80211com *ic = vap->iv_ic;
++
++ if (skb->len < sizeof(struct ieee80211_frame_min)) {
++ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
++ ni->ni_macaddr, NULL,
++ "too short (1): len %u", skb->len);
++ vap->iv_stats.is_rx_tooshort++;
++ return;
++ }
++
++ wh = (struct ieee80211_frame *)skb->data;
++
++ hdrspace = ieee80211_hdrspace(ic, wh);
++ key = ieee80211_crypto_decap(ni, skb, hdrspace);
++ if (key == NULL) {
++ /* NB: stats+msgs handled in crypto_decap */
++ IEEE80211_NODE_STAT(ni, rx_wepfail);
++ return;
++ }
++
++ if (!ieee80211_crypto_demic(vap, key, skb, hdrspace, 1)) {
++ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
++ ni->ni_macaddr, "data", "%s", "demic error");
++ IEEE80211_NODE_STAT(ni, rx_demicfail);
++ }
++ return;
++}
++EXPORT_SYMBOL(ieee80211_check_mic);
++
+ #ifdef IEEE80211_DEBUG
+ /*
+ * Debugging support.
+--- a/net80211/ieee80211_proto.h
++++ b/net80211/ieee80211_proto.h
+@@ -90,6 +90,7 @@ int ieee80211_iserp_rateset(struct ieee8
+ void ieee80211_set11gbasicrates(struct ieee80211_rateset *, enum ieee80211_phymode);
+ enum ieee80211_phymode ieee80211_get11gbasicrates(struct ieee80211_rateset *);
+ void ieee80211_send_pspoll(struct ieee80211_node *);
++void ieee80211_check_mic(struct ieee80211_node *, struct sk_buff *);
+
+ /*
+ * Return the size of the 802.11 header for a management or data frame.
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -337,8 +337,8 @@ ieee80211_notify_replay_failure(struct i
+ /* TODO: needed parameters: count, keyid, key type, src address, TSC */
+ snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=" MAC_FMT ")", tag,
+ k->wk_keyix,
+- IEEE80211_IS_MULTICAST(wh->i_addr1) ? "broad" : "uni",
+- MAC_ADDR(wh->i_addr1));
++ IEEE80211_IS_MULTICAST(wh->i_addr2) ? "broad" : "uni",
++ MAC_ADDR(wh->i_addr2));
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -1074,13 +1074,16 @@ ieee80211_encap(struct ieee80211_node *n
+ cip = (struct ieee80211_cipher *) key->wk_cipher;
+ ciphdrsize = cip->ic_header;
+ tailsize += (cip->ic_trailer + cip->ic_miclen);
++
++ /* add the 8 bytes MIC length */
++ if (cip->ic_cipher == IEEE80211_CIPHER_TKIP)
++ pktlen += IEEE80211_WEP_MICLEN;
+ }
+
+ pdusize = vap->iv_fragthreshold - (hdrsize_nopad + ciphdrsize);
+ fragcnt = *framecnt =
+- ((pktlen - (hdrsize_nopad + ciphdrsize)) / pdusize) +
+- (((pktlen - (hdrsize_nopad + ciphdrsize)) %
+- pdusize == 0) ? 0 : 1);
++ ((pktlen - hdrsize_nopad) / pdusize) +
++ (((pktlen - hdrsize_nopad) % pdusize == 0) ? 0 : 1);
+
+ /*
+ * Allocate sk_buff for each subsequent fragment; First fragment
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -2264,11 +2264,13 @@ ieee80211_node_leave(struct ieee80211_no
+ /* From this point onwards we can no longer find the node,
+ * so no more references are generated
+ */
+- ieee80211_remove_wds_addr(nt, ni->ni_macaddr);
+- ieee80211_del_wds_node(nt, ni);
+- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+- node_table_leave_locked(nt, ni);
+- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
++ if (nt) {
++ ieee80211_remove_wds_addr(nt, ni->ni_macaddr);
++ ieee80211_del_wds_node(nt, ni);
++ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
++ node_table_leave_locked(nt, ni);
++ IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
++ }
+
+ /*
+ * If node wasn't previously associated all
diff --git a/package/madwifi/patches/310-noise_get.patch b/package/madwifi/patches/310-noise_get.patch
new file mode 100644
index 000000000..d8821583b
--- /dev/null
+++ b/package/madwifi/patches/310-noise_get.patch
@@ -0,0 +1,55 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1699,8 +1699,6 @@ ath_uapsd_processtriggers(struct ath_sof
+ * get to reality. This value is used in monitor mode and by tools like
+ * Wireshark and Kismet.
+ */
+- ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
+-
+ ATH_RXBUF_LOCK_IRQ(sc);
+ if (sc->sc_rxbufcur == NULL)
+ sc->sc_rxbufcur = STAILQ_FIRST(&sc->sc_rxbuf);
+@@ -8975,6 +8973,7 @@ ath_calibrate(unsigned long arg)
+ sc->sc_curchan.channel);
+ sc->sc_stats.ast_per_calfail++;
+ }
++ ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
+
+ ath_hal_process_noisefloor(ah);
+ if (isIQdone == AH_TRUE) {
+@@ -9043,6 +9042,7 @@ ath_set_channel(struct ieee80211com *ic)
+ struct ath_softc *sc = dev->priv;
+
+ (void) ath_chan_set(sc, ic->ic_curchan);
++ ic->ic_channoise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
+ /*
+ * If we are returning to our bss channel then mark state
+ * so the next recv'd beacon's TSF will be used to sync the
+@@ -9311,6 +9311,7 @@ ath_newstate(struct ieee80211vap *vap, e
+ }
+
+ ath_hal_process_noisefloor(ah);
++ ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
+ /*
+ * Reset rssi stats; maybe not the best place...
+ */
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -4358,6 +4358,7 @@ get_sta_info(void *arg, struct ieee80211
+ si->isi_state = ni->ni_flags;
+ si->isi_authmode = ni->ni_authmode;
+ si->isi_rssi = ic->ic_node_getrssi(ni);
++ si->isi_noise = ic->ic_channoise;
+ si->isi_capinfo = ni->ni_capinfo;
+ si->isi_athflags = ni->ni_ath_flags;
+ si->isi_erp = ni->ni_erp;
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -311,6 +311,7 @@ struct ieee80211req_sta_info {
+ u_int16_t isi_state; /* state flags */
+ u_int8_t isi_authmode; /* authentication algorithm */
+ u_int8_t isi_rssi;
++ int8_t isi_noise;
+ u_int16_t isi_capinfo; /* capabilities */
+ u_int8_t isi_athflags; /* Atheros capabilities */
+ u_int8_t isi_erp; /* ERP element */
diff --git a/package/madwifi/patches/311-bssid_alloc.patch b/package/madwifi/patches/311-bssid_alloc.patch
new file mode 100644
index 000000000..005a67738
--- /dev/null
+++ b/package/madwifi/patches/311-bssid_alloc.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1354,7 +1354,7 @@ ath_vap_create(struct ieee80211com *ic,
+ TAILQ_FOREACH(v, &ic->ic_vaps, iv_next)
+ id_mask |= (1 << ATH_GET_VAP_ID(v->iv_myaddr));
+
+- for (id = 1; id < ath_maxvaps; id++) {
++ for (id = 0; id < ath_maxvaps; id++) {
+ /* get the first available slot */
+ if ((id_mask & (1 << id)) == 0) {
+ ATH_SET_VAP_BSSID(vap->iv_myaddr, id);
diff --git a/package/madwifi/patches/312-erpupdate.patch b/package/madwifi/patches/312-erpupdate.patch
new file mode 100644
index 000000000..f878acd8f
--- /dev/null
+++ b/package/madwifi/patches/312-erpupdate.patch
@@ -0,0 +1,68 @@
+--- a/net80211/ieee80211_beacon.c
++++ b/net80211/ieee80211_beacon.c
+@@ -542,10 +542,10 @@ ieee80211_beacon_update(struct ieee80211
+ vap->iv_flags &= ~IEEE80211_F_XRUPDATE;
+ }
+ #endif
+- if ((ic->ic_flags_ext & IEEE80211_FEXT_ERPUPDATE) &&
++ if ((vap->iv_flags_ext & IEEE80211_FEXT_ERPUPDATE) &&
+ (bo->bo_erp != NULL)) {
+ (void)ieee80211_add_erp(bo->bo_erp, ic);
+- ic->ic_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE;
++ vap->iv_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE;
+ }
+ }
+ /* if it is a mode change beacon for dynamic turbo case */
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3431,9 +3431,12 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ /* Assume no ERP IE == 11b AP */
+ if ((!has_erp || (has_erp && (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) &&
+ !(ic->ic_flags & IEEE80211_F_USEPROT)) {
++ struct ieee80211vap *tmpvap;
+
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+- ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) {
++ tmpvap->iv_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ }
+ }
+ }
+
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -2025,8 +2025,12 @@ ieee80211_node_join_11g(struct ieee80211
+ }
+
+ /* Update ERP element if this is first non ERP station */
+- if (ic->ic_nonerpsta == 1)
+- ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ if (ic->ic_nonerpsta == 1) {
++ struct ieee80211vap *tmpvap;
++ TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) {
++ tmpvap->iv_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ }
++ }
+ } else
+ ni->ni_flags |= IEEE80211_NODE_ERP;
+ }
+@@ -2229,6 +2233,8 @@ ieee80211_node_leave_11g(struct ieee8021
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "non-ERP station leaves, count now %d", ic->ic_nonerpsta);
+ if (ic->ic_nonerpsta == 0) {
++ struct ieee80211vap *tmpvap;
++
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
+ "%s: disable use of protection\n", __func__);
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+@@ -2240,7 +2246,9 @@ ieee80211_node_leave_11g(struct ieee8021
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ }
+- ic->ic_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) {
++ tmpvap->iv_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ }
+ }
+ }
+ }
diff --git a/package/madwifi/patches/317-bmask.patch b/package/madwifi/patches/317-bmask.patch
new file mode 100644
index 000000000..3355dc722
--- /dev/null
+++ b/package/madwifi/patches/317-bmask.patch
@@ -0,0 +1,13 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8689,6 +8689,10 @@ ath_startrecv(struct ath_softc *sc)
+
+ sc->sc_rxbufcur = NULL;
+
++ /* configure bssid mask */
++ if (sc->sc_hasbmask)
++ ath_hal_setbssidmask(ah, sc->sc_bssidmask);
++
+ bf = STAILQ_FIRST(&sc->sc_rxbuf);
+ ath_hal_putrxbuf(ah, bf->bf_daddr);
+ ath_hal_rxena(ah); /* enable recv descriptors */
diff --git a/package/madwifi/patches/323-dfs_optional.patch b/package/madwifi/patches/323-dfs_optional.patch
new file mode 100644
index 000000000..2336d7484
--- /dev/null
+++ b/package/madwifi/patches/323-dfs_optional.patch
@@ -0,0 +1,38 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1778,17 +1778,14 @@ ath_uapsd_processtriggers(struct ath_sof
+ * may have occurred in the intervening timeframe. */
+ bf->bf_channoise = ic->ic_channoise;
+
+- if (rs->rs_status) {
+- if ((HAL_RXERR_PHY == rs->rs_status) &&
+- (HAL_PHYERR_RADAR ==
+- (rs->rs_phyerr & 0x1f)) &&
+- (0 == (bf->bf_status &
+- ATH_BUFSTATUS_RADAR_DONE))) {
+- check_for_radar = 1;
+- }
+- /* Skip past the error now */
++ if ((HAL_RXERR_PHY == rs->rs_status) &&
++ (HAL_PHYERR_RADAR == (rs->rs_phyerr & 0x1f)) &&
++ (0 == (bf->bf_status & ATH_BUFSTATUS_RADAR_DONE)) &&
++ (ic->ic_flags & IEEE80211_F_DOTH))
++ check_for_radar = 1;
++
++ if (rs->rs_status) /* Skip past the error now */
+ continue;
+- }
+
+ /* Prepare wireless header for examination */
+ bus_dma_sync_single(sc->sc_bdev, bf->bf_skbaddr,
+--- a/ath/if_ath_radar.c
++++ b/ath/if_ath_radar.c
+@@ -265,7 +265,7 @@ int ath_radar_update(struct ath_softc *s
+ unsigned int new_rxfilt = old_rxfilt;
+
+ ath_hal_intrset(ah, old_ier & ~HAL_INT_GLOBAL);
+- if (required) {
++ if ((required) && (ic->ic_flags & IEEE80211_F_DOTH)) {
+ new_radar |= AR5K_PHY_RADAR_ENABLE;
+ new_filter |= AR5K_AR5212_PHY_ERR_FIL_RADAR;
+ new_rxfilt |= (HAL_RX_FILTER_PHYERR |
diff --git a/package/madwifi/patches/324-alignment.patch b/package/madwifi/patches/324-alignment.patch
new file mode 100644
index 000000000..c01135f3e
--- /dev/null
+++ b/package/madwifi/patches/324-alignment.patch
@@ -0,0 +1,19 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1275,14 +1275,8 @@ ieee80211_decap(struct ieee80211vap *vap
+ eh->ether_type = ether_type;
+
+ if (!ALIGNED_POINTER(skb->data + sizeof(*eh), u_int32_t)) {
+- struct sk_buff *tskb;
+-
+- /* XXX: does this always work? */
+- tskb = skb_copy(skb, GFP_ATOMIC);
+- if (tskb)
+- ieee80211_skb_copy_noderef(skb, tskb);
+- ieee80211_dev_kfree_skb(&skb);
+- skb = tskb;
++ memmove(skb->data - 2, skb->data, skb->len);
++ skb->data -= 2;
+ }
+ return skb;
+ }
diff --git a/package/madwifi/patches/325-channel_spam.patch b/package/madwifi/patches/325-channel_spam.patch
new file mode 100644
index 000000000..e34b7a409
--- /dev/null
+++ b/package/madwifi/patches/325-channel_spam.patch
@@ -0,0 +1,28 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -9792,7 +9792,9 @@ ath_getchannels(struct net_device *dev,
+ /*
+ * Convert HAL channels to ieee80211 ones.
+ */
++#ifdef AR_DEBUG
+ IPRINTF(sc, "HAL returned %d channels.\n", nchan);
++#endif
+ for (i = 0; i < nchan; i++) {
+ HAL_CHANNEL *c = &chans[i];
+ struct ieee80211_channel *ichan = &ic->ic_channels[i];
+@@ -9819,6 +9821,7 @@ ath_getchannels(struct net_device *dev,
+ ic->ic_chan_non_occupy[i].tv_sec = 0;
+ ic->ic_chan_non_occupy[i].tv_usec = 0;
+
++#ifdef AR_DEBUG
+ IPRINTF(sc, "Channel %3d (%4d MHz) Max Tx Power %d dBm%s "
+ "[%d hw %d reg] Flags%s%s%s%s%s%s%s%s%s%s%s%s%"
+ "s%s%s%s%s%s%s%s%s%s%s%s\n",
+@@ -9907,6 +9910,7 @@ ath_getchannels(struct net_device *dev,
+ (c->privFlags & 0x0080 ?
+ " PF & (1 << 7)" : "")
+ );
++#endif
+ }
+ ic->ic_nchans = nchan;
+ kfree(chans);
diff --git a/package/madwifi/patches/327-queue.patch b/package/madwifi/patches/327-queue.patch
new file mode 100644
index 000000000..228aae349
--- /dev/null
+++ b/package/madwifi/patches/327-queue.patch
@@ -0,0 +1,40 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8438,8 +8438,6 @@ process_tx_again:
+ ath_hal_intrset(sc->sc_ah, sc->sc_imask);
+ local_irq_restore(flags);
+
+- netif_wake_queue(dev);
+-
+ if (sc->sc_softled)
+ ath_led_event(sc, ATH_LED_TX);
+ }
+@@ -8486,8 +8484,6 @@ process_tx_again:
+ ath_hal_intrset(sc->sc_ah, sc->sc_imask);
+ local_irq_restore(flags);
+
+- netif_wake_queue(dev);
+-
+ if (sc->sc_softled)
+ ath_led_event(sc, ATH_LED_TX);
+ }
+@@ -8520,8 +8516,6 @@ process_tx_again:
+ ath_hal_intrset(sc->sc_ah, sc->sc_imask);
+ local_irq_restore(flags);
+
+- netif_wake_queue(dev);
+-
+ if (sc->sc_softled)
+ ath_led_event(sc, ATH_LED_TX);
+ }
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1132,7 +1132,7 @@ ieee80211_deliver_data(struct ieee80211_
+ (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) {
+ struct sk_buff *skb1 = NULL;
+
+- if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
++ if (ETHER_IS_MULTICAST(eh->ether_dhost) && !netif_queue_stopped(dev)) {
+ /* Create a SKB for the BSS to send out. */
+ skb1 = skb_copy(skb, GFP_ATOMIC);
+ if (skb1)
diff --git a/package/madwifi/patches/330-beaconcal.patch b/package/madwifi/patches/330-beaconcal.patch
new file mode 100644
index 000000000..a338dc743
--- /dev/null
+++ b/package/madwifi/patches/330-beaconcal.patch
@@ -0,0 +1,166 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -397,6 +397,7 @@ static int countrycode = -1;
+ static int maxvaps = -1;
+ static int outdoor = -1;
+ static int xchanmode = -1;
++static int beacon_cal = 1;
+
+ static const char *hal_status_desc[] = {
+ "No error",
+@@ -422,6 +423,7 @@ static struct notifier_block ath_event_b
+ };
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
++MODULE_PARM(beacon_cal, "i");
+ MODULE_PARM(countrycode, "i");
+ MODULE_PARM(maxvaps, "i");
+ MODULE_PARM(outdoor, "i");
+@@ -434,6 +436,7 @@ MODULE_PARM(autocreate, "s");
+ MODULE_PARM(ratectl, "s");
+ #else
+ #include <linux/moduleparam.h>
++module_param(beacon_cal, int, 0600);
+ module_param(countrycode, int, 0600);
+ module_param(maxvaps, int, 0600);
+ module_param(outdoor, int, 0600);
+@@ -2600,7 +2603,8 @@ ath_stop_locked(struct net_device *dev)
+ }
+ if (!sc->sc_invalid) {
+ del_timer_sync(&sc->sc_dfs_cac_timer);
+- del_timer_sync(&sc->sc_cal_ch);
++ if (!sc->sc_beacon_cal)
++ del_timer_sync(&sc->sc_cal_ch);
+ }
+ ath_draintxq(sc);
+ if (!sc->sc_invalid) {
+@@ -2617,6 +2621,20 @@ ath_stop_locked(struct net_device *dev)
+ return 0;
+ }
+
++static void ath_set_beacon_cal(struct ath_softc *sc, int val)
++{
++ if (sc->sc_beacon_cal == !!val)
++ return;
++
++ if (val) {
++ del_timer_sync(&sc->sc_cal_ch);
++ } else {
++ sc->sc_cal_ch.expires = jiffies + (ath_calinterval * HZ);
++ add_timer(&sc->sc_cal_ch);
++ }
++ sc->sc_beacon_cal = !!val && beacon_cal;
++}
++
+ /*
+ * Stop the device, grabbing the top-level lock to protect
+ * against concurrent entry through ath_init (which can happen
+@@ -2742,6 +2760,12 @@ ath_reset(struct net_device *dev)
+ HAL_STATUS status;
+
+ /*
++ * XXX: starting the calibration too early seems to lead to
++ * problems with the beacons.
++ */
++ sc->sc_lastcal = jiffies;
++
++ /*
+ * Convert to a HAL channel description with the flags
+ * constrained to reflect the current operating mode.
+ */
+@@ -5154,6 +5178,10 @@ ath_beacon_send(struct ath_softc *sc, in
+ "Invoking ath_hal_txstart with sc_bhalq: %d\n",
+ sc->sc_bhalq);
+ ath_hal_txstart(ah, sc->sc_bhalq);
++ if (sc->sc_beacon_cal && (jiffies > sc->sc_lastcal + (ath_calinterval * HZ))) {
++ sc->sc_cal_ch.expires = jiffies + msecs_to_jiffies(10);
++ add_timer(&sc->sc_cal_ch);
++ }
+
+ sc->sc_stats.ast_be_xmit++; /* XXX per-VAP? */
+ }
+@@ -5403,6 +5431,7 @@ ath_beacon_config(struct ath_softc *sc,
+ ath_hal_beacontimers(ah, &bs);
+ sc->sc_imask |= HAL_INT_BMISS;
+ ath_hal_intrset(ah, sc->sc_imask);
++ ath_set_beacon_cal(sc, 0);
+ } else {
+ ath_hal_intrset(ah, 0);
+ if (reset_tsf)
+@@ -5414,8 +5443,11 @@ ath_beacon_config(struct ath_softc *sc,
+ */
+ intval |= HAL_BEACON_ENA;
+ sc->sc_imask |= HAL_INT_SWBA;
++ ath_set_beacon_cal(sc, 1);
+ ath_beaconq_config(sc);
+- }
++ } else
++ ath_set_beacon_cal(sc, 0);
++
+ #ifdef ATH_SUPERG_DYNTURBO
+ ath_beacon_dturbo_config(vap, intval &
+ ~(HAL_BEACON_RESET_TSF | HAL_BEACON_ENA));
+@@ -8879,6 +8911,9 @@ ath_chan_set(struct ath_softc *sc, struc
+ /* Enter DFS wait period */
+ mod_timer(&sc->sc_dfs_cac_timer,
+ jiffies + (sc->sc_dfs_cac_period * HZ));
++
++ /* This is a good time to start a calibration */
++ ath_set_beacon_cal(sc, 1);
+ }
+ /*
+ * re configure beacons when it is a turbo mode switch.
+@@ -8988,8 +9023,11 @@ ath_calibrate(unsigned long arg)
+ sc->sc_curchan.channel, sc->sc_curchan.channelFlags,
+ isIQdone ? "done" : "not done");
+
+- sc->sc_cal_ch.expires = jiffies + (ath_calinterval * HZ);
+- add_timer(&sc->sc_cal_ch);
++ sc->sc_lastcal = jiffies;
++ if (!sc->sc_beacon_cal) {
++ sc->sc_cal_ch.expires = jiffies + (ath_calinterval * HZ);
++ add_timer(&sc->sc_cal_ch);
++ }
+ }
+
+ static void
+@@ -9096,7 +9134,8 @@ ath_newstate(struct ieee80211vap *vap, e
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+- del_timer(&sc->sc_cal_ch); /* periodic calibration timer */
++ if (!sc->sc_beacon_cal)
++ del_timer(&sc->sc_cal_ch); /* periodic calibration timer */
+
+ ath_hal_setledstate(ah, leds[nstate]); /* set LED */
+ netif_stop_queue(dev); /* before we do anything else */
+@@ -9321,7 +9360,8 @@ ath_newstate(struct ieee80211vap *vap, e
+ "VAP -> DFSWAIT_PENDING \n");
+ /* start calibration timer with a really small value
+ * 1/10 sec */
+- mod_timer(&sc->sc_cal_ch, jiffies + (HZ/10));
++ if (!sc->sc_beacon_cal)
++ mod_timer(&sc->sc_cal_ch, jiffies + (HZ/10));
+ /* wake the receiver */
+ netif_wake_queue(dev);
+ /* don't do the other usual stuff... */
+@@ -9364,7 +9404,7 @@ done:
+ error = avp->av_newstate(vap, nstate, arg);
+
+ /* Finally, start any timers. */
+- if (nstate == IEEE80211_S_RUN) {
++ if (nstate == IEEE80211_S_RUN && !sc->sc_beacon_cal) {
+ /* start periodic recalibration timer */
+ mod_timer(&sc->sc_cal_ch, jiffies + (ath_calinterval * HZ));
+ }
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -778,6 +778,8 @@ struct ath_softc {
+ struct ieee80211vap **sc_bslot; /* beacon xmit slots */
+ int sc_bnext; /* next slot for beacon xmit */
+
++ int sc_beacon_cal; /* use beacon timer for calibration */
++ u_int64_t sc_lastcal; /* last time the calibration was performed */
+ struct timer_list sc_cal_ch; /* calibration timer */
+ HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */
+
diff --git a/package/madwifi/patches/331-memory_alloc.patch b/package/madwifi/patches/331-memory_alloc.patch
new file mode 100644
index 000000000..6b01d79c0
--- /dev/null
+++ b/package/madwifi/patches/331-memory_alloc.patch
@@ -0,0 +1,36 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -3320,17 +3320,18 @@ ath_hardstart(struct sk_buff *skb, struc
+ * without affecting any other bridge ports. */
+ if (skb_cloned(skb)) {
+ /* Remember the original SKB so we can free up our references */
+- struct sk_buff *skb_orig = skb;
+- skb = skb_copy(skb, GFP_ATOMIC);
+- if (skb == NULL) {
++ struct sk_buff *skb_new;
++ skb_new = skb_copy(skb, GFP_ATOMIC);
++ if (skb_new == NULL) {
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "Dropping; skb_copy failure.\n");
+ /* No free RAM, do not requeue! */
+ goto hardstart_fail;
+ }
+- ieee80211_skb_copy_noderef(skb_orig, skb);
+- ieee80211_dev_kfree_skb(&skb_orig);
+- }
++ ieee80211_skb_copy_noderef(skb, skb_new);
++ ieee80211_dev_kfree_skb(&skb);
++ skb = skb_new;
++ }
+ eh = (struct ether_header *)skb->data;
+
+ #ifdef ATH_SUPERG_FF
+@@ -3601,6 +3602,8 @@ ath_mgtstart(struct ieee80211com *ic, st
+ sc->sc_stats.ast_tx_mgmt++;
+ return 0;
+ bad:
++ if (skb)
++ ieee80211_dev_kfree_skb(&skb);
+ ath_return_txbuf(sc, &bf);
+ return error;
+ }
diff --git a/package/madwifi/patches/332-reset_beacons.patch b/package/madwifi/patches/332-reset_beacons.patch
new file mode 100644
index 000000000..b776426f2
--- /dev/null
+++ b/package/madwifi/patches/332-reset_beacons.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8922,7 +8922,7 @@ ath_chan_set(struct ath_softc *sc, struc
+ * re configure beacons when it is a turbo mode switch.
+ * HW seems to turn off beacons during turbo mode switch.
+ */
+- if (sc->sc_beacons && tswitch && !sc->sc_dfs_cac)
++ if (sc->sc_beacons && !sc->sc_dfs_cac)
+ ath_beacon_config(sc, NULL);
+ /*
+ * Re-enable interrupts.
diff --git a/package/madwifi/patches/333-apscan_mode.patch b/package/madwifi/patches/333-apscan_mode.patch
new file mode 100644
index 000000000..877eea6ce
--- /dev/null
+++ b/package/madwifi/patches/333-apscan_mode.patch
@@ -0,0 +1,15 @@
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -783,12 +783,6 @@ pick_channel(struct ieee80211_scan_state
+ /* break the loop as the subsequent chans won't be
+ * better */
+ break;
+-
+- if (!IEEE80211_ARE_CHANS_SAME_MODE(c->chan,
+- ic->ic_bsschan))
+- /* break the loop as the subsequent chans won't be
+- * better */
+- break;
+ }
+
+ if (sta_assoc != 0) {
diff --git a/package/madwifi/patches/334-input.patch b/package/madwifi/patches/334-input.patch
new file mode 100644
index 000000000..7c1336701
--- /dev/null
+++ b/package/madwifi/patches/334-input.patch
@@ -0,0 +1,12 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -950,6 +950,9 @@ ieee80211_input_all(struct ieee80211com
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct sk_buff *skb1;
+
++ if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
++ continue;
++
+ if (TAILQ_NEXT(vap, iv_next) != NULL) {
+ skb1 = skb_copy(skb, GFP_ATOMIC);
+ if (skb1 == NULL) {
diff --git a/package/madwifi/patches/340-maxrate.patch b/package/madwifi/patches/340-maxrate.patch
new file mode 100644
index 000000000..ae93f02d2
--- /dev/null
+++ b/package/madwifi/patches/340-maxrate.patch
@@ -0,0 +1,98 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1307,6 +1307,7 @@ ath_vap_create(struct ieee80211com *ic,
+ vap->iv_key_set = ath_key_set;
+ vap->iv_key_update_begin = ath_key_update_begin;
+ vap->iv_key_update_end = ath_key_update_end;
++ vap->iv_maxrateindex = 0;
+ if (sc->sc_default_ieee80211_debug) {
+ /* User specified defaults for new VAPs were provided, so
+ * use those (only). */
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -622,8 +622,12 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ return;
+ }
+ sn->static_rate_ndx = -1;
++ if (vap->iv_maxrateindex == 0 || ni->ni_rates.rs_nrates <= 0
++ || vap->iv_maxrateindex > ni->ni_rates.rs_nrates)
++ sn->num_rates = ni->ni_rates.rs_nrates;
++ else
++ sn->num_rates = vap->iv_maxrateindex;
+
+- sn->num_rates = ni->ni_rates.rs_nrates;
+ for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+ sn->rs_rateattempts [x] = 0;
+ sn->rs_thisprob [x] = 0;
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -835,7 +835,12 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ }
+ sn->static_rate_ndx = -1;
+
+- sn->num_rates = ni->ni_rates.rs_nrates;
++ if (vap->iv_maxrateindex == 0 || ni->ni_rates.rs_nrates <= 0
++ || vap->iv_maxrateindex > ni->ni_rates.rs_nrates)
++ sn->num_rates = ni->ni_rates.rs_nrates;
++ else
++ sn->num_rates = vap->iv_maxrateindex;
++
+ for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+ sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
+ sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -641,6 +641,7 @@ enum {
+ FCC requires 30m, so that is the default. */
+ IEEE80211_PARAM_BEACON_MISS_THRESH = 73, /* Beacon miss threshold (in beacons) */
+ IEEE80211_PARAM_BEACON_MISS_THRESH_MS = 74, /* Beacon miss threshold (in ms) */
++ IEEE80211_PARAM_MAXRATE = 75, /* Maximum rate (by table index) */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -281,6 +281,7 @@ struct ieee80211vap {
+ struct ieee80211_spy iv_spy; /* IWSPY support */
+ struct ieee80211_app_ie app_ie[IEEE80211_APPIE_NUM_OF_FRAME]; /* app-specified IEs by frame type */
+ u_int32_t app_filter; /* filters which management frames are forwarded to app */
++ u_int iv_maxrateindex;
+ };
+
+ /* Debug functions need the defintion of struct ieee80211vap because iv_debug
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2839,6 +2839,12 @@ ieee80211_ioctl_setparam(struct net_devi
+ else
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_MARKDFS;
+ break;
++ case IEEE80211_PARAM_MAXRATE:
++ if (value > 0)
++ vap->iv_maxrateindex = value;
++ else
++ vap->iv_maxrateindex = 0;
++ break;
+ #ifdef ATH_REVERSE_ENGINEERING
+ case IEEE80211_PARAM_DUMPREGS:
+ ieee80211_dump_registers(dev, info, w, extra);
+@@ -3174,6 +3180,9 @@ ieee80211_ioctl_getparam(struct net_devi
+ else
+ param[0] = 0;
+ break;
++ case IEEE80211_PARAM_MAXRATE:
++ param[0] = vap->iv_maxrateindex;
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -5610,6 +5619,10 @@ static const struct iw_priv_args ieee802
+ 0, IW_PRIV_TYPE_APPIEBUF, "getiebuf" },
+ { IEEE80211_IOCTL_FILTERFRAME,
+ IW_PRIV_TYPE_FILTER , 0, "setfilter" },
++ {IEEE80211_PARAM_MAXRATE,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maxrate"},
++ {IEEE80211_PARAM_MAXRATE,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_maxrate"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
diff --git a/package/madwifi/patches/341-minrate.patch b/package/madwifi/patches/341-minrate.patch
new file mode 100644
index 000000000..cc04ae074
--- /dev/null
+++ b/package/madwifi/patches/341-minrate.patch
@@ -0,0 +1,114 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1308,6 +1308,7 @@ ath_vap_create(struct ieee80211com *ic,
+ vap->iv_key_update_begin = ath_key_update_begin;
+ vap->iv_key_update_end = ath_key_update_end;
+ vap->iv_maxrateindex = 0;
++ vap->iv_minrateindex = 0;
+ if (sc->sc_default_ieee80211_debug) {
+ /* User specified defaults for new VAPs were provided, so
+ * use those (only). */
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -638,9 +638,15 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ sn->rs_succ_hist [x] = 0;
+ sn->rs_att_hist [x] = 0;
+ sn->rs_this_tp [x] = 0;
+-
++ if (vap->iv_minrateindex && vap->iv_minrateindex<ni->ni_rates.rs_nrates)
++ {
++ int idx = vap->iv_minrateindex;
++ sn->rates[x].rate = ni->ni_rates.rs_rates[idx] & IEEE80211_RATE_VAL;
++ sn->rates[x].rix = sc->sc_rixmap[sn->rates[idx].rate];
++ }else{
+ sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
+ sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
++ }
+ if (sn->rates[x].rix == 0xff) {
+ DPRINTF(sc, "%s: %s ignore bogus rix at %d\n",
+ dev_info, __func__, x);
+@@ -649,7 +655,7 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ sn->rates[x].rateCode = rt->info[sn->rates[x].rix].rateCode;
+ sn->rates[x].shortPreambleRateCode =
+ rt->info[sn->rates[x].rix].rateCode |
+- rt->info[sn->rates[x].rix].shortPreamble;
++ rt->info[sn->rates[x].rix].shortPreamble;
+ }
+
+ ath_fill_sample_table(sn);
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -842,8 +842,15 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ sn->num_rates = vap->iv_maxrateindex;
+
+ for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+- sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
+- sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
++ if (vap->iv_minrateindex && vap->iv_minrateindex<ni->ni_rates.rs_nrates)
++ {
++ int idx = vap->iv_minrateindex;
++ sn->rates[x].rate = ni->ni_rates.rs_rates[idx] & IEEE80211_RATE_VAL;
++ sn->rates[x].rix = sc->sc_rixmap[sn->rates[idx].rate];
++ }else{
++ sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
++ sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
++ }
+ if (sn->rates[x].rix == 0xff) {
+ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s ignore bogus rix at %u\n",
+ dev_info, __func__, x);
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -642,6 +642,7 @@ enum {
+ IEEE80211_PARAM_BEACON_MISS_THRESH = 73, /* Beacon miss threshold (in beacons) */
+ IEEE80211_PARAM_BEACON_MISS_THRESH_MS = 74, /* Beacon miss threshold (in ms) */
+ IEEE80211_PARAM_MAXRATE = 75, /* Maximum rate (by table index) */
++ IEEE80211_PARAM_MINRATE = 76, /* Minimum rate (by table index) */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -282,6 +282,7 @@ struct ieee80211vap {
+ struct ieee80211_app_ie app_ie[IEEE80211_APPIE_NUM_OF_FRAME]; /* app-specified IEs by frame type */
+ u_int32_t app_filter; /* filters which management frames are forwarded to app */
+ u_int iv_maxrateindex;
++ u_int iv_minrateindex;
+ };
+
+ /* Debug functions need the defintion of struct ieee80211vap because iv_debug
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2845,6 +2845,12 @@ ieee80211_ioctl_setparam(struct net_devi
+ else
+ vap->iv_maxrateindex = 0;
+ break;
++ case IEEE80211_PARAM_MINRATE:
++ if (value > 0)
++ vap->iv_minrateindex = value;
++ else
++ vap->iv_minrateindex = 0;
++ break;
+ #ifdef ATH_REVERSE_ENGINEERING
+ case IEEE80211_PARAM_DUMPREGS:
+ ieee80211_dump_registers(dev, info, w, extra);
+@@ -3183,6 +3189,9 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_MAXRATE:
+ param[0] = vap->iv_maxrateindex;
+ break;
++ case IEEE80211_PARAM_MINRATE:
++ param[0] = vap->iv_minrateindex;
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -5623,6 +5632,10 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maxrate"},
+ {IEEE80211_PARAM_MAXRATE,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_maxrate"},
++ {IEEE80211_PARAM_MINRATE,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "minrate"},
++ {IEEE80211_PARAM_MINRATE,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
diff --git a/package/madwifi/patches/342-performance.patch b/package/madwifi/patches/342-performance.patch
new file mode 100644
index 000000000..88cec182d
--- /dev/null
+++ b/package/madwifi/patches/342-performance.patch
@@ -0,0 +1,263 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -3239,7 +3239,6 @@ ath_hardstart(struct sk_buff *skb, struc
+ struct ath_softc *sc = dev->priv;
+ struct ieee80211_node *ni = NULL;
+ struct ath_buf *bf = NULL;
+- struct ether_header *eh;
+ ath_bufhead bf_head;
+ struct ath_buf *tbf, *tempbf;
+ struct sk_buff *tskb;
+@@ -3251,6 +3250,7 @@ ath_hardstart(struct sk_buff *skb, struc
+ */
+ int requeue = 0;
+ #ifdef ATH_SUPERG_FF
++ struct ether_header *eh;
+ unsigned int pktlen;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_node *an;
+@@ -3316,27 +3316,9 @@ ath_hardstart(struct sk_buff *skb, struc
+ requeue = 1;
+ goto hardstart_fail;
+ }
+-#endif
+
+- /* If the skb data is shared, we will copy it so we can strip padding
+- * without affecting any other bridge ports. */
+- if (skb_cloned(skb)) {
+- /* Remember the original SKB so we can free up our references */
+- struct sk_buff *skb_new;
+- skb_new = skb_copy(skb, GFP_ATOMIC);
+- if (skb_new == NULL) {
+- DPRINTF(sc, ATH_DEBUG_XMIT,
+- "Dropping; skb_copy failure.\n");
+- /* No free RAM, do not requeue! */
+- goto hardstart_fail;
+- }
+- ieee80211_skb_copy_noderef(skb, skb_new);
+- ieee80211_dev_kfree_skb(&skb);
+- skb = skb_new;
+- }
+ eh = (struct ether_header *)skb->data;
+
+-#ifdef ATH_SUPERG_FF
+ /* NB: use this lock to protect an->an_tx_ffbuf (and txq->axq_stageq)
+ * in athff_can_aggregate() call too. */
+ ATH_TXQ_LOCK_IRQ(txq);
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -283,7 +283,7 @@ ieee80211_hardstart(struct sk_buff *skb,
+ * normal vap. */
+ if (vap->iv_xrvap && (ni == vap->iv_bss) &&
+ vap->iv_xrvap->iv_sta_assoc) {
+- struct sk_buff *skb1 = skb_copy(skb, GFP_ATOMIC);
++ struct sk_buff *skb1 = skb_clone(skb, GFP_ATOMIC);
+ if (skb1) {
+ memset(SKB_CB(skb1), 0, sizeof(struct ieee80211_cb));
+ #ifdef IEEE80211_DEBUG_REFCNT
+@@ -566,7 +566,7 @@ ieee80211_skbhdr_adjust(struct ieee80211
+ struct ieee80211_key *key, struct sk_buff *skb, int ismulticast)
+ {
+ /* XXX pre-calculate per node? */
+- int need_headroom = LLC_SNAPFRAMELEN + hdrsize + IEEE80211_ADDR_LEN;
++ int need_headroom = LLC_SNAPFRAMELEN + hdrsize;
+ int need_tailroom = 0;
+ #ifdef ATH_SUPERG_FF
+ int isff = ATH_FF_MAGIC_PRESENT(skb);
+@@ -608,109 +608,56 @@ ieee80211_skbhdr_adjust(struct ieee80211
+ need_tailroom += cip->ic_miclen;
+ }
+
+- if (skb_shared(skb)) {
+- /* Take our own reference to the node in the clone */
+- ieee80211_ref_node(SKB_CB(skb)->ni);
+- /* Unshare the node, decrementing users in the old skb */
+- skb = skb_unshare(skb, GFP_ATOMIC);
+- }
++ need_headroom -= skb_headroom(skb);
++ if (isff)
++ need_tailroom -= skb_tailroom(skb2);
++ else
++ need_tailroom -= skb_tailroom(skb);
++
++ if (need_headroom < 0)
++ need_headroom = 0;
++ if (need_tailroom < 0)
++ need_tailroom = 0;
+
+-#ifdef ATH_SUPERG_FF
+- if (isff) {
+- if (skb == NULL) {
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+- "%s: cannot unshare for encapsulation\n",
+- __func__);
+- vap->iv_stats.is_tx_nobuf++;
+- ieee80211_dev_kfree_skb(&skb2);
++ if (skb_cloned(skb) || (need_headroom > 0) ||
++ (!isff && (need_tailroom > 0))) {
+
+- return NULL;
++ if (pskb_expand_head(skb, need_headroom, need_tailroom, GFP_ATOMIC)) {
++ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
++ "%s: cannot expand storage (tail)\n", __func__);
++ goto error;
+ }
++ }
+
+- /* first skb header */
+- if (skb_headroom(skb) < need_headroom) {
+- struct sk_buff *tmp = skb;
+- skb = skb_realloc_headroom(skb, need_headroom);
+- if (skb == NULL) {
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+- "%s: cannot expand storage (head1)\n",
+- __func__);
+- vap->iv_stats.is_tx_nobuf++;
+- ieee80211_dev_kfree_skb(&skb2);
+- return NULL;
+- } else
+- ieee80211_skb_copy_noderef(tmp, skb);
+- ieee80211_dev_kfree_skb(&tmp);
+- /* NB: cb[] area was copied, but not next ptr. must do that
+- * prior to return on success. */
+- }
++#ifdef ATH_SUPERG_FF
++ if (isff) {
++ inter_headroom -= skb_headroom(skb2);
++ if (inter_headroom < 0)
++ inter_headroom = 0;
++ if ((skb_cloned(skb2) ||
++ (inter_headroom > 0) || (need_tailroom > 0))) {
+
+- /* second skb with header and tail adjustments possible */
+- if (skb_tailroom(skb2) < need_tailroom) {
+- int n = 0;
+- if (inter_headroom > skb_headroom(skb2))
+- n = inter_headroom - skb_headroom(skb2);
+- if (pskb_expand_head(skb2, n,
+- need_tailroom - skb_tailroom(skb2), GFP_ATOMIC)) {
+- ieee80211_dev_kfree_skb(&skb2);
++ if (pskb_expand_head(skb2, inter_headroom,
++ need_tailroom, GFP_ATOMIC)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+- "%s: cannot expand storage (tail2)\n",
+- __func__);
+- vap->iv_stats.is_tx_nobuf++;
+- /* this shouldn't happen, but don't send first ff either */
+- ieee80211_dev_kfree_skb(&skb);
++ "%s: cannot expand storage (tail)\n", __func__);
++ goto error;
+ }
+- } else if (skb_headroom(skb2) < inter_headroom) {
+- struct sk_buff *tmp = skb2;
+-
+- skb2 = skb_realloc_headroom(skb2, inter_headroom);
+- if (skb2 == NULL) {
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+- "%s: cannot expand storage (head2)\n",
+- __func__);
+- vap->iv_stats.is_tx_nobuf++;
+- /* this shouldn't happen, but don't send first ff either */
+- ieee80211_dev_kfree_skb(&skb);
+- skb = NULL;
+- } else
+- ieee80211_skb_copy_noderef(tmp, skb);
+- ieee80211_dev_kfree_skb(&tmp);
+- }
+- if (skb) {
+- skb->next = skb2;
+ }
+- return skb;
++ skb->next = skb2;
+ }
+ #endif /* ATH_SUPERG_FF */
+- if (skb == NULL) {
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+- "%s: cannot unshare for encapsulation\n", __func__);
+- vap->iv_stats.is_tx_nobuf++;
+- } else if (skb_tailroom(skb) < need_tailroom) {
+- int n = 0;
+- if (need_headroom > skb_headroom(skb))
+- n = need_headroom - skb_headroom(skb);
+- if (pskb_expand_head(skb, n, need_tailroom -
+- skb_tailroom(skb), GFP_ATOMIC)) {
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+- "%s: cannot expand storage (tail)\n", __func__);
+- vap->iv_stats.is_tx_nobuf++;
+- ieee80211_dev_kfree_skb(&skb);
+- }
+- } else if (skb_headroom(skb) < need_headroom) {
+- struct sk_buff *tmp = skb;
+- skb = skb_realloc_headroom(skb, need_headroom);
+- /* Increment reference count after copy */
+- if (skb == NULL) {
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+- "%s: cannot expand storage (head)\n", __func__);
+- vap->iv_stats.is_tx_nobuf++;
+- } else
+- ieee80211_skb_copy_noderef(tmp, skb);
+- ieee80211_dev_kfree_skb(&tmp);
+- }
+
+ return skb;
++
++error:
++ vap->iv_stats.is_tx_nobuf++;
++ ieee80211_dev_kfree_skb(&skb);
++#ifdef ATH_SUPERG_FF
++ if (skb2)
++ ieee80211_dev_kfree_skb(&skb2);
++#endif
++ return NULL;
+ }
+
+ #define KEY_UNDEFINED(k) ((k).wk_cipher == &ieee80211_cipher_none)
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -204,7 +204,6 @@ ieee80211_input(struct ieee80211vap * va
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+- struct sk_buff *skb2;
+ #ifdef ATH_SUPERG_FF
+ struct llc *llc;
+ #endif
+@@ -244,20 +243,6 @@ ieee80211_input(struct ieee80211vap * va
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+- /* Clone the SKB... we assume somewhere in this driver that we 'own'
+- * the skbuff passed into hard start and we do a lot of messing with it
+- * but bridges under some cases will not clone for the first pass of skb
+- * to a bridge port, but will then clone for subsequent ones. This is
+- * odd behavior but it means that if we have trashed the skb we are given
+- * then other ports get clones of the residual garbage.
+- */
+- if ((skb2 = skb_copy(skb, GFP_ATOMIC)) == NULL) {
+- vap->iv_devstats.tx_dropped++;
+- goto out;
+- }
+- ieee80211_skb_copy_noderef(skb, skb2);
+- ieee80211_dev_kfree_skb(&skb);
+- skb = skb2;
+
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+@@ -738,7 +723,7 @@ ieee80211_input(struct ieee80211vap * va
+ /* ether_type must be length as FF frames are always LLC/SNAP encap'd */
+ frame_len = ntohs(eh_tmp->ether_type);
+
+- skb1 = skb_copy(skb, GFP_ATOMIC);
++ skb1 = skb_clone(skb, GFP_ATOMIC);
+ if (skb1 == NULL)
+ goto err;
+ ieee80211_skb_copy_noderef(skb, skb1);
+@@ -1137,7 +1122,7 @@ ieee80211_deliver_data(struct ieee80211_
+
+ if (ETHER_IS_MULTICAST(eh->ether_dhost) && !netif_queue_stopped(dev)) {
+ /* Create a SKB for the BSS to send out. */
+- skb1 = skb_copy(skb, GFP_ATOMIC);
++ skb1 = skb_clone(skb, GFP_ATOMIC);
+ if (skb1)
+ SKB_CB(skb1)->ni = ieee80211_ref_node(vap->iv_bss);
+ }
diff --git a/package/madwifi/patches/343-txqueue_races.patch b/package/madwifi/patches/343-txqueue_races.patch
new file mode 100644
index 000000000..a2b14d69a
--- /dev/null
+++ b/package/madwifi/patches/343-txqueue_races.patch
@@ -0,0 +1,34 @@
+Merged from madwifi trunk r3551, r3552
+
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8253,6 +8253,17 @@ ath_tx_processq(struct ath_softc *sc, st
+ goto bf_fail;
+ }
+
++ /* We make sure we don't remove the TX descriptor on
++ * which the HW is pointing since it contains the
++ * ds_link field, except if this is the last TX
++ * descriptor in the queue */
++
++ if ((txq->axq_depth > 1) &&
++ (bf->bf_daddr == ath_hal_gettxbuf(ah, txq->axq_qnum))) {
++ ATH_TXQ_UNLOCK_IRQ_EARLY(txq);
++ goto bf_fail;
++ }
++
+ ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+ ATH_TXQ_UNLOCK_IRQ(txq);
+
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -586,7 +586,8 @@ struct ath_vap {
+ } while (0)
+ #define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \
+ STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
+- (_tq)->axq_depth--; \
++ if (--(_tq)->axq_depth <= 0) \
++ (_tq)->axq_link = NULL; \
+ } while (0)
+ /* move buffers from MCASTQ to CABQ */
+ #define ATH_TXQ_MOVE_MCASTQ(_tqs,_tqd) do { \
diff --git a/package/madwifi/patches/344-minstrel_failcnt.patch b/package/madwifi/patches/344-minstrel_failcnt.patch
new file mode 100644
index 000000000..ea92dc4d6
--- /dev/null
+++ b/package/madwifi/patches/344-minstrel_failcnt.patch
@@ -0,0 +1,11 @@
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -475,7 +475,7 @@ ath_rate_tx_complete(struct ath_softc *s
+ /* 'tries' is the total number of times we have endeavoured to
+ * send this packet, and is a sum of the #attempts at each
+ * level in the multi-rate retry chain */
+- tries = ts->ts_shortretry + ts->ts_longretry + 1;
++ tries = ts->ts_longretry + 1;
+
+ if (sn->num_rates <= 0) {
+ DPRINTF(sc, "%s: " MAC_FMT " %s no rates yet\n", dev_info,
diff --git a/package/madwifi/patches/345-minstrel_sampling.patch b/package/madwifi/patches/345-minstrel_sampling.patch
new file mode 100644
index 000000000..63fcb8a61
--- /dev/null
+++ b/package/madwifi/patches/345-minstrel_sampling.patch
@@ -0,0 +1,80 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8103,6 +8103,7 @@ ath_tx_start(struct net_device *dev, str
+ ath_hal_setupxtxdesc(sc->sc_ah, ds, mrr.rate1, mrr.retries1,
+ mrr.rate2, mrr.retries2,
+ mrr.rate3, mrr.retries3);
++ bf->rcflags = mrr.privflags;
+ }
+
+ #ifndef ATH_SUPERG_FF
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -446,6 +446,7 @@ struct ath_buf {
+ u_int16_t bf_flags; /* tx descriptor flags */
+ u_int64_t bf_tsf;
+ int16_t bf_channoise;
++ unsigned int rcflags;
+ #ifdef ATH_SUPERG_FF
+ /* XXX: combine this with bf_skbaddr if it ever changes to accommodate
+ * multiple segments.
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -333,15 +333,19 @@ ath_rate_findrate(struct ath_softc *sc,
+ if (sn->static_rate_ndx >= 0) {
+ ndx = sn->static_rate_ndx;
+ } else {
++ int delta;
+ sn->packet_count++;
+ sn->random_n = (sn->a * sn->random_n) + sn->b;
+ offset = sn->random_n & 0xf;
+- if ((((100 * sn->sample_count) / (sn->sample_count + sn->packet_count)) < ath_lookaround_rate) && (offset < 2)) {
++ delta = (sn->packet_count * ath_lookaround_rate / 100) - sn->sample_count;
++ if ((delta > 0) && (offset < 2)) {
+ sn->sample_count++;
+ sn->is_sampling = 1;
+ if (sn->packet_count >= 10000) {
+ sn->sample_count = 0;
+ sn->packet_count = 0;
++ } else if (delta > sn->num_rates * 2) {
++ sn->sample_count += ((delta - sn->num_rates * 2) * ath_lookaround_rate) / 100;
+ }
+
+ /* Don't look for slowest rate (i.e. slowest
+@@ -398,11 +402,14 @@ ath_rate_get_mrr(struct ath_softc *sc, s
+ if (sn->num_rates <= 0)
+ return;
+
++ mrr->privflags = sn->is_sampling;
+ if (sn->is_sampling) {
+ sn->is_sampling = 0;
+- if (sn->rs_sample_rate_slower)
++ if (sn->rs_sample_rate_slower) {
+ rc1 = sn->rs_sample_rate;
+- else
++ if (sn->sample_count > 0)
++ sn->sample_count--;
++ } else
+ rc1 = sn->max_tp_rate;
+ } else {
+ rc1 = sn->max_tp_rate2;
+@@ -525,6 +532,9 @@ ath_rate_tx_complete(struct ath_softc *s
+ if (tries <= tries1)
+ return;
+
++ if (bf->rcflags)
++ sn->sample_count++;
++
+ if (tries2 < 0)
+ return;
+ tries = tries - tries1;
+--- a/net80211/ieee80211_rate.h
++++ b/net80211/ieee80211_rate.h
+@@ -87,6 +87,7 @@ struct ieee80211_mrr {
+ int retries2;
+ int rate3;
+ int retries3;
++ int privflags;
+ };
+
+ struct ieee80211_rate_ops {
diff --git a/package/madwifi/patches/346-protmode_trig.patch b/package/madwifi/patches/346-protmode_trig.patch
new file mode 100644
index 000000000..5b5cec6e9
--- /dev/null
+++ b/package/madwifi/patches/346-protmode_trig.patch
@@ -0,0 +1,135 @@
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -333,7 +333,9 @@ ieee80211_ifattach(struct ieee80211com *
+ IEEE80211_MS_TO_TU(IEEE80211_BMISSTHRESH_DEFAULT_MS),
+ ic->ic_lintval), ic->ic_lintval);
+ }
+-
++ ic->ic_protmode_timeout = IEEE80211_PROTMODE_TIMEOUT;
++ ic->ic_protmode_rssi = IEEE80211_PROTMODE_RSSITHR;
++
+ IEEE80211_LOCK_INIT(ic, "ieee80211com");
+ IEEE80211_VAPS_LOCK_INIT(ic, "ieee80211com_vaps");
+ TAILQ_INIT(&ic->ic_vaps);
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3411,14 +3411,18 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
+
+ /* Assume no ERP IE == 11b AP */
+- if ((!has_erp || (has_erp && (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) &&
+- !(ic->ic_flags & IEEE80211_F_USEPROT)) {
++ if ((!has_erp || (has_erp &&
++ (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) &&
++ (rssi > ic->ic_protmode_rssi)) {
+ struct ieee80211vap *tmpvap;
+
+- ic->ic_flags |= IEEE80211_F_USEPROT;
+- TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) {
+- tmpvap->iv_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ if (!(ic->ic_flags & IEEE80211_F_USEPROT)) {
++ ic->ic_flags |= IEEE80211_F_USEPROT;
++ TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) {
++ tmpvap->iv_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ }
+ }
++ ic->ic_protmode_lasttrig = jiffies;
+ }
+ }
+
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -643,6 +643,8 @@ enum {
+ IEEE80211_PARAM_BEACON_MISS_THRESH_MS = 74, /* Beacon miss threshold (in ms) */
+ IEEE80211_PARAM_MAXRATE = 75, /* Maximum rate (by table index) */
+ IEEE80211_PARAM_MINRATE = 76, /* Minimum rate (by table index) */
++ IEEE80211_PARAM_PROTMODE_RSSI = 77, /* RSSI Threshold for enabling protection mode */
++ IEEE80211_PARAM_PROTMODE_TIMEOUT = 78, /* Timeout for expiring protection mode */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -128,6 +128,9 @@
+
+ #define IEEE80211_APPIE_MAX 1024
+
++#define IEEE80211_PROTMODE_RSSITHR 15 /* default rssi threshold for protection mode trigger */
++#define IEEE80211_PROTMODE_TIMEOUT 30 /* timeout for keeping protection mode alive */
++
+ #define IEEE80211_PWRCONSTRAINT_VAL(ic) \
+ (((ic)->ic_bsschan->ic_maxregpower > (ic)->ic_curchanmaxpwr) ? \
+ (ic)->ic_bsschan->ic_maxregpower - (ic)->ic_curchanmaxpwr : 0)
+@@ -324,6 +327,9 @@ struct ieee80211com {
+ u_int16_t ic_newtxpowlimit; /* tx power limit to change to (in 0.5 dBm) */
+ u_int16_t ic_uapsdmaxtriggers; /* max triggers that could arrive */
+ u_int8_t ic_coverageclass; /* coverage class */
++ u_int8_t ic_protmode_rssi; /* rssi threshold for protection mode */
++ u_int64_t ic_protmode_lasttrig; /* last trigger for protection mode */
++ u_int16_t ic_protmode_timeout; /* protection mode timeout */
+
+ /* Channel state:
+ *
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2312,6 +2312,12 @@ ieee80211_ioctl_setparam(struct net_devi
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
+ retv = ENETRESET;
+ break;
++ case IEEE80211_PARAM_PROTMODE_TIMEOUT:
++ ic->ic_protmode_timeout = value;
++ break;
++ case IEEE80211_PARAM_PROTMODE_RSSI:
++ ic->ic_protmode_rssi = value;
++ break;
+ case IEEE80211_PARAM_MCASTCIPHER:
+ if ((vap->iv_caps & cipher2cap(value)) == 0 &&
+ !ieee80211_crypto_available(vap, value))
+@@ -2955,6 +2961,12 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_PROTMODE:
+ param[0] = ic->ic_protmode;
+ break;
++ case IEEE80211_PARAM_PROTMODE_TIMEOUT:
++ param[0] = ic->ic_protmode_timeout;
++ break;
++ case IEEE80211_PARAM_PROTMODE_RSSI:
++ param[0] = ic->ic_protmode_rssi;
++ break;
+ case IEEE80211_PARAM_MCASTCIPHER:
+ param[0] = rsn->rsn_mcastcipher;
+ break;
+@@ -5346,6 +5358,14 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "protmode" },
+ { IEEE80211_PARAM_PROTMODE,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_protmode" },
++ { IEEE80211_PARAM_PROTMODE_RSSI,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "protrssi" },
++ { IEEE80211_PARAM_PROTMODE_RSSI,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_protrssi" },
++ { IEEE80211_PARAM_PROTMODE_TIMEOUT,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "prottime" },
++ { IEEE80211_PARAM_PROTMODE_TIMEOUT,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_prottime" },
+ { IEEE80211_PARAM_MCASTCIPHER,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcastcipher" },
+ { IEEE80211_PARAM_MCASTCIPHER,
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -1877,6 +1877,17 @@ ieee80211_node_timeout(unsigned long arg
+
+ ieee80211_scan_timeout(ic);
+ ieee80211_timeout_stations(&ic->ic_sta);
++ if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
++ (ic->ic_protmode_lasttrig + ic->ic_protmode_timeout * HZ <
++ jiffies)) {
++ struct ieee80211vap *tmpvap;
++
++ /* expire protection mode */
++ ic->ic_flags &= ~IEEE80211_F_USEPROT;
++ TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) {
++ tmpvap->iv_flags_ext |= IEEE80211_FEXT_ERPUPDATE;
++ }
++ }
+
+ ic->ic_inact.expires = jiffies + IEEE80211_INACT_WAIT * HZ;
+ add_timer(&ic->ic_inact);
diff --git a/package/madwifi/patches/347-tuning.patch b/package/madwifi/patches/347-tuning.patch
new file mode 100644
index 000000000..1a73c4274
--- /dev/null
+++ b/package/madwifi/patches/347-tuning.patch
@@ -0,0 +1,99 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -10276,11 +10276,11 @@ ath_setcurmode(struct ath_softc *sc, enu
+ sc->sc_currates = rt;
+ sc->sc_curmode = mode;
+ /*
+- * All protection frames are transmitted at 2Mb/s for
+- * 11g, otherwise at 1Mb/s.
++ * All protection frames are transmitted at 11Mb/s for
++ * 11g, otherwise at 2Mb/s.
+ * XXX select protection rate index from rate table.
+ */
+- sc->sc_protrix = (mode == IEEE80211_MODE_11G ? 1 : 0);
++ sc->sc_protrix = (mode == IEEE80211_MODE_11G ? 3 : 1);
+ /* rate index used to send mgt frames */
+ sc->sc_minrateix = 0;
+ }
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -272,6 +272,10 @@ static inline struct net_device *_alloc_
+ #define AES_ICV_FIELD_SIZE 8 /* AES ICV field size */
+ #define EXT_IV_FIELD_SIZE 4 /* ext IV field size */
+
++/* This is what the HAL uses by default for 11a+g */
++#define ATH_DEFAULT_CWMIN 15
++#define ATH_DEFAULT_CWMAX 1023
++
+ /* XR specific macros */
+
+ #define XR_DEFAULT_GRPPOLL_RATE_STR "0.25 1 1 3 3 6 6 20"
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -197,7 +197,7 @@ calc_usecs_unicast_packet(struct ath_sof
+ unsigned int x = 0, tt = 0;
+ unsigned int cix = rt->info[rix].controlRate;
+ int rts = 0, cts = 0;
+- int cw = WIFI_CW_MIN;
++ int cw = ATH_DEFAULT_CWMIN;
+
+ KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+@@ -281,7 +281,7 @@ calc_usecs_unicast_packet(struct ath_sof
+ tt += (long_retries + 1) * ath_hal_computetxtime(sc->sc_ah, rt, length,
+ rix, AH_TRUE);
+ for (x = 0; x <= short_retries + long_retries; x++) {
+- cw = MIN(WIFI_CW_MAX, (cw + 1) * 2);
++ cw = MIN(ATH_DEFAULT_CWMAX, (cw + 1) * 2);
+ tt += (t_slot * cw / 2);
+ }
+ return tt;
+--- a/ath_rate/minstrel/minstrel.h
++++ b/ath_rate/minstrel/minstrel.h
+@@ -180,14 +180,6 @@ struct minstrel_node {
+ #define MAX(a,b) ((a) > (b) ? (a) : (b))
+ #endif
+
+-#if 0
+-#define WIFI_CW_MIN 31
+-#define WIFI_CW_MAX 1023
+-#else
+-#define WIFI_CW_MIN 3
+-#define WIFI_CW_MAX 10
+-#endif
+-
+ /*
+ * Definitions for pulling the rate and trie counts from
+ * a 5212 h/w descriptor. These Don't belong here; the
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -170,7 +170,7 @@ calc_usecs_unicast_packet(struct ath_sof
+ struct ieee80211com *ic = &sc->sc_ic;
+ unsigned int tt = 0;
+ unsigned int x;
+- unsigned int cw = WIFI_CW_MIN;
++ unsigned int cw = ATH_DEFAULT_CWMIN;
+ unsigned int cix = rt->info[rix].controlRate;
+ KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+@@ -254,7 +254,7 @@ calc_usecs_unicast_packet(struct ath_sof
+ tt += (long_retries+1)*ath_hal_computetxtime(sc->sc_ah, rt, length,
+ rix, AH_TRUE);
+ for (x = 0; x <= short_retries + long_retries; x++) {
+- cw = MIN(WIFI_CW_MAX, (cw + 1) * 2);
++ cw = MIN(ATH_DEFAULT_CWMAX, (cw + 1) * 2);
+ tt += (t_slot * cw / 2);
+ }
+ return tt;
+--- a/ath_rate/sample/sample.h
++++ b/ath_rate/sample/sample.h
+@@ -106,9 +106,6 @@ struct sample_node {
+ #define MAX(a,b) ((a) > (b) ? (a) : (b))
+ #endif
+
+-#define WIFI_CW_MIN 31
+-#define WIFI_CW_MAX 1023
+-
+ /*
+ * Definitions for pulling the rate and trie counts from
+ * a 5212 h/w descriptor. These Don't belong here; the
diff --git a/package/madwifi/patches/348-ackcts.patch b/package/madwifi/patches/348-ackcts.patch
new file mode 100644
index 000000000..42b6fe261
--- /dev/null
+++ b/package/madwifi/patches/348-ackcts.patch
@@ -0,0 +1,38 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -2723,6 +2723,9 @@ ar_device(int devid)
+ static int
+ ath_set_ack_bitrate(struct ath_softc *sc, int high)
+ {
++ if (!sc->sc_ackrate_override)
++ return 0;
++
+ if (ar_device(sc->devid) == 5212 || ar_device(sc->devid) == 5213) {
+ /* set ack to be sent at low bit-rate */
+ /* registers taken from the OpenBSD 5212 HAL */
+@@ -10791,8 +10794,13 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ break;
+ #endif
+ case ATH_ACKRATE:
+- sc->sc_ackrate = val;
+- ath_set_ack_bitrate(sc, sc->sc_ackrate);
++ if (val == -1)
++ sc->sc_ackrate_override = 0;
++ else {
++ sc->sc_ackrate_override = 1;
++ sc->sc_ackrate = val;
++ ath_set_ack_bitrate(sc, sc->sc_ackrate);
++ }
+ break;
+ case ATH_RP:
+ ath_rp_record(sc,
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -681,6 +681,7 @@ struct ath_softc {
+ unsigned int sc_devstopped:1; /* stopped due to of no tx bufs */
+ unsigned int sc_stagbeacons:1; /* use staggered beacons */
+ unsigned int sc_dfswait:1; /* waiting on channel for radar detect */
++ unsigned int sc_ackrate_override:1; /* override ack rate */
+ unsigned int sc_ackrate:1; /* send acks at high bitrate */
+ unsigned int sc_dfs_cac:1; /* waiting on channel for radar detect */
+ unsigned int sc_hasintmit:1; /* Interference mitigation */
diff --git a/package/madwifi/patches/349-reset.patch b/package/madwifi/patches/349-reset.patch
new file mode 100644
index 000000000..06e3fa86f
--- /dev/null
+++ b/package/madwifi/patches/349-reset.patch
@@ -0,0 +1,12 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8864,8 +8864,7 @@ ath_chan_set(struct ath_softc *sc, struc
+ * needed to do the reset with chanchange = AH_FALSE in order
+ * to receive traffic when peforming high velocity channel
+ * changes. */
+- if (!ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_TRUE, &status) ||
+- !ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_FALSE, &status)) {
++ if (!ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_TRUE, &status)) {
+ EPRINTF(sc, "Unable to reset channel %u (%u MHz) "
+ "flags 0x%x '%s' (HAL status %u)\n",
+ ieee80211_chan2ieee(ic, chan), chan->ic_freq,
diff --git a/package/madwifi/patches/350-wisoc_softled.patch b/package/madwifi/patches/350-wisoc_softled.patch
new file mode 100644
index 000000000..dae9f218d
--- /dev/null
+++ b/package/madwifi/patches/350-wisoc_softled.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_ath_ahb.c
++++ b/ath/if_ath_ahb.c
+@@ -245,6 +245,8 @@ init_ath_wmac(u_int16_t devid, u_int16_t
+ num_activesc++;
+ /* Ready to process interrupts */
+
++ sc->aps_sc.sc_softled = 1; /* SoftLED over GPIO */
++ sc->aps_sc.sc_ledpin = config->board->sysLedGpio;
+ sc->aps_sc.sc_invalid = 0;
+ return 0;
+
diff --git a/package/madwifi/patches/351-scanlist.patch b/package/madwifi/patches/351-scanlist.patch
new file mode 100644
index 000000000..c11f28e39
--- /dev/null
+++ b/package/madwifi/patches/351-scanlist.patch
@@ -0,0 +1,904 @@
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -317,147 +317,6 @@ found:
+ #undef ISPROBE
+ }
+
+-static struct ieee80211_channel *
+-find11gchannel(struct ieee80211com *ic, int i, int freq)
+-{
+- struct ieee80211_channel *c;
+- int j;
+-
+- /*
+- * The normal ordering in the channel list is b channel
+- * immediately followed by g so optimize the search for
+- * this. We'll still do a full search just in case.
+- */
+- for (j = i+1; j < ic->ic_nchans; j++) {
+- c = &ic->ic_channels[j];
+- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+- return c;
+- }
+- for (j = 0; j < i; j++) {
+- c = &ic->ic_channels[j];
+- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
+- return c;
+- }
+- return NULL;
+-}
+-static const u_int chanflags[] = {
+- IEEE80211_CHAN_B, /* IEEE80211_MODE_AUTO */
+- IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
+- IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
+- IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */
+- IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
+- IEEE80211_CHAN_A, /* IEEE80211_MODE_TURBO_A */ /* for turbo mode look for AP in normal channel */
+- IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_TURBO_G */
+- IEEE80211_CHAN_ST, /* IEEE80211_MODE_TURBO_STATIC_A */
+-};
+-
+-static void
+-add_channels(struct ieee80211com *ic,
+- struct ieee80211_scan_state *ss,
+- enum ieee80211_phymode mode, const u_int16_t freq[], int nfreq)
+-{
+- struct ieee80211_channel *c, *cg;
+- u_int modeflags;
+- int i;
+-
+- KASSERT(mode < ARRAY_SIZE(chanflags), ("Unexpected mode %u", mode));
+- modeflags = chanflags[mode];
+- for (i = 0; i < nfreq; i++) {
+- c = ieee80211_find_channel(ic, freq[i], modeflags);
+- if (c == NULL || isclr(ic->ic_chan_active, c->ic_ieee))
+- continue;
+- if (mode == IEEE80211_MODE_AUTO) {
+- /*
+- * XXX special-case 11b/g channels so we select
+- * the g channel if both are present.
+- */
+- if (IEEE80211_IS_CHAN_B(c) &&
+- (cg = find11gchannel(ic, i, c->ic_freq)) != NULL)
+- c = cg;
+- }
+- if (ss->ss_last >= IEEE80211_SCAN_MAX)
+- break;
+- ss->ss_chans[ss->ss_last++] = c;
+- }
+-}
+-
+-static const u_int16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
+-{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
+-static const u_int16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
+-{ 5170, 5190, 5210, 5230 };
+-static const u_int16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
+-{ 2412, 2437, 2462, 2442, 2472 };
+-static const u_int16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
+-{ 5745, 5765, 5785, 5805, 5825 };
+-static const u_int16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
+-{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
+-static const u_int16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
+-{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
+-static const u_int16_t rcl9[] = /* 2.4Ghz ch: 14 */
+-{ 2484 };
+-static const u_int16_t rcl10[] = /* Added Korean channels 2312-2372 */
+-{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
+-static const u_int16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
+-{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
+-#ifdef ATH_TURBO_SCAN
+-static const u_int16_t rcl5[] = /* 3 static turbo channels */
+-{ 5210, 5250, 5290 };
+-static const u_int16_t rcl6[] = /* 2 static turbo channels */
+-{ 5760, 5800 };
+-static const u_int16_t rcl6x[] = /* 4 FCC3 turbo channels */
+-{ 5540, 5580, 5620, 5660 };
+-static const u_int16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
+-{ 2437 };
+-static const u_int16_t rcl13[] = /* dynamic Turbo channels */
+-{ 5200, 5240, 5280, 5765, 5805 };
+-#endif /* ATH_TURBO_SCAN */
+-
+-struct scanlist {
+- u_int16_t mode;
+- u_int16_t count;
+- const u_int16_t *list;
+-};
+-
+-#define IEEE80211_MODE_TURBO_STATIC_A IEEE80211_MODE_MAX
+-#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
+-
+-static const struct scanlist staScanTable[] = {
+- { IEEE80211_MODE_11B, X(rcl3) },
+- { IEEE80211_MODE_11A, X(rcl1) },
+- { IEEE80211_MODE_11A, X(rcl2) },
+- { IEEE80211_MODE_11B, X(rcl8) },
+- { IEEE80211_MODE_11B, X(rcl9) },
+- { IEEE80211_MODE_11A, X(rcl4) },
+-#ifdef ATH_TURBO_SCAN
+- { IEEE80211_MODE_TURBO_STATIC_A, X(rcl5) },
+- { IEEE80211_MODE_TURBO_STATIC_A, X(rcl6) },
+- { IEEE80211_MODE_TURBO_A, X(rcl6x) },
+- { IEEE80211_MODE_TURBO_A, X(rcl13) },
+-#endif /* ATH_TURBO_SCAN */
+- { IEEE80211_MODE_11A, X(rcl7) },
+- { IEEE80211_MODE_11B, X(rcl10) },
+- { IEEE80211_MODE_11A, X(rcl11) },
+-#ifdef ATH_TURBO_SCAN
+- { IEEE80211_MODE_TURBO_G, X(rcl12) },
+-#endif /* ATH_TURBO_SCAN */
+- { .list = NULL }
+-};
+-
+-#undef X
+-
+-static int
+-checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
+-{
+- int i;
+-
+- for (; scan->list != NULL; scan++) {
+- for (i = 0; i < scan->count; i++)
+- if (scan->list[i] == c->ic_freq)
+- return 1;
+- }
+- return 0;
+-}
+-
+ /*
+ * Start a station-mode scan by populating the channel list.
+ */
+@@ -466,81 +325,14 @@ sta_start(struct ieee80211_scan_state *s
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+ struct sta_table *st = ss->ss_priv;
+- const struct scanlist *scan;
+ enum ieee80211_phymode mode;
+ struct ieee80211_channel *c;
+ int i;
+
+ ss->ss_last = 0;
+- /*
+- * Use the table of ordered channels to construct the list
+- * of channels for scanning. Any channels in the ordered
+- * list not in the master list will be discarded.
+- */
+- for (scan = staScanTable; scan->list != NULL; scan++) {
+- mode = scan->mode;
+- if (vap->iv_des_mode != IEEE80211_MODE_AUTO) {
+- /*
+- * If a desired mode was specified, scan only
+- * channels that satisfy that constraint.
+- */
+- if (vap->iv_des_mode != mode) {
+- /*
+- * The scan table marks 2.4Ghz channels as b
+- * so if the desired mode is 11g, then use
+- * the 11b channel list but upgrade the mode.
+- */
+- if (vap->iv_des_mode != IEEE80211_MODE_11G ||
+- mode != IEEE80211_MODE_11B)
+- continue;
+- mode = IEEE80211_MODE_11G; /* upgrade */
+- }
+- } else {
+- /*
+- * This lets ieee80211_scan_add_channels
+- * upgrade an 11b channel to 11g if available.
+- */
+- if (mode == IEEE80211_MODE_11B)
+- mode = IEEE80211_MODE_AUTO;
+- }
+- /* XR does not operate on turbo channels */
+- if ((vap->iv_flags & IEEE80211_F_XR) &&
+- (mode == IEEE80211_MODE_TURBO_A ||
+- mode == IEEE80211_MODE_TURBO_G))
+- continue;
+- /*
+- * Add the list of the channels; any that are not
+- * in the master channel list will be discarded.
+- */
+- add_channels(ic, ss, mode, scan->list, scan->count);
+- }
+-
+- /*
+- * Add the channels from the ic (from HAL) that are not present
+- * in the staScanTable.
+- */
+- for (i = 0; i < ic->ic_nchans; i++) {
+- c = &ic->ic_channels[i];
+- /*
+- * scan dynamic turbo channels in normal mode.
+- */
+- if (IEEE80211_IS_CHAN_DTURBO(c))
+- continue;
+- mode = ieee80211_chan2mode(c);
+- if (vap->iv_des_mode != IEEE80211_MODE_AUTO) {
+- /*
+- * If a desired mode was specified, scan only
+- * channels that satisfy that constraint.
+- */
+- if (vap->iv_des_mode != mode)
+- continue;
+-
+- }
+- if (!checktable(staScanTable, c))
+- ss->ss_chans[ss->ss_last++] = c;
+- }
+-
++ ieee80211_scan_add_channels(ic, ss, vap->iv_des_mode);
+ ss->ss_next = 0;
++
+ /* XXX tunables */
+ /*
+ * The scanner will stay on station for ss_maxdwell ms (using a
+@@ -749,17 +541,7 @@ match_bss(struct ieee80211vap *vap,
+ fail = 0;
+ if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan)))
+ fail |= 0x01;
+- /*
+- * NB: normally the desired mode is used to construct
+- * the channel list, but it's possible for the scan
+- * cache to include entries for stations outside this
+- * list so we check the desired mode here to weed them
+- * out.
+- */
+- if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
+- (se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
+- chanflags[vap->iv_des_mode])
+- fail |= 0x01;
++
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ fail |= 0x02;
+@@ -1168,78 +950,6 @@ static const struct ieee80211_scanner st
+ .scan_default = ieee80211_sta_join,
+ };
+
+-/*
+- * Start an adhoc-mode scan by populating the channel list.
+- */
+-static int
+-adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+-{
+- struct ieee80211com *ic = vap->iv_ic;
+- struct sta_table *st = ss->ss_priv;
+- const struct scanlist *scan;
+- enum ieee80211_phymode mode;
+-
+- ss->ss_last = 0;
+- /*
+- * Use the table of ordered channels to construct the list
+- * of channels for scanning. Any channels in the ordered
+- * list not in the master list will be discarded.
+- */
+- for (scan = staScanTable; scan->list != NULL; scan++) {
+- mode = scan->mode;
+- if (vap->iv_des_mode != IEEE80211_MODE_AUTO) {
+- /*
+- * If a desired mode was specified, scan only
+- * channels that satisfy that constraint.
+- */
+- if (vap->iv_des_mode != mode) {
+- /*
+- * The scan table marks 2.4Ghz channels as b
+- * so if the desired mode is 11g, then use
+- * the 11b channel list but upgrade the mode.
+- */
+- if (vap->iv_des_mode != IEEE80211_MODE_11G ||
+- mode != IEEE80211_MODE_11B)
+- continue;
+- mode = IEEE80211_MODE_11G; /* upgrade */
+- }
+- } else {
+- /*
+- * This lets ieee80211_scan_add_channels
+- * upgrade an 11b channel to 11g if available.
+- */
+- if (mode == IEEE80211_MODE_11B)
+- mode = IEEE80211_MODE_AUTO;
+- }
+- /* XR does not operate on turbo channels */
+- if ((vap->iv_flags & IEEE80211_F_XR) &&
+- (mode == IEEE80211_MODE_TURBO_A ||
+- mode == IEEE80211_MODE_TURBO_G))
+- continue;
+- /*
+- * Add the list of the channels; any that are not
+- * in the master channel list will be discarded.
+- */
+- add_channels(ic, ss, mode, scan->list, scan->count);
+- }
+- ss->ss_next = 0;
+- /* XXX tunables */
+- ss->ss_mindwell = msecs_to_jiffies(200); /* 200ms */
+- ss->ss_maxdwell = msecs_to_jiffies(200); /* 200ms */
+-
+-#ifdef IEEE80211_DEBUG
+- if (ieee80211_msg_scan(vap)) {
+- printk("%s: scan set ", vap->iv_dev->name);
+- ieee80211_scan_dump_channels(ss);
+- printk(" dwell min %ld max %ld\n",
+- ss->ss_mindwell, ss->ss_maxdwell);
+- }
+-#endif /* IEEE80211_DEBUG */
+-
+- st->st_newscan = 1;
+-
+- return 0;
+-}
+
+ /*
+ * Select a channel to start an adhoc network on.
+@@ -1405,7 +1115,7 @@ static const struct ieee80211_scanner ad
+ .scan_name = "default",
+ .scan_attach = sta_attach,
+ .scan_detach = sta_detach,
+- .scan_start = adhoc_start,
++ .scan_start = sta_start,
+ .scan_restart = sta_restart,
+ .scan_cancel = sta_cancel,
+ .scan_end = adhoc_pick_bss,
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -278,6 +278,11 @@ ieee80211_ifattach(struct ieee80211com *
+ ("channel with bogus ieee number %u", c->ic_ieee));
+ setbit(ic->ic_chan_avail, c->ic_ieee);
+
++ if (c->ic_scanflags & IEEE80211_NOSCAN_DEFAULT)
++ c->ic_scanflags |= IEEE80211_NOSCAN_SET;
++ else
++ c->ic_scanflags &= ~IEEE80211_NOSCAN_SET;
++
+ /* Identify mode capabilities. */
+ if (IEEE80211_IS_CHAN_A(c))
+ ic->ic_modecaps |= 1 << IEEE80211_MODE_11A;
+@@ -1447,10 +1452,6 @@ ieee80211_media_change(struct net_device
+ vap->iv_fixed_rate = newrate; /* fixed TX rate */
+ error = -ENETRESET;
+ }
+- if (vap->iv_des_mode != newmode) {
+- vap->iv_des_mode = newmode; /* desired PHY mode */
+- error = -ENETRESET;
+- }
+ return error;
+ }
+ EXPORT_SYMBOL(ieee80211_media_change);
+--- a/net80211/_ieee80211.h
++++ b/net80211/_ieee80211.h
+@@ -132,6 +132,11 @@ enum ieee80211_scanmode {
+ IEEE80211_SCAN_FIRST = 2, /* take first suitable candidate */
+ };
+
++enum ieee80211_scanflags {
++ IEEE80211_NOSCAN_DEFAULT = (1 << 0),
++ IEEE80211_NOSCAN_SET = (1 << 1),
++};
++
+ /*
+ * Channels are specified by frequency and attributes.
+ */
+@@ -142,6 +147,7 @@ struct ieee80211_channel {
+ int8_t ic_maxregpower; /* maximum regulatory tx power in dBm */
+ int8_t ic_maxpower; /* maximum tx power in dBm */
+ int8_t ic_minpower; /* minimum tx power in dBm */
++ u_int8_t ic_scanflags;
+ };
+
+ #define IEEE80211_CHAN_MAX 255
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -555,6 +555,7 @@ struct ieee80211req_scan_result {
+ #define IEEE80211_IOCTL_WDSADDMAC (SIOCIWFIRSTPRIV+26)
+ #define IEEE80211_IOCTL_WDSDELMAC (SIOCIWFIRSTPRIV+28)
+ #define IEEE80211_IOCTL_KICKMAC (SIOCIWFIRSTPRIV+30)
++#define IEEE80211_IOCTL_SETSCANLIST (SIOCIWFIRSTPRIV+31)
+
+ enum {
+ IEEE80211_WMMPARAMS_CWMIN = 1,
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -105,11 +105,6 @@ struct scan_entry {
+ };
+
+ struct ap_state {
+- unsigned int as_vap_desired_mode; /* Used for channel selection,
+- * vap->iv_des_mode */
+- unsigned int as_required_mode; /* Used for channel selection,
+- * filtered version of
+- * as_vap_desired_mode */
+ int as_maxrssi[IEEE80211_CHAN_MAX]; /* Used for channel selection */
+
+ /* These fields are just for scan caching for returning responses to
+@@ -129,131 +124,7 @@ struct ap_state {
+
+ static int ap_flush(struct ieee80211_scan_state *);
+ static void action_tasklet(IEEE80211_TQUEUE_ARG);
+-static struct ieee80211_channel *find11gchannel(struct ieee80211com *ic,
+- int i, int freq);
+
+-static const u_int chanflags[] = {
+- IEEE80211_CHAN_B, /* IEEE80211_MODE_AUTO */
+- IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
+- IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
+- IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */
+- IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
+- IEEE80211_CHAN_A, /* IEEE80211_MODE_TURBO_A */ /* for turbo mode
+- * look for AP in
+- * normal channel
+- */
+- IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_TURBO_G */
+- IEEE80211_CHAN_ST, /* IEEE80211_MODE_TURBO_STATIC_A */
+-};
+-
+-static const u_int16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64,
+- * 36, 40, 44, 48 */
+-{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
+-static const u_int16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
+-{ 5170, 5190, 5210, 5230 };
+-static const u_int16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
+-{ 2412, 2437, 2462, 2442, 2472 };
+-static const u_int16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
+-{ 5745, 5765, 5785, 5805, 5825 };
+-static const u_int16_t rcl7[] = /* 11 ETSI channel: 100, 104, 108, 112,
+- * 116, 120, 124, 128,
+- * 132, 136, 140 */
+-{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
+-static const u_int16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
+-{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
+-static const u_int16_t rcl9[] = /* 2.4Ghz ch: 14 */
+-{ 2484 };
+-static const u_int16_t rcl10[] = /* Added Korean channels 2312-2372 */
+-{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
+-static const u_int16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
+-{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
+-#ifdef ATH_TURBO_SCAN
+-static const u_int16_t rcl5[] = /* 3 static turbo channels */
+-{ 5210, 5250, 5290 };
+-static const u_int16_t rcl6[] = /* 2 static turbo channels */
+-{ 5760, 5800 };
+-static const u_int16_t rcl6x[] = /* 4 FCC3 turbo channels */
+-{ 5540, 5580, 5620, 5660 };
+-static const u_int16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
+-{ 2437 };
+-static const u_int16_t rcl13[] = /* dynamic Turbo channels */
+-{ 5200, 5240, 5280, 5765, 5805 };
+-#endif /* ATH_TURBO_SCAN */
+-
+-struct scanlist {
+- u_int16_t mode;
+- u_int16_t count;
+- const u_int16_t *list;
+-};
+-
+-#define IEEE80211_MODE_TURBO_STATIC_A IEEE80211_MODE_MAX
+-#define X(a) .count = ARRAY_SIZE(a), .list = a
+-
+-static const struct scanlist staScanTable[] = {
+- { IEEE80211_MODE_11B, X(rcl3) },
+- { IEEE80211_MODE_11A, X(rcl1) },
+- { IEEE80211_MODE_11A, X(rcl2) },
+- { IEEE80211_MODE_11B, X(rcl8) },
+- { IEEE80211_MODE_11B, X(rcl9) },
+- { IEEE80211_MODE_11A, X(rcl4) },
+-#ifdef ATH_TURBO_SCAN
+- { IEEE80211_MODE_TURBO_STATIC_A, X(rcl5) },
+- { IEEE80211_MODE_TURBO_STATIC_A, X(rcl6) },
+- { IEEE80211_MODE_TURBO_A, X(rcl6x) },
+- { IEEE80211_MODE_TURBO_A, X(rcl13) },
+-#endif /* ATH_TURBO_SCAN */
+- { IEEE80211_MODE_11A, X(rcl7) },
+- { IEEE80211_MODE_11B, X(rcl10) },
+- { IEEE80211_MODE_11A, X(rcl11) },
+-#ifdef ATH_TURBO_SCAN
+- { IEEE80211_MODE_TURBO_G, X(rcl12) },
+-#endif /* ATH_TURBO_SCAN */
+- { .list = NULL }
+-};
+-
+-#undef X
+-/* This function must be invoked with locks acquired */
+-static void
+-add_channels(struct ieee80211com *ic,
+- struct ieee80211_scan_state *ss,
+- enum ieee80211_phymode mode, const u_int16_t freq[], int nfreq)
+-{
+- struct ieee80211_channel *c, *cg;
+- u_int modeflags;
+- int i;
+-
+- KASSERT(mode < ARRAY_SIZE(chanflags), ("Unexpected mode %u", mode));
+- modeflags = chanflags[mode];
+- for (i = 0; i < nfreq; i++) {
+- c = ieee80211_find_channel(ic, freq[i], modeflags);
+- if ((c == NULL) || isclr(ic->ic_chan_active, c->ic_ieee))
+- continue;
+- if (mode == IEEE80211_MODE_AUTO) {
+- /* XXX special-case 11b/g channels so we select
+- * the g channel if both are present. */
+- if (IEEE80211_IS_CHAN_B(c) &&
+- (cg = find11gchannel(ic, i, c->ic_freq)) != NULL)
+- c = cg;
+- }
+- if (ss->ss_last >= IEEE80211_SCAN_MAX)
+- break;
+- ss->ss_chans[ss->ss_last++] = c;
+- }
+-}
+-
+-/* This function must be invoked with locks acquired */
+-static int
+-checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
+-{
+- int i;
+-
+- for (; scan->list != NULL; scan++) {
+- for (i = 0; i < scan->count; i++)
+- if (scan->list[i] == c->ic_freq)
+- return 1;
+- }
+- return 0;
+-}
+
+ /*
+ * Attach prior to any scanning work.
+@@ -327,29 +198,6 @@ saveie(u_int8_t **iep, const u_int8_t *i
+ ieee80211_saveie(iep, ie);
+ }
+
+-/* This function must be invoked with locks acquired */
+-static struct ieee80211_channel *
+-find11gchannel(struct ieee80211com *ic, int i, int freq)
+-{
+- struct ieee80211_channel *c;
+- int j;
+-
+- /* The normal ordering in the channel list is b channel
+- * immediately followed by g so optimize the search for
+- * this. We'll still do a full search just in case. */
+- for (j = i + 1; j < ic->ic_nchans; j++) {
+- c = &ic->ic_channels[j];
+- if ((c->ic_freq == freq) && IEEE80211_IS_CHAN_ANYG(c))
+- return c;
+- }
+- for (j = 0; j < i; j++) {
+- c = &ic->ic_channels[j];
+- if ((c->ic_freq == freq) && IEEE80211_IS_CHAN_ANYG(c))
+- return c;
+- }
+- return NULL;
+-}
+-
+ /*
+ * Start an ap scan by populating the channel list.
+ */
+@@ -358,90 +206,15 @@ ap_start(struct ieee80211_scan_state *ss
+ {
+ struct ap_state *as = ss->ss_priv;
+ struct ieee80211com *ic = NULL;
+- const struct scanlist *sl = NULL;
+- struct ieee80211_channel *c = NULL;
+ int i;
+ unsigned int mode = 0;
+
+ SCAN_AP_LOCK_IRQ(as);
+ ic = vap->iv_ic;
+ /* Determine mode flags to match, or leave zero for auto mode */
+- as->as_vap_desired_mode = vap->iv_des_mode;
+- as->as_required_mode = 0;
+- if (as->as_vap_desired_mode != IEEE80211_MODE_AUTO) {
+- as->as_required_mode = chanflags[as->as_vap_desired_mode];
+- if ((vap->iv_ath_cap & IEEE80211_ATHC_TURBOP) &&
+- (as->as_required_mode != IEEE80211_CHAN_ST)) {
+- /* Fixup for dynamic turbo flags */
+- if (as->as_vap_desired_mode == IEEE80211_MODE_11G)
+- as->as_required_mode = IEEE80211_CHAN_108G;
+- else
+- as->as_required_mode = IEEE80211_CHAN_108A;
+- }
+- }
+-
+ ss->ss_last = 0;
+- /* Use the table of ordered channels to construct the list
+- * of channels for scanning. Any channels in the ordered
+- * list not in the master list will be discarded. */
+- for (sl = staScanTable; sl->list != NULL; sl++) {
+- mode = sl->mode;
+-
+- /* The scan table marks 2.4Ghz channels as b
+- * so if the desired mode is 11g, then use
+- * the 11b channel list but upgrade the mode. */
+- if (as->as_vap_desired_mode &&
+- (as->as_vap_desired_mode != mode) &&
+- (as->as_vap_desired_mode == IEEE80211_MODE_11G) &&
+- (mode == IEEE80211_MODE_11B))
+- mode = IEEE80211_MODE_11G;
+-
+- /* If we are in "AUTO" mode, upgrade the mode to auto.
+- * This lets add_channels upgrade an 11b channel to
+- * 11g if available. */
+- if (!as->as_vap_desired_mode && (mode == IEEE80211_MODE_11B))
+- mode = IEEE80211_MODE_AUTO;
+-
+- /* Add the list of the channels; any that are not
+- * in the master channel list will be discarded. */
+- add_channels(ic, ss, mode, sl->list, sl->count);
+- }
+-
+- /* Add the channels from the ic (from HAL) that are not present
+- * in the staScanTable, assuming they pass the sanity checks... */
+- for (i = 0; i < ic->ic_nchans; i++) {
+- c = &ic->ic_channels[i];
+-
+- /* XR is not supported on turbo channels */
+- if (IEEE80211_IS_CHAN_TURBO(c) && vap->iv_flags & IEEE80211_F_XR)
+- continue;
+-
+- /* Dynamic channels are scanned in base mode */
+- if (!as->as_required_mode && !IEEE80211_IS_CHAN_ST(c))
+- continue;
+-
+- /* Use any 11g channel instead of 11b one. */
+- if (vap->iv_des_mode == IEEE80211_MODE_AUTO &&
+- IEEE80211_IS_CHAN_B(c) &&
+- find11gchannel(ic, i, c->ic_freq))
+- continue;
+-
+- /* Do not add channels already put into the scan list by the
+- * scan table - these have already been filtered by mode
+- * and for whether they are in the active channel list. */
+- if (checktable(staScanTable, c))
+- continue;
+-
+- /* Make sure the channel is active */
+- if ((c == NULL) || isclr(ic->ic_chan_active, c->ic_ieee))
+- continue;
++ ieee80211_scan_add_channels(ic, ss, vap->iv_des_mode);
+
+- /* Don't overrun */
+- if (ss->ss_last >= IEEE80211_SCAN_MAX)
+- break;
+-
+- ss->ss_chans[ss->ss_last++] = c;
+- }
+ ss->ss_next = 0;
+ /* XXX tunables */
+ ss->ss_mindwell = msecs_to_jiffies(200); /* 200ms */
+@@ -761,18 +534,6 @@ pick_channel(struct ieee80211_scan_state
+ if (IEEE80211_IS_CHAN_RADAR(c->chan))
+ continue;
+
+- /* Do not select 802.11a ST if mode is specified and is not
+- * 802.11a ST */
+- if (as->as_required_mode &&
+- IEEE80211_IS_CHAN_STURBO(c->chan) &&
+- (as->as_vap_desired_mode != IEEE80211_MODE_TURBO_STATIC_A))
+- continue;
+-
+- /* Verify mode matches any fixed mode specified */
+- if((c->chan->ic_flags & as->as_required_mode) !=
+- as->as_required_mode)
+- continue;
+-
+ if ((ic->ic_bsschan != NULL) &&
+ (ic->ic_bsschan != IEEE80211_CHAN_ANYC)) {
+
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -958,6 +958,80 @@ ieee80211_scan_flush(struct ieee80211com
+ }
+ }
+
++static const u_int chanflags[] = {
++ 0, /* IEEE80211_MODE_AUTO */
++ IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
++ IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
++ IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */
++ IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
++ IEEE80211_CHAN_A, /* IEEE80211_MODE_TURBO_A */ /* for turbo mode look for AP in normal channel */
++ IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_TURBO_G */
++ IEEE80211_CHAN_ST, /* IEEE80211_MODE_TURBO_STATIC_A */
++};
++
++static struct ieee80211_channel *
++find11gchannel(struct ieee80211com *ic, int i, int freq)
++{
++ struct ieee80211_channel *c;
++ int j;
++
++ /*
++ * The normal ordering in the channel list is b channel
++ * immediately followed by g so optimize the search for
++ * this. We'll still do a full search just in case.
++ */
++ for (j = i+1; j < ic->ic_nchans; j++) {
++ c = &ic->ic_channels[j];
++ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
++ return c;
++ }
++ for (j = 0; j < i; j++) {
++ c = &ic->ic_channels[j];
++ if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
++ return c;
++ }
++ return NULL;
++}
++
++
++void
++ieee80211_scan_add_channels(struct ieee80211com *ic,
++ struct ieee80211_scan_state *ss,
++ enum ieee80211_phymode mode)
++{
++ struct ieee80211_channel *c, *cg;
++ u_int modeflags;
++ int i;
++
++ KASSERT(mode < ARRAY_SIZE(chanflags), ("Unexpected mode %u", mode));
++ modeflags = chanflags[mode];
++ for (i = 0; i < ic->ic_nchans; i++) {
++ c = &ic->ic_channels[i];
++ if (c == NULL || isclr(ic->ic_chan_active, c->ic_ieee))
++ continue;
++ if (c->ic_scanflags & IEEE80211_NOSCAN_SET)
++ continue;
++ if (modeflags &&
++ ((c->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
++ (modeflags & IEEE80211_CHAN_ALLTURBO)))
++ continue;
++ if (mode == IEEE80211_MODE_AUTO) {
++ /*
++ * XXX special-case 11b/g channels so we select
++ * the g channel if both are present.
++ */
++ if (IEEE80211_IS_CHAN_B(c) &&
++ (cg = find11gchannel(ic, i, c->ic_freq)) != NULL)
++ continue;
++ }
++ if (ss->ss_last >= IEEE80211_SCAN_MAX)
++ break;
++ ss->ss_chans[ss->ss_last++] = c;
++ }
++}
++EXPORT_SYMBOL(ieee80211_scan_add_channels);
++
++
+ /*
+ * Execute radar channel change. This is called when a radar/dfs
+ * signal is detected. AP mode only. Return 1 on success, 0 on
+--- a/net80211/ieee80211_scan.h
++++ b/net80211/ieee80211_scan.h
+@@ -219,4 +219,7 @@ void ieee80211_scanner_register(enum iee
+ void ieee80211_scanner_unregister(enum ieee80211_opmode,
+ const struct ieee80211_scanner *);
+ void ieee80211_scanner_unregister_all(const struct ieee80211_scanner *);
++void ieee80211_scan_add_channels(struct ieee80211com *ic,
++ struct ieee80211_scan_state *ss,
++ enum ieee80211_phymode mode);
+ #endif /* _NET80211_IEEE80211_SCAN_H_ */
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -3873,6 +3873,106 @@ ieee80211_ioctl_kickmac(struct net_devic
+ return ieee80211_ioctl_setmlme(dev, info, w, (char *)&mlme);
+ }
+
++static inline void setflag(struct ieee80211_channel *c, int flag)
++{
++ if (flag)
++ c->ic_scanflags |= IEEE80211_NOSCAN_SET;
++ else
++ c->ic_scanflags &= ~IEEE80211_NOSCAN_SET;
++}
++
++static void setscanflag(struct ieee80211com *ic, int min, int max, int set)
++{
++ int i;
++
++ for (i = 0; i < ic->ic_nchans; i++) {
++ struct ieee80211_channel *c = &ic->ic_channels[i];
++
++ if (min == -1) {
++ if (!(c->ic_scanflags & IEEE80211_NOSCAN_DEFAULT))
++ setflag(c, set);
++ } else if ((c->ic_freq >= min) && (c->ic_freq <= max)) {
++ setflag(c, set);
++ }
++ }
++}
++
++static int
++ieee80211_ioctl_setscanlist(struct net_device *dev,
++ struct iw_request_info *info,
++ struct iw_point *data, char *extra)
++{
++ struct ieee80211vap *vap = dev->priv;
++ struct ieee80211com *ic = vap->iv_ic;
++ char *s, *next;
++ int val = 1;
++
++ if (data->length <= 0)
++ return -EINVAL;
++
++ s = kmalloc(data->length + 1, GFP_KERNEL);
++ if (!s)
++ return -ENOMEM;
++
++ memset(s, 0, data->length + 1);
++ if (copy_from_user(s, data->pointer, data->length))
++ return -EFAULT;
++
++ s[data->length - 1] = '\0'; /* ensure null termination */
++
++ switch(*s) {
++ case '-':
++ val = 1;
++ break;
++ case '+':
++ val = 0;
++ break;
++ default:
++ goto error;
++ }
++ s++;
++ next = s;
++ do {
++ next = strchr(s, ',');
++ if (next) {
++ *next = 0;
++ next++;
++ }
++ if (!strcmp(s, "ALL")) {
++ setscanflag(ic, 0, 10000, val);
++ } else if (!strcmp(s, "REG")) {
++ setscanflag(ic, -1, -1, val);
++ } else {
++ int min, max;
++ char *n, *end = NULL;
++
++ n = strchr(s, '-');
++ if (n) {
++ *n = 0;
++ n++;
++ }
++ min = simple_strtoul(s, &end, 10);
++ if (end && *end)
++ goto error;
++ if (n) {
++ max = simple_strtoul(n, &end, 10);
++ if (end && *end)
++ goto error;
++ } else {
++ max = min;
++ }
++ setscanflag(ic, min, max, val);
++ }
++ s = next;
++ } while (next);
++ return 0;
++
++error:
++ if (s)
++ kfree(s);
++ return -EINVAL;
++}
++
+ static int
+ ieee80211_ioctl_addmac(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+@@ -5656,6 +5756,8 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "minrate"},
+ {IEEE80211_PARAM_MINRATE,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
++ { IEEE80211_IOCTL_SETSCANLIST,
++ IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
+@@ -5753,6 +5855,7 @@ static const iw_handler ieee80211_priv_h
+ set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsmac),
+ set_priv(IEEE80211_IOCTL_WDSDELMAC, ieee80211_ioctl_wdsdelmac),
+ set_priv(IEEE80211_IOCTL_KICKMAC, ieee80211_ioctl_kickmac),
++ set_priv(IEEE80211_IOCTL_SETSCANLIST, ieee80211_ioctl_setscanlist),
+ #ifdef ATH_REVERSE_ENGINEERING
+ set_priv(IEEE80211_IOCTL_READREG, ieee80211_ioctl_readreg),
+ set_priv(IEEE80211_IOCTL_WRITEREG, ieee80211_ioctl_writereg),
diff --git a/package/madwifi/patches/352-ani_fix.patch b/package/madwifi/patches/352-ani_fix.patch
new file mode 100644
index 000000000..938d11c62
--- /dev/null
+++ b/package/madwifi/patches/352-ani_fix.patch
@@ -0,0 +1,265 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1014,9 +1014,7 @@ ath_attach(u_int16_t devid, struct net_d
+ */
+ sc->sc_hasveol = ath_hal_hasveol(ah);
+
+- /* Interference mitigation/ambient noise immunity (ANI).
+- * In modes other than HAL_M_STA, it causes receive sensitivity
+- * problems for OFDM. */
++ /* Interference mitigation/ambient noise immunity (ANI). */
+ sc->sc_hasintmit = ath_hal_hasintmit(ah);
+
+ /* get mac address from hardware */
+@@ -1144,6 +1142,11 @@ ath_attach(u_int16_t devid, struct net_d
+ sc->sc_rp_lasttsf = 0;
+ sc->sc_last_tsf = 0;
+
++ /* set all 3 to auto */
++ sc->sc_intmit = -1;
++ sc->sc_noise_immunity = -1;
++ sc->sc_ofdm_weak_det = -1;
++
+ return 0;
+ bad3:
+ ieee80211_ifdetach(ic);
+@@ -2428,6 +2431,43 @@ ath_chan2flags(struct ieee80211_channel
+ return flags;
+ }
+
++static int ath_setintmit(struct ath_softc *sc)
++{
++ struct ath_hal *ah = sc->sc_ah;
++ int ret;
++ int val;
++
++ if (!sc->sc_hasintmit)
++ return 0;
++
++ switch(sc->sc_intmit) {
++ case -1:
++ if (sc->sc_opmode != IEEE80211_M_MONITOR)
++ val = 1;
++ else
++ val = 0;
++ break;
++ case 0: /* disabled */
++ case 1: /* enabled */
++ val = sc->sc_intmit;
++ break;
++ default:
++ return 0;
++ }
++ ret = ath_hal_setintmit(ah, val);
++ if (val)
++ goto done;
++
++ /* manual settings */
++ if ((sc->sc_noise_immunity >= 0) && (sc->sc_noise_immunity <= 5))
++ ath_hal_setcapability(ah, HAL_CAP_INTMIT, 2, sc->sc_noise_immunity, NULL);
++ if ((sc->sc_ofdm_weak_det == 0) || (sc->sc_ofdm_weak_det == 1))
++ ath_hal_setcapability(ah, HAL_CAP_INTMIT, 3, sc->sc_ofdm_weak_det, NULL);
++
++done:
++ return ret;
++}
++
+ /*
+ * Context: process context
+ */
+@@ -2493,8 +2533,7 @@ ath_init(struct net_device *dev)
+ if (sc->sc_softled)
+ ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+
+- if ((sc->sc_opmode != HAL_M_STA) && sc->sc_hasintmit)
+- ath_hal_setintmit(ah, 0);
++ ath_setintmit(sc);
+
+ /*
+ * This is needed only to setup initial state
+@@ -2530,7 +2569,7 @@ ath_init(struct net_device *dev)
+ * Enable MIB interrupts when there are hardware phy counters.
+ * Note we only do this (at the moment) for station mode.
+ */
+- if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
++ if (sc->sc_needmib && ath_hal_getintmit(ah, NULL))
+ sc->sc_imask |= HAL_INT_MIB;
+ ath_hal_intrset(ah, sc->sc_imask);
+
+@@ -2787,9 +2826,7 @@ ath_reset(struct net_device *dev)
+ EPRINTF(sc, "Unable to reset hardware: '%s' (HAL status %u)\n",
+ ath_get_hal_status_desc(status), status);
+
+- if ((sc->sc_opmode != HAL_M_STA) && sc->sc_hasintmit)
+- ath_hal_setintmit(ah, 0);
+-
++ ath_setintmit(sc);
+ ath_update_txpow(sc); /* update tx power state */
+ ath_radar_update(sc);
+ ath_setdefantenna(sc, sc->sc_defant);
+@@ -4174,6 +4211,8 @@ ath_calcrxfilter(struct ath_softc *sc)
+ if (sc->sc_nmonvaps > 0)
+ rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON |
+ HAL_RX_FILTER_PROBEREQ | HAL_RX_FILTER_PROM);
++ if (sc->sc_hasintmit && !sc->sc_needmib && ath_hal_getintmit(ah, NULL))
++ rfilt |= HAL_RX_FILTER_PHYERR;
+ if (sc->sc_curchan.privFlags & CHANNEL_DFS)
+ rfilt |= (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR);
+ return rfilt;
+@@ -6526,9 +6565,6 @@ process_rx_again:
+ rs->rs_rssi = 0;
+
+ len = rs->rs_datalen;
+- /* DMA sync. dies spectacularly if len == 0 */
+- if (len == 0)
+- goto rx_next;
+
+ if (rs->rs_more) {
+ /*
+@@ -8876,9 +8912,7 @@ ath_chan_set(struct ath_softc *sc, struc
+ if (sc->sc_softled)
+ ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+
+- if ((sc->sc_opmode != HAL_M_STA) && sc->sc_hasintmit)
+- ath_hal_setintmit(ah, 0);
+-
++ ath_setintmit(sc);
+ sc->sc_curchan = hchan;
+ ath_update_txpow(sc); /* update tx power state */
+ ath_radar_update(sc);
+@@ -10655,9 +10689,54 @@ enum {
+ ATH_RP_IGNORED = 24,
+ ATH_RADAR_IGNORED = 25,
+ ATH_MAXVAPS = 26,
++ ATH_INTMIT = 27,
++ ATH_NOISE_IMMUNITY = 28,
++ ATH_OFDM_WEAK_DET = 29
+ };
+
+ static int
++ath_sysctl_set_intmit(struct ath_softc *sc, long ctl, u_int val)
++{
++ int ret;
++
++ switch(ctl) {
++ case ATH_INTMIT:
++ sc->sc_intmit = val;
++ break;
++ case ATH_NOISE_IMMUNITY:
++ sc->sc_noise_immunity = val;
++ break;
++ case ATH_OFDM_WEAK_DET:
++ sc->sc_ofdm_weak_det = val;
++ break;
++ default:
++ return -EINVAL;
++ }
++ ret = ath_setintmit(sc);
++ ath_calcrxfilter(sc);
++ return ret;
++}
++
++static int
++ath_sysctl_get_intmit(struct ath_softc *sc, long ctl, u_int *val)
++{
++ struct ath_hal *ah = sc->sc_ah;
++
++ switch(ctl) {
++ case ATH_INTMIT:
++ *val = (ath_hal_getcapability(ah, HAL_CAP_INTMIT, 1, NULL) == HAL_OK);
++ break;
++ case ATH_NOISE_IMMUNITY:
++ return ath_hal_getcapability(ah, HAL_CAP_INTMIT, 2, val);
++ case ATH_OFDM_WEAK_DET:
++ return ath_hal_getcapability(ah, HAL_CAP_INTMIT, 3, val);
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int
+ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos)
+ {
+ struct ath_softc *sc = ctl->extra1;
+@@ -10843,6 +10922,11 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_RADAR_IGNORED:
+ sc->sc_radar_ignored = val;
+ break;
++ case ATH_INTMIT:
++ case ATH_NOISE_IMMUNITY:
++ case ATH_OFDM_WEAK_DET:
++ ret = ath_sysctl_set_intmit(sc, (long)ctl->extra2, val);
++ break;
+ default:
+ ret = -EINVAL;
+ break;
+@@ -10909,6 +10993,11 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_RADAR_IGNORED:
+ val = sc->sc_radar_ignored;
+ break;
++ case ATH_INTMIT:
++ case ATH_NOISE_IMMUNITY:
++ case ATH_OFDM_WEAK_DET:
++ ret = ath_sysctl_get_intmit(sc, (long)ctl->extra2, &val);
++ break;
+ default:
+ ret = -EINVAL;
+ break;
+@@ -11086,6 +11175,24 @@ static const ctl_table ath_sysctl_templa
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RADAR_IGNORED,
+ },
++ { .ctl_name = CTL_AUTO,
++ .procname = "intmit",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_INTMIT,
++ },
++ { .ctl_name = CTL_AUTO,
++ .procname = "noise_immunity",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_NOISE_IMMUNITY,
++ },
++ { .ctl_name = CTL_AUTO,
++ .procname = "ofdm_weak_det",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_OFDM_WEAK_DET,
++ },
+ { 0 }
+ };
+
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -693,6 +693,10 @@ struct ath_softc {
+ unsigned int sc_txcont_power; /* Continuous transmit power in 0.5dBm units */
+ unsigned int sc_txcont_rate; /* Continuous transmit rate in Mbps */
+
++ int8_t sc_intmit; /* Interference mitigation enabled, -1 = auto, based on mode, 0/1 = off/on */
++ int8_t sc_noise_immunity; /* Noise immunity level, 0-4, -1 == auto) */
++ int8_t sc_ofdm_weak_det; /* OFDM weak frames detection, -1 == auto */
++
+ /* rate tables */
+ const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX];
+ const HAL_RATE_TABLE *sc_currates; /* current rate table */
+--- a/ath/if_ath_hal.h
++++ b/ath/if_ath_hal.h
+@@ -67,14 +67,14 @@ static inline HAL_POWER_MODE ath_hal_get
+
+ static inline HAL_BOOL ath_hal_getdiagstate(struct ath_hal *ah, int request,
+ const void *args, u_int32_t argsize,
+- void **result,
++ void *result,
+ u_int32_t *resultsize)
+ {
+ HAL_BOOL ret;
+ ATH_HAL_LOCK_IRQ(ah->ah_sc);
+ ath_hal_set_function(__func__);
+ ret =
+- ah->ah_getDiagState(ah, request, args, argsize, *result,
++ ah->ah_getDiagState(ah, request, args, argsize, result,
+ resultsize);
+ ath_hal_set_function(NULL);
+ ATH_HAL_UNLOCK_IRQ(ah->ah_sc);
diff --git a/package/madwifi/patches/353-devid.patch b/package/madwifi/patches/353-devid.patch
new file mode 100644
index 000000000..ee149ff0c
--- /dev/null
+++ b/package/madwifi/patches/353-devid.patch
@@ -0,0 +1,19 @@
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -114,11 +114,15 @@ static struct pci_device_id ath_pci_id_t
+ { 0x168c, 0x0023, PCI_ANY_ID, PCI_ANY_ID },
+ { 0x168c, 0x0024, PCI_ANY_ID, PCI_ANY_ID },
+ { 0x168c, 0x9013, PCI_ANY_ID, PCI_ANY_ID }, /* sonicwall */
++ { 0x168c, 0xff16, PCI_ANY_ID, PCI_ANY_ID },
++ { 0x168c, 0xff1a, PCI_ANY_ID, PCI_ANY_ID },
+ { 0 }
+ };
+
+ static u16 ath_devidmap[][2] = {
+- { 0x9013, 0x0013 }
++ { 0x9013, 0x0013 },
++ { 0xff16, 0x0013 },
++ { 0xff1a, 0x001a }
+ };
+
+ static int
diff --git a/package/madwifi/patches/354-lantiq_eeprom.patch b/package/madwifi/patches/354-lantiq_eeprom.patch
new file mode 100644
index 000000000..59036b1b4
--- /dev/null
+++ b/package/madwifi/patches/354-lantiq_eeprom.patch
@@ -0,0 +1,95 @@
+--- a/ath_hal/ah_os.c
++++ b/ath_hal/ah_os.c
+@@ -343,6 +343,46 @@ EXPORT_SYMBOL(ath_hal_func);
+ * NB: see the comments in ah_osdep.h about byte-swapping register
+ * reads and writes to understand what's going on below.
+ */
++
++#ifdef CONFIG_LANTIQ
++extern int lantiq_emulate_madwifi_eep;
++extern unsigned long long lantiq_madwifi_eep_addr;
++#define EEPROM_EMULATION 1
++#endif
++
++#ifdef EEPROM_EMULATION
++static int ath_hal_eeprom(struct ath_hal *ah, unsigned long addr, int val, int write)
++{
++ static int addrsel = 0;
++ static int rc = 0;
++
++ if (write) {
++ if(addr == 0x6000) {
++ addrsel = val * 2;
++ rc = 0;
++ }
++ } else {
++ switch(addr)
++ {
++ case 0x600c:
++ if(rc++ < 2)
++ val = 0x00000000;
++ else
++ val = 0x00000002;
++ break;
++ case 0x6004:
++ val = cpu_to_le16(__raw_readw((u16 *) KSEG1ADDR(lantiq_madwifi_eep_addr + addrsel)));
++ /* this forces the regdomain to 0x00 (worldwide), as the original setting
++ * causes issues with the HAL */
++ if (addrsel == 0x17e)
++ val = 0;
++ break;
++ }
++ }
++ return val;
++}
++#endif
++
+ void __ahdecl
+ ath_hal_reg_write(struct ath_hal *ah, u_int reg, u_int32_t val)
+ {
+@@ -351,20 +391,33 @@ ath_hal_reg_write(struct ath_hal *ah, u_
+ ath_hal_printf(ah, "%s: WRITE 0x%x <= 0x%x\n",
+ (ath_hal_func ?: "unknown"), reg, val);
+ #endif
+- _OS_REG_WRITE(ah, reg, val);
++#ifdef EEPROM_EMULATION
++ if((reg >= 0x6000) && (reg <= 0x6010) && lantiq_emulate_madwifi_eep)
++ {
++ val = ath_hal_eeprom(ah, reg, val, 1);
++ } else
++#endif
++ _OS_REG_WRITE(ah, reg, val);
+ }
+ EXPORT_SYMBOL(ath_hal_reg_write);
+
++
+ /* This should only be called while holding the lock, sc->sc_hal_lock. */
+ u_int32_t __ahdecl
+ ath_hal_reg_read(struct ath_hal *ah, u_int reg)
+ {
+- u_int32_t val;
++ u_int32_t val;
++#ifdef EEPROM_EMULATION
++ if((reg >= 0x6000) && (reg <= 0x6010) && lantiq_emulate_madwifi_eep)
++ {
++ val = ath_hal_eeprom(ah, reg, 0, 0);
++ } else
++#endif
++ val = _OS_REG_READ(ah, reg);
+
+- val = _OS_REG_READ(ah, reg);
+ #ifdef AH_DEBUG
+ if (ath_hal_debug > 1)
+- ath_hal_printf(ah, "%s: READ 0x%x => 0x%x\n",
++ ath_hal_printf(ah, "%s: READ 0x%x => 0x%x\n",
+ (ath_hal_func ?: "unknown"), reg, val);
+ #endif
+ return val;
+@@ -581,7 +634,6 @@ init_ath_hal(void)
+ {
+ const char *sep;
+ int i;
+-
+ printk(KERN_INFO "%s: %s (", dev_info, ath_hal_version);
+ sep = "";
+ for (i = 0; ath_hal_buildopts[i] != NULL; i++) {
diff --git a/package/madwifi/patches/355-eap_auth_disassoc.patch b/package/madwifi/patches/355-eap_auth_disassoc.patch
new file mode 100644
index 000000000..8bb1e9323
--- /dev/null
+++ b/package/madwifi/patches/355-eap_auth_disassoc.patch
@@ -0,0 +1,77 @@
+This patch causes STA mode interfaces to disassociate if transmission of assoc/auth
+critical packets failed.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8334,6 +8334,14 @@ ath_tx_processq(struct ath_softc *sc, st
+ #endif
+ if (ts->ts_status & HAL_TXERR_XRETRY) {
+ sc->sc_stats.ast_tx_xretries++;
++ if (SKB_CB(bf->bf_skb)->auth_pkt &&
++ (ni->ni_vap->iv_opmode == IEEE80211_M_STA)) {
++ struct ieee80211vap *vap = ni->ni_vap;
++
++ /* if roaming is enabled, try reassociating, otherwise
++ * disassociate and go back to the scan state */
++ vap->iv_mgtsend.function(vap->iv_mgtsend.data);
++ }
+ if (ni->ni_flags & IEEE80211_NODE_UAPSD_TRIG) {
+ ni->ni_stats.ns_tx_eosplost++;
+ DPRINTF(sc, ATH_DEBUG_UAPSD,
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -156,6 +156,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_
+ if (off != 0)
+ skb_reserve(skb, align - off);
+
++ SKB_CB(skb)->auth_pkt = 0;
+ SKB_CB(skb)->ni = NULL;
+ SKB_CB(skb)->flags = 0;
+ SKB_CB(skb)->next = NULL;
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -393,6 +393,7 @@ typedef spinlock_t acl_lock_t;
+ void (*next_destructor)(struct sk_buff *skb);
+ #endif
+ struct sk_buff *next; /* fast frame sk_buf chain */
++ u_int8_t auth_pkt;
+ };
+
+
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -778,6 +778,8 @@ ieee80211_encap(struct ieee80211_node *n
+ else
+ hdrsize = sizeof(struct ieee80211_frame);
+
++ SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE));
++
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+@@ -1622,6 +1624,7 @@ ieee80211_add_xr_param(u_int8_t *frm, st
+ ie->param_len = frm - &ie->param_oui[0];
+ return frm;
+ }
++
+ #endif
+ /*
+ * Send a probe request frame with the specified ssid
+@@ -1886,6 +1889,7 @@ ieee80211_send_mgmt(struct ieee80211_nod
+ sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0));
+ if (skb == NULL)
+ senderr(ENOMEM, is_tx_nobuf);
++ SKB_CB(skb)->auth_pkt = 1;
+
+ ((__le16 *)frm)[0] =
+ (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED)
+@@ -1960,6 +1964,7 @@ ieee80211_send_mgmt(struct ieee80211_nod
+ vap->app_ie[IEEE80211_APPIE_FRAME_ASSOC_REQ].length);
+ if (skb == NULL)
+ senderr(ENOMEM, is_tx_nobuf);
++ SKB_CB(skb)->auth_pkt = 1;
+
+ capinfo = 0;
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
diff --git a/package/madwifi/patches/356-hidden_ssid.patch b/package/madwifi/patches/356-hidden_ssid.patch
new file mode 100644
index 000000000..b87569e76
--- /dev/null
+++ b/package/madwifi/patches/356-hidden_ssid.patch
@@ -0,0 +1,49 @@
+This patch fixes the detection of hidden SSIDs as transmitted
+by some cisco systems.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -209,6 +209,19 @@ saveie(u_int8_t **iep, const u_int8_t *i
+ ieee80211_saveie(iep, ie);
+ }
+
++
++static inline int is_empty_ssid(u_int8_t *ssid)
++{
++ if (!ssid)
++ return 1;
++ if (ssid[1] == 0)
++ return 1;
++ if ((ssid[1] == 1) && (ssid[2] == 0))
++ return 1;
++ return 0;
++}
++
++
+ /*
+ * Process a beacon or probe response frame; create an
+ * entry in the scan cache or update any previous entry.
+@@ -233,8 +246,8 @@ sta_add(struct ieee80211_scan_state *ss,
+ SCAN_STA_LOCK_IRQ(st);
+ LIST_FOREACH(se, &st->st_hash[hash], se_hash)
+ if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr) &&
+- sp->ssid[1] == se->base.se_ssid[1] &&
+- !memcmp(se->base.se_ssid+2, sp->ssid+2, se->base.se_ssid[1]))
++ (is_empty_ssid(sp->ssid) || (sp->ssid[1] == se->base.se_ssid[1] &&
++ !memcmp(se->base.se_ssid+2, sp->ssid+2, se->base.se_ssid[1]))))
+ goto found;
+
+ MALLOC(se, struct sta_entry *, sizeof(struct sta_entry),
+@@ -252,8 +265,8 @@ found:
+ ise = &se->base;
+
+ /* XXX ap beaconing multiple ssid w/ same bssid */
+- if (sp->ssid[1] != 0 &&
+- (ISPROBE(subtype) || ise->se_ssid[1] == 0))
++ if (!is_empty_ssid(sp->ssid) &&
++ (ISPROBE(subtype) || is_empty_ssid(ise->se_ssid)))
+ memcpy(ise->se_ssid, sp->ssid, 2 + sp->ssid[1]);
+
+ memcpy(ise->se_rates, sp->rates,
diff --git a/package/madwifi/patches/357-bgscan_thresh.patch b/package/madwifi/patches/357-bgscan_thresh.patch
new file mode 100644
index 000000000..bf3483776
--- /dev/null
+++ b/package/madwifi/patches/357-bgscan_thresh.patch
@@ -0,0 +1,160 @@
+Add an optional background scanning threshold triggered by low rssi
+(useful for passing updated scan results to the supplicant ahead of
+time, before losing connectivity entirely)
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -646,6 +646,7 @@ enum {
+ IEEE80211_PARAM_MINRATE = 76, /* Minimum rate (by table index) */
+ IEEE80211_PARAM_PROTMODE_RSSI = 77, /* RSSI Threshold for enabling protection mode */
+ IEEE80211_PARAM_PROTMODE_TIMEOUT = 78, /* Timeout for expiring protection mode */
++ IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -92,6 +92,8 @@
+ #define IEEE80211_BGSCAN_IDLE_MIN 100 /* min idle time (ms) */
+ #define IEEE80211_BGSCAN_IDLE_DEFAULT 250 /* default idle time (ms) */
+
++#define IEEE80211_BGSCAN_TRIGGER_INTVL 20 /* min trigger interval for thresh based bgscan (secs) */
++
+ #define IEEE80211_COVERAGE_CLASS_MAX 31 /* max coverage class */
+ #define IEEE80211_REGCLASSIDS_MAX 10 /* max regclass id list */
+
+@@ -219,6 +221,10 @@ struct ieee80211vap {
+ u_int8_t iv_nickname[IEEE80211_NWID_LEN];
+ u_int iv_bgscanidle; /* bg scan idle threshold */
+ u_int iv_bgscanintvl; /* bg scan min interval */
++ u_int iv_bgscanthr; /* bg scan rssi threshold */
++ u_int iv_bgscantrintvl; /* bg scan trigger interval */
++ unsigned long iv_bgscanthr_next; /* last trigger for bgscan */
++ unsigned long iv_lastconnect; /* time of last connect attempt */
+ u_int iv_scanvalid; /* scan cache valid threshold */
+ struct ieee80211_roam iv_roam; /* sta-mode roaming state */
+
+@@ -608,6 +614,7 @@ MALLOC_DECLARE(M_80211_VAP);
+ #define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: use software beacon timer */
+ #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */
+ #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */
++#define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */
+
+ #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
+ #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2744,6 +2744,9 @@ ieee80211_ioctl_setparam(struct net_devi
+ else
+ retv = EINVAL;
+ break;
++ case IEEE80211_PARAM_BGSCAN_THRESH:
++ vap->iv_bgscanthr = value;
++ break;
+ case IEEE80211_PARAM_MCAST_RATE:
+ /* units are in KILObits per second */
+ if (value >= 256 && value <= 54000)
+@@ -3144,6 +3147,9 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_BGSCAN_INTERVAL:
+ param[0] = vap->iv_bgscanintvl / HZ; /* seconds */
+ break;
++ case IEEE80211_PARAM_BGSCAN_THRESH:
++ param[0] = vap->iv_bgscanthr; /* rssi */
++ break;
+ case IEEE80211_PARAM_MCAST_RATE:
+ param[0] = vap->iv_mcast_rate; /* seconds */
+ break;
+@@ -5666,6 +5672,10 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bgscanintvl" },
+ { IEEE80211_PARAM_BGSCAN_INTERVAL,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bgscanintvl" },
++ { IEEE80211_PARAM_BGSCAN_THRESH,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bgscanthr" },
++ { IEEE80211_PARAM_BGSCAN_THRESH,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bgscanthr" },
+ { IEEE80211_PARAM_MCAST_RATE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcast_rate" },
+ { IEEE80211_PARAM_MCAST_RATE,
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3013,8 +3013,10 @@ contbgscan(struct ieee80211vap *vap)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+
++ vap->iv_bgscantrintvl = (vap->iv_bgscantrintvl + 1) % 4;
+ return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
+- time_after(jiffies, ic->ic_lastdata + vap->iv_bgscanidle));
++ (((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN_THR) && !vap->iv_bgscantrintvl) ||
++ time_after(jiffies, ic->ic_lastdata + vap->iv_bgscanidle)));
+ }
+
+ static __inline int
+@@ -3258,6 +3260,25 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ /* record tsf of last beacon */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
++
++ /* When rssi is low, start doing bgscans more frequently to allow
++ * the supplicant to make a better switching decision */
++ if (!(ic->ic_flags & IEEE80211_F_SCAN) && (rssi < vap->iv_bgscanthr) &&
++ (!vap->iv_bgscanthr_next ||
++ !time_before(jiffies, vap->iv_bgscanthr_next)) &&
++ (vap->iv_state == IEEE80211_S_RUN) &&
++ time_after(jiffies, vap->iv_lastconnect +
++ msecs_to_jiffies(IEEE80211_BGSCAN_INTVAL_MIN * 1000))) {
++ int ret;
++
++ ic->ic_lastdata = 0;
++ ic->ic_lastscan = 0;
++ ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN_THR;
++ ret = ieee80211_bg_scan(vap);
++ if (ret)
++ vap->iv_bgscanthr_next = jiffies + msecs_to_jiffies(IEEE80211_BGSCAN_TRIGGER_INTVL * 1000);
++ }
++
+ if (ni->ni_intval != scan.bintval) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "beacon interval divergence: "
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -616,6 +616,7 @@ ieee80211_cancel_scan(struct ieee80211va
+
+ /* clear bg scan NOPICK and mark cancel request */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
++ ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN_THR;
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
+ ss->ss_ops->scan_cancel(ss, vap);
+ /* force it to fire asap */
+@@ -782,7 +783,7 @@ again:
+ ieee80211_sta_pwrsave(vap, 0);
+ if (ss->ss_next >= ss->ss_last) {
+ ieee80211_notify_scan_done(vap);
+- ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
++ ic->ic_flags_ext &= ~(IEEE80211_FEXT_BGSCAN|IEEE80211_FEXT_BGSCAN_THR);
+ }
+ }
+ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL;
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -1450,6 +1450,7 @@ __ieee80211_newstate(struct ieee80211vap
+ }
+ break;
+ case IEEE80211_S_AUTH:
++ vap->iv_lastconnect = jiffies;
+ /* auth frames are possible between IBSS nodes,
+ * see 802.11-1999, chapter 5.7.6 */
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA ||
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -238,7 +238,8 @@ ieee80211_hardstart(struct sk_buff *skb,
+ }
+
+ /* Cancel any running BG scan */
+- ieee80211_cancel_scan(vap);
++ if (!(ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN_THR) && (vap->iv_state == IEEE80211_S_RUN))
++ ieee80211_cancel_scan(vap);
+
+ /*
+ * Find the node for the destination so we can do
diff --git a/package/madwifi/patches/358-ignore_broken_bssid.patch b/package/madwifi/patches/358-ignore_broken_bssid.patch
new file mode 100644
index 000000000..b54927220
--- /dev/null
+++ b/package/madwifi/patches/358-ignore_broken_bssid.patch
@@ -0,0 +1,18 @@
+Some misconfigured APs broadcast NULL BSSIDs, which can confuse the STA
+Ignore those when scanning.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -242,6 +242,10 @@ sta_add(struct ieee80211_scan_state *ss,
+ struct ieee80211_scan_entry *ise;
+ int hash;
+
++ /* workaround for broken APs that broadcast NULL BSSIDs */
++ if (memcmp(wh->i_addr3, "\x00\x00\x00\x00\x00\x00", 6) == 0)
++ return 0;
++
+ hash = STA_HASH(macaddr);
+ SCAN_STA_LOCK_IRQ(st);
+ LIST_FOREACH(se, &st->st_hash[hash], se_hash)
diff --git a/package/madwifi/patches/359-disable_reassoc.patch b/package/madwifi/patches/359-disable_reassoc.patch
new file mode 100644
index 000000000..8d25dc2a8
--- /dev/null
+++ b/package/madwifi/patches/359-disable_reassoc.patch
@@ -0,0 +1,31 @@
+Add a preliminary fix for the reassoc check, but disable reassoc entirely for now
+until we've figured out why it fails frequently.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -599,10 +599,9 @@ ieee80211_ibss_merge(struct ieee80211_no
+ EXPORT_SYMBOL(ieee80211_ibss_merge);
+
+ static __inline int
+-ssid_equal(const struct ieee80211_node *a, const struct ieee80211_node *b)
++bssid_equal(const struct ieee80211_node *a, const struct ieee80211_node *b)
+ {
+- return (a->ni_esslen == b->ni_esslen &&
+- memcmp(a->ni_essid, b->ni_essid, a->ni_esslen) == 0);
++ return (memcmp(a->ni_bssid, b->ni_bssid, IEEE80211_ADDR_LEN) == 0);
+ }
+
+ /*
+@@ -634,8 +633,8 @@ ieee80211_sta_join1(struct ieee80211_nod
+ * Check if old+new node have the same ssid in which
+ * case we can reassociate when operating in sta mode.
+ */
+- canreassoc = ((obss != NULL) &&
+- (vap->iv_state == IEEE80211_S_RUN) && ssid_equal(obss, selbs));
++ canreassoc = 0; /* ((obss != NULL) &&
++ (vap->iv_state == IEEE80211_S_RUN) && bssid_equal(obss, selbs)); */
+ vap->iv_bss = selbs;
+ IEEE80211_ADDR_COPY(vap->iv_bssid, selbs->ni_bssid);
+ if (obss != NULL)
diff --git a/package/madwifi/patches/360-sta_nodes.patch b/package/madwifi/patches/360-sta_nodes.patch
new file mode 100644
index 000000000..8d2dd9ddd
--- /dev/null
+++ b/package/madwifi/patches/360-sta_nodes.patch
@@ -0,0 +1,242 @@
+Drop stale AP nodes from the client list when disconnecting.
+Fixes some reassoc issues.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -1348,7 +1348,7 @@ __ieee80211_newstate(struct ieee80211vap
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+- ieee80211_sta_leave(ni);
++ ieee80211_node_leave(ni);
+ break;
+ case IEEE80211_M_HOSTAP:
+ ieee80211_iterate_nodes(&ic->ic_sta,
+@@ -1358,12 +1358,14 @@ __ieee80211_newstate(struct ieee80211vap
+ break;
+ }
+ goto reset;
++ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_STA:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
++ ieee80211_node_leave(ni);
+ break;
+ case IEEE80211_M_HOSTAP:
+ ieee80211_iterate_nodes(&ic->ic_sta,
+@@ -1376,7 +1378,6 @@ __ieee80211_newstate(struct ieee80211vap
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ goto reset;
+- case IEEE80211_S_AUTH:
+ reset:
+ ieee80211_reset_bss(vap);
+ break;
+@@ -1429,10 +1430,12 @@ __ieee80211_newstate(struct ieee80211vap
+ IEEE80211_SCAN_FOREVER,
+ vap->iv_des_nssid, vap->iv_des_ssid,
+ NULL);
++ else
++ ieee80211_node_leave(vap->iv_bss);
+ break;
+ case IEEE80211_S_RUN: /* beacon miss */
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+- ieee80211_sta_leave(ni);
++ ieee80211_node_leave(ni);
+ vap->iv_flags &= ~IEEE80211_F_SIBSS; /* XXX */
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan(vap,
+@@ -1511,7 +1514,7 @@ __ieee80211_newstate(struct ieee80211vap
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ break;
+ case IEEE80211_S_RUN:
+- ieee80211_sta_leave(ni);
++ ieee80211_node_leave(ni);
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
+ /* NB: caller specifies ASSOC/REASSOC by arg */
+ IEEE80211_SEND_MGMT(ni, arg ?
+@@ -1779,6 +1782,7 @@ ieee80211_newstate(struct ieee80211vap *
+ ieee80211_state_name[nstate],
+ ieee80211_state_name[dstate]);
+
++ ieee80211_update_link_status(vap, nstate, ostate);
+ switch (nstate) {
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -233,33 +233,59 @@ ieee80211_vlan_vdetach(struct ieee80211v
+ }
+
+ void
+-ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
++ieee80211_update_link_status(struct ieee80211vap *vap, int nstate, int ostate)
+ {
+- struct ieee80211vap *vap = ni->ni_vap;
+ struct net_device *dev = vap->iv_dev;
+ union iwreq_data wreq;
++ int active;
++
++ if (vap->iv_opmode != IEEE80211_M_STA)
++ return;
++
++ if (ostate == nstate)
++ return;
++
++ if (nstate == IEEE80211_S_RUN)
++ active = 1;
++ else if ((ostate >= IEEE80211_S_AUTH) && (nstate < ostate))
++ active = 0;
++ else
++ return;
++
++ if (active && !vap->iv_bss)
++ return;
++
++ memset(&wreq, 0, sizeof(wreq));
++ wreq.ap_addr.sa_family = ARPHRD_ETHER;
+
+- if (ni == vap->iv_bss) {
+- if (newassoc)
+- netif_carrier_on(dev);
+- memset(&wreq, 0, sizeof(wreq));
++ if (active) {
++ //netif_carrier_on(vap->iv_dev);
+ IEEE80211_ADDR_COPY(wreq.addr.sa_data, vap->iv_bssid);
+- wreq.addr.sa_family = ARPHRD_ETHER;
+-#ifdef ATH_SUPERG_XR
+- if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR)
+- dev = vap->iv_xrvap->iv_dev;
+-#endif
+- wireless_send_event(dev, SIOCGIWAP, &wreq, NULL);
+ } else {
+- memset(&wreq, 0, sizeof(wreq));
+- IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
+- wreq.addr.sa_family = ARPHRD_ETHER;
++ //netif_carrier_off(vap->iv_dev);
++ memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN);
++ }
++ wireless_send_event(dev, SIOCGIWAP, &wreq, NULL);
++}
++
++void
++ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
++{
++ struct ieee80211vap *vap = ni->ni_vap;
++ struct net_device *dev = vap->iv_dev;
++ union iwreq_data wreq;
++
++ if (ni == vap->iv_bss)
++ return;
++
++ memset(&wreq, 0, sizeof(wreq));
++ IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
++ wreq.addr.sa_family = ARPHRD_ETHER;
+ #ifdef ATH_SUPERG_XR
+- if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR)
+- dev = vap->iv_xrvap->iv_dev;
++ if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR)
++ dev = vap->iv_xrvap->iv_dev;
+ #endif
+- wireless_send_event(dev, IWEVREGISTERED, &wreq, NULL);
+- }
++ wireless_send_event(dev, IWEVREGISTERED, &wreq, NULL);
+ }
+
+ void
+@@ -269,18 +295,14 @@ ieee80211_notify_node_leave(struct ieee8
+ struct net_device *dev = vap->iv_dev;
+ union iwreq_data wreq;
+
+- if (ni == vap->iv_bss) {
+- netif_carrier_off(dev);
+- memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN);
+- wreq.ap_addr.sa_family = ARPHRD_ETHER;
+- wireless_send_event(dev, SIOCGIWAP, &wreq, NULL);
+- } else {
+- /* fire off wireless event station leaving */
+- memset(&wreq, 0, sizeof(wreq));
+- IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
+- wreq.addr.sa_family = ARPHRD_ETHER;
+- wireless_send_event(dev, IWEVEXPIRED, &wreq, NULL);
+- }
++ if (ni == vap->iv_bss)
++ return;
++
++ /* fire off wireless event station leaving */
++ memset(&wreq, 0, sizeof(wreq));
++ IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr);
++ wreq.addr.sa_family = ARPHRD_ETHER;
++ wireless_send_event(dev, IWEVEXPIRED, &wreq, NULL);
+ }
+
+ void
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -2332,6 +2332,7 @@ ieee80211_node_leave(struct ieee80211_no
+ count_suppchans(ic, ni, -1);
+ IEEE80211_UNLOCK_IRQ(ic);
+
++done:
+ /*
+ * Cleanup station state. In particular clear various
+ * state that might otherwise be reused if the node
+@@ -2339,7 +2340,7 @@ ieee80211_node_leave(struct ieee80211_no
+ * (and memory is reclaimed).
+ */
+ ieee80211_sta_leave(ni);
+-done:
++
+ /* Run a cleanup */
+ #ifdef IEEE80211_DEBUG_REFCNT
+ ic->ic_node_cleanup_debug(ni, __func__, __LINE__);
+--- a/net80211/ieee80211_node.h
++++ b/net80211/ieee80211_node.h
+@@ -60,7 +60,7 @@
+ #define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */
+ #define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */
+
+-#define IEEE80211_TRANS_WAIT 5 /* mgt frame tx timer (secs) */
++#define IEEE80211_TRANS_WAIT 300 /* mgt frame tx timer (msecs) */
+
+ #define IEEE80211_NODE_HASHSIZE 32
+ /* simple hash is enough for variation of macaddr */
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -2141,7 +2141,7 @@ ieee80211_send_mgmt(struct ieee80211_nod
+
+ ieee80211_mgmt_output(ieee80211_ref_node(ni), skb, type);
+ if (timer)
+- mod_timer(&vap->iv_mgtsend, jiffies + timer * HZ);
++ mod_timer(&vap->iv_mgtsend, jiffies + msecs_to_jiffies(timer));
+ return 0;
+ bad:
+ return ret;
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -514,8 +514,9 @@ ieee80211_ioctl_siwap(struct net_device
+ vap->iv_flags |= IEEE80211_F_DESBSSID;
+
+ IEEE80211_ADDR_COPY(vap->iv_des_bssid, &ap_addr->sa_data);
+- if (IS_UP_AUTO(vap))
++ if (IS_UP(vap->iv_dev)) {
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
++ }
+ }
+ return 0;
+ }
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -643,6 +643,7 @@ void ieee80211_vlan_vdetach(struct ieee8
+ #define free_netdev(dev) kfree(dev)
+ #endif
+
++void ieee80211_update_link_status(struct ieee80211vap *vap, int nstate, int ostate);
+ void ieee80211_ioctl_vattach(struct ieee80211vap *);
+ void ieee80211_ioctl_vdetach(struct ieee80211vap *);
+ struct ifreq;
diff --git a/package/madwifi/patches/361-bmiss_handling.patch b/package/madwifi/patches/361-bmiss_handling.patch
new file mode 100644
index 000000000..15d238f7b
--- /dev/null
+++ b/package/madwifi/patches/361-bmiss_handling.patch
@@ -0,0 +1,102 @@
+Improve the beacon miss handling. Instead of just dropping the connection,
+send a directed probe request to the AP to see if it's still responding.
+Schedule a software beacon miss timer in this case, which adds a timeout
+for the APs probe response.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3400,12 +3400,17 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ }
+
+ /* WDS/Repeater: re-schedule software beacon timer for
+- * STA. */
+- if ((vap->iv_state == IEEE80211_S_RUN) &&
+- (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+- mod_timer(&vap->iv_swbmiss,
++ * STA. Reset consecutive bmiss counter as well */
++ IEEE80211_LOCK_IRQ(ic);
++ if (vap->iv_state == IEEE80211_S_RUN) {
++ vap->iv_bmiss_count = 0;
++ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
++ mod_timer(&vap->iv_swbmiss,
+ jiffies + vap->iv_swbmiss_period);
++ else
++ del_timer(&vap->iv_swbmiss);
+ }
++ IEEE80211_UNLOCK_IRQ(ic);
+
+ /* If scanning, pass the info to the scan module.
+ * Otherwise, check if it's the right time to do
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -1209,6 +1209,8 @@ ieee80211_beacon_miss(struct ieee80211co
+ }
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ int count;
++
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", "beacon miss");
+@@ -1221,6 +1223,29 @@ ieee80211_beacon_miss(struct ieee80211co
+ if (vap->iv_opmode != IEEE80211_M_STA ||
+ vap->iv_state != IEEE80211_S_RUN)
+ continue;
++
++ IEEE80211_LOCK_IRQ(ic);
++ count = vap->iv_bmiss_count++;
++ if (count) {
++ /* if the counter was already above zero, reset it
++ * here, since we're going to do the bmiss handling
++ * in any case */
++ vap->iv_bmiss_count = 0;
++ } else {
++ /* schedule the software beacon miss timer, it will be
++ * cancelled, if the probe request is acked */
++ mod_timer(&vap->iv_swbmiss, jiffies + vap->iv_swbmiss_period);
++ }
++ IEEE80211_UNLOCK_IRQ(ic);
++
++ if (!count) {
++ ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
++ vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
++ vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen,
++ NULL, 0);
++ continue;
++ }
++
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
+ #ifdef ATH_SUPERG_DYNTURBO
+ /*
+@@ -1621,14 +1646,14 @@ __ieee80211_newstate(struct ieee80211vap
+ }
+
+ /* WDS/Repeater: Start software beacon timer for STA */
++ vap->iv_swbmiss.function = ieee80211_sta_swbmiss;
++ vap->iv_swbmiss.data = (unsigned long) vap;
++ vap->iv_swbmiss_period = IEEE80211_TU_TO_JIFFIES(
++ vap->iv_ic->ic_bmissthreshold * ni->ni_intval);
++
+ if (ostate != IEEE80211_S_RUN &&
+ (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+- vap->iv_swbmiss.function = ieee80211_sta_swbmiss;
+- vap->iv_swbmiss.data = (unsigned long) vap;
+- vap->iv_swbmiss_period = IEEE80211_TU_TO_JIFFIES(
+- vap->iv_ic->ic_bmissthreshold * ni->ni_intval);
+-
+ mod_timer(&vap->iv_swbmiss, jiffies + vap->iv_swbmiss_period);
+ }
+
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -283,6 +283,7 @@ struct ieee80211vap {
+
+ struct timer_list iv_swbmiss; /* software beacon miss timer */
+ u_int16_t iv_swbmiss_period; /* software beacon miss timer period */
++ u_int16_t iv_bmiss_count; /* consecutive beacon miss counter */
+ struct ieee80211_nsparams iv_nsparams; /* new state parameters for tasklet for stajoin1 */
+ struct IEEE80211_TQ_STRUCT iv_stajoin1tq; /* tasklet for newstate action called from stajoin1tq */
+ unsigned int iv_nsdone; /* Done with scheduled newstate tasklet */
diff --git a/package/madwifi/patches/362-rssithr.patch b/package/madwifi/patches/362-rssithr.patch
new file mode 100644
index 000000000..5a86833bb
--- /dev/null
+++ b/package/madwifi/patches/362-rssithr.patch
@@ -0,0 +1,93 @@
+Add an optional threshold for low-rssi disconnection. This can be useful
+when letting wpa_supplicant control roaming.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -647,6 +647,8 @@ enum {
+ IEEE80211_PARAM_PROTMODE_RSSI = 77, /* RSSI Threshold for enabling protection mode */
+ IEEE80211_PARAM_PROTMODE_TIMEOUT = 78, /* Timeout for expiring protection mode */
+ IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */
++ IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */
++ IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2799,6 +2799,12 @@ ieee80211_ioctl_setparam(struct net_devi
+ case IEEE80211_PARAM_ROAM_RATE_11G:
+ vap->iv_roam.rate11b = value;
+ break;
++ case IEEE80211_PARAM_RSSI_DIS_THR:
++ vap->iv_rssi_dis_thr = value;
++ break;
++ case IEEE80211_PARAM_RSSI_DIS_COUNT:
++ vap->iv_rssi_dis_max = value;
++ break;
+ case IEEE80211_PARAM_UAPSDINFO:
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ if (ic->ic_caps & IEEE80211_C_UAPSD) {
+@@ -3184,6 +3190,12 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_ROAM_RATE_11G:
+ param[0] = vap->iv_roam.rate11b;
+ break;
++ case IEEE80211_PARAM_RSSI_DIS_THR:
++ param[0] = vap->iv_rssi_dis_thr;
++ break;
++ case IEEE80211_PARAM_RSSI_DIS_COUNT:
++ param[0] = vap->iv_rssi_dis_max;
++ break;
+ case IEEE80211_PARAM_UAPSDINFO:
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ if (IEEE80211_VAP_UAPSD_ENABLED(vap))
+@@ -5733,6 +5745,14 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rate11g" },
+ { IEEE80211_PARAM_ROAM_RATE_11G,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rate11g" },
++ { IEEE80211_PARAM_RSSI_DIS_THR,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rssi_disthr" },
++ { IEEE80211_PARAM_RSSI_DIS_THR,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rssi_disthr" },
++ { IEEE80211_PARAM_RSSI_DIS_COUNT,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rssi_discnt" },
++ { IEEE80211_PARAM_RSSI_DIS_COUNT,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rssi_discnt" },
+ { IEEE80211_PARAM_UAPSDINFO,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "uapsd" },
+ { IEEE80211_PARAM_UAPSDINFO,
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3261,6 +3261,19 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
+
++ /* when rssi falls below the disconnection threshold, drop the connection */
++ if ((vap->iv_rssi_dis_thr > 0) && (vap->iv_rssi_dis_max > 0)) {
++ if ((rssi > 0) && (rssi < vap->iv_rssi_dis_thr)) {
++ if (++vap->iv_rssi_dis_trig > vap->iv_rssi_dis_max) {
++ vap->iv_rssi_dis_trig = 0;
++ ieee80211_node_leave(ni);
++ return;
++ }
++ } else {
++ vap->iv_rssi_dis_trig = 0;
++ }
++ }
++
+ /* When rssi is low, start doing bgscans more frequently to allow
+ * the supplicant to make a better switching decision */
+ if (!(ic->ic_flags & IEEE80211_F_SCAN) && (rssi < vap->iv_bgscanthr) &&
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -223,6 +223,9 @@ struct ieee80211vap {
+ u_int iv_bgscanintvl; /* bg scan min interval */
+ u_int iv_bgscanthr; /* bg scan rssi threshold */
+ u_int iv_bgscantrintvl; /* bg scan trigger interval */
++ u_int iv_rssi_dis_thr; /* rssi disassoc threshold */
++ u_int iv_rssi_dis_max; /* max beacons below disconnect threshold */
++ u_int iv_rssi_dis_trig; /* rssi disassoc trigger count */
+ unsigned long iv_bgscanthr_next; /* last trigger for bgscan */
+ unsigned long iv_lastconnect; /* time of last connect attempt */
+ u_int iv_scanvalid; /* scan cache valid threshold */
diff --git a/package/madwifi/patches/363-fix_turbo.patch b/package/madwifi/patches/363-fix_turbo.patch
new file mode 100644
index 000000000..880f2d614
--- /dev/null
+++ b/package/madwifi/patches/363-fix_turbo.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -4925,7 +4925,7 @@ ath_beacon_generate(struct ath_softc *sc
+ * capability info and arrange for a mode change
+ * if needed.
+ */
+- if (sc->sc_dturbo) {
++ if (sc->sc_dturbo && NULL != avp->av_boff.bo_tim) {
+ u_int8_t dtim;
+ dtim = ((avp->av_boff.bo_tim[2] == 1) ||
+ (avp->av_boff.bo_tim[3] == 1));
diff --git a/package/madwifi/patches/364-memory_alloc.patch b/package/madwifi/patches/364-memory_alloc.patch
new file mode 100644
index 000000000..53fbc7709
--- /dev/null
+++ b/package/madwifi/patches/364-memory_alloc.patch
@@ -0,0 +1,13 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -539,8 +539,8 @@ ath_attach(u_int16_t devid, struct net_d
+
+ /* Allocate space for dynamically determined maximum VAP count */
+ sc->sc_bslot =
+- kmalloc(ath_maxvaps * sizeof(struct ieee80211vap), GFP_KERNEL);
+- memset(sc->sc_bslot, 0, ath_maxvaps * sizeof(struct ieee80211vap));
++ kmalloc(ath_maxvaps * sizeof(struct ieee80211vap*), GFP_KERNEL);
++ memset(sc->sc_bslot, 0, ath_maxvaps * sizeof(struct ieee80211vap*));
+
+ /*
+ * Cache line size is used to size and align various
diff --git a/package/madwifi/patches/365-turbo_channelsearch.patch b/package/madwifi/patches/365-turbo_channelsearch.patch
new file mode 100644
index 000000000..df4306b21
--- /dev/null
+++ b/package/madwifi/patches/365-turbo_channelsearch.patch
@@ -0,0 +1,10 @@
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -684,6 +684,7 @@ ieee80211_find_channel(struct ieee80211c
+ int i;
+
+ /* Brute force search */
++ flags &= IEEE80211_CHAN_ALLTURBO;
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (c->ic_freq == freq &&
diff --git a/package/madwifi/patches/366-bstuck_thresh.patch b/package/madwifi/patches/366-bstuck_thresh.patch
new file mode 100644
index 000000000..cde1f5c98
--- /dev/null
+++ b/package/madwifi/patches/366-bstuck_thresh.patch
@@ -0,0 +1,52 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -389,6 +389,7 @@ static int ath_countrycode = CTRY_DEFAUL
+ static int ath_outdoor = AH_FALSE; /* enable outdoor use */
+ static int ath_xchanmode = AH_TRUE; /* enable extended channels */
+ static int ath_maxvaps = ATH_MAXVAPS_DEFAULT; /* set default maximum vaps */
++static int bstuck_thresh = BSTUCK_THRESH; /* Stuck beacon count required for reset */
+ static char *autocreate = NULL;
+ static char *ratectl = DEF_RATE_CTL;
+ static int rfkill = 0;
+@@ -432,6 +433,7 @@ MODULE_PARM(rfkill, "i");
+ #ifdef ATH_CAP_TPC
+ MODULE_PARM(tpc, "i");
+ #endif
++MODULE_PARM(bstuck_thresh, "i");
+ MODULE_PARM(autocreate, "s");
+ MODULE_PARM(ratectl, "s");
+ #else
+@@ -445,6 +447,7 @@ module_param(rfkill, int, 0600);
+ #ifdef ATH_CAP_TPC
+ module_param(tpc, int, 0600);
+ #endif
++module_param(bstuck_thresh, int, 0600);
+ module_param(autocreate, charp, 0600);
+ module_param(ratectl, charp, 0600);
+ #endif
+@@ -457,6 +460,7 @@ MODULE_PARM_DESC(rfkill, "Enable/disable
+ MODULE_PARM_DESC(tpc, "Enable/disable per-packet transmit power control (TPC) "
+ "capability");
+ #endif
++MODULE_PARM_DESC(bstuck_thresh, "Override default stuck beacon threshold");
+ MODULE_PARM_DESC(autocreate, "Create ath device in "
+ "[sta|ap|wds|adhoc|ahdemo|monitor] mode. defaults to sta, use "
+ "'none' to disable");
+@@ -5072,7 +5076,7 @@ ath_beacon_send(struct ath_softc *sc, in
+ DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+ "Missed %u consecutive beacons (n_beacon=%u)\n",
+ sc->sc_bmisscount, n_beacon);
+- if (sc->sc_bmisscount > BSTUCK_THRESH)
++ if (sc->sc_bmisscount > bstuck_thresh)
+ ATH_SCHEDULE_TQUEUE(&sc->sc_bstucktq, needmark);
+ return;
+ }
+@@ -5230,7 +5234,7 @@ ath_bstuck_tasklet(TQUEUE_ARG data)
+ * check will be true, in which case return
+ * without resetting the driver.
+ */
+- if (sc->sc_bmisscount <= BSTUCK_THRESH)
++ if (sc->sc_bmisscount <= bstuck_thresh)
+ return;
+ EPRINTF(sc, "Stuck beacon; resetting (beacon miss count: %u)\n",
+ sc->sc_bmisscount);
diff --git a/package/madwifi/patches/367-roaming.patch b/package/madwifi/patches/367-roaming.patch
new file mode 100644
index 000000000..5950f9fd4
--- /dev/null
+++ b/package/madwifi/patches/367-roaming.patch
@@ -0,0 +1,77 @@
+Patch adapted from ubnt madwifi patchset
+
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -659,7 +659,7 @@ ieee80211_sta_join1(struct ieee80211_nod
+ */
+ if (canreassoc) {
+ vap->iv_nsparams.newstate = IEEE80211_S_ASSOC;
+- vap->iv_nsparams.arg = 0;
++ vap->iv_nsparams.arg = IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
+ IEEE80211_SCHEDULE_TQUEUE(&vap->iv_stajoin1tq);
+ } else {
+ vap->iv_nsparams.newstate = IEEE80211_S_AUTH;
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -748,14 +748,17 @@ notfound:
+ * a reference to an entry w/o holding the lock on the table.
+ */
+ static struct sta_entry *
+-sta_lookup(struct sta_table *st, const u_int8_t macaddr[IEEE80211_ADDR_LEN])
++sta_lookup(struct sta_table *st, const u_int8_t macaddr[IEEE80211_ADDR_LEN], struct ieee80211_scan_ssid* essid)
+ {
+ struct sta_entry *se;
+ int hash = STA_HASH(macaddr);
+
+ SCAN_STA_LOCK_IRQ(st);
+ LIST_FOREACH(se, &st->st_hash[hash], se_hash)
+- if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr))
++ if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr) &&
++ (essid->len == se->base.se_ssid[1] &&
++ !memcmp(se->base.se_ssid+2, essid->ssid,
++ se->base.se_ssid[1])))
+ break;
+ SCAN_STA_UNLOCK_IRQ(st);
+
+@@ -772,7 +775,7 @@ sta_roam_check(struct ieee80211_scan_sta
+ u_int8_t roamRate, curRate;
+ int8_t roamRssi, curRssi;
+
+- se = sta_lookup(st, ni->ni_macaddr);
++ se = sta_lookup(st, ni->ni_macaddr, ss->ss_ssid);
+ if (se == NULL) {
+ /* XXX something is wrong */
+ return;
+@@ -866,8 +869,8 @@ sta_age(struct ieee80211_scan_state *ss)
+ */
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
+- /* XXX turn this off until the ap release is cut */
+- if (0 && vap->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO &&
++ if (vap->iv_opmode == IEEE80211_M_STA &&
++ vap->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO &&
+ vap->iv_state >= IEEE80211_S_RUN)
+ /* XXX vap is implicit */
+ sta_roam_check(ss, vap);
+@@ -922,7 +925,11 @@ sta_assoc_fail(struct ieee80211_scan_sta
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se;
+
+- se = sta_lookup(st, macaddr);
++ /* Let outside apps to decide what peer is blacklisted */
++ if (ss->ss_vap->iv_ic->ic_roaming == IEEE80211_ROAMING_MANUAL)
++ return;
++
++ se = sta_lookup(st, macaddr, ss->ss_ssid);
+ if (se != NULL) {
+ se->se_fails++;
+ se->se_lastfail = jiffies;
+@@ -939,7 +946,7 @@ sta_assoc_success(struct ieee80211_scan_
+ struct sta_table *st = ss->ss_priv;
+ struct sta_entry *se;
+
+- se = sta_lookup(st, macaddr);
++ se = sta_lookup(st, macaddr, ss->ss_ssid);
+ if (se != NULL) {
+ #if 0
+ se->se_fails = 0;
diff --git a/package/madwifi/patches/368-sta_ie_preserve.patch b/package/madwifi/patches/368-sta_ie_preserve.patch
new file mode 100644
index 000000000..fbd779ea0
--- /dev/null
+++ b/package/madwifi/patches/368-sta_ie_preserve.patch
@@ -0,0 +1,49 @@
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -201,8 +201,10 @@ sta_flush_table(struct sta_table *st)
+ }
+
+ static void
+-saveie(u_int8_t **iep, const u_int8_t *ie)
++saveie(u_int8_t **iep, const u_int8_t *ie, int preserve)
+ {
++ if (preserve && *iep)
++ return;
+ if (ie == NULL)
+ *iep = NULL;
+ else
+@@ -304,10 +306,10 @@ found:
+ (const struct ieee80211_tim_ie *) sp->tim;
+ ise->se_dtimperiod = tim->tim_period;
+ }
+- saveie(&ise->se_wme_ie, sp->wme);
+- saveie(&ise->se_wpa_ie, sp->wpa);
+- saveie(&ise->se_rsn_ie, sp->rsn);
+- saveie(&ise->se_ath_ie, sp->ath);
++ saveie(&ise->se_wme_ie, sp->wme, 0);
++ saveie(&ise->se_wpa_ie, sp->wpa, !sp->isprobe);
++ saveie(&ise->se_rsn_ie, sp->rsn, !sp->isprobe);
++ saveie(&ise->se_ath_ie, sp->ath, 0);
+
+ /* clear failure count after STA_FAIL_AGE passes */
+ if (se->se_fails && (jiffies - se->se_lastfail) > STA_FAILS_AGE*HZ) {
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3106,6 +3106,7 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+ memset(&scan, 0, sizeof(scan));
++ scan.isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) && IEEE80211_ADDR_EQ(wh->i_addr2, vap->iv_myaddr);
+ scan.tstamp = frm;
+ frm += 8;
+ scan.bintval = le16toh(*(__le16 *)frm);
+--- a/net80211/ieee80211_scan.h
++++ b/net80211/ieee80211_scan.h
+@@ -133,6 +133,7 @@ struct ieee80211_scanparams {
+ u_int8_t erp;
+ u_int16_t bintval;
+ u_int8_t timoff;
++ u_int8_t isprobe;
+ u_int8_t *tim;
+ u_int8_t *tstamp;
+ u_int8_t *country;
diff --git a/package/madwifi/patches/369-mlme_assoc.patch b/package/madwifi/patches/369-mlme_assoc.patch
new file mode 100644
index 000000000..43aac668c
--- /dev/null
+++ b/package/madwifi/patches/369-mlme_assoc.patch
@@ -0,0 +1,10 @@
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -3723,6 +3723,7 @@ ieee80211_ioctl_setmlme(struct net_devic
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ struct scanlookup lookup;
+
++ preempt_scan(dev, 100, 100);
+ lookup.se = NULL;
+ lookup.mac = mlme->im_macaddr;
+ /* XXX use revised api w/ explicit ssid */
diff --git a/package/madwifi/patches/370-wdsvap.patch b/package/madwifi/patches/370-wdsvap.patch
new file mode 100644
index 000000000..8a0e823ae
--- /dev/null
+++ b/package/madwifi/patches/370-wdsvap.patch
@@ -0,0 +1,1665 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -124,7 +124,7 @@ enum {
+ };
+
+ static struct ieee80211vap *ath_vap_create(struct ieee80211com *,
+- const char *, int, int, struct net_device *);
++ const char *, int, int, struct net_device *, struct ieee80211vap *);
+ static void ath_vap_delete(struct ieee80211vap *);
+ static int ath_init(struct net_device *);
+ static int ath_set_ack_bitrate(struct ath_softc *, int);
+@@ -1123,8 +1123,6 @@ ath_attach(u_int16_t devid, struct net_d
+ autocreatemode = IEEE80211_M_IBSS;
+ else if (!strcmp(autocreate, "ahdemo"))
+ autocreatemode = IEEE80211_M_AHDEMO;
+- else if (!strcmp(autocreate, "wds"))
+- autocreatemode = IEEE80211_M_WDS;
+ else if (!strcmp(autocreate, "monitor"))
+ autocreatemode = IEEE80211_M_MONITOR;
+ else {
+@@ -1137,7 +1135,7 @@ ath_attach(u_int16_t devid, struct net_d
+ if (autocreatemode != -1) {
+ rtnl_lock();
+ vap = ieee80211_create_vap(ic, "ath%d", dev,
+- autocreatemode, 0);
++ autocreatemode, 0, NULL);
+ rtnl_unlock();
+ if (vap == NULL)
+ EPRINTF(sc, "Autocreation of %s VAP failed.", autocreate);
+@@ -1230,14 +1228,14 @@ ath_detach(struct net_device *dev)
+
+ static struct ieee80211vap *
+ ath_vap_create(struct ieee80211com *ic, const char *name,
+- int opmode, int flags, struct net_device *mdev)
++ int opmode, int flags, struct net_device *mdev, struct ieee80211vap *master)
+ {
+ struct ath_softc *sc = ic->ic_dev->priv;
+ struct ath_hal *ah = sc->sc_ah;
+ struct net_device *dev;
+ struct ath_vap *avp;
+ struct ieee80211vap *vap;
+- int ic_opmode;
++ int ic_opmode = IEEE80211_M_STA;
+
+ if (ic->ic_dev->flags & IFF_RUNNING) {
+ /* needs to disable hardware too */
+@@ -1271,8 +1269,12 @@ ath_vap_create(struct ieee80211com *ic,
+ } else
+ ic_opmode = opmode;
+ break;
+- case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_WDS:
++ ic_opmode = ic->ic_opmode;
++ if (!master)
++ return NULL;
++ break;
++ case IEEE80211_M_HOSTAP:
+ /* permit multiple APs and/or WDS links */
+ /* XXX sta+ap for repeater/bridge application */
+ if ((sc->sc_nvaps != 0) && (ic->ic_opmode == IEEE80211_M_STA))
+@@ -1304,7 +1306,7 @@ ath_vap_create(struct ieee80211com *ic,
+ }
+
+ avp = dev->priv;
+- ieee80211_vap_setup(ic, dev, name, opmode, flags);
++ ieee80211_vap_setup(ic, dev, name, opmode, flags, master);
+ /* override with driver methods */
+ vap = &avp->av_vap;
+ avp->av_newstate = vap->iv_newstate;
+@@ -4209,8 +4211,7 @@ ath_calcrxfilter(struct ath_softc *sc)
+ if (ic->ic_opmode == IEEE80211_M_STA ||
+ sc->sc_opmode == HAL_M_IBSS || /* NB: AHDEMO too */
+ (sc->sc_nostabeacons) || sc->sc_scanning ||
+- ((ic->ic_opmode == IEEE80211_M_HOSTAP) &&
+- (ic->ic_protmode != IEEE80211_PROT_NONE)))
++ (ic->ic_opmode == IEEE80211_M_HOSTAP))
+ rfilt |= HAL_RX_FILTER_BEACON;
+ if (sc->sc_nmonvaps > 0)
+ rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON |
+@@ -9032,8 +9033,6 @@ ath_calibrate(unsigned long arg)
+ * set sc->beacons if we might need to restart
+ * them after ath_reset. */
+ if (!sc->sc_beacons &&
+- (TAILQ_FIRST(&ic->ic_vaps)->iv_opmode !=
+- IEEE80211_M_WDS) &&
+ !txcont_was_active &&
+ !sc->sc_dfs_cac) {
+ sc->sc_beacons = 1;
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -373,10 +373,25 @@ void
+ ieee80211_ifdetach(struct ieee80211com *ic)
+ {
+ struct ieee80211vap *vap;
++ int count;
++
++ /* bring down all vaps */
++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ ieee80211_stop(vap->iv_dev);
++ }
++
++ /* wait for all subifs to disappear */
++ do {
++ schedule();
++ rtnl_lock();
++ count = ic->ic_subifs;
++ rtnl_unlock();
++ } while (count > 0);
+
+ rtnl_lock();
+- while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
++ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) {
+ ic->ic_vap_delete(vap);
++ }
+ rtnl_unlock();
+
+ del_timer(&ic->ic_dfs_excl_timer);
+@@ -396,7 +411,7 @@ EXPORT_SYMBOL(ieee80211_ifdetach);
+
+ int
+ ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,
+- const char *name, int opmode, int flags)
++ const char *name, int opmode, int flags, struct ieee80211vap *master)
+ {
+ #define IEEE80211_C_OPMODE \
+ (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
+@@ -510,9 +525,18 @@ ieee80211_vap_setup(struct ieee80211com
+
+ vap->iv_monitor_crc_errors = 0;
+ vap->iv_monitor_phy_errors = 0;
++ TAILQ_INIT(&vap->iv_wdslinks);
+
+- IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr);
+- IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr);
++ if (master && (vap->iv_opmode == IEEE80211_M_WDS)) {
++ vap->iv_master = master;
++ TAILQ_INSERT_TAIL(&master->iv_wdslinks, vap, iv_wdsnext);
++ /* use the same BSSID as the master interface */
++ IEEE80211_ADDR_COPY(vap->iv_myaddr, vap->iv_master->iv_myaddr);
++ IEEE80211_ADDR_COPY(vap->iv_bssid, vap->iv_master->iv_myaddr);
++ } else {
++ IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr);
++ IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr);
++ }
+ /* NB: Defer setting dev_addr so driver can override */
+
+ ieee80211_crypto_vattach(vap);
+@@ -547,7 +571,8 @@ ieee80211_vap_attach(struct ieee80211vap
+ ifmedia_set(&vap->iv_media, imr.ifm_active);
+
+ IEEE80211_LOCK_IRQ(ic);
+- TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
++ if (vap->iv_opmode != IEEE80211_M_WDS)
++ TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
+ IEEE80211_UNLOCK_IRQ(ic);
+
+ IEEE80211_ADDR_COPY(dev->dev_addr, vap->iv_myaddr);
+@@ -579,10 +604,27 @@ ieee80211_vap_detach(struct ieee80211vap
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *dev = vap->iv_dev;
++ struct ieee80211vap *avp;
++
++ /* Drop all WDS links that belong to this vap */
++ while ((avp = TAILQ_FIRST(&vap->iv_wdslinks)) != NULL) {
++ if (avp->iv_state != IEEE80211_S_INIT)
++ ieee80211_stop(avp->iv_dev);
++ ic->ic_vap_delete(avp);
++ }
+
+ IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
+ IEEE80211_LOCK_IRQ(ic);
+- TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
++ if (vap->iv_wdsnode) {
++ vap->iv_wdsnode->ni_subif = NULL;
++ ieee80211_unref_node(&vap->iv_wdsnode);
++ }
++ if ((vap->iv_opmode == IEEE80211_M_WDS) &&
++ (vap->iv_master != NULL))
++ TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
++ else
++ TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
++
+ if (TAILQ_EMPTY(&ic->ic_vaps)) /* reset to supported mode */
+ ic->ic_opmode = IEEE80211_M_STA;
+ IEEE80211_UNLOCK_IRQ(ic);
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -474,7 +474,7 @@ struct ieee80211req {
+ #define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */
+ #define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */
+ #define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */
+-#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */
++#define IEEE80211_IOC_SETMAC 55 /* set interface wds mac addr */
+ #define IEEE80211_IOC_FF 56 /* ATH fast frames (on, off) */
+ #define IEEE80211_IOC_TURBOP 57 /* ATH turbo' (on, off) */
+ #define IEEE80211_IOC_APPIEBUF 58 /* IE in the management frame */
+@@ -552,8 +552,8 @@ struct ieee80211req_scan_result {
+ #define IEEE80211_IOCTL_HALMAP (SIOCIWFIRSTPRIV+21)
+ #define IEEE80211_IOCTL_ADDMAC (SIOCIWFIRSTPRIV+22)
+ #define IEEE80211_IOCTL_DELMAC (SIOCIWFIRSTPRIV+24)
+-#define IEEE80211_IOCTL_WDSADDMAC (SIOCIWFIRSTPRIV+26)
+-#define IEEE80211_IOCTL_WDSDELMAC (SIOCIWFIRSTPRIV+28)
++#define IEEE80211_IOCTL_WDSADDMAC (SIOCIWFIRSTPRIV+25)
++#define IEEE80211_IOCTL_WDSSETMAC (SIOCIWFIRSTPRIV+26)
+ #define IEEE80211_IOCTL_KICKMAC (SIOCIWFIRSTPRIV+30)
+ #define IEEE80211_IOCTL_SETSCANLIST (SIOCIWFIRSTPRIV+31)
+
+@@ -649,6 +649,7 @@ enum {
+ IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */
+ IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */
+ IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */
++ IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -81,6 +81,12 @@ set_quality(struct iw_quality *iq, u_int
+ #endif
+ }
+
++#ifndef container_of
++#define container_of(ptr, type, member) ({ \
++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++ (type *)( (char *)__mptr - offsetof(type,member) );})
++#endif
++
+ /*
+ * Task deferral
+ *
+@@ -113,6 +119,29 @@ typedef void *IEEE80211_TQUEUE_ARG;
+
+ #define IEEE80211_RESCHEDULE schedule
+
++#include <linux/sched.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
++#include <linux/tqueue.h>
++#define work_struct tq_struct
++#define schedule_work(t) schedule_task((t))
++#define flush_scheduled_work() flush_scheduled_tasks()
++#define IEEE80211_INIT_WORK(t, f) do { \
++ memset((t), 0, sizeof(struct tq_struct)); \
++ (t)->routine = (void (*)(void*)) (f); \
++ (t)->data=(void *) (t); \
++} while (0)
++#else
++#include <linux/workqueue.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
++#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
++#else
++#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
++#endif
++
++#endif /* KERNEL_VERSION < 2.5.41 */
++
++
+ /* Locking */
+ /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP)
+ * because spinlocks do not exist in this configuration. Instead IRQs
+@@ -167,6 +196,14 @@ typedef spinlock_t ieee80211com_lock_t;
+ IEEE80211_VAPS_LOCK_ASSERT(_ic); \
+ spin_unlock_bh(&(_ic)->ic_vapslock); \
+ } while (0)
++#define IEEE80211_VAPS_LOCK_IRQ(_ic) do { \
++ unsigned long __ilockflags; \
++ IEEE80211_VAPS_LOCK_CHECK(_ic); \
++ spin_lock_irqsave(&(_ic)->ic_vapslock, __ilockflags);
++#define IEEE80211_VAPS_UNLOCK_IRQ(_ic) \
++ IEEE80211_VAPS_LOCK_ASSERT(_ic); \
++ spin_unlock_irqrestore(&(_ic)->ic_vapslock, __ilockflags); \
++} while (0)
+
+ #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
+ #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \
+@@ -650,5 +687,5 @@ struct ifreq;
+ int ieee80211_ioctl_create_vap(struct ieee80211com *, struct ifreq *,
+ struct net_device *);
+ struct ieee80211vap *ieee80211_create_vap(struct ieee80211com *, char *,
+- struct net_device *, int, int);
++ struct net_device *, int, int, struct ieee80211vap *);
+ #endif /* _NET80211_IEEE80211_LINUX_H_ */
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -187,6 +187,12 @@ struct ieee80211vap {
+ struct ieee80211_proc_entry *iv_proc_entries;
+ struct vlan_group *iv_vlgrp; /* vlan group state */
+
++ /* list of wds links */
++ TAILQ_HEAD(, ieee80211vap) iv_wdslinks;
++ TAILQ_ENTRY(ieee80211vap) iv_wdsnext;
++ struct ieee80211vap *iv_master;
++ struct ieee80211_node *iv_wdsnode;
++
+ TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */
+ struct ieee80211com *iv_ic; /* back ptr to common state */
+ u_int32_t iv_debug; /* debug msg flags */
+@@ -316,6 +322,7 @@ struct ieee80211com {
+ u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ struct timer_list ic_inact; /* mgmt/inactivity timer */
+
++ unsigned int ic_subifs;
+ u_int32_t ic_flags; /* state flags */
+ u_int32_t ic_flags_ext; /* extension of state flags */
+ u_int32_t ic_caps; /* capabilities */
+@@ -447,7 +454,7 @@ struct ieee80211com {
+ atomic_t ic_node_counter;
+ /* Virtual AP create/delete */
+ struct ieee80211vap *(*ic_vap_create)(struct ieee80211com *,
+- const char *, int, int, struct net_device *);
++ const char *, int, int, struct net_device *, struct ieee80211vap *);
+ void (*ic_vap_delete)(struct ieee80211vap *);
+
+ /* Send/recv 802.11 management frame */
+@@ -619,6 +626,7 @@ MALLOC_DECLARE(M_80211_VAP);
+ #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */
+ #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */
+ #define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */
++#define IEEE80211_FEXT_WDSSEP 0x00004000 /* move wds clients into separate interfaces */
+
+ #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
+ #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
+@@ -703,7 +711,7 @@ MALLOC_DECLARE(M_80211_VAP);
+ int ieee80211_ifattach(struct ieee80211com *);
+ void ieee80211_ifdetach(struct ieee80211com *);
+ int ieee80211_vap_setup(struct ieee80211com *, struct net_device *,
+- const char *, int, int);
++ const char *, int, int, struct ieee80211vap *);
+ int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t);
+ void ieee80211_vap_detach(struct ieee80211vap *);
+ void ieee80211_mark_dfs(struct ieee80211com *, struct ieee80211_channel *);
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2190,7 +2190,7 @@ ieee80211_setupxr(struct ieee80211vap *v
+ ieee80211_scan_flush(ic); /* NB: could optimize */
+
+ if (!(xrvap = ic->ic_vap_create(ic, name, IEEE80211_M_HOSTAP,
+- IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev)))
++ IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev, NULL)))
+ return;
+
+ /* We use iv_xrvap to link to the parent VAP as well */
+@@ -2867,6 +2867,14 @@ ieee80211_ioctl_setparam(struct net_devi
+ else
+ vap->iv_minrateindex = 0;
+ break;
++ case IEEE80211_PARAM_WDS_SEP:
++ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
++ retv = -EINVAL;
++ else if (value)
++ vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP;
++ else
++ vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
++ break;
+ #ifdef ATH_REVERSE_ENGINEERING
+ case IEEE80211_PARAM_DUMPREGS:
+ ieee80211_dump_registers(dev, info, w, extra);
+@@ -3223,6 +3231,9 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_MINRATE:
+ param[0] = vap->iv_minrateindex;
+ break;
++ case IEEE80211_PARAM_WDS_SEP:
++ param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -3801,74 +3812,54 @@ ieee80211_ioctl_setmlme(struct net_devic
+ return 0;
+ }
+
++#define WDSNAME ".wds%d"
+ static int
+-ieee80211_ioctl_wdsmac(struct net_device *dev, struct iw_request_info *info,
++ieee80211_ioctl_wdsaddmac(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+ struct ieee80211vap *vap = dev->priv;
+ struct sockaddr *sa = (struct sockaddr *)extra;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct ieee80211vap *avp;
++ char *name;
+
+- if (!IEEE80211_ADDR_NULL(vap->wds_mac)) {
+- printk("%s: Failed to add WDS MAC: " MAC_FMT "\n", dev->name,
+- MAC_ADDR(sa->sa_data));
+- printk("%s: Device already has WDS mac address attached,"
+- " remove first\n", dev->name);
+- return -1;
+- }
+-
+- memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN);
++ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
++ return -EINVAL;
+
+- printk("%s: Added WDS MAC: " MAC_FMT "\n", dev->name,
+- MAC_ADDR(vap->wds_mac));
++ name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSNAME) + 1, GFP_KERNEL);
++ if (!name)
++ return -ENOMEM;
+
+- if (IS_UP(vap->iv_dev)) {
+- /* Force us back to scan state to force us to go back through RUN
+- * state and create/pin the WDS peer node into memory. */
+- return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+- }
++ strcpy(name, vap->iv_dev->name);
++ strcat(name, WDSNAME);
++ avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
++ kfree(name);
++ if (!avp)
++ return -ENOMEM;
+
++ memcpy(avp->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN);
+ return 0;
+ }
++#undef WDSNAME
+
+ static int
+-ieee80211_ioctl_wdsdelmac(struct net_device *dev, struct iw_request_info *info,
++ieee80211_ioctl_wdssetmac(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+ struct ieee80211vap *vap = dev->priv;
+ struct sockaddr *sa = (struct sockaddr *)extra;
+- struct ieee80211com *ic = vap->iv_ic;
+- struct ieee80211_node *wds_ni;
+
+- /* WDS Mac address filed already? */
+- if (IEEE80211_ADDR_NULL(vap->wds_mac))
+- return 0;
++ if (vap->iv_opmode != IEEE80211_M_WDS)
++ return -EINVAL;
+
+- /* Compare suplied MAC address with WDS MAC of this interface
+- * remove when mac address is known
+- */
+- if (memcmp(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN) == 0) {
+- if (IS_UP(vap->iv_dev)) {
+- wds_ni = ieee80211_find_txnode(vap, vap->wds_mac);
+- if (wds_ni != NULL) {
+- /* Release reference created by find node */
+- ieee80211_unref_node(&wds_ni);
+- /* Release reference created by transition to RUN state,
+- * [pinning peer node into the table] */
+- ieee80211_unref_node(&wds_ni);
+- }
+- }
+- memset(vap->wds_mac, 0x00, IEEE80211_ADDR_LEN);
+- if (IS_UP(vap->iv_dev)) {
+- /* This leaves a dead WDS node, until started again */
+- return ic->ic_reset(ic->ic_dev);
+- }
+- return 0;
++ memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN);
++ if (IS_UP(vap->iv_dev)) {
++ /* Force us back to scan state to force us to go back through RUN
++ * state and create/pin the WDS peer node into memory. */
++ return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ }
+
+- printk("%s: WDS MAC address " MAC_FMT " is not known by this interface\n",
+- dev->name, MAC_ADDR(sa->sa_data));
+-
+- return -1;
++ return 0;
+ }
+
+ /*
+@@ -4470,6 +4461,8 @@ get_sta_space(void *arg, struct ieee8021
+ struct ieee80211vap *vap = ni->ni_vap;
+ size_t ielen;
+
++ if (req->vap->iv_wdsnode && ni->ni_subif)
++ vap = ni->ni_subif;
+ if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap */
+ return;
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+@@ -4489,6 +4482,8 @@ get_sta_info(void *arg, struct ieee80211
+ size_t ielen, len;
+ u_int8_t *cp;
+
++ if (req->vap->iv_wdsnode && ni->ni_subif)
++ vap = ni->ni_subif;
+ if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap (or) xrvap */
+ return;
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+@@ -5391,8 +5386,8 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac"},
+ { IEEE80211_IOCTL_WDSADDMAC,
+ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_add" },
+- { IEEE80211_IOCTL_WDSDELMAC,
+- IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_del" },
++ { IEEE80211_IOCTL_WDSSETMAC,
++ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_set" },
+ { IEEE80211_IOCTL_SETCHANLIST,
+ IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED, 0,"setchanlist" },
+ { IEEE80211_IOCTL_GETCHANLIST,
+@@ -5790,6 +5785,10 @@ static const struct iw_priv_args ieee802
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
+ { IEEE80211_IOCTL_SETSCANLIST,
+ IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
++ { IEEE80211_PARAM_WDS_SEP,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
++ { IEEE80211_PARAM_WDS_SEP,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
+@@ -5884,8 +5883,8 @@ static const iw_handler ieee80211_priv_h
+ #endif
+ set_priv(IEEE80211_IOCTL_ADDMAC, ieee80211_ioctl_addmac),
+ set_priv(IEEE80211_IOCTL_DELMAC, ieee80211_ioctl_delmac),
+- set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsmac),
+- set_priv(IEEE80211_IOCTL_WDSDELMAC, ieee80211_ioctl_wdsdelmac),
++ set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsaddmac),
++ set_priv(IEEE80211_IOCTL_WDSSETMAC, ieee80211_ioctl_wdssetmac),
+ set_priv(IEEE80211_IOCTL_KICKMAC, ieee80211_ioctl_kickmac),
+ set_priv(IEEE80211_IOCTL_SETSCANLIST, ieee80211_ioctl_setscanlist),
+ #ifdef ATH_REVERSE_ENGINEERING
+@@ -5913,6 +5912,8 @@ static int
+ ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+ struct ieee80211vap *vap = dev->priv;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct ieee80211_node *ni;
+
+ switch (cmd) {
+ case SIOCG80211STATS:
+@@ -5921,8 +5922,20 @@ ieee80211_ioctl(struct net_device *dev,
+ case SIOC80211IFDESTROY:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
++ /* drop all node subifs */
++ TAILQ_FOREACH(ni, &ic->ic_sta.nt_node, ni_list) {
++ struct ieee80211vap *avp = ni->ni_subif;
++
++ if (ni->ni_vap != vap)
++ continue;
++ if (!avp)
++ continue;
++ ni->ni_subif = NULL;
++ ieee80211_stop(avp->iv_dev);
++ ic->ic_vap_delete(avp);
++ }
+ ieee80211_stop(vap->iv_dev); /* force state before cleanup */
+- vap->iv_ic->ic_vap_delete(vap);
++ ic->ic_vap_delete(vap);
+ return 0;
+ case IEEE80211_IOCTL_GETKEY:
+ return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr);
+@@ -5956,7 +5969,7 @@ ieee80211_ioctl_create_vap(struct ieee80
+
+ strncpy(name, cp.icp_name, sizeof(name));
+
+- vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags);
++ vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags, NULL);
+ if (vap == NULL)
+ return -EIO;
+
+@@ -5973,9 +5986,9 @@ EXPORT_SYMBOL(ieee80211_ioctl_create_vap
+ */
+ struct ieee80211vap*
+ ieee80211_create_vap(struct ieee80211com *ic, char *name,
+- struct net_device *mdev, int opmode, int opflags)
++ struct net_device *mdev, int opmode, int opflags, struct ieee80211vap *master)
+ {
+- return ic->ic_vap_create(ic, name, opmode, opflags, mdev);
++ return ic->ic_vap_create(ic, name, opmode, opflags, mdev, master);
+ }
+ EXPORT_SYMBOL(ieee80211_create_vap);
+
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -199,8 +199,10 @@ ieee80211_input(struct ieee80211vap * va
+ {
+ #define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211_node * ni = ni_or_null;
+- struct ieee80211com *ic = vap->iv_ic;
+- struct net_device *dev = vap->iv_dev;
++ struct ieee80211com *ic;
++ struct net_device *dev;
++ struct ieee80211_node *ni_wds = NULL;
++ struct net_device_stats *stats;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+@@ -212,6 +214,19 @@ ieee80211_input(struct ieee80211vap * va
+ u_int8_t *bssid;
+ u_int16_t rxseq;
+
++ type = -1; /* undefined */
++
++ if (!vap)
++ goto out;
++
++ ic = vap->iv_ic;
++ if (!ic)
++ goto out;
++
++ dev = vap->iv_dev;
++ if (!dev)
++ goto out;
++
+ /* initialize ni as in the previous API */
+ if (ni_or_null == NULL) {
+ /* This function does not 'own' vap->iv_bss, so we cannot
+@@ -227,7 +242,6 @@ ieee80211_input(struct ieee80211vap * va
+
+ /* XXX adjust device in sk_buff? */
+
+- type = -1; /* undefined */
+ /*
+ * In monitor mode, send everything directly to bpf.
+ * Also do not process frames w/o i_addr2 any further.
+@@ -434,7 +448,7 @@ ieee80211_input(struct ieee80211vap * va
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+- hdrspace = ieee80211_hdrspace(ic, wh);
++ hdrspace = ieee80211_hdrsize(wh);
+ if (skb->len < hdrspace) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "data", "too short: len %u, expecting %u",
+@@ -444,16 +458,24 @@ ieee80211_input(struct ieee80211vap * va
+ }
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_STA:
+- if ((dir != IEEE80211_FC1_DIR_FROMDS) &&
+- (!((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
+- (dir == IEEE80211_FC1_DIR_DSTODS)))) {
++ switch(dir) {
++ case IEEE80211_FC1_DIR_FROMDS:
++ break;
++ case IEEE80211_FC1_DIR_DSTODS:
++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS)
++ break;
++ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "data", "invalid dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+
+- if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
++ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
++ /* ignore 3-addr mcast if we're WDS STA */
++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS)
++ goto out;
++
+ /* Discard multicast if IFF_MULTICAST not set */
+ if ((0 != memcmp(wh->i_addr3, dev->broadcast, ETH_ALEN)) &&
+ (0 == (dev->flags & IFF_MULTICAST))) {
+@@ -481,24 +503,10 @@ ieee80211_input(struct ieee80211vap * va
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+- /*
+- * if it is brodcasted by me on behalf of
+- * a station behind me, drop it.
+- */
+- if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) {
+- struct ieee80211_node_table *nt;
+- struct ieee80211_node *ni_wds;
+- nt = &ic->ic_sta;
+- ni_wds = ieee80211_find_wds_node(nt, wh->i_addr3);
+- if (ni_wds) {
+- ieee80211_unref_node(&ni_wds);
+- IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+- wh, NULL, "%s",
+- "multicast echo originated from node behind me");
+- vap->iv_stats.is_rx_mcastecho++;
+- goto out;
+- }
+- }
++ } else {
++ /* Same BSSID, but not meant for us to receive */
++ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr))
++ goto out;
+ }
+ break;
+ case IEEE80211_M_IBSS:
+@@ -540,16 +548,28 @@ ieee80211_input(struct ieee80211vap * va
+ vap->iv_stats.is_rx_notassoc++;
+ goto err;
+ }
++
+ /*
+ * If we're a 4 address packet, make sure we have an entry in
+ * the node table for the packet source address (addr4).
+ * If not, add one.
+ */
++ /* check for wds link first */
++ if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni->ni_subif) {
++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP) {
++ ieee80211_wds_addif(ni);
++ /* we must drop frames here until the interface has
++ * been fully separated, otherwise a bridge might get
++ * confused */
++ goto err;
++ }
++ }
++
+ /* XXX: Useless node mgmt API; make better */
+- if (dir == IEEE80211_FC1_DIR_DSTODS) {
+- struct ieee80211_node_table *nt;
++ if ((dir == IEEE80211_FC1_DIR_DSTODS) && !vap->iv_wdsnode &&
++ !ni_wds && !ni->ni_subif) {
++ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_frame_addr4 *wh4;
+- struct ieee80211_node *ni_wds;
+
+ if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+@@ -557,7 +577,6 @@ ieee80211_input(struct ieee80211vap * va
+ goto err;
+ }
+ wh4 = (struct ieee80211_frame_addr4 *)skb->data;
+- nt = &ic->ic_sta;
+ ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4);
+ /* Last call increments ref count if !NULL */
+ if ((ni_wds != NULL) && (ni_wds != ni)) {
+@@ -608,6 +627,11 @@ ieee80211_input(struct ieee80211vap * va
+ goto out;
+ }
+
++ /* check if there is any data left */
++ hdrspace = ieee80211_hdrspace(ic, wh);
++ if (skb->len < hdrspace)
++ goto out;
++
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+@@ -680,8 +704,12 @@ ieee80211_input(struct ieee80211vap * va
+ if (! accept_data_frame(vap, ni, key, skb, eh))
+ goto out;
+
+- vap->iv_devstats.rx_packets++;
+- vap->iv_devstats.rx_bytes += skb->len;
++ if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
++ stats = &ni->ni_subif->iv_devstats;
++ else
++ stats = &vap->iv_devstats;
++ stats->rx_packets++;
++ stats->rx_bytes += skb->len;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
+ ic->ic_lastdata = jiffies;
+@@ -1114,6 +1142,18 @@ ieee80211_deliver_data(struct ieee80211_
+ dev = vap->iv_xrvap->iv_dev;
+ #endif
+
++ /* if the node has a wds subif, move data frames there,
++ * but keep EAP traffic on the master */
++ if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) {
++ if (ni->ni_vap == ni->ni_subif) {
++ ieee80211_dev_kfree_skb(&skb);
++ return;
++ } else {
++ vap = ni->ni_subif;
++ dev = vap->iv_dev;
++ }
++ }
++
+ /* perform as a bridge within the vap */
+ /* XXX intra-vap bridging only */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+@@ -1139,7 +1179,16 @@ ieee80211_deliver_data(struct ieee80211_
+ if (ni1 != NULL) {
+ if (ni1->ni_vap == vap &&
+ ieee80211_node_is_authorized(ni1) &&
++ !ni1->ni_subif &&
+ ni1 != vap->iv_bss) {
++
++ /* tried to bridge to a subif, drop the packet */
++ if (ni->ni_subif) {
++ ieee80211_unref_node(&ni1);
++ ieee80211_dev_kfree_skb(&skb);
++ return;
++ }
++
+ skb1 = skb;
+ skb = NULL;
+ }
+@@ -3084,8 +3133,7 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ (vap->iv_opmode == IEEE80211_M_STA && ni->ni_associd) ||
+ (vap->iv_opmode == IEEE80211_M_IBSS) ||
+ ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
+- (vap->iv_opmode == IEEE80211_M_HOSTAP) &&
+- (ic->ic_protmode != IEEE80211_PROT_NONE)))) {
++ (vap->iv_opmode == IEEE80211_M_HOSTAP)))) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+@@ -3471,13 +3519,56 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ ieee80211_add_scan(vap, &scan, wh, subtype, rssi, rtsf);
+- return;
+ }
+- if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
+- (scan.capinfo & IEEE80211_CAPINFO_IBSS)) {
++ /* NB: Behavior of WDS-Link and Ad-Hoc is very similar here:
++ * When we receive a beacon that belongs to the AP that we're
++ * connected to, use it to refresh the local node info.
++ * If no node is found, go through the vap's wds link table
++ * and try to find the sub-vap that is interested in this address
++ */
++ if (((vap->iv_opmode == IEEE80211_M_IBSS) &&
++ (scan.capinfo & IEEE80211_CAPINFO_IBSS)) ||
++ (((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
++ (vap->iv_opmode == IEEE80211_M_WDS)) &&
++ (scan.capinfo & IEEE80211_CAPINFO_ESS))) {
++ struct ieee80211vap *avp = NULL;
++ int found = 0;
++
++ IEEE80211_LOCK_IRQ(vap->iv_ic);
++ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
++ if (!memcmp(avp->wds_mac, wh->i_addr2, IEEE80211_ADDR_LEN)) {
++ if (avp->iv_state != IEEE80211_S_RUN)
++ continue;
++ if (!avp->iv_wdsnode)
++ continue;
++ found = 1;
++ break;
++ }
++ }
++ if (found)
++ ni = ni_or_null = avp->iv_wdsnode;
++ } else if ((vap->iv_opmode == IEEE80211_M_WDS) && vap->iv_wdsnode) {
++ found = 1;
++ ni = ni_or_null = vap->iv_wdsnode;
++ }
++ IEEE80211_UNLOCK_IRQ(vap->iv_ic);
++
++ if (!found)
++ break;
++
+ if (ni_or_null == NULL) {
+- /* Create a new entry in the neighbor table. */
+- ni = ieee80211_add_neighbor(vap, wh, &scan);
++ if (avp) {
++ IEEE80211_LOCK_IRQ(ic);
++ ni = ieee80211_add_neighbor(avp, wh, &scan);
++ /* force assoc */
++ ni->ni_associd |= 0xc000;
++ avp->iv_wdsnode = ieee80211_ref_node(ni);
++ IEEE80211_UNLOCK_IRQ(ic);
++ } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
++ /* Create a new entry in the neighbor table. */
++ ni = ieee80211_add_neighbor(vap, wh, &scan);
++ }
+ } else {
+ /*
+ * Copy data from beacon to neighbor table.
+@@ -3490,6 +3581,7 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
++ ni->ni_inact = ni->ni_inact_reload;
+ ni->ni_intval =
+ IEEE80211_BINTVAL_SANITISE(scan.bintval);
+ ni->ni_capinfo = scan.capinfo;
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -47,6 +47,7 @@
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/random.h>
++#include <linux/rtnetlink.h>
+
+ #include "if_media.h"
+
+@@ -236,7 +237,11 @@ void
+ ieee80211_node_vdetach(struct ieee80211vap *vap)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
++ struct ieee80211_node *ni;
+
++ ni = vap->iv_wdsnode;
++ if (ni)
++ ni->ni_subif = NULL;
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ if (vap->iv_bss != NULL) {
+ ieee80211_unref_node(&vap->iv_bss);
+@@ -309,7 +314,7 @@ ieee80211_create_ibss(struct ieee80211va
+ /* Check to see if we already have a node for this mac
+ * NB: we gain a node reference here
+ */
+- ni = ieee80211_find_node(&ic->ic_sta, vap->iv_myaddr);
++ ni = ieee80211_find_txnode(vap, vap->iv_myaddr);
+ if (ni == NULL) {
+ ni = ieee80211_alloc_node_table(vap, vap->iv_myaddr);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
+@@ -831,12 +836,18 @@ node_table_leave_locked(struct ieee80211
+ LIST_REMOVE(ni, ni_hash);
+ }
+ ni->ni_table = NULL;
++ if (ni->ni_vap->iv_wdsnode == ni) {
++#ifdef IEEE80211_DEBUG_REFCNT
++ ieee80211_unref_node_debug(&ni->ni_vap->iv_wdsnode, func, line);
++#else
++ ieee80211_unref_node(&ni->ni_vap->iv_wdsnode);
++#endif
++ }
+ #ifdef IEEE80211_DEBUG_REFCNT
+ ieee80211_unref_node_debug(&ni, func, line);
+ #else
+ ieee80211_unref_node(&ni);
+ #endif
+-
+ }
+
+ /* This is overridden by ath_node_alloc in ath/if_ath.c, and so
+@@ -1134,6 +1145,65 @@ ieee80211_alloc_node(struct ieee80211vap
+ return ni;
+ }
+
++#define WDSIFNAME ".sta%d"
++static void
++ieee80211_wds_do_addif(struct work_struct *work)
++{
++ struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_create);
++ struct ieee80211vap *vap = ni->ni_vap;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct ieee80211vap *avp = NULL;
++ char *name;
++
++ rtnl_lock();
++ /* did we get cancelled by the destroy call? */
++ if (!ni->ni_subif)
++ goto done;
++
++ ni->ni_subif = NULL;
++ name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL);
++ if (!name)
++ goto done;
++
++ strcpy(name, vap->iv_dev->name);
++ strcat(name, WDSIFNAME);
++ avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
++ kfree(name);
++ if (!avp)
++ goto done;
++
++ memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN);
++ avp->iv_wdsnode = ieee80211_ref_node(ni);
++ ni->ni_subif = avp;
++ ic->ic_subifs++;
++
++done:
++ if (avp) {
++ IEEE80211_VAPS_LOCK_IRQ(ic);
++ avp->iv_newstate(vap, IEEE80211_S_RUN, -1);
++ IEEE80211_VAPS_UNLOCK_IRQ(ic);
++ }
++ rtnl_unlock();
++ ieee80211_unref_node(&ni);
++}
++#undef WDSIFNAME
++
++void ieee80211_wds_addif(struct ieee80211_node *ni)
++{
++ /* check if the node is split out already,
++ * or if we're in progress of setting up a new interface already */
++ if (ni->ni_subif)
++ return;
++
++ if (!ni->ni_table)
++ return;
++
++ ieee80211_ref_node(ni);
++ ni->ni_subif = ni->ni_vap;
++ IEEE80211_INIT_WORK(&ni->ni_create, ieee80211_wds_do_addif);
++ schedule_work(&ni->ni_create);
++}
++
+ /* Add wds address to the node table */
+ int
+ #ifdef IEEE80211_DEBUG_REFCNT
+@@ -1553,22 +1623,39 @@ ieee80211_find_rxnode(struct ieee80211co
+ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni;
++ struct ieee80211vap *vap, *avp;
++ const u_int8_t *addr;
++
++ if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
++ addr = wh->i_addr1;
++ else
++ addr = wh->i_addr2;
++
++ if (IEEE80211_IS_MULTICAST(addr))
++ return NULL;
+
+ /* XXX check ic_bss first in station mode */
+ /* XXX 4-address frames? */
+ nt = &ic->ic_sta;
+ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+- if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+-#ifdef IEEE80211_DEBUG_REFCNT
+- ni = ieee80211_find_node_locked_debug(nt, wh->i_addr1, func, line);
+-#else
+- ni = ieee80211_find_node_locked(nt, wh->i_addr1);
+-#endif
+- else
++ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) {
++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
++ if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac))
++ continue;
++
++ if (avp->iv_wdsnode)
++ return ieee80211_ref_node(avp->iv_wdsnode);
++ else
++ return NULL;
++ }
++ }
++ }
++
+ #ifdef IEEE80211_DEBUG_REFCNT
+- ni = ieee80211_find_node_locked_debug(nt, wh->i_addr2, func, line);
++ ni = ieee80211_find_node_locked_debug(nt, addr, func, line);
+ #else
+- ni = ieee80211_find_node_locked(nt, wh->i_addr2);
++ ni = ieee80211_find_node_locked(nt, addr);
+ #endif
+ IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+
+@@ -1596,9 +1683,19 @@ ieee80211_find_txnode_debug(struct ieee8
+ ieee80211_find_txnode(struct ieee80211vap *vap, const u_int8_t *mac)
+ #endif
+ {
++ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni = NULL;
+
++ IEEE80211_LOCK_IRQ(ic);
++ if (vap->iv_opmode == IEEE80211_M_WDS) {
++ if (vap->iv_wdsnode && (vap->iv_state == IEEE80211_S_RUN))
++ return ieee80211_ref_node(vap->iv_wdsnode);
++ else
++ return NULL;
++ }
++ IEEE80211_UNLOCK_IRQ(ic);
++
+ /*
+ * The destination address should be in the node table
+ * unless we are operating in station mode or this is a
+@@ -1669,6 +1766,11 @@ ieee80211_free_node(struct ieee80211_nod
+ {
+ struct ieee80211vap *vap = ni->ni_vap;
+
++ IEEE80211_LOCK_IRQ(ni->ni_ic);
++ if (vap && ni == vap->iv_wdsnode)
++ vap->iv_wdsnode = NULL;
++ IEEE80211_UNLOCK_IRQ(ni->ni_ic);
++
+ atomic_dec(&ni->ni_ic->ic_node_counter);
+ node_print_message(IEEE80211_MSG_NODE|IEEE80211_MSG_NODE_REF,
+ 1 /* show counter */,
+@@ -1781,22 +1883,6 @@ restart:
+ jiffies > ni->ni_rxfragstamp + HZ) {
+ ieee80211_dev_kfree_skb(&ni->ni_rxfrag);
+ }
+- /*
+- * Special case ourself; we may be idle for extended periods
+- * of time and regardless reclaiming our state is wrong.
+- * Special case a WDS link: it may be dead or idle, but it is
+- * never ok to reclaim it, as this will block transmissions
+- * and nobody will recreate the node when the WDS peer is
+- * available again. */
+- if ((ni == ni->ni_vap->iv_bss) ||
+- (ni->ni_vap->iv_opmode == IEEE80211_M_WDS &&
+- !memcmp(ni->ni_macaddr, ni->ni_vap->wds_mac, ETH_ALEN)))
+- {
+- /* NB: don't permit it to go negative */
+- if (ni->ni_inact > 0)
+- ni->ni_inact--;
+- continue;
+- }
+ ni->ni_inact--;
+ if (ni->ni_associd != 0 || isadhoc) {
+ struct ieee80211vap *vap = ni->ni_vap;
+@@ -2263,6 +2349,35 @@ ieee80211_node_leave_11g(struct ieee8021
+ }
+ }
+
++static void
++ieee80211_subif_destroy(struct work_struct *work)
++{
++ struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy);
++ struct ieee80211vap *vap;
++ struct ieee80211com *ic;
++
++ /* wait for full initialization before we start the teardown
++ * otherwise we could leak interfaces */
++ while (ni->ni_subif == ni->ni_vap)
++ schedule();
++
++ rtnl_lock();
++ vap = ni->ni_subif;
++
++ if (!vap)
++ goto done;
++
++ ic = vap->iv_ic;
++ ni->ni_subif = NULL;
++ ieee80211_stop(vap->iv_dev);
++ ic->ic_vap_delete(vap);
++ ic->ic_subifs--;
++
++done:
++ ieee80211_unref_node(&ni);
++ rtnl_unlock();
++}
++
+ /*
+ * Handle bookkeeping for a station/neighbor leaving
+ * the bss when operating in ap or adhoc modes.
+@@ -2279,6 +2394,12 @@ ieee80211_node_leave(struct ieee80211_no
+ ni, "station with aid %d leaves (refcnt %u)",
+ IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt));
+
++ if (ni->ni_subif) {
++ ieee80211_ref_node(ni);
++ IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy);
++ schedule_work(&ni->ni_destroy);
++ }
++
+ /* From this point onwards we can no longer find the node,
+ * so no more references are generated
+ */
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -246,15 +246,16 @@ ieee80211_hardstart(struct sk_buff *skb,
+ * things like power save.
+ */
+ eh = (struct ether_header *)skb->data;
+- if (vap->iv_opmode == IEEE80211_M_WDS)
+- ni = ieee80211_find_txnode(vap, vap->wds_mac);
+- else
+- ni = ieee80211_find_txnode(vap, eh->ether_dhost);
++ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
+ goto bad;
+ }
+
++ if (ni->ni_subif && (vap != ni->ni_subif) &&
++ ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
++ goto bad;
++
+ /* calculate priority so drivers can find the TX queue */
+ if (ieee80211_classify(ni, skb)) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
+@@ -334,20 +335,33 @@ void ieee80211_parent_queue_xmit(struct
+ * constructing a frame as it sets i_fc[1]; other bits can
+ * then be or'd in.
+ */
+-static void
++static struct ieee80211_frame *
+ ieee80211_send_setup(struct ieee80211vap *vap,
+ struct ieee80211_node *ni,
+- struct ieee80211_frame *wh,
++ struct sk_buff *skb,
+ int type,
+ const u_int8_t sa[IEEE80211_ADDR_LEN],
+ const u_int8_t da[IEEE80211_ADDR_LEN],
+ const u_int8_t bssid[IEEE80211_ADDR_LEN])
+ {
+ #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh)
++ struct ieee80211_frame *wh;
++ int len = sizeof(struct ieee80211_frame);
++ int opmode = vap->iv_opmode;
++
++ if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
++ if ((opmode == IEEE80211_M_STA) &&
++ (vap->iv_flags_ext & IEEE80211_FEXT_WDS))
++ opmode = IEEE80211_M_WDS;
+
++ if (opmode == IEEE80211_M_WDS)
++ len = sizeof(struct ieee80211_frame_addr4);
++ }
++
++ wh = (struct ieee80211_frame *)skb_push(skb, len);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
+ if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
+- switch (vap->iv_opmode) {
++ switch (opmode) {
+ case IEEE80211_M_STA:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
+@@ -389,6 +403,8 @@ ieee80211_send_setup(struct ieee80211vap
+ *(__le16 *)&wh->i_seq[0] =
+ htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseqs[0]++;
++
++ return wh;
+ #undef WH4
+ }
+
+@@ -410,9 +426,7 @@ ieee80211_mgmt_output(struct ieee80211_n
+
+ SKB_CB(skb)->ni = ni;
+
+- wh = (struct ieee80211_frame *)
+- skb_push(skb, sizeof(struct ieee80211_frame));
+- ieee80211_send_setup(vap, ni, wh,
++ wh = ieee80211_send_setup(vap, ni, skb,
+ IEEE80211_FC0_TYPE_MGT | type,
+ vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid);
+ /* XXX power management */
+@@ -458,6 +472,9 @@ ieee80211_send_nulldata(struct ieee80211
+ struct ieee80211_frame *wh;
+ u_int8_t *frm;
+
++ if (ni->ni_subif)
++ vap = ni->ni_subif;
++
+ skb = ieee80211_getmgtframe(&frm, 0);
+ if (skb == NULL) {
+ /* XXX debug msg */
+@@ -466,9 +483,7 @@ ieee80211_send_nulldata(struct ieee80211
+ return -ENOMEM;
+ }
+
+- wh = (struct ieee80211_frame *)
+- skb_push(skb, sizeof(struct ieee80211_frame));
+- ieee80211_send_setup(vap, ni, wh,
++ wh = ieee80211_send_setup(vap, ni, skb,
+ IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
+ vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid);
+ /* NB: power management bit is never sent by an AP */
+@@ -506,6 +521,7 @@ ieee80211_send_qosnulldata(struct ieee80
+ struct sk_buff *skb;
+ struct ieee80211_qosframe *qwh;
+ u_int8_t *frm;
++ u_int8_t *i_qos;
+ int tid;
+
+ skb = ieee80211_getmgtframe(&frm, 2);
+@@ -517,11 +533,12 @@ ieee80211_send_qosnulldata(struct ieee80
+ SKB_CB(skb)->ni = ieee80211_ref_node(ni);
+
+ skb->priority = ac;
+- qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe));
+
+- qwh = (struct ieee80211_qosframe *)skb->data;
++ /* grab a pointer to QoS control and also compensate for the header length
++ * difference between QoS and non-QoS frame */
++ i_qos = skb_push(skb, sizeof(struct ieee80211_qosframe) - sizeof(struct ieee80211_frame));
+
+- ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh,
++ qwh = (struct ieee80211_qosframe *) ieee80211_send_setup(vap, ni, skb,
+ IEEE80211_FC0_TYPE_DATA,
+ vap->iv_myaddr, /* SA */
+ ni->ni_macaddr, /* DA */
+@@ -535,10 +552,10 @@ ieee80211_send_qosnulldata(struct ieee80
+
+ /* map from access class/queue to 11e header priority value */
+ tid = WME_AC_TO_TID(ac);
+- qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
++ i_qos[0] = tid & IEEE80211_QOS_TID;
+ if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
+ qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY;
+- qwh->i_qos[1] = 0;
++ i_qos[1] = 0;
+
+ IEEE80211_NODE_STAT(ni, tx_data);
+
+@@ -780,6 +797,8 @@ ieee80211_encap(struct ieee80211_node *n
+ hdrsize = sizeof(struct ieee80211_frame);
+
+ SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE));
++ if (ni->ni_subif)
++ vap = ni->ni_subif;
+
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_IBSS:
+@@ -788,7 +807,7 @@ ieee80211_encap(struct ieee80211_node *n
+ break;
+ case IEEE80211_M_WDS:
+ use4addr = 1;
+- ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr);
++ ismulticast = 0;
+ break;
+ case IEEE80211_M_HOSTAP:
+ if (!IEEE80211_IS_MULTICAST(eh.ether_dhost) &&
+@@ -799,20 +818,9 @@ ieee80211_encap(struct ieee80211_node *n
+ ismulticast = IEEE80211_IS_MULTICAST(eh.ether_dhost);
+ break;
+ case IEEE80211_M_STA:
+- if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
+- !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) {
++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) {
+ use4addr = 1;
+- ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr);
+- /* Add a WDS entry to the station VAP */
+- if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
+- struct ieee80211_node_table *nt = &ic->ic_sta;
+- struct ieee80211_node *ni_wds
+- = ieee80211_find_wds_node(nt, eh.ether_shost);
+- if (ni_wds)
+- ieee80211_unref_node(&ni_wds);
+- else
+- ieee80211_add_wds_addr(nt, ni, eh.ether_shost, 0);
+- }
++ ismulticast = 0;
+ } else
+ ismulticast = IEEE80211_IS_MULTICAST(vap->iv_bssid);
+ break;
+@@ -973,7 +981,7 @@ ieee80211_encap(struct ieee80211_node *n
+ break;
+ case IEEE80211_M_WDS:
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+- IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
++ IEEE80211_ADDR_COPY(wh->i_addr1, vap->wds_mac);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
+@@ -1683,9 +1691,7 @@ ieee80211_send_probereq(struct ieee80211
+
+ SKB_CB(skb)->ni = ieee80211_ref_node(ni);
+
+- wh = (struct ieee80211_frame *)
+- skb_push(skb, sizeof(struct ieee80211_frame));
+- ieee80211_send_setup(vap, ni, wh,
++ wh = ieee80211_send_setup(vap, ni, skb,
+ IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
+ sa, da, bssid);
+ /* XXX power management? */
+--- a/tools/athkey.c
++++ b/tools/athkey.c
+@@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo
+ IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
+- IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
++ IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_READREG),
+ IOCTL_ERR(IEEE80211_IOCTL_WRITEREG),
+ };
+--- a/tools/athchans.c
++++ b/tools/athchans.c
+@@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo
+ IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
+- IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
++ IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_READREG),
+ IOCTL_ERR(IEEE80211_IOCTL_WRITEREG),
+ };
+--- a/tools/wlanconfig.c
++++ b/tools/wlanconfig.c
+@@ -968,7 +968,7 @@ do80211priv(struct iwreq *iwr, const cha
+ IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
+- IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
++ IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC),
+ IOCTL_ERR(IEEE80211_IOCTL_READREG),
+ IOCTL_ERR(IEEE80211_IOCTL_WRITEREG),
+ };
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -979,6 +979,12 @@ ieee80211_init(struct net_device *dev, i
+ "start running (state=%d)\n", vap->iv_state);
+
+
++ if (vap->iv_master && vap->iv_master->iv_state == IEEE80211_S_INIT) {
++ int ret = ieee80211_init(vap->iv_master->iv_dev, forcescan);
++ if (ret < 0)
++ return ret;
++ }
++
+ if ((dev->flags & IFF_RUNNING) == 0) {
+ if (ic->ic_nopened++ == 0 &&
+ (parent->flags & IFF_RUNNING) == 0)
+@@ -1081,6 +1087,8 @@ ieee80211_init(struct net_device *dev, i
+ int
+ ieee80211_open(struct net_device *dev)
+ {
++ struct ieee80211vap *vap = dev->priv;
++
+ return ieee80211_init(dev, 0);
+ }
+
+@@ -1090,7 +1098,7 @@ ieee80211_open(struct net_device *dev)
+ void
+ ieee80211_start_running(struct ieee80211com *ic)
+ {
+- struct ieee80211vap *vap;
++ struct ieee80211vap *vap, *avp;
+ struct net_device *dev;
+
+ /* XXX locking */
+@@ -1099,6 +1107,16 @@ ieee80211_start_running(struct ieee80211
+ /* NB: avoid recursion */
+ if ((dev->flags & IFF_UP) && !(dev->flags & IFF_RUNNING))
+ ieee80211_open(dev);
++
++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
++ if (avp->iv_wdsnode && avp->iv_wdsnode->ni_subif == avp)
++ continue;
++
++ dev = avp->iv_dev;
++ /* NB: avoid recursion */
++ if ((dev->flags & IFF_UP) && !(dev->flags & IFF_RUNNING))
++ ieee80211_open(dev);
++ }
+ }
+ }
+ EXPORT_SYMBOL(ieee80211_start_running);
+@@ -1116,11 +1134,43 @@ ieee80211_stop(struct net_device *dev)
+ struct ieee80211vap *vap = dev->priv;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *parent = ic->ic_dev;
++ struct ieee80211_node *tni, *ni;
++ struct ieee80211vap *avp;
+
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", "stop running");
+
++ if (vap->iv_wdsnode && !vap->iv_wdsnode->ni_subif)
++ ieee80211_unref_node(&vap->iv_wdsnode);
++
++ /* stop wds interfaces */
++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_next) {
++ if (avp->iv_state != IEEE80211_S_INIT)
++ ieee80211_stop(avp->iv_dev);
++ }
++
++ /* get rid of all wds nodes while we're still locked */
++ do {
++ ni = NULL;
++
++ IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
++ TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
++ if (tni->ni_vap != vap)
++ continue;
++ if (!tni->ni_subif)
++ continue;
++ ni = tni;
++ break;
++ }
++ IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
++
++ if (!ni)
++ break;
++
++ ieee80211_node_leave(ni);
++ } while (1);
++
+ ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
+ if (dev->flags & IFF_RUNNING) {
+ dev->flags &= ~IFF_RUNNING; /* mark us stopped */
+@@ -1148,7 +1198,7 @@ EXPORT_SYMBOL(ieee80211_stop);
+ void
+ ieee80211_stop_running(struct ieee80211com *ic)
+ {
+- struct ieee80211vap *vap;
++ struct ieee80211vap *vap, *avp;
+ struct net_device *dev;
+
+ /* XXX locking */
+@@ -1156,6 +1206,12 @@ ieee80211_stop_running(struct ieee80211c
+ dev = vap->iv_dev;
+ if (dev->flags & IFF_RUNNING) /* NB: avoid recursion */
+ ieee80211_stop(dev);
++
++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
++ dev = avp->iv_dev;
++ if (dev->flags & IFF_RUNNING) /* NB: avoid recursion */
++ ieee80211_stop(dev);
++ }
+ }
+ }
+ EXPORT_SYMBOL(ieee80211_stop_running);
+@@ -1342,9 +1398,9 @@ ieee80211_new_state(struct ieee80211vap
+ struct ieee80211com *ic = vap->iv_ic;
+ int rc;
+
+- IEEE80211_VAPS_LOCK_BH(ic);
++ IEEE80211_VAPS_LOCK_IRQ(ic);
+ rc = vap->iv_newstate(vap, nstate, arg);
+- IEEE80211_VAPS_UNLOCK_BH(ic);
++ IEEE80211_VAPS_UNLOCK_IRQ(ic);
+ return rc;
+ }
+
+@@ -1557,57 +1613,12 @@ __ieee80211_newstate(struct ieee80211vap
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ if (vap->iv_opmode == IEEE80211_M_MONITOR ||
+- vap->iv_opmode == IEEE80211_M_WDS ||
+ vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ */
+ ieee80211_create_ibss(vap, ic->ic_curchan);
+-
+- /* In WDS mode, allocate and initialize peer node. */
+- if (vap->iv_opmode == IEEE80211_M_WDS) {
+- /* XXX: This is horribly non-atomic. */
+- struct ieee80211_node *wds_ni =
+- ieee80211_find_node(&ic->ic_sta,
+- vap->wds_mac);
+-
+- if (wds_ni == NULL) {
+- wds_ni = ieee80211_alloc_node_table(
+- vap,
+- vap->wds_mac);
+- if (wds_ni != NULL) {
+- ieee80211_add_wds_addr(
+- &ic->ic_sta,
+- wds_ni,
+- vap->wds_mac,
+- 1);
+- ieee80211_ref_node(wds_ni); /* pin in memory */
+- }
+- else
+- IEEE80211_DPRINTF(
+- vap,
+- IEEE80211_MSG_NODE,
+- "%s: Unable to "
+- "allocate node for "
+- "WDS: " MAC_FMT "\n",
+- __func__,
+- MAC_ADDR(
+- vap->wds_mac)
+- );
+- }
+-
+- if (wds_ni != NULL) {
+- ieee80211_node_authorize(wds_ni);
+- wds_ni->ni_chan =
+- vap->iv_bss->ni_chan;
+- wds_ni->ni_capinfo =
+- ni->ni_capinfo;
+- wds_ni->ni_associd = 1;
+- wds_ni->ni_ath_flags =
+- vap->iv_ath_cap;
+- }
+- }
+ break;
+ }
+ /* fall thru... */
+@@ -1675,6 +1686,7 @@ __ieee80211_newstate(struct ieee80211vap
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
++
+ #ifdef ATH_SUPERG_XR
+ /*
+ * fire a timer to bring up XR vap if configured.
+@@ -1808,6 +1820,11 @@ ieee80211_newstate(struct ieee80211vap *
+ ieee80211_state_name[dstate]);
+
+ ieee80211_update_link_status(vap, nstate, ostate);
++
++ if ((nstate != IEEE80211_S_RUN) && vap->iv_wdsnode &&
++ !vap->iv_wdsnode->ni_subif)
++ ieee80211_unref_node(&vap->iv_wdsnode);
++
+ switch (nstate) {
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+@@ -1930,8 +1947,15 @@ ieee80211_newstate(struct ieee80211vap *
+ if (ostate == IEEE80211_S_SCAN ||
+ ostate == IEEE80211_S_AUTH ||
+ ostate == IEEE80211_S_ASSOC) {
++
+ /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_RUN */
+ __ieee80211_newstate(vap, nstate, arg);
++
++ /* if we're in wds, let the ap know that we're doing this */
++ if ((vap->iv_opmode == IEEE80211_M_STA) &&
++ (vap->iv_flags_ext & IEEE80211_FEXT_WDS))
++ ieee80211_send_nulldata(ieee80211_ref_node(vap->iv_bss));
++
+ /* Then bring up all other vaps pending on the scan */
+ dstate = get_dominant_state(ic);
+ if (dstate == IEEE80211_S_RUN) {
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -79,28 +79,6 @@ typedef void *TQUEUE_ARG;
+ #define tasklet_enable(t) do { (void) t; local_bh_enable(); } while (0)
+ #endif /* !DECLARE_TASKLET */
+
+-#include <linux/sched.h>
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
+-#include <linux/tqueue.h>
+-#define work_struct tq_struct
+-#define schedule_work(t) schedule_task((t))
+-#define flush_scheduled_work() flush_scheduled_tasks()
+-#define ATH_INIT_WORK(t, f) do { \
+- memset((t), 0, sizeof(struct tq_struct)); \
+- (t)->routine = (void (*)(void*)) (f); \
+- (t)->data=(void *) (t); \
+-} while (0)
+-#else
+-#include <linux/workqueue.h>
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+-#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
+-#else
+-#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
+-#endif
+-
+-#endif /* KERNEL_VERSION < 2.5.41 */
+-
+ /*
+ * Guess how the interrupt handler should work.
+ */
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -145,7 +145,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_
+ struct sk_buff *skb;
+ u_int len;
+
+- len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
++ len = roundup(sizeof(struct ieee80211_frame_addr4) + pktlen, 4);
+ #ifdef IEEE80211_DEBUG_REFCNT
+ skb = ieee80211_dev_alloc_skb_debug(len + align - 1, func, line);
+ #else
+@@ -161,7 +161,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_
+ SKB_CB(skb)->flags = 0;
+ SKB_CB(skb)->next = NULL;
+
+- skb_reserve(skb, sizeof(struct ieee80211_frame));
++ skb_reserve(skb, sizeof(struct ieee80211_frame_addr4));
+ *frm = skb_put(skb, pktlen);
+ }
+ return skb;
+--- a/net80211/ieee80211_node.h
++++ b/net80211/ieee80211_node.h
+@@ -92,11 +92,13 @@ struct ath_softc;
+ * the ieee80211com structure.
+ */
+ struct ieee80211_node {
+- struct ieee80211vap *ni_vap;
++ struct ieee80211vap *ni_vap, *ni_subif;
+ struct ieee80211com *ni_ic;
+ struct ieee80211_node_table *ni_table;
+ TAILQ_ENTRY(ieee80211_node) ni_list;
+ LIST_ENTRY(ieee80211_node) ni_hash;
++ struct work_struct ni_create; /* task for creating a subif */
++ struct work_struct ni_destroy; /* task for destroying a subif */
+ atomic_t ni_refcnt;
+ u_int ni_scangen; /* gen# for timeout scan */
+ u_int8_t ni_authmode; /* authentication algorithm */
+@@ -430,5 +432,6 @@ void ieee80211_node_join(struct ieee8021
+ void ieee80211_node_leave(struct ieee80211_node *);
+ u_int8_t ieee80211_getrssi(struct ieee80211com *);
+ int32_t ieee80211_get_node_count(struct ieee80211com *);
++void ieee80211_wds_addif(struct ieee80211_node *ni);
+ #endif /* _NET80211_IEEE80211_NODE_H_ */
+
diff --git a/package/madwifi/patches/372-queue_vif.patch b/package/madwifi/patches/372-queue_vif.patch
new file mode 100644
index 000000000..8b13abc33
--- /dev/null
+++ b/package/madwifi/patches/372-queue_vif.patch
@@ -0,0 +1,39 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1198,6 +1198,7 @@ ieee80211_deliver_data(struct ieee80211_
+ }
+ if (skb1 != NULL) {
+ struct ieee80211_node *ni_tmp;
++ int ret;
+ skb1->dev = dev;
+ skb_reset_mac_header(skb1);
+ skb_set_network_header(skb1, sizeof(struct ether_header));
+@@ -1205,7 +1206,12 @@ ieee80211_deliver_data(struct ieee80211_
+ skb1->protocol = __constant_htons(ETH_P_802_2);
+ /* XXX insert vlan tag before queue it? */
+ ni_tmp = SKB_CB(skb1)->ni; /* remember node so we can free it */
+- if (dev_queue_xmit(skb1) == NET_XMIT_DROP) {
++ ret = dev->hard_start_xmit(skb1, dev);
++
++ if (ret == NETDEV_TX_BUSY)
++ ieee80211_dev_kfree_skb(&skb1);
++
++ else if (ret != NETDEV_TX_OK) {
+ /* If queue dropped the packet because device was
+ * too busy */
+ vap->iv_devstats.tx_dropped++;
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -324,9 +324,10 @@ void ieee80211_parent_queue_xmit(struct
+ /* Dispatch the packet to the parent device */
+ skb->dev = vap->iv_ic->ic_dev;
+
+- if (dev_queue_xmit(skb) == NET_XMIT_DROP)
++ if (netif_queue_stopped(skb->dev))
++ ieee80211_dev_kfree_skb(&skb);
++ else if (dev_queue_xmit(skb) == NET_XMIT_DROP)
+ vap->iv_devstats.tx_dropped++;
+-
+ }
+
+ /*
diff --git a/package/madwifi/patches/373-sanity_check.patch b/package/madwifi/patches/373-sanity_check.patch
new file mode 100644
index 000000000..06c6acf08
--- /dev/null
+++ b/package/madwifi/patches/373-sanity_check.patch
@@ -0,0 +1,12 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -250,6 +250,9 @@ ieee80211_input(struct ieee80211vap * va
+ if (vap->iv_opmode == IEEE80211_M_MONITOR)
+ goto out;
+
++ if (!skb->data)
++ goto out;
++
+ if (skb->len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
diff --git a/package/madwifi/patches/374-nbtt_fix.patch b/package/madwifi/patches/374-nbtt_fix.patch
new file mode 100644
index 000000000..38a1c59d5
--- /dev/null
+++ b/package/madwifi/patches/374-nbtt_fix.patch
@@ -0,0 +1,22 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -5486,6 +5486,9 @@ ath_beacon_config(struct ath_softc *sc,
+ ath_beacon_dturbo_config(vap, intval &
+ ~(HAL_BEACON_RESET_TSF | HAL_BEACON_ENA));
+ #endif
++ if ((nexttbtt & HAL_BEACON_PERIOD) - (ath_hal_gettsf32(ah) >> 10)
++ <= ath_hal_sw_beacon_response_time)
++ nexttbtt += intval;
+ sc->sc_nexttbtt = nexttbtt;
+ ath_hal_beaconinit(ah, nexttbtt, intval);
+ if (intval & HAL_BEACON_RESET_TSF) {
+--- a/ath_hal/ah_os.c
++++ b/ath_hal/ah_os.c
+@@ -71,6 +71,7 @@ static int ath_hal_debug = 99;
+ int ath_hal_dma_beacon_response_time = 2; /* in TUs */
+ int ath_hal_sw_beacon_response_time = 10; /* in TUs */
+ int ath_hal_additional_swba_backoff = 0; /* in TUs */
++EXPORT_SYMBOL(ath_hal_sw_beacon_response_time);
+
+ struct ath_hal *
+ _ath_hal_attach(u_int16_t devid, HAL_SOFTC sc,
diff --git a/package/madwifi/patches/375-atim_tsf_update.patch b/package/madwifi/patches/375-atim_tsf_update.patch
new file mode 100644
index 000000000..d1313aa56
--- /dev/null
+++ b/package/madwifi/patches/375-atim_tsf_update.patch
@@ -0,0 +1,141 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -161,6 +161,7 @@ static void ath_beacon_send(struct ath_s
+ static void ath_beacon_return(struct ath_softc *, struct ath_buf *);
+ static void ath_beacon_free(struct ath_softc *);
+ static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *);
++static void ath_hw_beacon_stop(struct ath_softc *sc);
+ static int ath_desc_alloc(struct ath_softc *);
+ static void ath_desc_free(struct ath_softc *);
+ static void ath_desc_swap(struct ath_desc *);
+@@ -2793,6 +2794,72 @@ ath_set_ack_bitrate(struct ath_softc *sc
+ return 1;
+ }
+
++static void
++ath_hw_beacon_stop(struct ath_softc *sc)
++{
++ HAL_BEACON_TIMERS btimers;
++
++ btimers.bt_intval = 0;
++ btimers.bt_nexttbtt = 0;
++ btimers.bt_nextdba = 0xffffffff;
++ btimers.bt_nextswba = 0xffffffff;
++ btimers.bt_nextatim = 0;
++
++ ath_hal_setbeacontimers(sc->sc_ah, &btimers);
++}
++
++/* Fix up the ATIM window after TSF resync */
++static int
++ath_hw_check_atim(struct ath_softc *sc, int window, int intval)
++{
++#define AR5K_TIMER0_5210 0x802c /* Next beacon time register */
++#define AR5K_TIMER0_5211 0x8028
++#define AR5K_TIMER3_5210 0x8038 /* End of ATIM window time register */
++#define AR5K_TIMER3_5211 0x8034
++ struct ath_hal *ah = sc->sc_ah;
++ int dev = sc->sc_ah->ah_macType;
++ unsigned int nbtt, atim;
++ int is_5210 = 0;
++
++ /*
++ * check if the ATIM window is still correct:
++ * 1.) usually ATIM should be NBTT + window
++ * 2.) nbtt already updated
++ * 3.) nbtt already updated and has wrapped around
++ * 4.) atim has wrapped around
++ */
++ switch(dev) {
++ case 5210:
++ nbtt = OS_REG_READ(ah, AR5K_TIMER0_5210);
++ atim = OS_REG_READ(ah, AR5K_TIMER3_5210);
++ is_5210 = 1;
++ break;
++ case 5211:
++ case 5212:
++ nbtt = OS_REG_READ(ah, AR5K_TIMER0_5211);
++ atim = OS_REG_READ(ah, AR5K_TIMER3_5211);
++ break;
++ /* NB: 5416+ doesn't do ATIM in hw */
++ case 5416:
++ default:
++ return 0;
++ }
++
++ if ((atim - nbtt != window) && /* 1.) */
++ (nbtt - atim != intval - window) && /* 2.) */
++ ((nbtt | 0x10000) - atim != intval - window) && /* 3.) */
++ ((atim | 0x10000) - nbtt != window)) { /* 4.) */
++ if (is_5210)
++ OS_REG_WRITE(ah, AR5K_TIMER3_5210, nbtt + window );
++ else
++ OS_REG_WRITE(ah, AR5K_TIMER3_5211, nbtt + window );
++ return atim - nbtt;
++ }
++
++ return 0;
++}
++
++
+ /*
+ * Reset the hardware w/o losing operational state. This is
+ * basically a more efficient way of doing ath_stop, ath_init,
+@@ -5294,6 +5361,7 @@ ath_beacon_config(struct ath_softc *sc,
+ u_int64_t tsf, hw_tsf;
+ u_int32_t tsftu, hw_tsftu;
+ u_int32_t intval, nexttbtt = 0;
++ unsigned long flags;
+ int reset_tsf = 0;
+
+ if (vap == NULL)
+@@ -5301,6 +5369,9 @@ ath_beacon_config(struct ath_softc *sc,
+
+ ni = vap->iv_bss;
+
++ /* TSF calculation is timing critical - we don't want to be interrupted here */
++ local_irq_save(flags);
++
+ hw_tsf = ath_hal_gettsf64(ah);
+ tsf = le64_to_cpu(ni->ni_tstamp.tsf);
+ hw_tsftu = hw_tsf >> 10;
+@@ -5490,15 +5561,27 @@ ath_beacon_config(struct ath_softc *sc,
+ <= ath_hal_sw_beacon_response_time)
+ nexttbtt += intval;
+ sc->sc_nexttbtt = nexttbtt;
++
++ /* stop beacons before reconfiguring the timers to avoid race
++ * conditions. ath_hal_beaconinit will start them again */
++ ath_hw_beacon_stop(sc);
++
+ ath_hal_beaconinit(ah, nexttbtt, intval);
+ if (intval & HAL_BEACON_RESET_TSF) {
+ sc->sc_last_tsf = 0;
+ }
+ sc->sc_bmisscount = 0;
+ ath_hal_intrset(ah, sc->sc_imask);
++
++ if ((sc->sc_opmode == HAL_M_IBSS) && ath_hw_check_atim(sc, 1, intval & HAL_BEACON_PERIOD)) {
++ DPRINTF(sc, ATH_DEBUG_BEACON,
++ "fixed atim window after beacon init\n");
++ }
+ }
+
+ ath_beacon_config_debug:
++ local_irq_restore(flags);
++
+ /* We print all debug messages here, in order to preserve the
+ * time critical aspect of this function */
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+@@ -6401,6 +6484,11 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "Updated beacon timers\n");
+ }
++ if ((sc->sc_opmode == HAL_M_IBSS) &&
++ IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid) &&
++ ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval)) {
++ DPRINTF(sc, ATH_DEBUG_ANY, "Fixed ATIM window after beacon recv\n");
++ }
+ /* NB: Fall Through */
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ if (vap->iv_opmode == IEEE80211_M_IBSS &&
diff --git a/package/madwifi/patches/377-disable_vlan_code.patch b/package/madwifi/patches/377-disable_vlan_code.patch
new file mode 100644
index 000000000..8a132484e
--- /dev/null
+++ b/package/madwifi/patches/377-disable_vlan_code.patch
@@ -0,0 +1,25 @@
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -657,22 +657,7 @@ int ieee80211_proc_vcreate(struct ieee80
+ char *);
+ void ieee80211_proc_cleanup(struct ieee80211vap *);
+
+-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+-#define IEEE80211_VLAN_TAG_USED 1
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)
+-#define vlan_hwaccel_receive_skb(skb, grp, tag) vlan_hwaccel_rx(skb, grp, tag)
+-#endif
+-
+-#ifndef VLAN_GROUP_ARRAY_PART_LEN
+-#define vlan_group_set_device(group, vid, dev) do { \
+- group->vlan_devices[vid] = dev; \
+-} while (0);
+-#endif
+-
+-#else
+ #define IEEE80211_VLAN_TAG_USED 0
+-#endif
+ void ieee80211_vlan_vattach(struct ieee80211vap *);
+ void ieee80211_vlan_vdetach(struct ieee80211vap *);
+
diff --git a/package/madwifi/patches/378-adhoc_crash_fix.patch b/package/madwifi/patches/378-adhoc_crash_fix.patch
new file mode 100644
index 000000000..5b9168367
--- /dev/null
+++ b/package/madwifi/patches/378-adhoc_crash_fix.patch
@@ -0,0 +1,14 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3529,6 +3529,11 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ ieee80211_add_scan(vap, &scan, wh, subtype, rssi, rtsf);
+ }
++
++ /* stop processing if the bss channel is not set up yet */
++ if (!ic->ic_bsschan || ic->ic_bsschan == IEEE80211_CHAN_ANYC)
++ break;
++
+ /* NB: Behavior of WDS-Link and Ad-Hoc is very similar here:
+ * When we receive a beacon that belongs to the AP that we're
+ * connected to, use it to refresh the local node info.
diff --git a/package/madwifi/patches/379-invalid_rate_fix.patch b/package/madwifi/patches/379-invalid_rate_fix.patch
new file mode 100644
index 000000000..c96c04daf
--- /dev/null
+++ b/package/madwifi/patches/379-invalid_rate_fix.patch
@@ -0,0 +1,405 @@
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -111,27 +111,13 @@
+ #include <net80211/ieee80211_var.h>
+ #include <net80211/ieee80211_rate.h>
+
++#include "if_ath_debug.h"
+ #include "if_athvar.h"
+ #include "if_ath_hal.h"
+ #include "ah_desc.h"
+
+ #include "minstrel.h"
+
+-#ifdef AR_DEBUG
+-#define MINSTREL_DEBUG
+-#endif
+-#ifdef MINSTREL_DEBUG
+-enum {
+- ATH_DEBUG_RATE = 0x00000010 /* rate control */
+-};
+-#define DPRINTF(sc, _fmt, ...) do { \
+- if (sc->sc_debug & ATH_DEBUG_RATE) \
+- printk(_fmt, __VA_ARGS__); \
+-} while (0)
+-#else
+-#define DPRINTF(sc, _fmt, ...)
+-#endif
+-
+ #define ONE_SECOND (1000 * 1000) /* 1 second, or 1000 milliseconds; eternity, in other words */
+
+ #include "release.h"
+@@ -471,11 +457,11 @@ ath_rate_tx_complete(struct ath_softc *s
+ final_rate = sc->sc_hwmap[ts->ts_rate & ~HAL_TXSTAT_ALTRATE].ieeerate;
+ final_ndx = rate_to_ndx(sn, final_rate);
+ if (final_ndx >= sn->num_rates) {
+- DPRINTF(sc, "%s: final ndx too high\n", __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: final ndx too high\n", __func__);
+ final_ndx = 0;
+ }
+ if (final_ndx < 0) {
+- DPRINTF(sc, "%s: final ndx too low\n", __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: final ndx too low\n", __func__);
+ final_ndx = 0;
+ }
+
+@@ -485,7 +471,7 @@ ath_rate_tx_complete(struct ath_softc *s
+ tries = ts->ts_longretry + 1;
+
+ if (sn->num_rates <= 0) {
+- DPRINTF(sc, "%s: " MAC_FMT " %s no rates yet\n", dev_info,
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: " MAC_FMT " %s no rates yet\n", dev_info,
+ MAC_ADDR(an->an_node.ni_macaddr), __func__);
+ return;
+ }
+@@ -551,7 +537,7 @@ ath_rate_tx_complete(struct ath_softc *s
+ static void
+ ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
+ {
+- DPRINTF(sc, "%s: " MAC_FMT " %s\n", dev_info,
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: " MAC_FMT " %s\n", dev_info,
+ MAC_ADDR(an->an_node.ni_macaddr), __func__);
+ if (isnew)
+ ath_rate_ctl_reset(sc, &an->an_node);
+@@ -601,7 +587,7 @@ ath_fill_sample_table(struct minstrel_no
+ p = rates + sprintf(rates, "rates :: %d ", column_index);
+ for (i = 0; i < num_sample_rates; i++)
+ p += sprintf(p, "%2u ", sn->rs_sampleTable[i][column_index]);
+- DPRINTF(sc, "%s\n", rates);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s\n", rates);
+ };
+ #endif
+ }
+@@ -628,7 +614,7 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ sn->is_sampling = 0;
+
+ if (rt == NULL) {
+- DPRINTF(sc, "no rates yet! mode %u\n", sc->sc_curmode);
++ DPRINTF(sc, ATH_DEBUG_RATE, "no rates yet! mode %u\n", sc->sc_curmode);
+ return;
+ }
+ sn->static_rate_ndx = -1;
+@@ -658,7 +644,7 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
+ }
+ if (sn->rates[x].rix == 0xff) {
+- DPRINTF(sc, "%s: %s ignore bogus rix at %d\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s ignore bogus rix at %d\n",
+ dev_info, __func__, x);
+ continue;
+ }
+@@ -673,7 +659,7 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ ni->ni_txrate = 0;
+
+ if (sn->num_rates <= 0) {
+- DPRINTF(sc, "%s: %s " MAC_FMT " no rates (fixed %d) \n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s " MAC_FMT " no rates (fixed %d) \n",
+ dev_info, __func__, MAC_ADDR(ni->ni_macaddr),
+ vap->iv_fixed_rate);
+ /* There are no rates yet; we're done */
+@@ -689,23 +675,23 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ * the node. We know the rate is there because the
+ * rate set is checked when the station associates. */
+ /* NB: the rate set is assumed sorted */
+- for (; (srate >= 0) && (ni->ni_rates.rs_rates[srate] & IEEE80211_RATE_VAL) != vap->iv_fixed_rate; srate--);
+-
+- KASSERT(srate >= 0,
+- ("fixed rate %d not in rate set", vap->iv_fixed_rate));
++ for (; (srate > 0) && (ni->ni_rates.rs_rates[srate] & IEEE80211_RATE_VAL) != vap->iv_fixed_rate; srate--);
+
+ sn->static_rate_ndx = srate;
+ ni->ni_txrate = srate;
+- DPRINTF(sc, "%s: %s " MAC_FMT " fixed rate %d%sMbps\n",
+- dev_info, __func__, MAC_ADDR(ni->ni_macaddr),
+- sn->rates[srate].rate / 2,
+- (sn->rates[srate].rate % 2) ? ".5 " : " ");
++ if ((ni->ni_rates.rs_rates[srate] & IEEE80211_RATE_VAL) != vap->iv_fixed_rate)
++ EPRINTF(sc, "Invalid static rate, falling back to basic rate\n");
++ else
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s " MAC_FMT " fixed rate %d%sMbps\n",
++ dev_info, __func__, MAC_ADDR(ni->ni_macaddr),
++ sn->rates[srate].rate / 2,
++ (sn->rates[srate].rate % 2) ? ".5 " : " ");
+ return;
+ }
+
+ for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+ if (sn->rates[x].rix == 0xff) {
+- DPRINTF(sc, "%s: %s ignore bogus rix at %d\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s ignore bogus rix at %d\n",
+ dev_info, __func__, x);
+ continue;
+ }
+@@ -735,9 +721,9 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ }
+
+ #if 0
+- DPRINTF(sc, "%s: Retry table for this node\n", __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: Retry table for this node\n", __func__);
+ for (x = 0; x < ni->ni_rates.rs_nrates; x++)
+- DPRINTF(sc, "%2d %2d %6d \n", x, sn->retry_count[x], sn->perfect_tx_time[x]);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%2d %2d %6d \n", x, sn->retry_count[x], sn->perfect_tx_time[x]);
+ #endif
+
+ /* Set the initial rate */
+@@ -781,10 +767,10 @@ ath_timer_function(unsigned long data)
+ unsigned int interval = ath_timer_interval;
+
+ if (dev == NULL)
+- DPRINTF(sc, "%s: 'dev' is null in this timer \n", __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: 'dev' is null in this timer \n", __func__);
+
+ if (sc == NULL)
+- DPRINTF(sc, "%s: 'sc' is null in this timer\n", __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: 'sc' is null in this timer\n", __func__);
+
+ ic = &sc->sc_ic;
+
+@@ -808,7 +794,7 @@ ath_timer_function(unsigned long data)
+
+ timer = &(ssc->timer);
+ if (timer == NULL)
+- DPRINTF(sc, "%s: timer is null - leave it\n", __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: timer is null - leave it\n", __func__);
+
+ timer->expires = jiffies + ((HZ * interval) / 1000);
+ add_timer(timer);
+@@ -904,7 +890,7 @@ static struct ath_ratectrl *
+ ath_rate_attach(struct ath_softc *sc)
+ {
+ struct minstrel_softc *osc;
+- DPRINTF(sc, "%s: %s\n", dev_info, __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s\n", dev_info, __func__);
+
+ _MOD_INC_USE(THIS_MODULE, return NULL);
+ osc = kmalloc(sizeof(struct minstrel_softc), GFP_ATOMIC);
+@@ -963,7 +949,7 @@ ath_proc_read_nodes(struct ieee80211vap
+ p += sprintf(p, "out of room for node " MAC_FMT "\n\n", MAC_ADDR(ni->ni_macaddr));
+ break;
+ }
+- DPRINTF(sc, "%s: out of memeory to write tall of the nodes\n", __func__);
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: out of memeory to write tall of the nodes\n", __func__);
+ break;
+ }
+ an = ATH_NODE(ni);
+--- a/ath_rate/amrr/amrr.c
++++ b/ath_rate/amrr/amrr.c
+@@ -64,24 +64,13 @@
+ #include <net80211/ieee80211_var.h>
+ #include <net80211/ieee80211_rate.h>
+
++#include "if_ath_debug.h"
+ #include "if_athvar.h"
+ #include "if_ath_hal.h"
+ #include "ah_desc.h"
+
+ #include "amrr.h"
+
+-#ifdef AR_DEBUG
+-#define AMRR_DEBUG
+-#endif
+-#ifdef AMRR_DEBUG
+-#define DPRINTF(sc, _fmt, ...) do { \
+- if (sc->sc_debug & 0x10) \
+- printk(_fmt, __VA_ARGS__); \
+-} while (0)
+-#else
+-#define DPRINTF(sc, _fmt, ...)
+-#endif
+-
+ static int ath_rateinterval = 1000; /* rate ctl interval (ms) */
+ static int ath_rate_max_success_threshold = 10;
+ static int ath_rate_min_success_threshold = 1;
+@@ -197,7 +186,7 @@ ath_rate_update(struct ath_softc *sc, st
+
+ KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+- DPRINTF(sc, "%s: set xmit rate for " MAC_FMT " to %dM\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: set xmit rate for " MAC_FMT " to %dM\n",
+ __func__, MAC_ADDR(ni->ni_macaddr),
+ ni->ni_rates.rs_nrates > 0 ?
+ (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
+@@ -297,9 +286,9 @@ ath_rate_ctl_start(struct ath_softc *sc,
+ * rate set is checked when the station associates.
+ */
+ srate = ni->ni_rates.rs_nrates - 1;
+- for (; srate >= 0 && RATE(srate) != vap->iv_fixed_rate; srate--);
+- KASSERT(srate >= 0,
+- ("fixed rate %d not in rate set", vap->iv_fixed_rate));
++ for (; srate > 0 && RATE(srate) != vap->iv_fixed_rate; srate--);
++ if (RATE(srate) != vap->iv_fixed_rate)
++ EPRINTF(sc, "Invalid static rate, falling back to basic rate\n");
+ }
+ ath_rate_update(sc, ni, srate);
+ #undef RATE
+@@ -377,7 +366,7 @@ ath_rate_ctl(void *arg, struct ieee80211
+
+ old_rate = ni->ni_txrate;
+
+- DPRINTF (sc, "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d\n",
+ amn->amn_tx_try0_cnt,
+ amn->amn_tx_try1_cnt,
+ amn->amn_tx_try2_cnt,
+@@ -390,7 +379,7 @@ ath_rate_ctl(void *arg, struct ieee80211
+ amn->amn_recovery = 1;
+ amn->amn_success = 0;
+ ni->ni_txrate++;
+- DPRINTF(sc, "increase rate to %d\n", ni->ni_txrate);
++ DPRINTF(sc, ATH_DEBUG_RATE, "increase rate to %d\n", ni->ni_txrate);
+ } else
+ amn->amn_recovery = 0;
+ } else if (is_failure(amn)) {
+@@ -401,12 +390,12 @@ ath_rate_ctl(void *arg, struct ieee80211
+ amn->amn_success_threshold *= 2;
+ amn->amn_success_threshold = min(amn->amn_success_threshold,
+ (u_int)ath_rate_max_success_threshold);
+- DPRINTF(sc, "decrease rate recovery thr: %d\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "decrease rate recovery thr: %d\n",
+ amn->amn_success_threshold);
+ } else {
+ /* simple failure. */
+ amn->amn_success_threshold = ath_rate_min_success_threshold;
+- DPRINTF(sc, "decrease rate normal thr: %d\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "decrease rate normal thr: %d\n",
+ amn->amn_success_threshold);
+ }
+ amn->amn_recovery = 0;
+--- a/ath_rate/onoe/onoe.c
++++ b/ath_rate/onoe/onoe.c
+@@ -60,27 +60,13 @@
+ #include <net80211/ieee80211_var.h>
+ #include <net80211/ieee80211_rate.h>
+
++#include "if_ath_debug.h"
+ #include "if_athvar.h"
+ #include "if_ath_hal.h"
+ #include "ah_desc.h"
+
+ #include "onoe.h"
+
+-#ifdef AR_DEBUG
+-#define ONOE_DEBUG
+-#endif
+-#ifdef ONOE_DEBUG
+-enum {
+- ATH_DEBUG_RATE = 0x00000010, /* rate control */
+-};
+-#define DPRINTF(sc, _fmt, ...) do { \
+- if (sc->sc_debug & ATH_DEBUG_RATE) \
+- printk(_fmt, __VA_ARGS__); \
+-} while (0)
+-#else
+-#define DPRINTF(sc, _fmt, ...)
+-#endif
+-
+ /*
+ * Default parameters for the rate control algorithm. These are
+ * all tunable with sysctls. The rate controller runs periodically
+@@ -186,7 +172,7 @@ ath_rate_update(struct ath_softc *sc, st
+
+ KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+- DPRINTF(sc, "%s: set xmit rate for " MAC_FMT " to %dM\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: set xmit rate for " MAC_FMT " to %dM\n",
+ __func__, MAC_ADDR(ni->ni_macaddr),
+ ni->ni_rates.rs_nrates > 0 ?
+ (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
+@@ -283,9 +269,9 @@ ath_rate_ctl_start(struct ath_softc *sc,
+ */
+ /* NB: the rate set is assumed sorted */
+ srate = ni->ni_rates.rs_nrates - 1;
+- for (; srate >= 0 && RATE(srate) != vap->iv_fixed_rate; srate--);
+- KASSERT(srate >= 0,
+- ("fixed rate %d not in rate set", vap->iv_fixed_rate));
++ for (; srate > 0 && RATE(srate) != vap->iv_fixed_rate; srate--);
++ if (RATE(srate) != vap->iv_fixed_rate)
++ EPRINTF(sc, "Invalid static rate, falling back to basic rate\n");
+ }
+ ath_rate_update(sc, ni, srate);
+ #undef RATE
+@@ -364,7 +350,7 @@ ath_rate_ctl(void *arg, struct ieee80211
+ on->on_tx_retr < (on->on_tx_ok * ath_rate_raise) / 100)
+ dir = 1;
+
+- DPRINTF(sc, MAC_FMT ": ok %d err %d retr %d upper %d dir %d\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, MAC_FMT ": ok %d err %d retr %d upper %d dir %d\n",
+ MAC_ADDR(ni->ni_macaddr),
+ on->on_tx_ok, on->on_tx_err, on->on_tx_retr,
+ on->on_tx_upper, dir);
+@@ -395,7 +381,7 @@ ath_rate_ctl(void *arg, struct ieee80211
+ }
+
+ if (nrate != ni->ni_txrate) {
+- DPRINTF(sc, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n",
+ __func__,
+ (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL) / 2,
+ (rs->rs_rates[nrate] & IEEE80211_RATE_VAL) / 2,
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -62,30 +62,13 @@
+ #include <net80211/ieee80211_var.h>
+ #include <net80211/ieee80211_rate.h>
+
++#include "if_ath_debug.h"
+ #include "if_athvar.h"
+ #include "if_ath_hal.h"
+ #include "ah_desc.h"
+
+ #include "sample.h"
+
+-#ifdef AR_DEBUG
+-#define SAMPLE_DEBUG
+-#endif
+-#ifdef SAMPLE_DEBUG
+-enum {
+- ATH_DEBUG_RATE = 0x00000010, /* rate control */
+- ATH_DEBUG_ANY = 0xffffffff
+-};
+-#define DPRINTF(sc, m, fmt, ...) do { \
+- if (sc->sc_debug & (m)) \
+- printk(fmt, __VA_ARGS__); \
+-} while (0)
+-#else
+-#define DPRINTF(sc, m, fmt, ...) do { \
+- (void) sc; \
+-} while (0)
+-#endif
+-
+ /*
+ * This file is an implementation of the SampleRate algorithm
+ * in "Bit-rate Selection in Wireless Networks"
+@@ -740,7 +723,7 @@ ath_rate_tx_complete(struct ath_softc *s
+ ndx[3] = rate_to_ndx(sn, rate[3]);
+
+ #if 0
+- DPRINTF(sc, "%s: " MAC_FMT " size %u finaltsidx %u tries %u status %u rate/try %u/%u %u/%u %u/%u %u/%u\n",
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: " MAC_FMT " size %u finaltsidx %u tries %u status %u rate/try %u/%u %u/%u %u/%u %u/%u\n",
+ dev_info, MAC_ADDR(an->an_node.ni_macaddr),
+ bin_to_size(size_to_bin(frame_size)),
+ finalTSIdx,
+@@ -886,15 +869,16 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ if ((ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL) == vap->iv_fixed_rate)
+ srate = x;
+
+- KASSERT(((ni->ni_rates.rs_rates[srate] & IEEE80211_RATE_VAL) == vap->iv_fixed_rate),
+- ("fixed rate %u not in rate set", vap->iv_fixed_rate));
+-
+ sn->static_rate_ndx = srate;
+ ni->ni_txrate = srate;
+- DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s " MAC_FMT " fixed rate %u%sMbps\n",
+- dev_info, __func__, MAC_ADDR(ni->ni_macaddr),
+- sn->rates[srate].rate / 2,
+- (sn->rates[srate].rate % 0x1) ? ".5" : " ");
++
++ if ((ni->ni_rates.rs_rates[srate] & IEEE80211_RATE_VAL) != vap->iv_fixed_rate)
++ EPRINTF(sc, "Invalid static rate, falling back to basic rate\n");
++ else
++ DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s " MAC_FMT " fixed rate %u%sMbps\n",
++ dev_info, __func__, MAC_ADDR(ni->ni_macaddr),
++ sn->rates[srate].rate / 2,
++ (sn->rates[srate].rate % 0x1) ? ".5" : " ");
+ return;
+ }
+
diff --git a/package/madwifi/patches/380-noderef_hack.patch b/package/madwifi/patches/380-noderef_hack.patch
new file mode 100644
index 000000000..7c082d414
--- /dev/null
+++ b/package/madwifi/patches/380-noderef_hack.patch
@@ -0,0 +1,13 @@
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -427,8 +427,8 @@ ieee80211_reset_bss(struct ieee80211vap
+ __func__, ni, MAC_ADDR(vap->iv_myaddr));
+ KASSERT(ni != NULL, ("unable to setup inital BSS node"));
+
+- vap->iv_bss = PASS_NODE(ni);
+- KASSERT((atomic_read(&vap->iv_bss->ni_refcnt) == 2),
++ vap->iv_bss = ieee80211_ref_node(ni);
++ KASSERT((atomic_read(&vap->iv_bss->ni_refcnt) == 3),
+ ("wrong refcount for new node."));
+
+ if (obss != NULL) {
diff --git a/package/madwifi/patches/381-ibss_modes.patch b/package/madwifi/patches/381-ibss_modes.patch
new file mode 100644
index 000000000..38969b053
--- /dev/null
+++ b/package/madwifi/patches/381-ibss_modes.patch
@@ -0,0 +1,23 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1260,7 +1260,10 @@ ath_vap_create(struct ieee80211com *ic,
+ case IEEE80211_M_IBSS:
+ if ((sc->sc_nvaps != 0) && (ic->ic_opmode == IEEE80211_M_STA))
+ return NULL;
+- ic_opmode = opmode;
++ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
++ ic_opmode = ic->ic_opmode;
++ else
++ ic_opmode = opmode;
+ break;
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_MONITOR:
+@@ -1455,7 +1458,7 @@ ath_vap_create(struct ieee80211com *ic,
+ * frames. Other modes carry over directly to the HAL.
+ */
+ if (ic->ic_opmode == IEEE80211_M_AHDEMO)
+- sc->sc_opmode = HAL_M_IBSS;
++ sc->sc_opmode = HAL_M_HOSTAP;
+ else
+ sc->sc_opmode = (HAL_OPMODE) ic->ic_opmode; /* NB: compatible */
+
diff --git a/package/madwifi/patches/382-relax_bintval.patch b/package/madwifi/patches/382-relax_bintval.patch
new file mode 100644
index 000000000..0901949d9
--- /dev/null
+++ b/package/madwifi/patches/382-relax_bintval.patch
@@ -0,0 +1,13 @@
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -61,8 +61,8 @@
+ #define IEEE80211_DTIM_MIN 1 /* min DTIM period */
+ #define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */
+
+-#define IEEE80211_BINTVAL_MAX 1000 /* max beacon interval (TUs) */
+-#define IEEE80211_BINTVAL_MIN 25 /* min beacon interval (TUs) */
++#define IEEE80211_BINTVAL_MAX 5000 /* max beacon interval (TUs) */
++#define IEEE80211_BINTVAL_MIN 10 /* min beacon interval (TUs) */
+ #define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval (TUs) */
+ #define IEEE80211_BINTVAL_VALID(_bi) \
+ ((IEEE80211_BINTVAL_MIN <= (_bi)) && \
diff --git a/package/madwifi/patches/383-ibss_hostap.patch b/package/madwifi/patches/383-ibss_hostap.patch
new file mode 100644
index 000000000..d449c3037
--- /dev/null
+++ b/package/madwifi/patches/383-ibss_hostap.patch
@@ -0,0 +1,105 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1452,6 +1452,23 @@ ath_vap_create(struct ieee80211com *ic,
+ sc->sc_nstavaps++;
+ else if (opmode == IEEE80211_M_MONITOR)
+ sc->sc_nmonvaps++;
++
++
++ /* Driving the HAL in IBSS sometimes adapts the TSF and other timing registers
++ * from received beacons/probes. If that happens, expected TX interrupts may
++ * not occur until next reset. Which triggers the "lost beacon" tasklet.
++ * Resulting effectively in not sending packets for minutes. Because that only
++ * happens in large mesh networks, this mode needs to be activated by a kernel
++ * module parameter: hostap_for_ibss=1. Note that using this mode has side
++ * effects. Such as not supressing beacons/probe answers randomly when
++ * receiving other node beacons. It's recommended to lower the beacon interval
++ * then. When using an IBSS-VAP together with an HOSTAP-VAP, you may also need
++ * to re-trigger IBSS beacon generation after creating the HOSTAP-VAP by
++ * issueing "iwpriv athX bintval 1000".
++ */
++ if ((flags & IEEE80211_NO_STABEACONS) && (ic->ic_opmode == IEEE80211_M_IBSS))
++ sc->sc_opmode = HAL_M_HOSTAP;
++ else
+ /*
+ * Adhoc demo mode is a pseudo mode; to the HAL it's
+ * just IBSS mode and the driver doesn't use management
+@@ -4279,7 +4296,8 @@ ath_calcrxfilter(struct ath_softc *sc)
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP && (dev->flags & IFF_PROMISC))
+ rfilt |= HAL_RX_FILTER_PROM;
+ if (ic->ic_opmode == IEEE80211_M_STA ||
+- sc->sc_opmode == HAL_M_IBSS || /* NB: AHDEMO too */
++ ic->ic_opmode == IEEE80211_M_IBSS ||
++ ic->ic_opmode == IEEE80211_M_AHDEMO ||
+ (sc->sc_nostabeacons) || sc->sc_scanning ||
+ (ic->ic_opmode == IEEE80211_M_HOSTAP))
+ rfilt |= HAL_RX_FILTER_BEACON;
+@@ -6435,6 +6453,33 @@ ath_capture(struct net_device *dev, cons
+ }
+
+ /*
++ * Advances (forwards/adds) a microsecond value to current chip's TSF registers
++ */
++
++/* from ath_info.c */
++#define AR5K_TSF_L32_5210 0x806c /* TSF (lower 32 bits) */
++#define AR5K_TSF_L32_5211 0x804c
++#define AR5K_TSF_L32 (ar_device(ah->ah_sc->devid) == 5210 ? \
++ AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211)
++
++#define AR5K_TSF_U32_5210 0x8070
++#define AR5K_TSF_U32_5211 0x8050
++#define AR5K_TSF_U32 (ar_device(ah->ah_sc->devid) == 5210 ? \
++ AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211)
++
++static inline void ath_hal_settsf64(struct ath_hal *ah, u_int64_t tsf_adv)
++{
++ ATH_HAL_LOCK_IRQ(ah->ah_sc);
++ ath_hal_set_function(__func__);
++ tsf_adv += ah->ah_getTsf64(ah);
++ OS_REG_WRITE(ah, AR5K_TSF_L32, 0ll);
++ OS_REG_WRITE(ah, AR5K_TSF_U32, (tsf_adv >> 32) & 0xffffffffll);
++ OS_REG_WRITE(ah, AR5K_TSF_L32, (tsf_adv >> 00) & 0xffffffffll);
++ ath_hal_set_function(NULL);
++ ATH_HAL_UNLOCK_IRQ(ah->ah_sc);
++}
++
++/*
+ * Intercept management frames to collect beacon RSSI data and to do
+ * ibss merges. This function is called for all management frames,
+ * including those belonging to other BSS.
+@@ -6487,10 +6532,19 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "Updated beacon timers\n");
+ }
+- if ((sc->sc_opmode == HAL_M_IBSS) &&
+- IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid) &&
+- ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval)) {
+- DPRINTF(sc, ATH_DEBUG_ANY, "Fixed ATIM window after beacon recv\n");
++ if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
++ (sc->sc_opmode == HAL_M_HOSTAP) &&
++ IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) {
++ /* In this mode, we drive the HAL in HOSTAP mode. Hence
++ * we do the IBSS merging in software. Also do not merge
++ * if the difference it too small. Otherwise we are playing
++ * tsf-pingpong with other vendors drivers */
++ beacon_tsf = le64_to_cpu(ni->ni_tstamp.tsf);
++ if (beacon_tsf > rtsf + 0xffff) {
++ ath_hal_settsf64(sc->sc_ah, beacon_tsf - rtsf);
++ ieee80211_ibss_merge(ni);
++ }
++ break;
+ }
+ /* NB: Fall Through */
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+@@ -6563,6 +6617,10 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ #endif
+ if (do_merge)
+ ieee80211_ibss_merge(ni);
++
++ if ((sc->sc_opmode == HAL_M_IBSS) &&
++ ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval))
++ DPRINTF(sc, ATH_DEBUG_ANY, "Fixed ATIM window after beacon recv\n");
+ }
+ break;
+ }
diff --git a/package/madwifi/patches/384-hwdetect.patch b/package/madwifi/patches/384-hwdetect.patch
new file mode 100644
index 000000000..3b67615b0
--- /dev/null
+++ b/package/madwifi/patches/384-hwdetect.patch
@@ -0,0 +1,325 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -62,6 +62,7 @@
+ #include <linux/if_arp.h>
+ #include <linux/rtnetlink.h>
+ #include <linux/time.h>
++#include <linux/pci.h>
+ #include <asm/uaccess.h>
+
+ #include "if_ethersubr.h" /* for ETHER_IS_MULTICAST */
+@@ -401,6 +402,15 @@ static int outdoor = -1;
+ static int xchanmode = -1;
+ static int beacon_cal = 1;
+
++static const struct ath_hw_detect generic_hw_info = {
++ .vendor_name = "Unknown",
++ .card_name = "Generic",
++ .vendor = PCI_ANY_ID,
++ .id = PCI_ANY_ID,
++ .subvendor = PCI_ANY_ID,
++ .subid = PCI_ANY_ID
++};
++
+ static const char *hal_status_desc[] = {
+ "No error",
+ "No hardware present or device not yet supported",
+@@ -542,6 +552,8 @@ ath_attach(u_int16_t devid, struct net_d
+ DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
+ #endif
+
++ sc->sc_hwinfo = &generic_hw_info;
++
+ /* Allocate space for dynamically determined maximum VAP count */
+ sc->sc_bslot =
+ kmalloc(ath_maxvaps * sizeof(struct ieee80211vap*), GFP_KERNEL);
+@@ -1508,6 +1520,29 @@ ath_vap_create(struct ieee80211com *ic,
+ return vap;
+ }
+
++void
++ath_hw_detect(struct ath_softc *sc, const struct ath_hw_detect *cards, int n_cards, u32 vendor, u32 id, u32 subvendor, u32 subid)
++{
++ int i;
++
++ for (i = 0; i < n_cards; i++) {
++ const struct ath_hw_detect *c = &cards[i];
++
++ if ((c->vendor != PCI_ANY_ID) && c->vendor != vendor)
++ continue;
++ if ((c->id != PCI_ANY_ID) && c->id != id)
++ continue;
++ if ((c->subvendor != PCI_ANY_ID) && c->subvendor != subvendor)
++ continue;
++ if ((c->subid != PCI_ANY_ID) && c->subid != subid)
++ continue;
++
++ sc->sc_hwinfo = c;
++ sc->sc_poweroffset = c->poweroffset;
++ break;
++ }
++}
++
+ static void
+ ath_vap_delete(struct ieee80211vap *vap)
+ {
+@@ -10225,6 +10260,7 @@ static u_int32_t
+ ath_set_clamped_maxtxpower(struct ath_softc *sc,
+ u_int32_t new_clamped_maxtxpower)
+ {
++ new_clamped_maxtxpower -= sc->sc_poweroffset;
+ (void)ath_hal_settxpowlimit(sc->sc_ah, new_clamped_maxtxpower);
+ return ath_get_clamped_maxtxpower(sc);
+ }
+@@ -10238,6 +10274,7 @@ ath_get_clamped_maxtxpower(struct ath_so
+ {
+ u_int32_t clamped_maxtxpower;
+ (void)ath_hal_getmaxtxpow(sc->sc_ah, &clamped_maxtxpower);
++ clamped_maxtxpower += sc->sc_poweroffset;
+ return clamped_maxtxpower;
+ }
+
+@@ -10821,6 +10858,12 @@ ath_ioctl(struct net_device *dev, struct
+ * is to add module parameters.
+ */
+
++/* sysctls for hardware info */
++enum {
++ ATH_CARD_VENDOR,
++ ATH_CARD_NAME,
++};
++
+ /*
+ * Dynamic (i.e. per-device) sysctls. These are automatically
+ * mirrored in /proc/sys.
+@@ -10900,6 +10943,38 @@ ath_sysctl_get_intmit(struct ath_softc *
+ }
+
+ static int
++ATH_SYSCTL_DECL(ath_sysctl_hwinfo, ctl, write, filp, buffer, lenp, ppos)
++{
++ struct ath_softc *sc = ctl->extra1;
++ struct ath_hal *ah = sc->sc_ah;
++ int ret = 0;
++
++ if (write)
++ return -EINVAL;
++
++ ATH_LOCK(sc);
++ switch((long)ctl->extra2) {
++ case ATH_CARD_VENDOR:
++ ctl->data = (char *)sc->sc_hwinfo->vendor_name;
++ break;
++ case ATH_CARD_NAME:
++ ctl->data = (char *)sc->sc_hwinfo->card_name;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ if (ret == 0) {
++ ctl->maxlen = strlen(ctl->data);
++ ret = ATH_SYSCTL_PROC_DOSTRING(ctl, write, filp,
++ buffer, lenp, ppos);
++ }
++ ATH_UNLOCK(sc);
++
++ return ret;
++}
++
++static int
+ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos)
+ {
+ struct ath_softc *sc = ctl->extra1;
+@@ -11179,6 +11254,24 @@ static int maxint = 0x7fffffff; /* 32-b
+
+ static const ctl_table ath_sysctl_template[] = {
+ { .ctl_name = CTL_AUTO,
++ .procname = "dev_vendor",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_hwinfo,
++ .strategy = &sysctl_string,
++ .data = "N/A",
++ .maxlen = 1,
++ .extra2 = (void *)ATH_CARD_VENDOR,
++ },
++ { .ctl_name = CTL_AUTO,
++ .procname = "dev_name",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_hwinfo,
++ .strategy = &sysctl_string,
++ .data = "N/A",
++ .maxlen = 1,
++ .extra2 = (void *)ATH_CARD_NAME,
++ },
++ { .ctl_name = CTL_AUTO,
+ .procname = "slottime",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -168,12 +168,16 @@ static inline struct net_device *_alloc_
+ void __user *buffer, size_t *lenp)
+ #define ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+ proc_dointvec(ctl, write, filp, buffer, lenp)
++#define ATH_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dostring(ctl, write, filp, buffer, lenp)
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) */
+ #define ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
+ f(ctl_table *ctl, int write, struct file *filp, \
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+ #define ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+ proc_dointvec(ctl, write, filp, buffer, lenp, ppos)
++#define ATH_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dostring(ctl, write, filp, buffer, lenp, ppos)
+ #endif
+
+ #define ATH_TIMEOUT 1000
+@@ -469,6 +473,7 @@ struct ath_hal;
+ struct ath_desc;
+ struct ath_ratectrl;
+ struct ath_tx99;
++struct ath_hw_detect;
+ struct proc_dir_entry;
+
+ /*
+@@ -629,6 +634,7 @@ struct ath_softc {
+ struct ath_ratectrl *sc_rc; /* tx rate control support */
+ struct ath_tx99 *sc_tx99; /* tx99 support */
+ void (*sc_setdefantenna)(struct ath_softc *, u_int);
++ const struct ath_hw_detect *sc_hwinfo;
+
+ unsigned int sc_invalid:1; /* being detached */
+ unsigned int sc_mrretry:1; /* multi-rate retry support */
+@@ -683,6 +689,7 @@ struct ath_softc {
+ const HAL_RATE_TABLE *sc_quarter_rates; /* quarter rate table */
+ HAL_OPMODE sc_opmode; /* current hal operating mode */
+ enum ieee80211_phymode sc_curmode; /* current phy mode */
++ u_int sc_poweroffset; /* hardware power offset */
+ u_int16_t sc_curtxpow; /* current tx power limit */
+ u_int16_t sc_curaid; /* current association id */
+ HAL_CHANNEL sc_curchan; /* current h/w channel */
+@@ -929,4 +936,16 @@ int ar_device(int devid);
+
+ void ath_radar_detected(struct ath_softc *sc, const char* message);
+
++struct ath_hw_detect {
++ const char *vendor_name;
++ const char *card_name;
++ u32 vendor;
++ u32 id;
++ u32 subvendor;
++ u32 subid;
++ u32 poweroffset;
++};
++
++extern void ath_hw_detect(struct ath_softc *sc, const struct ath_hw_detect *cards, int n_cards, u32 vendor, u32 id, u32 subvendor, u32 subid);
++
+ #endif /* _DEV_ATH_ATHVAR_H */
+--- a/ath/if_ath_ahb.c
++++ b/ath/if_ath_ahb.c
+@@ -20,6 +20,7 @@
+ #include <linux/netdevice.h>
+ #include <linux/cache.h>
+ #include <linux/platform_device.h>
++#include <linux/pci.h>
+
+ #include <asm/io.h>
+ #include <asm/uaccess.h>
+@@ -181,12 +182,32 @@ exit_ath_wmac(u_int16_t wlanNum, struct
+ return 0;
+ }
+
++static const char ubnt[] = "Ubiquiti Networks";
++/* { vendorname, cardname, vendorid, cardid, subsys vendorid, subsys id, poweroffset } */
++static const struct ath_hw_detect cards[] = {
++ { ubnt, "PowerStation2 (18V)", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xb102 },
++ { ubnt, "PowerStation2 (16D)", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xb202 },
++ { ubnt, "PowerStation2 (EXT)", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xb302 },
++ { ubnt, "PowerStation5 (22V)", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xb105 },
++ { ubnt, "PowerStation5 (EXT)", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xb305 },
++ { ubnt, "WispStation5", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xa105 },
++ { ubnt, "LiteStation2", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xa002 },
++ { ubnt, "LiteStation5", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xa005 },
++ { ubnt, "NanoStation2", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xc002 },
++ { ubnt, "NanoStation5", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xc005 },
++ { ubnt, "NanoStation Loco2", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xc102 },
++ { ubnt, "NanoStation Loco5", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xc105 },
++ { ubnt, "Bullet2", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xc202 },
++ { ubnt, "Bullet5", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xc205 },
++};
++
+ static int
+ init_ath_wmac(u_int16_t devid, u_int16_t wlanNum, struct ar531x_config *config)
+ {
+ const char *athname;
+ struct net_device *dev;
+ struct ath_ahb_softc *sc;
++ u16 *radio_data;
+
+ if (((wlanNum != 0) && (wlanNum != 1)) ||
+ (sclist[wlanNum] != NULL))
+@@ -248,6 +269,16 @@ init_ath_wmac(u_int16_t devid, u_int16_t
+ sc->aps_sc.sc_softled = 1; /* SoftLED over GPIO */
+ sc->aps_sc.sc_ledpin = config->board->sysLedGpio;
+ sc->aps_sc.sc_invalid = 0;
++ radio_data = (u16 *) config->radio;
++ if (radio_data) {
++ u16 vendor, id, subvendor, subid;
++ vendor = radio_data[1];
++ id = radio_data[0];
++ subvendor = radio_data[8];
++ subid = radio_data[7];
++ ath_hw_detect(&sc->aps_sc, cards, ARRAY_SIZE(cards), vendor, id, subvendor, subid);
++ }
++
+ return 0;
+
+ bad4:
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -123,6 +123,33 @@ static u16 ath_devidmap[][2] = {
+ { 0xff1a, 0x001a }
+ };
+
++static const char ubnt[] = "Ubiquiti Networks";
++/* { vendorname, cardname, vendorid, cardid, subsys vendorid, subsys id, poweroffset } */
++static const struct ath_hw_detect cards[] = {
++ { ubnt, "XR2", 0x168c, 0x001b, 0x0777, 0x3002, 10 },
++ { ubnt, "XR2", 0x168c, 0x001b, 0x7777, 0x3002, 10 },
++ { ubnt, "XR2.3", 0x168c, 0x001b, 0x0777, 0x3b02, 10 },
++ { ubnt, "XR2.6", 0x168c, 0x001b, 0x0777, 0x3c02, 10 },
++ { ubnt, "XR3-2.8", 0x168c, 0x001b, 0x0777, 0x3b03, 10 },
++ { ubnt, "XR3-3.6", 0x168c, 0x001b, 0x0777, 0x3c03, 10 },
++ { ubnt, "XR3", 0x168c, 0x001b, 0x0777, 0x3003, 10 },
++ { ubnt, "XR4", 0x168c, 0x001b, 0x0777, 0x3004, 10 },
++ { ubnt, "XR5", 0x168c, 0x001b, 0x0777, 0x3005, 10 },
++ { ubnt, "XR5", 0x168c, 0x001b, 0x7777, 0x3005, 10 },
++ { ubnt, "XR7", 0x168c, 0x001b, 0x0777, 0x3007, 10 },
++ { ubnt, "XR9", 0x168c, 0x001b, 0x0777, 0x3009, 10 },
++ { ubnt, "SRC", 0x168c, 0x0013, 0x168c, 0x1042, 1 },
++ { ubnt, "SR2", 0x168c, 0x0013, 0x0777, 0x2041, 10 },
++ { ubnt, "SR4", 0x168c, 0x0013, 0x0777, 0x2004, 6 },
++ { ubnt, "SR4", 0x168c, 0x0013, 0x7777, 0x2004, 6 },
++ { ubnt, "SR4C", 0x168c, 0x0013, 0x0777, 0x1004, 6 },
++ { ubnt, "SR4C", 0x168c, 0x0013, 0x7777, 0x1004, 6 },
++ { ubnt, "SR5", 0x168c, 0x0013, 0x168c, 0x2042, 7 },
++ { ubnt, "SR9", 0x168c, 0x0013, 0x7777, 0x2009, 12 },
++ { ubnt, "SR71A", 0x168c, 0x0027, 0x168c, 0x2082, 10 },
++ { ubnt, "SR71", 0x168c, 0x0027, 0x0777, 0x4082, 10 },
++};
++
+ static int
+ ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ {
+@@ -257,6 +284,10 @@ ath_pci_probe(struct pci_dev *pdev, cons
+ printk(KERN_INFO "%s: %s: %s: mem=0x%lx, irq=%d\n",
+ dev_info, dev->name, athname ? athname : "Atheros ???", phymem, dev->irq);
+
++ ath_hw_detect(&sc->aps_sc, cards, ARRAY_SIZE(cards),
++ pdev->vendor, pdev->device,
++ pdev->subsystem_vendor, pdev->subsystem_device);
++
+ /* ready to process interrupts */
+ sc->aps_sc.sc_invalid = 0;
+
diff --git a/package/madwifi/patches/385-antenna_fix.patch b/package/madwifi/patches/385-antenna_fix.patch
new file mode 100644
index 000000000..16c7d95c7
--- /dev/null
+++ b/package/madwifi/patches/385-antenna_fix.patch
@@ -0,0 +1,10 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6669,6 +6669,7 @@ ath_setdefantenna(struct ath_softc *sc,
+ struct ath_hal *ah = sc->sc_ah;
+
+ /* XXX block beacon interrupts */
++ ath_hal_setdiversity(ah, (sc->sc_diversity != 0));
+ ath_hal_setdefantenna(ah, antenna);
+ if (sc->sc_defant != antenna)
+ sc->sc_stats.ast_ant_defswitch++;
diff --git a/package/madwifi/patches/386-acl_crashfix.patch b/package/madwifi/patches/386-acl_crashfix.patch
new file mode 100644
index 000000000..04a1ec911
--- /dev/null
+++ b/package/madwifi/patches/386-acl_crashfix.patch
@@ -0,0 +1,116 @@
+fixes ACL race condition caused by acl list modifications at run time
+
+Signed-off-by: Sebastian Gottschall <brainslayer@dd-wrt.com>
+
+--- a/net80211/ieee80211_acl.c
++++ b/net80211/ieee80211_acl.c
+@@ -112,9 +112,9 @@ acl_detach(struct ieee80211vap *vap)
+ {
+ struct aclstate *as = vap->iv_as;
+
+- ACL_LOCK(as);
++ ACL_LOCK_IRQ(as);
+ acl_free_all_locked(as);
+- ACL_UNLOCK(as);
++ ACL_UNLOCK_IRQ(as);
+ vap->iv_as = NULL;
+ ACL_LOCK_DESTROY(as);
+ FREE(as, M_DEVBUF);
+@@ -128,11 +128,18 @@ _find_acl(struct aclstate *as, const u_i
+ struct acl *acl;
+ int hash;
+
++ /* locking needed, as inserts are not atomic */
++ ACL_LOCK_IRQ(as);
+ hash = ACL_HASH(macaddr);
+ LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+- if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
+- return acl;
++ if (!IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
++ continue;
++
++ ACL_UNLOCK_IRQ_EARLY(as);
++ return acl;
+ }
++ ACL_UNLOCK_IRQ(as);
++
+ return NULL;
+ }
+
+@@ -176,11 +183,11 @@ acl_add(struct ieee80211vap *vap, const
+ return -ENOMEM;
+ }
+
+- ACL_LOCK(as);
++ ACL_LOCK_IRQ(as);
+ hash = ACL_HASH(mac);
+ LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+ if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
+- ACL_UNLOCK_EARLY(as);
++ ACL_UNLOCK_IRQ_EARLY(as);
+ FREE(new, M_80211_ACL);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
+ "ACL: add " MAC_FMT " failed, already present\n",
+@@ -191,7 +198,7 @@ acl_add(struct ieee80211vap *vap, const
+ IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
+ TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
+ LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
+- ACL_UNLOCK(as);
++ ACL_UNLOCK_IRQ(as);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
+ "ACL: add " MAC_FMT "\n", MAC_ADDR(mac));
+@@ -204,11 +211,11 @@ acl_remove(struct ieee80211vap *vap, con
+ struct aclstate *as = vap->iv_as;
+ struct acl *acl;
+
+- ACL_LOCK(as);
++ ACL_LOCK_IRQ(as);
+ acl = _find_acl(as, mac);
+ if (acl != NULL)
+ _acl_free(as, acl);
+- ACL_UNLOCK(as);
++ ACL_UNLOCK_IRQ(as);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
+ "ACL: remove " MAC_FMT "%s\n", MAC_ADDR(mac),
+@@ -235,9 +242,9 @@ acl_free_all(struct ieee80211vap *vap)
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
+
+- ACL_LOCK(as);
++ ACL_LOCK_IRQ(as);
+ acl_free_all_locked(vap->iv_as);
+- ACL_UNLOCK(as);
++ ACL_UNLOCK_IRQ(as);
+
+ return 0;
+ }
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -319,16 +319,15 @@ typedef spinlock_t ieee80211_scan_lock_t
+ typedef spinlock_t acl_lock_t;
+ #define ACL_LOCK_INIT(_as, _name) spin_lock_init(&(_as)->as_lock)
+ #define ACL_LOCK_DESTROY(_as)
+-#define ACL_LOCK(_as) do { \
+- ACL_LOCK_CHECK(_as); \
+- spin_lock(&(_as)->as_lock);
+-#define ACL_UNLOCK(_as) \
+- ACL_LOCK_ASSERT(_as); \
+- spin_unlock(&(_as)->as_lock); \
+-} while(0)
+-#define ACL_UNLOCK_EARLY(_as) \
+- ACL_LOCK_ASSERT(_as); \
+- spin_unlock(&(_as)->as_lock);
++#define ACL_LOCK_IRQ(_as) do { \
++ unsigned long __acl_lockflags; \
++ spin_lock_irqsave(&(_as)->as_lock, __acl_lockflags);
++#define ACL_UNLOCK_IRQ(_as) \
++ spin_unlock_irqrestore(&(_as)->as_lock, __acl_lockflags); \
++} while (0)
++#define ACL_UNLOCK_IRQ_EARLY(_as) do { \
++ spin_unlock_irqrestore(&(_as)->as_lock, __acl_lockflags); \
++} while (0)
+
+ #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
+ #define ACL_LOCK_ASSERT(_as) \
diff --git a/package/madwifi/patches/387-maxassoc.patch b/package/madwifi/patches/387-maxassoc.patch
new file mode 100644
index 000000000..79e5b2f4f
--- /dev/null
+++ b/package/madwifi/patches/387-maxassoc.patch
@@ -0,0 +1,85 @@
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -198,6 +198,7 @@ struct ieee80211vap {
+ u_int32_t iv_debug; /* debug msg flags */
+ struct ieee80211_stats iv_stats; /* statistics */
+
++ int iv_max_nodes;
+ int iv_monitor_nods_only; /* in monitor mode only nods traffic */
+ int iv_monitor_txf_len; /* in monitor mode, truncate tx packets */
+ int iv_monitor_phy_errors; /* in monitor mode, accept phy errors */
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -650,6 +650,7 @@ enum {
+ IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */
+ IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */
+ IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */
++ IEEE80211_PARAM_MAXASSOC = 83, /* maximum associated stations */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2875,6 +2875,12 @@ ieee80211_ioctl_setparam(struct net_devi
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
+ break;
++ case IEEE80211_PARAM_MAXASSOC:
++ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
++ retv = -EINVAL;
++ else
++ vap->iv_max_nodes = value;
++ break;
+ #ifdef ATH_REVERSE_ENGINEERING
+ case IEEE80211_PARAM_DUMPREGS:
+ ieee80211_dump_registers(dev, info, w, extra);
+@@ -3234,6 +3240,9 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_WDS_SEP:
+ param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
+ break;
++ case IEEE80211_PARAM_MAXASSOC:
++ param[0] = vap->iv_max_nodes;
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -5789,6 +5798,10 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
+ { IEEE80211_PARAM_WDS_SEP,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
++ { IEEE80211_PARAM_MAXASSOC,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maxassoc"},
++ { IEEE80211_PARAM_MAXASSOC,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_maxassoc"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -4020,7 +4020,26 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ vap->iv_stats.is_rx_assoc_norate++;
+ return;
+ }
++ if (vap->iv_max_nodes > 0) {
++ unsigned int active_nodes = 0;
++ struct ieee80211_node *tni;
+
++ IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
++ TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
++ if (tni->ni_vap != vap)
++ continue;
++ if (tni->ni_associd == 0)
++ continue;
++ active_nodes++;
++ }
++ IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
++
++ if (active_nodes >= vap->iv_max_nodes) {
++ /* too many nodes connected */
++ ieee80211_node_leave(ni);
++ return;
++ }
++ }
+ if (ni->ni_associd != 0 &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
diff --git a/package/madwifi/patches/388-apsta_fix.patch b/package/madwifi/patches/388-apsta_fix.patch
new file mode 100644
index 000000000..b0cb8e9df
--- /dev/null
+++ b/package/madwifi/patches/388-apsta_fix.patch
@@ -0,0 +1,60 @@
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -1415,7 +1415,8 @@ __ieee80211_newstate(struct ieee80211vap
+ vap->iv_state = nstate; /* state transition */
+ del_timer(&vap->iv_mgtsend);
+ if ((vap->iv_opmode != IEEE80211_M_HOSTAP) &&
+- (ostate != IEEE80211_S_SCAN))
++ (ostate != IEEE80211_S_SCAN) &&
++ !(vap->iv_flags_ext & IEEE80211_FEXT_SCAN_PENDING))
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ switch (nstate) {
+@@ -1457,7 +1458,8 @@ __ieee80211_newstate(struct ieee80211vap
+ }
+ goto reset;
+ case IEEE80211_S_SCAN:
+- ieee80211_cancel_scan(vap);
++ if (!(vap->iv_flags_ext & IEEE80211_FEXT_SCAN_PENDING))
++ ieee80211_cancel_scan(vap);
+ goto reset;
+ reset:
+ ieee80211_reset_bss(vap);
+@@ -1995,7 +1997,9 @@ ieee80211_newstate(struct ieee80211vap *
+ }
+ }
+ }
+- } else if (dstate == IEEE80211_S_SCAN) {
++ } else if ((dstate == IEEE80211_S_SCAN) ||
++ (dstate == IEEE80211_S_AUTH) ||
++ (dstate == IEEE80211_S_ASSOC)) {
+ /* Force to scan pending... someone is scanning */
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCAN_PENDING;
+ __ieee80211_newstate(vap, IEEE80211_S_INIT, arg);
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -238,7 +238,9 @@ ieee80211_hardstart(struct sk_buff *skb,
+ }
+
+ /* Cancel any running BG scan */
+- if (!(ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN_THR) && (vap->iv_state == IEEE80211_S_RUN))
++ if (!(ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN_THR) &&
++ (vap->iv_state == IEEE80211_S_RUN) &&
++ (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN))
+ ieee80211_cancel_scan(vap);
+
+ /*
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2728,9 +2728,9 @@ ieee80211_ioctl_setparam(struct net_devi
+ return -EINVAL;
+ vap->iv_flags |= IEEE80211_F_BGSCAN;
+ } else {
+- /* XXX racey? */
++ if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN)
++ ieee80211_cancel_scan(vap); /* anything current */
+ vap->iv_flags &= ~IEEE80211_F_BGSCAN;
+- ieee80211_cancel_scan(vap); /* anything current */
+ }
+ break;
+ case IEEE80211_PARAM_BGSCAN_IDLE:
diff --git a/package/madwifi/patches/389-autochannel.patch b/package/madwifi/patches/389-autochannel.patch
new file mode 100644
index 000000000..548f09e7d
--- /dev/null
+++ b/package/madwifi/patches/389-autochannel.patch
@@ -0,0 +1,249 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -384,6 +384,7 @@ static u_int32_t ath_get_real_maxtxpower
+
+ static void ath_poll_disable(struct net_device *dev);
+ static void ath_poll_enable(struct net_device *dev);
++static void ath_fetch_idle_time(struct ath_softc *sc);
+
+ /* calibrate every 30 secs in steady state but check every second at first. */
+ static int ath_calinterval = ATH_SHORT_CALINTERVAL;
+@@ -2581,6 +2582,7 @@ ath_init(struct net_device *dev)
+ * be followed by initialization of the appropriate bits
+ * and then setup of the interrupt mask.
+ */
++ ath_fetch_idle_time(sc);
+ sc->sc_curchan.channel = ic->ic_curchan->ic_freq;
+ sc->sc_curchan.channelFlags = ath_chan2flags(ic->ic_curchan);
+ if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
+@@ -2914,6 +2916,48 @@ ath_hw_check_atim(struct ath_softc *sc,
+ return 0;
+ }
+
++#define AR5K_MIBC 0x0040
++#define AR5K_MIBC_FREEZE (1 << 1)
++#define AR5K_TXFC 0x80ec
++#define AR5K_RXFC 0x80f0
++#define AR5K_RXCLEAR 0x80f4
++#define AR5K_CYCLES 0x80f8
++static void
++ath_fetch_idle_time(struct ath_softc *sc)
++{
++ struct ieee80211com *ic = &sc->sc_ic;
++ struct ath_hal *ah = sc->sc_ah;
++ u_int32_t cc, rx;
++ u_int32_t time = 0;
++
++ if (sc->sc_ah->ah_macType < 5212)
++ return;
++
++ if (!ic->ic_curchan || (ic->ic_curchan == IEEE80211_CHAN_ANYC))
++ return;
++
++ OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
++ rx = OS_REG_READ(ah, AR5K_RXCLEAR);
++ cc = OS_REG_READ(ah, AR5K_CYCLES);
++
++ if (!cc)
++ return;
++
++ if (rx > cc)
++ return; /* should not happen */
++
++ if (sc->sc_last_chan)
++ sc->sc_last_chan->ic_idletime = 100 * (cc - rx) / cc;
++ sc->sc_last_chan = ic->ic_curchan;
++
++ OS_REG_WRITE(ah, AR5K_RXCLEAR, 0);
++ OS_REG_WRITE(ah, AR5K_CYCLES, 0);
++ OS_REG_WRITE(ah, AR5K_TXFC, 0);
++ OS_REG_WRITE(ah, AR5K_RXFC, 0);
++ OS_REG_WRITE(ah, AR5K_MIBC, 0);
++}
++#undef AR5K_RXCLEAR
++#undef AR5K_CYCLES
+
+ /*
+ * Reset the hardware w/o losing operational state. This is
+@@ -2941,6 +2985,7 @@ ath_reset(struct net_device *dev)
+ * Convert to a HAL channel description with the flags
+ * constrained to reflect the current operating mode.
+ */
++ ath_fetch_idle_time(sc);
+ c = ic->ic_curchan;
+ sc->sc_curchan.channel = c->ic_freq;
+ sc->sc_curchan.channelFlags = ath_chan2flags(c);
+@@ -9023,6 +9068,7 @@ ath_chan_set(struct ath_softc *sc, struc
+ u_int8_t channel_change_required = 0;
+ struct timeval tv;
+
++
+ /*
+ * Convert to a HAL channel description with
+ * the flags constrained to reflect the current
+@@ -9031,6 +9077,14 @@ ath_chan_set(struct ath_softc *sc, struc
+ memset(&hchan, 0, sizeof(HAL_CHANNEL));
+ hchan.channel = chan->ic_freq;
+ hchan.channelFlags = ath_chan2flags(chan);
++
++ /* don't do duplicate channel changes, but do
++ * store the available idle time */
++ ath_fetch_idle_time(sc);
++ if ((sc->sc_curchan.channel == hchan.channel) &&
++ (sc->sc_curchan.channelFlags == hchan.channelFlags))
++ return 0;
++
+ KASSERT(hchan.channel != 0,
+ ("bogus channel %u/0x%x", hchan.channel, hchan.channelFlags));
+ do_gettimeofday(&tv);
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -774,6 +774,7 @@ struct ath_softc {
+ struct ieee80211vap **sc_bslot; /* beacon xmit slots */
+ int sc_bnext; /* next slot for beacon xmit */
+
++ struct ieee80211_channel *sc_last_chan;
+ int sc_beacon_cal; /* use beacon timer for calibration */
+ u_int64_t sc_lastcal; /* last time the calibration was performed */
+ struct timer_list sc_cal_ch; /* calibration timer */
+--- a/net80211/_ieee80211.h
++++ b/net80211/_ieee80211.h
+@@ -148,6 +148,7 @@ struct ieee80211_channel {
+ int8_t ic_maxpower; /* maximum tx power in dBm */
+ int8_t ic_minpower; /* minimum tx power in dBm */
+ u_int8_t ic_scanflags;
++ u_int8_t ic_idletime; /* phy idle time in % */
+ };
+
+ #define IEEE80211_CHAN_MAX 255
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -417,6 +417,19 @@ pc_cmp_rssi(struct ap_state *as, struct
+
+ /* This function must be invoked with locks acquired */
+ static int
++pc_cmp_idletime(struct ieee80211_channel *a,
++ struct ieee80211_channel *b)
++{
++ if (!a->ic_idletime || !b->ic_idletime)
++ return 0;
++
++ /* a is better than b (return < 0) when a has more idle time than b */
++ return b->ic_idletime - a->ic_idletime;
++}
++
++
++/* This function must be invoked with locks acquired */
++static int
+ pc_cmp_samechan(struct ieee80211com *ic, struct ieee80211_channel *a,
+ struct ieee80211_channel *b)
+ {
+@@ -451,6 +464,7 @@ pc_cmp(const void *_a, const void *_b)
+
+ EVALUATE_CRITERION(radar, a, b);
+ EVALUATE_CRITERION(keepmode, params, a, b);
++ EVALUATE_CRITERION(idletime, a, b);
+ EVALUATE_CRITERION(sc, ic, a, b);
+ /* XXX: rssi useless? pick_channel evaluates it anyway */
+ EVALUATE_CRITERION(rssi, params->ss->ss_priv, a, b);
+@@ -519,16 +533,9 @@ pick_channel(struct ieee80211_scan_state
+ #endif
+
+ best = NULL;
+- best_rssi = 0xff; /* If signal is bigger than 0xff, we'd be melting. */
+
+ for (i = 0; i < ss_last; i++) {
+ c = &chans[i];
+- benefit = best_rssi - as->as_maxrssi[c->chan->ic_ieee];
+- sta_assoc = ic->ic_sta_assoc;
+-
+- /* Don't switch... */
+- if (benefit <= 0)
+- continue;
+
+ /* Verify channel is not marked for non-occupancy */
+ if (IEEE80211_IS_CHAN_RADAR(c->chan))
+@@ -546,31 +553,8 @@ pick_channel(struct ieee80211_scan_state
+ break;
+ }
+
+- if (sta_assoc != 0) {
+- int sl = ic->ic_cn_total -
+- ic->ic_chan_nodes[c->chan->ic_ieee]; /* count */
+- if (ic->ic_sc_algorithm == IEEE80211_SC_LOOSE) {
+- int sl_max = ic->ic_sc_sldg * benefit;
+- sl = 1000 * sl / sta_assoc; /* permil */
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+- "%s: chan %d, dB gained: %d, "
+- "STAs lost: %d permil (max %d)\n",
+- __func__, c->chan->ic_ieee,
+- benefit, sl, sl_max);
+- if (sl > sl_max)
+- continue;
+- } else if (((ic->ic_sc_algorithm ==
+- IEEE80211_SC_TIGHT) ||
+- (ic->ic_sc_algorithm ==
+- IEEE80211_SC_STRICT)) &&
+- (sl > 0)) {
+- /* Break the loop as the subsequent chans
+- * won't be better. */
+- break;
+- }
+- }
+ best = c->chan;
+- best_rssi = as->as_maxrssi[best->ic_ieee];
++ break;
+ }
+
+ if (best != NULL) {
+@@ -599,6 +583,9 @@ ap_end(struct ieee80211_scan_state *ss,
+ ("wrong opmode %u", vap->iv_opmode));
+
+ ic = vap->iv_ic;
++
++ /* record stats for the channel that was scanned last */
++ ic->ic_set_channel(ic);
+ bestchan = pick_channel(ss, vap, flags);
+ if (bestchan == NULL) {
+ if (ss->ss_last > 0) {
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -1002,20 +1002,34 @@ ieee80211_scan_add_channels(struct ieee8
+ {
+ struct ieee80211_channel *c, *cg;
+ u_int modeflags;
++ int has_non_turbo = 0;
+ int i;
+
+ KASSERT(mode < ARRAY_SIZE(chanflags), ("Unexpected mode %u", mode));
+ modeflags = chanflags[mode];
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
++ if (c->ic_flags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO))
++ continue;
++
++ has_non_turbo = 1;
++ break;
++ }
++ for (i = 0; i < ic->ic_nchans; i++) {
++ c = &ic->ic_channels[i];
+ if (c == NULL || isclr(ic->ic_chan_active, c->ic_ieee))
+ continue;
+ if (c->ic_scanflags & IEEE80211_NOSCAN_SET)
+ continue;
+- if (modeflags &&
+- ((c->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
+- (modeflags & IEEE80211_CHAN_ALLTURBO)))
+- continue;
++ if (modeflags) {
++ if ((c->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
++ (modeflags & IEEE80211_CHAN_ALLTURBO))
++ continue;
++ } else if (has_non_turbo) {
++ if ((ss->ss_vap->iv_opmode == IEEE80211_M_HOSTAP) &&
++ (c->ic_flags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)))
++ continue;
++ }
+ if (mode == IEEE80211_MODE_AUTO) {
+ /*
+ * XXX special-case 11b/g channels so we select
diff --git a/package/madwifi/patches/390-frame_type.patch b/package/madwifi/patches/390-frame_type.patch
new file mode 100644
index 000000000..6de01102c
--- /dev/null
+++ b/package/madwifi/patches/390-frame_type.patch
@@ -0,0 +1,13 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -4443,7 +4443,9 @@ ath_eth_type_trans(struct sk_buff *skb,
+ if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
+ skb->pkt_type = PACKET_OTHERHOST;
+
+- return eth->h_proto;
++ if ((ntohs(eth->h_proto) >= 1536) || (ntohs(eth->h_proto) < 38))
++ return eth->h_proto;
++ return htons(ETH_P_802_2);
+ }
+ #endif
+
diff --git a/package/madwifi/patches/391-vap_auth.patch b/package/madwifi/patches/391-vap_auth.patch
new file mode 100644
index 000000000..832f9e116
--- /dev/null
+++ b/package/madwifi/patches/391-vap_auth.patch
@@ -0,0 +1,29 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1375,7 +1375,7 @@ ieee80211_auth_open(struct ieee80211_nod
+ vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ if (ni == vap->iv_bss) {
+- ni = ieee80211_dup_bss(vap, wh->i_addr2, 0);
++ ni = ieee80211_dup_bss(vap, wh->i_addr2, 1);
+ if (ni == NULL)
+ return;
+ tmpnode = 1;
+@@ -1763,6 +1763,8 @@ ieee80211_ssid_mismatch(struct ieee80211
+ }
+
+ #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
++ if ((_ni)->ni_esslen == 0) \
++ return; \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
+@@ -1777,6 +1779,8 @@ ieee80211_ssid_mismatch(struct ieee80211
+ } while (0)
+ #else /* !IEEE80211_DEBUG */
+ #define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
++ if ((_ni)->ni_esslen == 0) \
++ return; \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
diff --git a/package/madwifi/patches/392-remove_wds_nodetracking.patch b/package/madwifi/patches/392-remove_wds_nodetracking.patch
new file mode 100644
index 000000000..fb9fb6a22
--- /dev/null
+++ b/package/madwifi/patches/392-remove_wds_nodetracking.patch
@@ -0,0 +1,388 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -568,36 +568,6 @@ ieee80211_input(struct ieee80211vap * va
+ }
+ }
+
+- /* XXX: Useless node mgmt API; make better */
+- if ((dir == IEEE80211_FC1_DIR_DSTODS) && !vap->iv_wdsnode &&
+- !ni_wds && !ni->ni_subif) {
+- struct ieee80211_node_table *nt = &ic->ic_sta;
+- struct ieee80211_frame_addr4 *wh4;
+-
+- if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) {
+- IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+- wh, "data", "%s", "4 addr not allowed");
+- goto err;
+- }
+- wh4 = (struct ieee80211_frame_addr4 *)skb->data;
+- ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4);
+- /* Last call increments ref count if !NULL */
+- if ((ni_wds != NULL) && (ni_wds != ni)) {
+- /*
+- * node with source address (addr4) moved
+- * to another WDS capable station. remove the
+- * reference to the previous station and add
+- * reference to the new one
+- */
+- (void) ieee80211_remove_wds_addr(nt, wh4->i_addr4);
+- ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0);
+- }
+- if (ni_wds == NULL)
+- ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0);
+- else
+- ieee80211_unref_node(&ni_wds);
+- }
+-
+ /*
+ * Check for power save state change.
+ */
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -122,7 +122,6 @@ static void ieee80211_node_table_init(st
+ static void ieee80211_node_table_cleanup(struct ieee80211_node_table *);
+ static void ieee80211_node_table_reset(struct ieee80211_node_table *,
+ struct ieee80211vap *);
+-static void ieee80211_node_wds_ageout(unsigned long);
+
+ MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
+
+@@ -785,10 +784,6 @@ ieee80211_node_table_init(struct ieee802
+ nt->nt_name = name;
+ nt->nt_scangen = 1;
+ nt->nt_inact_init = inact;
+- init_timer(&nt->nt_wds_aging_timer);
+- nt->nt_wds_aging_timer.function = ieee80211_node_wds_ageout;
+- nt->nt_wds_aging_timer.data = (unsigned long) nt;
+- mod_timer(&nt->nt_wds_aging_timer, jiffies + HZ * WDS_AGING_TIMER_VAL);
+ }
+
+ static __inline
+@@ -1204,142 +1199,6 @@ void ieee80211_wds_addif(struct ieee8021
+ schedule_work(&ni->ni_create);
+ }
+
+-/* Add wds address to the node table */
+-int
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_add_wds_addr_debug(struct ieee80211_node_table *nt,
+- struct ieee80211_node *ni, const u_int8_t *macaddr, u_int8_t wds_static,
+- const char* func, int line)
+-#else
+-ieee80211_add_wds_addr(struct ieee80211_node_table *nt,
+- struct ieee80211_node *ni, const u_int8_t *macaddr, u_int8_t wds_static)
+-#endif
+-{
+- int hash;
+- struct ieee80211_wds_addr *wds;
+-
+- MALLOC(wds, struct ieee80211_wds_addr *, sizeof(struct ieee80211_wds_addr),
+- M_80211_WDS, M_NOWAIT | M_ZERO);
+- if (wds == NULL) {
+- /* XXX msg */
+- return 1;
+- }
+- if (wds_static)
+- wds->wds_agingcount = WDS_AGING_STATIC;
+- else
+- wds->wds_agingcount = WDS_AGING_COUNT;
+- hash = IEEE80211_NODE_HASH(macaddr);
+- IEEE80211_ADDR_COPY(wds->wds_macaddr, macaddr);
+-
+- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+-#ifdef IEEE80211_DEBUG_REFCNT
+- wds->wds_ni = ieee80211_ref_node_debug(ni, func, line);
+-#else
+- wds->wds_ni = ieee80211_ref_node(ni);
+-#endif
+- LIST_INSERT_HEAD(&nt->nt_wds_hash[hash], wds, wds_hash);
+- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+- return 0;
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_add_wds_addr_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_add_wds_addr);
+-#endif
+-
+-/* remove wds address from the wds hash table */
+-void
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_remove_wds_addr_debug(struct ieee80211_node_table *nt, const u_int8_t *macaddr,
+- const char* func, int line)
+-#else
+-ieee80211_remove_wds_addr(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+-#endif
+-{
+- int hash;
+- struct ieee80211_wds_addr *wds, *twds;
+-
+- hash = IEEE80211_NODE_HASH(macaddr);
+- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+- LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
+- if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
+- LIST_REMOVE(wds, wds_hash);
+-#ifdef IEEE80211_DEBUG_REFCNT
+- ieee80211_unref_node_debug(&wds->wds_ni, func, line);
+-#else
+- ieee80211_unref_node(&wds->wds_ni);
+-#endif
+- FREE(wds, M_80211_WDS);
+- break;
+- }
+- }
+- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_remove_wds_addr_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_remove_wds_addr);
+-#endif
+-
+-/* Remove node references from wds table */
+-void
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_del_wds_node_debug(struct ieee80211_node_table *nt, struct ieee80211_node *ni,
+- const char* func, int line)
+-#else
+-ieee80211_del_wds_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+-#endif
+-{
+- int hash;
+- struct ieee80211_wds_addr *wds, *twds;
+-
+- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+- for (hash = 0; hash < IEEE80211_NODE_HASHSIZE; hash++) {
+- LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
+- if (wds->wds_ni == ni) {
+- LIST_REMOVE(wds, wds_hash);
+-#ifdef IEEE80211_DEBUG_REFCNT
+- ieee80211_unref_node_debug(&wds->wds_ni, func, line);
+-#else
+- ieee80211_unref_node(&wds->wds_ni);
+-#endif
+- FREE(wds, M_80211_WDS);
+- }
+- }
+- }
+- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_del_wds_node_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_del_wds_node);
+-#endif
+-
+-static void
+-ieee80211_node_wds_ageout(unsigned long data)
+-{
+- struct ieee80211_node_table *nt = (struct ieee80211_node_table *)data;
+- int hash;
+- struct ieee80211_wds_addr *wds, *twds;
+-
+- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+- for (hash = 0; hash < IEEE80211_NODE_HASHSIZE; hash++) {
+- LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
+- if (wds->wds_agingcount != WDS_AGING_STATIC) {
+- if (!wds->wds_agingcount) {
+- LIST_REMOVE(wds, wds_hash);
+- ieee80211_unref_node(&wds->wds_ni);
+- FREE(wds, M_80211_WDS);
+- } else
+- wds->wds_agingcount--;
+- }
+- }
+- }
+- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+- mod_timer(&nt->nt_wds_aging_timer, jiffies + HZ * WDS_AGING_TIMER_VAL);
+-}
+-
+-
+ /* Add the specified station to the station table.
+ * Allocates a new ieee80211_node* that has a reference count of one
+ * If tmp is 0, it is added to the node table and the reference is used.
+@@ -1385,34 +1244,6 @@ ieee80211_dup_bss(struct ieee80211vap *v
+ return ni;
+ }
+
+-static struct ieee80211_node *
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_find_wds_node_locked_debug(struct ieee80211_node_table *nt,
+- const u_int8_t *macaddr, const char* func, int line)
+-#else
+-ieee80211_find_wds_node_locked(struct ieee80211_node_table *nt,
+- const u_int8_t *macaddr)
+-#endif
+-{
+- struct ieee80211_wds_addr *wds;
+- int hash;
+- IEEE80211_NODE_TABLE_LOCK_ASSERT(nt);
+-
+- hash = IEEE80211_NODE_HASH(macaddr);
+- LIST_FOREACH(wds, &nt->nt_wds_hash[hash], wds_hash) {
+- if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
+- if (wds->wds_agingcount != WDS_AGING_STATIC)
+- wds->wds_agingcount = WDS_AGING_COUNT; /* reset the aging count */
+-#ifdef IEEE80211_DEBUG_REFCNT
+- return ieee80211_ref_node_debug(wds->wds_ni, func, line);
+-#else
+- return ieee80211_ref_node(wds->wds_ni);
+-#endif
+- }
+- }
+- return NULL;
+-}
+-
+ /* NB: A node reference is acquired here; the caller MUST release it. */
+ #ifdef IEEE80211_DEBUG_REFCNT
+ #define ieee80211_find_node_locked(nt, mac) \
+@@ -1430,7 +1261,6 @@ ieee80211_find_node_locked(struct ieee80
+ {
+ struct ieee80211_node *ni;
+ int hash;
+- struct ieee80211_wds_addr *wds;
+
+ IEEE80211_NODE_TABLE_LOCK_ASSERT(nt);
+
+@@ -1445,48 +1275,11 @@ ieee80211_find_node_locked(struct ieee80
+ return ni;
+ }
+ }
+-
+- /* Now, we look for the desired mac address in the 4 address
+- nodes. */
+- LIST_FOREACH(wds, &nt->nt_wds_hash[hash], wds_hash) {
+- if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
+-#ifdef IEEE80211_DEBUG_REFCNT
+- return ieee80211_ref_node_debug(wds->wds_ni, func, line);
+-#else
+- return ieee80211_ref_node(wds->wds_ni);
+-#endif
+- }
+- }
+ return NULL;
+ }
+
+ struct ieee80211_node *
+ #ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_find_wds_node_debug(struct ieee80211_node_table *nt, const u_int8_t *macaddr,
+- const char* func, int line)
+-#else
+-ieee80211_find_wds_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+-#endif
+-{
+- struct ieee80211_node *ni;
+-
+- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+-#ifdef IEEE80211_DEBUG_REFCNT
+- ni = ieee80211_find_wds_node_locked_debug(nt, macaddr, func, line);
+-#else
+- ni = ieee80211_find_wds_node_locked(nt, macaddr);
+-#endif
+- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+- return ni;
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_find_wds_node_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_find_wds_node);
+-#endif
+-
+-struct ieee80211_node *
+-#ifdef IEEE80211_DEBUG_REFCNT
+ ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+ const u_int8_t *macaddr, const char *func, int line)
+ #else
+@@ -1838,7 +1631,6 @@ ieee80211_node_table_cleanup(struct ieee
+ ic->ic_node_cleanup(ni);
+ #endif
+ }
+- del_timer(&nt->nt_wds_aging_timer);
+ IEEE80211_SCAN_LOCK_DESTROY(nt);
+ IEEE80211_NODE_TABLE_LOCK_DESTROY(nt);
+ }
+@@ -2404,8 +2196,6 @@ ieee80211_node_leave(struct ieee80211_no
+ * so no more references are generated
+ */
+ if (nt) {
+- ieee80211_remove_wds_addr(nt, ni->ni_macaddr);
+- ieee80211_del_wds_node(nt, ni);
+ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+ node_table_leave_locked(nt, ni);
+ IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+--- a/net80211/ieee80211_node.h
++++ b/net80211/ieee80211_node.h
+@@ -231,13 +231,6 @@ void ieee80211_sta_leave(struct ieee8021
+ #define WDS_AGING_STATIC 0xffff
+ #define WDS_AGING_TIMER_VAL (WDS_AGING_TIME / 2)
+
+-struct ieee80211_wds_addr {
+- LIST_ENTRY(ieee80211_wds_addr) wds_hash;
+- u_int8_t wds_macaddr[IEEE80211_ADDR_LEN];
+- struct ieee80211_node *wds_ni;
+- u_int16_t wds_agingcount;
+-};
+-
+ /*
+ * Table of ieee80211_node instances. Each ieee80211com
+ * has at least one for holding the scan candidates.
+@@ -250,11 +243,9 @@ struct ieee80211_node_table {
+ ieee80211_node_table_lock_t nt_nodelock; /* on node table */
+ TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */
+ ATH_LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
+- ATH_LIST_HEAD(, ieee80211_wds_addr) nt_wds_hash[IEEE80211_NODE_HASHSIZE];
+ ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */
+ u_int nt_scangen; /* gen# for timeout scan */
+ int nt_inact_init; /* initial node inact setting */
+- struct timer_list nt_wds_aging_timer; /* timer to age out wds entries */
+ };
+
+ /* Allocates a new ieee80211_node* that has a reference count of one, and
+@@ -363,47 +354,6 @@ void
+ ieee80211_unref_node(struct ieee80211_node **pni);
+ #endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+
+-/* Increments reference count of ieee80211_node *ni */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_add_wds_addr(_table, _node, _mac, _static) \
+- ieee80211_add_wds_addr_debug(_table, _node, _mac, _static, __func__, __LINE__)
+-int ieee80211_add_wds_addr_debug(struct ieee80211_node_table *, struct ieee80211_node *,
+- const u_int8_t *, u_int8_t, const char* func, int line);
+-#else
+-int ieee80211_add_wds_addr(struct ieee80211_node_table *, struct ieee80211_node *,
+- const u_int8_t *, u_int8_t);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+-
+-/* Decrements reference count of ieee80211_node *ni */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_remove_wds_addr(_table, _mac) \
+- ieee80211_remove_wds_addr_debug(_table, _mac, __func__, __LINE__)
+-void ieee80211_remove_wds_addr_debug(struct ieee80211_node_table *, const u_int8_t *,
+- const char* func, int line);
+-#else
+-void ieee80211_remove_wds_addr(struct ieee80211_node_table *, const u_int8_t *);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+-
+-/* Decrements reference count of node, if found */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_del_wds_node(_table, _node) \
+- ieee80211_del_wds_node_debug(_table, _node, __func__, __LINE__)
+-void ieee80211_del_wds_node_debug(struct ieee80211_node_table *, struct ieee80211_node *,
+- const char* func, int line);
+-#else
+-void ieee80211_del_wds_node(struct ieee80211_node_table *, struct ieee80211_node *);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+-
+-/* Increments reference count of node, if found */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_find_wds_node(_table, _mac) \
+- ieee80211_find_wds_node_debug(_table, _mac, __func__, __LINE__)
+-struct ieee80211_node *ieee80211_find_wds_node_debug(struct ieee80211_node_table *,
+- const u_int8_t *, const char* func, int line);
+-#else
+-struct ieee80211_node *ieee80211_find_wds_node(struct ieee80211_node_table *,
+- const u_int8_t *);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+ typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
+ void ieee80211_iterate_nodes(struct ieee80211_node_table *,
+ ieee80211_iter_func *, void *);
diff --git a/package/madwifi/patches/393-mbss_vap_auth.patch b/package/madwifi/patches/393-mbss_vap_auth.patch
new file mode 100644
index 000000000..a2637cccb
--- /dev/null
+++ b/package/madwifi/patches/393-mbss_vap_auth.patch
@@ -0,0 +1,491 @@
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -123,6 +123,9 @@ static void ieee80211_node_table_cleanup
+ static void ieee80211_node_table_reset(struct ieee80211_node_table *,
+ struct ieee80211vap *);
+
++static struct ieee80211_node *
++lookup_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap, const u_int8_t *addr);
++
+ MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
+
+ void
+@@ -697,7 +700,7 @@ ieee80211_sta_join(struct ieee80211vap *
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+
+- ni = ieee80211_find_node(&ic->ic_sta, se->se_macaddr);
++ ni = lookup_rxnode(ic, vap, se->se_macaddr);
+ if (ni == NULL) {
+ ni = ieee80211_alloc_node_table(vap, se->se_macaddr);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
+@@ -1394,6 +1397,53 @@ ieee80211_add_neighbor(struct ieee80211v
+ return ni;
+ }
+
++struct ieee80211vap *
++ieee80211_find_rxvap(struct ieee80211com *ic, const u_int8_t *mac)
++{
++ struct ieee80211vap *vap;
++
++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, mac))
++ return vap;
++ }
++ return NULL;
++}
++EXPORT_SYMBOL(ieee80211_find_rxvap);
++
++static struct ieee80211_node *
++lookup_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap,
++ const u_int8_t *addr)
++{
++ struct ieee80211_node_table *nt;
++ struct ieee80211_node *ni = NULL;
++ int use_bss = 0;
++ int hash;
++
++ nt = &ic->ic_sta;
++ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
++ hash = IEEE80211_NODE_HASH(addr);
++ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
++ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, addr)) {
++ /* allow multiple nodes on different vaps */
++ if (vap && (ni->ni_vap != vap))
++ continue;
++
++ ieee80211_ref_node(ni);
++ goto out;
++ }
++ }
++
++ /* no match found */
++ ni = NULL;
++
++out:
++ IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
++ return ni;
++#undef IS_PSPOLL
++#undef IS_CTL
++}
++
++
+ /*
+ * Return the node for the sender of a frame; if the sender is unknown return
+ * NULL. The caller is expected to deal with this. (The frame is sent to all
+@@ -1403,10 +1453,10 @@ ieee80211_add_neighbor(struct ieee80211v
+ */
+ struct ieee80211_node *
+ #ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_find_rxnode_debug(struct ieee80211com *ic,
++ieee80211_find_rxnode_debug(struct ieee80211com *ic, struct ieee80211vap *vap,
+ const struct ieee80211_frame_min *wh, const char *func, int line)
+ #else
+-ieee80211_find_rxnode(struct ieee80211com *ic,
++ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap,
+ const struct ieee80211_frame_min *wh)
+ #endif
+ {
+@@ -1414,9 +1464,8 @@ ieee80211_find_rxnode(struct ieee80211co
+ ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+ #define IS_PSPOLL(wh) \
+ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+- struct ieee80211_node_table *nt;
+- struct ieee80211_node *ni;
+- struct ieee80211vap *vap, *avp;
++ struct ieee80211_node *ni = NULL;
++ struct ieee80211vap *avp;
+ const u_int8_t *addr;
+
+ if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+@@ -1429,32 +1478,25 @@ ieee80211_find_rxnode(struct ieee80211co
+
+ /* XXX check ic_bss first in station mode */
+ /* XXX 4-address frames? */
+- nt = &ic->ic_sta;
+- IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) {
+- TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ if (vap) { /* assume unicast if vap is set, mcast not supported for wds */
+ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
+- if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac))
++ if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac) ||
++ !IEEE80211_ADDR_EQ(wh->i_addr1, avp->iv_myaddr))
+ continue;
+
+ if (avp->iv_wdsnode)
+- return ieee80211_ref_node(avp->iv_wdsnode);
+- else
+- return NULL;
++ ni = ieee80211_ref_node(avp->iv_wdsnode);
++ return ni;
+ }
++ if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS))
++ return NULL;
++ } else {
++ return NULL;
+ }
+ }
+
+-#ifdef IEEE80211_DEBUG_REFCNT
+- ni = ieee80211_find_node_locked_debug(nt, addr, func, line);
+-#else
+- ni = ieee80211_find_node_locked(nt, addr);
+-#endif
+- IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-
+- return ni;
+-#undef IS_PSPOLL
+-#undef IS_CTL
++ return lookup_rxnode(ic, vap, addr);
+ }
+ #ifdef IEEE80211_DEBUG_REFCNT
+ EXPORT_SYMBOL(ieee80211_find_rxnode_debug);
+@@ -1479,15 +1521,14 @@ ieee80211_find_txnode(struct ieee80211va
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni = NULL;
++ int hash;
+
+- IEEE80211_LOCK_IRQ(ic);
+ if (vap->iv_opmode == IEEE80211_M_WDS) {
+ if (vap->iv_wdsnode && (vap->iv_state == IEEE80211_S_RUN))
+ return ieee80211_ref_node(vap->iv_wdsnode);
+ else
+ return NULL;
+ }
+- IEEE80211_UNLOCK_IRQ(ic);
+
+ /*
+ * The destination address should be in the node table
+@@ -1505,11 +1546,22 @@ ieee80211_find_txnode(struct ieee80211va
+ /* XXX: Can't hold lock across dup_bss due to recursive locking. */
+ nt = &vap->iv_ic->ic_sta;
+ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
++ hash = IEEE80211_NODE_HASH(mac);
++ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
++ if (ni->ni_vap != vap)
++ continue;
++
++ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, mac)) {
+ #ifdef IEEE80211_DEBUG_REFCNT
+- ni = ieee80211_find_node_locked_debug(nt, mac, func, line);
++ ieee80211_ref_node_debug(ni, func, line);
+ #else
+- ni = ieee80211_find_node_locked(nt, mac);
++ ieee80211_ref_node(ni);
+ #endif
++ goto found;
++ }
++ }
++ ni = NULL;
++found:
+ IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+
+ if (ni == NULL) {
+@@ -1964,13 +2016,32 @@ remove_worse_nodes(void *arg, struct iee
+ }
+ }
+
++static void
++remove_duplicate_nodes(void *arg, struct ieee80211_node *ni)
++{
++ struct ieee80211_node *rni = arg;
++
++ if (ni == rni)
++ return;
++
++ if (ni->ni_vap == rni->ni_vap)
++ return;
++
++ if (!IEEE80211_ADDR_EQ(rni->ni_macaddr, ni->ni_macaddr))
++ return;
++
++ ieee80211_node_leave(ni);
++}
++
+ void
+ ieee80211_node_join(struct ieee80211_node *ni, int resp)
+ {
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
++ struct ieee80211_node *tni;
+ int newassoc;
+
++ ieee80211_iterate_nodes(&ic->ic_sta, remove_duplicate_nodes, ni);
+ if (ni->ni_associd == 0) {
+ u_int16_t aid;
+
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -216,16 +216,14 @@ ieee80211_input(struct ieee80211vap * va
+
+ type = -1; /* undefined */
+
+- if (!vap)
+- goto out;
++ if (!vap || !vap->iv_bss || !vap->iv_dev || !vap->iv_ic)
++ goto discard;
+
+ ic = vap->iv_ic;
+- if (!ic)
+- goto out;
+-
+ dev = vap->iv_dev;
+- if (!dev)
+- goto out;
++
++ if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
++ goto discard;
+
+ /* initialize ni as in the previous API */
+ if (ni_or_null == NULL) {
+@@ -233,9 +231,10 @@ ieee80211_input(struct ieee80211vap * va
+ * guarantee its existence during the following call, hence
+ * briefly grab our own reference. */
+ ni = ieee80211_ref_node(vap->iv_bss);
++ KASSERT(ni != NULL, ("null node"));
++ } else {
++ ni->ni_inact = ni->ni_inact_reload;
+ }
+- KASSERT(ni != NULL, ("null node"));
+- ni->ni_inact = ni->ni_inact_reload;
+
+ KASSERT(skb->len >= sizeof(struct ieee80211_frame_min),
+ ("frame length too short: %u", skb->len));
+@@ -844,10 +843,11 @@ ieee80211_input(struct ieee80211vap * va
+ err:
+ vap->iv_devstats.rx_errors++;
+ out:
+- if (skb != NULL)
+- ieee80211_dev_kfree_skb(&skb);
+ if (ni_or_null == NULL)
+ ieee80211_unref_node(&ni);
++discard:
++ if (skb != NULL)
++ ieee80211_dev_kfree_skb(&skb);
+ return type;
+ #undef HAS_SEQ
+ }
+@@ -929,16 +929,23 @@ int
+ ieee80211_input_all(struct ieee80211com *ic,
+ struct sk_buff *skb, int rssi, u_int64_t rtsf)
+ {
++ struct ieee80211_frame_min *wh = (struct ieee80211_frame_min *) skb->data;
+ struct ieee80211vap *vap;
+ int type = -1;
+
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ struct ieee80211_node *ni = NULL;
+ struct sk_buff *skb1;
+
+ if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+ continue;
+
++ if ((vap->iv_opmode == IEEE80211_M_HOSTAP) &&
++ !IEEE80211_IS_MULTICAST(wh->i_addr1))
++ continue;
++
++ ni = ieee80211_find_rxnode(ic, vap, wh);
+ if (TAILQ_NEXT(vap, iv_next) != NULL) {
+ skb1 = skb_copy(skb, GFP_ATOMIC);
+ if (skb1 == NULL) {
+@@ -950,8 +957,12 @@ ieee80211_input_all(struct ieee80211com
+ skb1 = skb;
+ skb = NULL;
+ }
+- type = ieee80211_input(vap, NULL, skb1, rssi, rtsf);
++ type = ieee80211_input(vap, ni, skb1, rssi, rtsf);
++ if (ni)
++ ieee80211_unref_node(&ni);
+ }
++
++out:
+ if (skb != NULL) /* no vaps, reclaim skb */
+ ieee80211_dev_kfree_skb(&skb);
+ return type;
+@@ -1147,11 +1158,9 @@ ieee80211_deliver_data(struct ieee80211_
+ * sending it will not work; just let it be
+ * delivered normally.
+ */
+- struct ieee80211_node *ni1 = ieee80211_find_node(
+- &vap->iv_ic->ic_sta, eh->ether_dhost);
++ struct ieee80211_node *ni1 = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni1 != NULL) {
+- if (ni1->ni_vap == vap &&
+- ieee80211_node_is_authorized(ni1) &&
++ if (ieee80211_node_is_authorized(ni1) &&
+ !ni1->ni_subif &&
+ ni1 != vap->iv_bss) {
+
+@@ -3520,6 +3529,7 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ (vap->iv_opmode == IEEE80211_M_WDS)) &&
+ (scan.capinfo & IEEE80211_CAPINFO_ESS))) {
+ struct ieee80211vap *avp = NULL;
++ int do_unref = 0;
+ int found = 0;
+
+ IEEE80211_LOCK_IRQ(vap->iv_ic);
+@@ -3553,10 +3563,12 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ ni->ni_associd |= 0xc000;
+ avp->iv_wdsnode = ieee80211_ref_node(ni);
+ IEEE80211_UNLOCK_IRQ(ic);
+- } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
++ } else if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
++ IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid)) {
+ /* Create a new entry in the neighbor table. */
+ ni = ieee80211_add_neighbor(vap, wh, &scan);
+ }
++ do_unref = 1;
+ } else {
+ /*
+ * Copy data from beacon to neighbor table.
+@@ -3595,6 +3607,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ ni->ni_rssi = rssi;
+ ni->ni_rtsf = rtsf;
+ ni->ni_last_rx = jiffies;
++ if (do_unref)
++ ieee80211_unref_node(&ni);
+ }
+ }
+ break;
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6589,9 +6589,8 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+
+ sc->sc_recv_mgmt(vap, ni_or_null, skb, subtype, rssi, rtsf);
+
+-
+ /* Lookup the new node if any (this grabs a reference to it) */
+- ni = ieee80211_find_rxnode(vap->iv_ic,
++ ni = ieee80211_find_rxnode(vap->iv_ic, vap,
+ (const struct ieee80211_frame_min *)skb->data);
+ if (ni == NULL) {
+ DPRINTF(sc, ATH_DEBUG_BEACON, "Dropping; node unknown.\n");
+@@ -6746,7 +6745,9 @@ ath_rx_poll(struct net_device *dev, int
+ struct ath_desc *ds;
+ struct ath_rx_status *rs;
+ struct sk_buff *skb = NULL;
++ struct ieee80211vap *vap;
+ struct ieee80211_node *ni;
++ const struct ieee80211_frame_min *wh;
+ unsigned int len;
+ int type;
+ u_int phyerr;
+@@ -6901,12 +6902,15 @@ rx_accept:
+ skb_trim(skb, skb->len - IEEE80211_CRC_LEN);
+
+ if (mic_fail) {
++ wh = (const struct ieee80211_frame_min *) skb->data;
++
+ /* Ignore control frames which are reported with mic error */
+- if ((((struct ieee80211_frame *)skb->data)->i_fc[0] &
++ if ((wh->i_fc[0] &
+ IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+ goto drop_micfail;
+
+- ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) skb->data);
++ vap = ieee80211_find_rxvap(ic, wh->i_addr1);
++ ni = ieee80211_find_rxnode(ic, vap, wh);
+
+ if (ni && ni->ni_table) {
+ ieee80211_check_mic(ni, skb);
+@@ -6968,11 +6972,24 @@ drop_micfail:
+ * for its use. If the sender is unknown spam the
+ * frame; it'll be dropped where it's not wanted.
+ */
+- if (rs->rs_keyix != HAL_RXKEYIX_INVALID &&
++ wh = (const struct ieee80211_frame_min *) skb->data;
++ if ((rs->rs_keyix != HAL_RXKEYIX_INVALID) &&
+ (ni = sc->sc_keyixmap[rs->rs_keyix]) != NULL) {
+ /* Fast path: node is present in the key map;
+ * grab a reference for processing the frame. */
+- ni = ieee80211_ref_node(ni);
++ ieee80211_ref_node(ni);
++ if ((ATH_GET_VAP_ID(wh->i_addr1) !=
++ ATH_GET_VAP_ID(ni->ni_vap->iv_myaddr)) ||
++ ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
++ IEEE80211_FC1_DIR_DSTODS)) {
++ /* key cache node lookup is fast, but it can
++ * lead to problems in multi-bss (foreign vap
++ * node reference) or wds (wdsap node ref instead
++ * of base ap node ref).
++ * use slowpath lookup in both cases
++ */
++ goto lookup_slowpath;
++ }
+ ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
+ type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+ ieee80211_unref_node(&ni);
+@@ -6981,24 +6998,39 @@ drop_micfail:
+ * No key index or no entry, do a lookup and
+ * add the node to the mapping table if possible.
+ */
+- ni = ieee80211_find_rxnode(ic,
+- (const struct ieee80211_frame_min *)skb->data);
++
++lookup_slowpath:
++ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
++ vap = NULL;
++ else
++ vap = ieee80211_find_rxvap(ic, wh->i_addr1);
++
++ if (vap)
++ ni = ieee80211_find_rxnode(ic, vap, wh);
++ else
++ ni = NULL;
++
+ if (ni != NULL) {
+ ieee80211_keyix_t keyix;
+
+ ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
+- type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
++ type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+ /*
+ * If the station has a key cache slot assigned
+ * update the key->node mapping table.
+ */
+ keyix = ni->ni_ucastkey.wk_keyix;
+ if (keyix != IEEE80211_KEYIX_NONE &&
+- sc->sc_keyixmap[keyix] == NULL)
++ sc->sc_keyixmap[keyix] == NULL) {
+ sc->sc_keyixmap[keyix] = ieee80211_ref_node(ni);
++ }
+ ieee80211_unref_node(&ni);
+- } else
+- type = ieee80211_input_all(ic, skb, rs->rs_rssi, bf->bf_tsf);
++ } else {
++ if (vap)
++ type = ieee80211_input(vap, NULL, skb, rs->rs_rssi, bf->bf_tsf);
++ else
++ type = ieee80211_input_all(ic, skb, rs->rs_rssi, bf->bf_tsf);
++ }
+ }
+
+ if (sc->sc_diversity) {
+--- a/net80211/ieee80211_node.h
++++ b/net80211/ieee80211_node.h
+@@ -286,15 +286,18 @@ struct ieee80211_node *ieee80211_find_no
+ const u_int8_t *);
+ #endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+
++struct ieee80211vap *
++ieee80211_find_rxvap(struct ieee80211com *ic, const u_int8_t *mac);
++
+ /* Returns a ieee80211_node* with refcount incremented, if found */
+ #ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_find_rxnode(_nt, _wh) \
+- ieee80211_find_rxnode_debug(_nt, _wh, __func__, __LINE__)
++#define ieee80211_find_rxnode(_nt, _vap, _wh) \
++ ieee80211_find_rxnode_debug(_nt, _vap, _wh, __func__, __LINE__)
+ struct ieee80211_node *ieee80211_find_rxnode_debug(struct ieee80211com *,
+- const struct ieee80211_frame_min *, const char *, int);
++ struct ieee80211vap *, const struct ieee80211_frame_min *, const char *, int);
+ #else
+ struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *,
+- const struct ieee80211_frame_min *);
++ struct ieee80211vap *, const struct ieee80211_frame_min *);
+ #endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+
+ /* Returns a ieee80211_node* with refcount incremented, if found */
diff --git a/package/madwifi/patches/394-probereq.patch b/package/madwifi/patches/394-probereq.patch
new file mode 100644
index 000000000..706d5a5ac
--- /dev/null
+++ b/package/madwifi/patches/394-probereq.patch
@@ -0,0 +1,64 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -3621,6 +3621,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
++ if (vap->iv_no_probereq)
++ return;
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* frame must be directed */
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX: stat */
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -651,6 +651,7 @@ enum {
+ IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */
+ IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */
+ IEEE80211_PARAM_MAXASSOC = 83, /* maximum associated stations */
++ IEEE80211_PARAM_PROBEREQ = 84, /* enable handling of probe requests */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -198,6 +198,7 @@ struct ieee80211vap {
+ u_int32_t iv_debug; /* debug msg flags */
+ struct ieee80211_stats iv_stats; /* statistics */
+
++ int iv_no_probereq;
+ int iv_max_nodes;
+ int iv_monitor_nods_only; /* in monitor mode only nods traffic */
+ int iv_monitor_txf_len; /* in monitor mode, truncate tx packets */
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2881,6 +2881,9 @@ ieee80211_ioctl_setparam(struct net_devi
+ else
+ vap->iv_max_nodes = value;
+ break;
++ case IEEE80211_PARAM_PROBEREQ:
++ vap->iv_no_probereq = !value;
++ break;
+ #ifdef ATH_REVERSE_ENGINEERING
+ case IEEE80211_PARAM_DUMPREGS:
+ ieee80211_dump_registers(dev, info, w, extra);
+@@ -3243,6 +3246,9 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_MAXASSOC:
+ param[0] = vap->iv_max_nodes;
+ break;
++ case IEEE80211_PARAM_PROBEREQ:
++ param[0] = !vap->iv_no_probereq;
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -5802,6 +5808,10 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maxassoc"},
+ { IEEE80211_PARAM_MAXASSOC,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_maxassoc"},
++ { IEEE80211_PARAM_PROBEREQ,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "probereq"},
++ { IEEE80211_PARAM_PROBEREQ,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_probereq"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
diff --git a/package/madwifi/patches/395-ath_ff_unmap.patch b/package/madwifi/patches/395-ath_ff_unmap.patch
new file mode 100644
index 000000000..85ddc7bd9
--- /dev/null
+++ b/package/madwifi/patches/395-ath_ff_unmap.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -13534,7 +13534,7 @@ cleanup_ath_buf(struct ath_softc *sc, st
+ bus_unmap_single(
+ sc->sc_bdev,
+ bf->bf_skbaddrff[i],
+- (direction == BUS_DMA_TODEVICE ?
++ (direction == BUS_DMA_FROMDEVICE ?
+ sc->sc_rxbufsize : ffskb->len),
+ direction);
+ bf->bf_skbaddrff[i] = 0;
diff --git a/package/madwifi/patches/396-napi_ff_fix.patch b/package/madwifi/patches/396-napi_ff_fix.patch
new file mode 100644
index 000000000..ca35d4cca
--- /dev/null
+++ b/package/madwifi/patches/396-napi_ff_fix.patch
@@ -0,0 +1,65 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6734,10 +6734,10 @@ ath_rx_poll(struct net_device *dev, int
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ struct ath_softc *sc = container_of(napi, struct ath_softc, sc_napi);
+ struct net_device *dev = sc->sc_dev;
+- u_int rx_limit = budget;
++ int rx_limit = budget;
+ #else
+ struct ath_softc *sc = dev->priv;
+- u_int rx_limit = min(dev->quota, *budget);
++ int rx_limit = min(dev->quota, *budget);
+ #endif
+ struct ath_buf *bf;
+ struct ieee80211com *ic = &sc->sc_ic;
+@@ -6780,13 +6780,15 @@ process_rx_again:
+ break;
+ }
+
+- if (rx_limit-- < 2) {
++ processed += ic->ic_recv;
++ rx_limit -= ic->ic_recv;
++ ic->ic_recv = 0;
++
++ /* keep a reserve for napi */
++ if (rx_limit < 4) {
+ early_stop = 1;
+ break;
+ }
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+- processed++;
+-#endif
+
+ skb = bf->bf_skb;
+ if (skb == NULL) {
+@@ -7074,8 +7076,8 @@ rx_next:
+ if (sc->sc_isr & HAL_INT_RX) {
+ u_int64_t hw_tsf = ath_hal_gettsf64(ah);
+ sc->sc_isr &= ~HAL_INT_RX;
+- local_irq_restore(flags);
+ ath_uapsd_processtriggers(sc, hw_tsf);
++ local_irq_restore(flags);
+ goto process_rx_again;
+ }
+ local_irq_restore(flags);
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1206,6 +1206,7 @@ ieee80211_deliver_data(struct ieee80211_
+ }
+ }
+
++ vap->iv_ic->ic_recv++;
+ if (skb != NULL) {
+ skb->dev = dev;
+
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -323,6 +323,7 @@ struct ieee80211com {
+ struct ifmedia ic_media; /* interface media config */
+ u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ struct timer_list ic_inact; /* mgmt/inactivity timer */
++ u_int ic_recv; /* frame received counter */
+
+ unsigned int ic_subifs;
+ u_int32_t ic_flags; /* state flags */
diff --git a/package/madwifi/patches/400-new_hal.patch b/package/madwifi/patches/400-new_hal.patch
new file mode 100644
index 000000000..12a59687b
--- /dev/null
+++ b/package/madwifi/patches/400-new_hal.patch
@@ -0,0 +1,153 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -606,6 +606,14 @@ ath_attach(u_int16_t devid, struct net_d
+ }
+ sc->sc_ah = ah;
+
++ /* WAR for AR7100 PCI bug */
++#if defined(CONFIG_ATHEROS_AR71XX) || defined(CONFIG_ATH79)
++ if ((ar_device(sc->devid) >= 5210) && (ar_device(sc->devid) < 5416)) {
++ ath_hal_setcapability(ah, HAL_CAP_DMABURST_RX, 0, HAL_DMABURST_4B, NULL);
++ ath_hal_setcapability(ah, HAL_CAP_DMABURST_TX, 0, HAL_DMABURST_4B, NULL);
++ }
++#endif
++
+ /*
+ * Check if the MAC has multi-rate retry support.
+ * We do this by trying to setup a fake extended
+@@ -7568,7 +7576,7 @@ ath_txq_setup(struct ath_softc *sc, int
+ if (qtype == HAL_TX_QUEUE_UAPSD)
+ qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE;
+ else
+- qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE |
++ qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXOKINT_ENABLE |
+ HAL_TXQ_TXDESCINT_ENABLE;
+ qnum = ath_hal_setuptxqueue(ah, qtype, &qi);
+ if (qnum == -1) {
+--- a/ath_hal/ah_os.c
++++ b/ath_hal/ah_os.c
+@@ -126,6 +126,13 @@ ath_hal_printf(struct ath_hal *ah, const
+ }
+ EXPORT_SYMBOL(ath_hal_printf);
+
++void __ahdecl
++ath_hal_printstr(struct ath_hal *ah, const char *str)
++{
++ printk("%s", str);
++}
++EXPORT_SYMBOL(ath_hal_printstr);
++
+ /*
+ * Format an Ethernet MAC for printing.
+ */
+--- a/ath_hal/ah_os.h
++++ b/ath_hal/ah_os.h
+@@ -156,69 +156,23 @@ extern u_int32_t __ahdecl ath_hal_getupt
+ #endif
+ #endif /* AH_BYTE_ORDER */
+
+-/*
+- * Some big-endian architectures don't set CONFIG_GENERIC_IOMAP, but fail to
+- * implement iowrite32be and ioread32be. Provide compatibility macros when
+- * it's needed.
+- *
+- * As of Linux 2.6.24, only MIPS, PARISC and PowerPC implement iowrite32be and
+- * ioread32be as functions.
+- *
+- * The downside or the replacement macros it that we may be byte-swapping data
+- * for the second time, so the native implementations should be preferred.
+- */
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)) && \
+- !defined(CONFIG_GENERIC_IOMAP) && (AH_BYTE_ORDER == AH_BIG_ENDIAN) && \
+- !defined(__mips__) && !defined(__hppa__) && !defined(__powerpc__)
+-# ifndef iowrite32be
+-# define iowrite32be(_val, _addr) iowrite32(swab32((_val)), (_addr))
+-# endif
+-# ifndef ioread32be
+-# define ioread32be(_addr) swab32(ioread32((_addr)))
+-# endif
+-#endif
++#define IS_SWAPPED(_ah, _reg) \
++ ((_ah)->ah_swapped && \
++ (((0x4000 <= (_reg)) && ((_reg) < 0x5000)) || \
++ ((0x7000 <= (_reg)) && ((_reg) < 0x8000))))
++
++#define SWAPREG(_ah, _reg, _val) \
++ (IS_SWAPPED(_ah, _reg) ? cpu_to_le32(_val) : (_val))
+
+ /*
+ * The register accesses are done using target-specific functions when
+ * debugging is enabled (AH_DEBUG) or it's explicitly requested for the target.
+- *
+- * The hardware registers use little-endian byte order natively. Big-endian
+- * systems are configured by HAL to enable hardware byte-swap of register reads
+- * and writes at reset. This avoid the need to byte-swap the data in software.
+- * However, the registers in a certain area from 0x4000 to 0x4fff (PCI clock
+- * domain registers) are not byte swapped!
+- *
+- * Since Linux I/O primitives default to little-endian operations, we only
+- * need to suppress byte-swapping on big-endian systems outside the area used
+- * by the PCI clock domain registers.
+ */
+-#if (AH_BYTE_ORDER == AH_BIG_ENDIAN)
+-#define is_reg_le(__reg) ((0x4000 <= (__reg) && (__reg) < 0x5000))
+-#else
+-#define is_reg_le(__reg) 1
+-#endif
+-
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+-#define _OS_REG_WRITE(_ah, _reg, _val) do { \
+- is_reg_le(_reg) ? \
+- iowrite32((_val), (_ah)->ah_sh + (_reg)) : \
+- iowrite32be((_val), (_ah)->ah_sh + (_reg)); \
+- } while (0)
+-#define _OS_REG_READ(_ah, _reg) \
+- (is_reg_le(_reg) ? \
+- ioread32((_ah)->ah_sh + (_reg)) : \
+- ioread32be((_ah)->ah_sh + (_reg)))
+-#else
+ #define _OS_REG_WRITE(_ah, _reg, _val) do { \
+- writel(is_reg_le(_reg) ? \
+- (_val) : cpu_to_le32(_val), \
+- (_ah)->ah_sh + (_reg)); \
+- } while (0)
++ __raw_writel(SWAPREG(_ah, _reg, _val), (_ah)->ah_sh + (_reg)); \
++} while (0)
+ #define _OS_REG_READ(_ah, _reg) \
+- (is_reg_le(_reg) ? \
+- readl((_ah)->ah_sh + (_reg)) : \
+- cpu_to_le32(readl((_ah)->ah_sh + (_reg))))
+-#endif /* KERNEL_VERSION(2,6,12) */
++ SWAPREG(_ah, _reg, __raw_readl((_ah)->ah_sh + (_reg)))
+
+ /*
+ * The functions in this section are not intended to be invoked by MadWifi
+--- a/ath/if_ath_hal.h
++++ b/ath/if_ath_hal.h
+@@ -778,17 +778,6 @@ static inline HAL_STATUS ath_hal_getcapa
+ return ret;
+ }
+
+-static inline HAL_BOOL ath_hal_radar_wait(struct ath_hal *ah, HAL_CHANNEL *a1)
+-{
+- HAL_BOOL ret;
+- ATH_HAL_LOCK_IRQ(ah->ah_sc);
+- ath_hal_set_function(__func__);
+- ret = ah->ah_radarWait(ah, a1);
+- ath_hal_set_function(NULL);
+- ATH_HAL_UNLOCK_IRQ(ah->ah_sc);
+- return ret;
+-}
+-
+ static inline HAL_BOOL ath_hal_setmcastfilterindex(struct ath_hal *ah,
+ u_int32_t index)
+ {
+@@ -1268,8 +1257,6 @@ static inline void ath_hal_dump_map(stru
+ /* HAL_STATUS ah_getCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE a1, u_int32_t capability, u_int32_t *result) */
+ __print_symbol("%s=ah_getCapability\n",
+ (unsigned long)ah->ah_getCapability);
+- /* HAL_BOOL ah_radarWait(struct ath_hal *ah, HAL_CHANNEL *a1) */
+- __print_symbol("%s=ah_radarWait\n", (unsigned long)ah->ah_radarWait);
+ /* HAL_BOOL ah_setMulticastFilterIndex(struct ath_hal *ah, u_int32_t index) */
+ __print_symbol("%s=ah_setMulticastFilterIndex\n",
+ (unsigned long)ah->ah_setMulticastFilterIndex);
diff --git a/package/madwifi/patches/401-changeset_r3602.patch b/package/madwifi/patches/401-changeset_r3602.patch
new file mode 100644
index 000000000..cf9c87966
--- /dev/null
+++ b/package/madwifi/patches/401-changeset_r3602.patch
@@ -0,0 +1,11 @@
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -348,6 +348,8 @@ typedef spinlock_t acl_lock_t;
+ /* __skb_append got a third parameter in 2.6.14 */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+ #define __skb_append(a,b,c) __skb_append(a, b)
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
++#define __skb_append(a,b,c) __skb_queue_after(c, a, b)
+ #endif
+
+ /*
diff --git a/package/madwifi/patches/402-changeset_r3603.patch b/package/madwifi/patches/402-changeset_r3603.patch
new file mode 100644
index 000000000..92f6e6ea4
--- /dev/null
+++ b/package/madwifi/patches/402-changeset_r3603.patch
@@ -0,0 +1,176 @@
+--- a/Makefile
++++ b/Makefile
+@@ -40,10 +40,7 @@
+ # Makefile for the HAL-based Atheros driver.
+ #
+
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)
+
+ ifneq (svnversion.h,$(MAKECMDGOALS))
+--- a/ath/Makefile
++++ b/ath/Makefile
+@@ -40,10 +40,7 @@
+ # Makefile for the Atheros WLAN driver.
+ #
+
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/..
+
+ ifeq ($(strip $(BUS)),AHB)
+--- a/ath_hal/Makefile
++++ b/ath_hal/Makefile
+@@ -40,10 +40,7 @@
+ # Makefile for the Atheros WLAN driver.
+ #
+
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/..
+
+ include $(TOP)/Makefile.inc
+--- a/ath_rate/Makefile
++++ b/ath_rate/Makefile
+@@ -1,7 +1,4 @@
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/..
+
+ obj-y := amrr/ onoe/ sample/ minstrel/
+--- a/ath_rate/amrr/Makefile
++++ b/ath_rate/amrr/Makefile
+@@ -40,10 +40,7 @@
+ #
+ # Makefile for the Atheros Rate Control Support.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/../..
+
+ obj-m += ath_rate_amrr.o
+--- a/ath_rate/minstrel/Makefile
++++ b/ath_rate/minstrel/Makefile
+@@ -38,10 +38,7 @@
+ #
+ # Makefile for the Atheros Rate Control Support.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/../..
+
+ obj-m += ath_rate_minstrel.o
+--- a/ath_rate/onoe/Makefile
++++ b/ath_rate/onoe/Makefile
+@@ -40,10 +40,7 @@
+ #
+ # Makefile for the Atheros Rate Control Support.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/../..
+
+ obj-m += ath_rate_onoe.o
+--- a/ath_rate/sample/Makefile
++++ b/ath_rate/sample/Makefile
+@@ -38,10 +38,7 @@
+ #
+ # Makefile for the Atheros Rate Control Support.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/../..
+
+ obj-m += ath_rate_sample.o
+--- a/net80211/Makefile
++++ b/net80211/Makefile
+@@ -39,10 +39,7 @@
+ #
+ # Makefile for the 802.11 WLAN modules.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/..
+ #
+ # There is one authenticator mechanism: an in-kernel implementation
+--- a/regression/Makefile
++++ b/regression/Makefile
+@@ -1,7 +1,4 @@
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/..
+
+ obj-y := ccmp/ tkip/ wep/
+--- a/regression/ccmp/Makefile
++++ b/regression/ccmp/Makefile
+@@ -1,10 +1,7 @@
+ #
+ # Makefile for the CCMP regression test.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/../..
+
+ obj-m += ath_test_ccmp.o
+--- a/regression/tkip/Makefile
++++ b/regression/tkip/Makefile
+@@ -1,10 +1,7 @@
+ #
+ # Makefile for the TKIP regression test.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/../..
+
+ obj-m += ath_test_tkip.o
+--- a/regression/wep/Makefile
++++ b/regression/wep/Makefile
+@@ -1,10 +1,7 @@
+ #
+ # Makefile for the WEP regression test.
+ #
+-ifeq ($(obj),)
+-obj= .
+-endif
+-
++obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/../..
+
+ obj-m += ath_test_wep.o
diff --git a/package/madwifi/patches/403-changeset_r3605.patch b/package/madwifi/patches/403-changeset_r3605.patch
new file mode 100644
index 000000000..57ce521c1
--- /dev/null
+++ b/package/madwifi/patches/403-changeset_r3605.patch
@@ -0,0 +1,70 @@
+--- a/include/compat.h
++++ b/include/compat.h
+@@ -182,6 +182,13 @@ static inline int timeval_compare(struct
+ #define DEV_ATH CTL_UNNUMBERED
+ #endif
+
++/* __skb_append got a third parameter in 2.6.14 */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
++#define __skb_queue_after(_list, _old, _new) __skb_append(_old, _new)
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
++#define __skb_queue_after(_list, _old, _new) __skb_append(_old, _new, _list)
++#endif
++
+ #endif /* __KERNEL__ */
+
+ #endif /* _ATH_COMPAT_H_ */
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -345,13 +345,6 @@ typedef spinlock_t acl_lock_t;
+ #define ACL_LOCK_CHECK(_as)
+ #endif
+
+-/* __skb_append got a third parameter in 2.6.14 */
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+-#define __skb_append(a,b,c) __skb_append(a, b)
+-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
+-#define __skb_append(a,b,c) __skb_queue_after(c, a, b)
+-#endif
+-
+ /*
+ * Per-node power-save queue definitions. Beware of control
+ * flow with IEEE80211_NODE_SAVEQ_LOCK/IEEE80211_NODE_SAVEQ_UNLOCK.
+@@ -395,16 +388,16 @@ typedef spinlock_t acl_lock_t;
+ _skb = __skb_dequeue(&(_ni)->ni_savedq); \
+ (_qlen) = skb_queue_len(&(_ni)->ni_savedq); \
+ } while (0)
+-#define _IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _skb, _qlen, _age) do {\
+- struct sk_buff *tail = skb_peek_tail(&(_ni)->ni_savedq);\
+- if (tail != NULL) { \
+- _age -= M_AGE_GET(tail); \
+- __skb_append(tail, _skb, &(_ni)->ni_savedq); \
+- } else { \
+- __skb_queue_head(&(_ni)->ni_savedq, _skb); \
+- } \
+- M_AGE_SET(_skb, _age); \
+- (_qlen) = skb_queue_len(&(_ni)->ni_savedq); \
++#define _IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _skb, _qlen, _age) do { \
++ struct sk_buff *tail = skb_peek_tail(&(_ni)->ni_savedq); \
++ if (tail != NULL) { \
++ _age -= M_AGE_GET(tail); \
++ __skb_queue_after(&(_ni)->ni_savedq, tail, _skb); \
++ } else { \
++ __skb_queue_head(&(_ni)->ni_savedq, _skb); \
++ } \
++ M_AGE_SET(_skb, _age); \
++ (_qlen) = skb_queue_len(&(_ni)->ni_savedq); \
+ } while (0)
+
+ /*
+--- a/net80211/ieee80211_power.c
++++ b/net80211/ieee80211_power.c
+@@ -243,7 +243,7 @@ ieee80211_pwrsave(struct sk_buff *skb)
+ tail = skb_peek_tail(&ni->ni_savedq);
+ if (tail != NULL) {
+ age -= M_AGE_GET(tail);
+- __skb_append(tail, skb, &ni->ni_savedq);
++ __skb_queue_after(&ni->ni_savedq, tail, skb);
+ } else
+ __skb_queue_head(&ni->ni_savedq, skb);
+ M_AGE_SET(skb, age);
diff --git a/package/madwifi/patches/404-linux24_fix.patch b/package/madwifi/patches/404-linux24_fix.patch
new file mode 100644
index 000000000..4ea20b3cb
--- /dev/null
+++ b/package/madwifi/patches/404-linux24_fix.patch
@@ -0,0 +1,15 @@
+--- a/ath_hal/Makefile
++++ b/ath_hal/Makefile
+@@ -78,10 +78,11 @@ endif
+ quiet_cmd_uudecode = UUDECODE $@
+ cmd_uudecode = $(obj)/uudecode -o $@ $<
+
+-$(obj)/$(TARGET).hal.o: $(HAL)/public/$(TARGET).hal.o.uu $(obj)/uudecode
+ ifdef LINUX24
++$(TARGET).hal.o: $(HAL)/public/$(TARGET).hal.o.uu $(obj)/uudecode
+ $(Q)$(obj)/uudecode -o $@ $<
+ else
++$(obj)/$(TARGET).hal.o: $(HAL)/public/$(TARGET).hal.o.uu $(obj)/uudecode
+ $(call if_changed,uudecode)
+ endif
+
diff --git a/package/madwifi/patches/405-retransmit_check.patch b/package/madwifi/patches/405-retransmit_check.patch
new file mode 100644
index 000000000..11e78aba8
--- /dev/null
+++ b/package/madwifi/patches/405-retransmit_check.patch
@@ -0,0 +1,22 @@
+--- a/net80211/ieee80211.h
++++ b/net80211/ieee80211.h
+@@ -174,8 +174,6 @@ struct ieee80211_ctlframe_addr2 {
+ #define IEEE80211_SEQ_SEQ_MASK 0xfff0
+ #define IEEE80211_SEQ_SEQ_SHIFT 4
+
+-#define IEEE80211_SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+-
+ #define IEEE80211_NWID_LEN 32
+
+ #define IEEE80211_QOS_TXOP 0x00ff
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -428,7 +428,7 @@ ieee80211_input(struct ieee80211vap * va
+ tid = 0;
+ rxseq = le16toh(*(__le16 *)wh->i_seq);
+ if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+- IEEE80211_SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
++ (rxseq == ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
diff --git a/package/madwifi/patches/406-monitor_r3711.patch b/package/madwifi/patches/406-monitor_r3711.patch
new file mode 100644
index 000000000..4e2c6aefb
--- /dev/null
+++ b/package/madwifi/patches/406-monitor_r3711.patch
@@ -0,0 +1,20 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6530,7 +6530,7 @@ ath_capture(struct net_device *dev, cons
+
+ /* Never copy the SKB, as it is ours on the RX side, and this is the
+ * last process on the TX side and we only modify our own headers. */
+- tskb = ath_skb_removepad(skb, 0 /* Copy SKB */);
++ tskb = ath_skb_removepad(skb, !tx /* Copy SKB */);
+ if (tskb == NULL) {
+ DPRINTF(sc, ATH_DEBUG_ANY,
+ "Dropping; ath_skb_removepad failed!\n");
+@@ -6538,6 +6538,8 @@ ath_capture(struct net_device *dev, cons
+ }
+
+ ieee80211_input_monitor(ic, tskb, bf, tx, tsf, sc);
++ if (tskb != skb)
++ ieee80211_dev_kfree_skb(&tskb);
+ }
+
+ /*
diff --git a/package/madwifi/patches/407-new_athinfo.patch b/package/madwifi/patches/407-new_athinfo.patch
new file mode 100644
index 000000000..6c512ad9e
--- /dev/null
+++ b/package/madwifi/patches/407-new_athinfo.patch
@@ -0,0 +1,2352 @@
+--- a/tools/ath_info.c
++++ b/tools/ath_info.c
+@@ -16,78 +16,8 @@
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+-/* So here is how it works:
+- *
+- * First compile...
+- *
+- * gcc ath_info.c -o ath_info
+- *
+- * then find card's physical address
+- *
+- * lspci -v
+- *
+- * 02:02.0 Ethernet controller: Atheros Communications, Inc. AR5212 802.11abg NIC (rev 01)
+- * Subsystem: Fujitsu Limited. Unknown device 1234
+- * Flags: bus master, medium devsel, latency 168, IRQ 23
+- * Memory at c2000000 (32-bit, non-prefetchable) [size=64K]
+- * Capabilities: [44] Power Management version 2
+- *
+- * address here is 0xc2000000
+- *
+- * load madwifi-ng or madwifi-old if not already loaded (be sure the
+- * interface is down!)
+- *
+- * modprobe ath_pci
+- *
+- * OR
+- *
+- * call:
+- * setpci -s 02:02.0 command=0x41f cache_line_size=0x10
+- *
+- * to enable access to the PCI device.
+- *
+- * and we run the thing...
+- *
+- * ./ath_info 0xc2000000
+- *
+- * In order to change the regdomain to 0, call:
+- *
+- * ./ath_info -w 0xc2000000 regdomain 0
+- *
+- * to change any PCI ID value, say:
+- *
+- * ./ath_info -w 0xc2000000 <name> X
+- *
+- * with <name> ::= pci_dev_id | pci_vendor_id | pci_class |
+- * pci_subsys_dev_id | pci_subsys_vendor_id
+- *
+- * With newer chipsets (>= AR5004x, i.e. MAC >= AR5213), Atheros introduced
+- * write protection on the EEPROM. On a GIGABYTE GN-WI01HT you can set GPIO 4
+- * to low to be able to write the EEPROM. This depends highly on the PCB layout,
+- * so there may be different GPIO used.
+- * This program currently sets GPIO 4 to low for a MAC >= AR5213, but you can
+- * override this with the -g option:
+- *
+- * ./ath_info -g 5:0 -w 0xc2000000 regdomain X
+- *
+- * would set GPIO 5 to low (and wouldn't touch GPIO 4). -g can be given several times.
+- *
+- * The write function is currently not tested with 5210 devices.
+- *
+- * Use at your own risk, entering a false device address will have really
+- * nasty results!
+- *
+- * Writing wrong values to the PCI id fields may prevent the driver from
+- * detecting the card!
+- *
+- * Transmitting on illegal frequencies may violate state laws. Stick to the local
+- * regulations!
+- *
+- * DISCLAIMER:
+- * The authors are in no case responsible for damaged hardware or violation of
+- * local laws by operating modified hardware.
+- *
+- */
++/* Try accepting 64-bit device address even with 32-bit userspace */
++#define _FILE_OFFSET_BITS 64
+
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -130,109 +60,103 @@ fprintf(stderr, "#ERR %s: " fmt "\n", __
+ */
+ #define AR5K_GPIODI 0x401c
+
+-/*
+- * Common silicon revision/version values
+- */
+-enum ath5k_srev_type {
+- AR5K_VERSION_VER,
+- AR5K_VERSION_REV,
+- AR5K_VERSION_RAD,
+-};
+-
+ struct ath5k_srev_name {
+ const char *sr_name;
+- enum ath5k_srev_type sr_type;
+- u_int sr_val;
++ u_int8_t sr_val;
+ };
+
+-#define AR5K_SREV_UNKNOWN 0xffff
+-
+ /* Known MAC revision numbers */
+-#define AR5K_SREV_VER_AR5210 0x00
+-#define AR5K_SREV_VER_AR5311 0x10
+-#define AR5K_SREV_VER_AR5311A 0x20
+-#define AR5K_SREV_VER_AR5311B 0x30
+-#define AR5K_SREV_VER_AR5211 0x40
+-#define AR5K_SREV_VER_AR5212 0x50
+-#define AR5K_SREV_VER_AR5213 0x55
+-#define AR5K_SREV_VER_AR5213A 0x59
+-#define AR5K_SREV_VER_AR2424 0xa0
+-#define AR5K_SREV_VER_AR5424 0xa3
+-#define AR5K_SREV_VER_AR5413 0xa4
+-#define AR5K_SREV_VER_AR5414 0xa5
+-#define AR5K_SREV_VER_AR5416 0xc0
+-#define AR5K_SREV_VER_AR5418 0xca
+-#define AR5K_SREV_VER_AR2425 0xe0
+-
+-/* Known PHY revision nymbers */
+-#define AR5K_SREV_RAD_5110 0x00
+-#define AR5K_SREV_RAD_5111 0x10
+-#define AR5K_SREV_RAD_5111A 0x15
+-#define AR5K_SREV_RAD_2111 0x20
+-#define AR5K_SREV_RAD_5112 0x30
+-#define AR5K_SREV_RAD_5112A 0x35
+-#define AR5K_SREV_RAD_2112 0x40
+-#define AR5K_SREV_RAD_2112A 0x45
+-#define AR5K_SREV_RAD_SC1 0x63 /* Found on 5413/5414 */
+-#define AR5K_SREV_RAD_SC2 0xa2 /* Found on 2424/5424 */
+-#define AR5K_SREV_RAD_5133 0xc0 /* MIMO found on 5418 */
+-
+-static const struct ath5k_srev_name ath5k_srev_names[] = {
+- {"5210", AR5K_VERSION_VER, AR5K_SREV_VER_AR5210},
+- {"5311", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311},
+- {"5311A", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311A},
+- {"5311B", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311B},
+- {"5211", AR5K_VERSION_VER, AR5K_SREV_VER_AR5211},
+- {"5212", AR5K_VERSION_VER, AR5K_SREV_VER_AR5212},
+- {"5213", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213},
+- {"5213A", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213A},
+- {"2424", AR5K_VERSION_VER, AR5K_SREV_VER_AR2424},
+- {"5424", AR5K_VERSION_VER, AR5K_SREV_VER_AR5424},
+- {"5413", AR5K_VERSION_VER, AR5K_SREV_VER_AR5413},
+- {"5414", AR5K_VERSION_VER, AR5K_SREV_VER_AR5414},
+- {"5416", AR5K_VERSION_VER, AR5K_SREV_VER_AR5416},
+- {"5418", AR5K_VERSION_VER, AR5K_SREV_VER_AR5418},
+- {"2425", AR5K_VERSION_VER, AR5K_SREV_VER_AR2425},
+- {"xxxxx", AR5K_VERSION_VER, AR5K_SREV_UNKNOWN},
+- {"5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110},
+- {"5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111},
+- {"2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111},
+- {"5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112},
+- {"5112a", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A},
+- {"2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112},
+- {"2112a", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A},
+- {"SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC1},
+- {"SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC2},
+- {"5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133},
+- {"xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN},
++#define AR5K_SREV_MAC_AR5210 0x00
++#define AR5K_SREV_MAC_AR5311 0x10
++#define AR5K_SREV_MAC_AR5311A 0x20
++#define AR5K_SREV_MAC_AR5311B 0x30
++#define AR5K_SREV_MAC_AR5211 0x40
++#define AR5K_SREV_MAC_AR5212 0x50
++#define AR5K_SREV_MAC_AR5213 0x55
++#define AR5K_SREV_MAC_AR5213A 0x59
++#define AR5K_SREV_MAC_AR5513 0x61
++#define AR5K_SREV_MAC_AR2413 0x78
++#define AR5K_SREV_MAC_AR2414 0x79
++#define AR5K_SREV_MAC_AR2424 0xa0
++#define AR5K_SREV_MAC_AR5424 0xa3
++#define AR5K_SREV_MAC_AR5413 0xa4
++#define AR5K_SREV_MAC_AR5414 0xa5
++#define AR5K_SREV_MAC_AR5416 0xc0
++#define AR5K_SREV_MAC_AR5418 0xca
++#define AR5K_SREV_MAC_AR2425 0xe2
++
++/* Known PHY revision numbers */
++#define AR5K_SREV_PHY_5110 0x00
++#define AR5K_SREV_PHY_5111 0x10
++#define AR5K_SREV_PHY_5111A 0x15
++#define AR5K_SREV_PHY_2111 0x20
++#define AR5K_SREV_PHY_5112 0x30
++#define AR5K_SREV_PHY_5112A 0x35
++#define AR5K_SREV_PHY_2112 0x40
++#define AR5K_SREV_PHY_2112A 0x45
++#define AR5K_SREV_PHY_SC0 0x56 /* Found on 2413/2414 */
++#define AR5K_SREV_PHY_SC1 0x63 /* Found on 5413/5414 */
++#define AR5K_SREV_PHY_SC2 0xa2 /* Found on 2424/5424 */
++#define AR5K_SREV_PHY_5133 0xc0 /* MIMO found on 5418 */
++
++static const struct ath5k_srev_name ath5k_mac_names[] = {
++ {"5210", AR5K_SREV_MAC_AR5210},
++ {"5311", AR5K_SREV_MAC_AR5311},
++ {"5311A", AR5K_SREV_MAC_AR5311A},
++ {"5311B", AR5K_SREV_MAC_AR5311B},
++ {"5211", AR5K_SREV_MAC_AR5211},
++ {"5212", AR5K_SREV_MAC_AR5212},
++ {"5213", AR5K_SREV_MAC_AR5213},
++ {"5213A", AR5K_SREV_MAC_AR5213A},
++ {"2413", AR5K_SREV_MAC_AR2413},
++ {"2414", AR5K_SREV_MAC_AR2414},
++ {"2424", AR5K_SREV_MAC_AR2424},
++ {"5424", AR5K_SREV_MAC_AR5424},
++ {"5413", AR5K_SREV_MAC_AR5413},
++ {"5414", AR5K_SREV_MAC_AR5414},
++ {"5416", AR5K_SREV_MAC_AR5416},
++ {"5418", AR5K_SREV_MAC_AR5418},
++ {"2425", AR5K_SREV_MAC_AR2425},
++};
++
++static const struct ath5k_srev_name ath5k_phy_names[] = {
++ {"5110", AR5K_SREV_PHY_5110},
++ {"5111", AR5K_SREV_PHY_5111},
++ {"2111", AR5K_SREV_PHY_2111},
++ {"5112", AR5K_SREV_PHY_5112},
++ {"5112A", AR5K_SREV_PHY_5112A},
++ {"2112", AR5K_SREV_PHY_2112},
++ {"2112A", AR5K_SREV_PHY_2112A},
++ {"SChip", AR5K_SREV_PHY_SC0},
++ {"SChip", AR5K_SREV_PHY_SC1},
++ {"SChip", AR5K_SREV_PHY_SC2},
++ {"5133", AR5K_SREV_PHY_5133},
+ };
+
+ /*
+ * Silicon revision register
+ */
+ #define AR5K_SREV 0x4020 /* Register Address */
+-#define AR5K_SREV_REV 0x0000000f /* Mask for revision */
+-#define AR5K_SREV_REV_S 0
+-#define AR5K_SREV_VER 0x000000ff /* Mask for version */
+-#define AR5K_SREV_VER_S 4
++#define AR5K_SREV_VER 0x000000f0 /* Mask for version */
++#define AR5K_SREV_REV 0x000000ff /* Mask for revision */
+
+ /*
+ * PHY chip revision register
+ */
+-#define AR5K_PHY_CHIP_ID 0x9818
++#define AR5K_PHY_CHIP_ID 0x9818
+
+ /*
+ * PHY register
+ */
+-#define AR5K_PHY_BASE 0x9800
+-#define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2))
++#define AR5K_PHY_BASE 0x9800
++#define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2))
+ #define AR5K_PHY_SHIFT_2GHZ 0x00004007
+ #define AR5K_PHY_SHIFT_5GHZ 0x00000007
+
+ #define AR5K_RESET_CTL 0x4000 /* Register Address */
+ #define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */
+ #define AR5K_RESET_CTL_DMA 0x00000002 /* DMA (Rx/Tx) reset -5210 only */
+-#define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset (5211/5212) */
++#define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset (5211/5212) */
+ #define AR5K_RESET_CTL_MAC 0x00000004 /* MAC reset (PCU+Baseband?) -5210 only */
+ #define AR5K_RESET_CTL_PHY 0x00000008 /* PHY reset -5210 only */
+ #define AR5K_RESET_CTL_PCI 0x00000010 /* PCI Core reset (interrupts etc) */
+@@ -253,7 +177,7 @@ static const struct ath5k_srev_name ath5
+ #define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* not on 5210 */
+
+ #define AR5K_PCICFG 0x4010 /* Register Address */
+-#define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */
++#define AR5K_PCICFG_EEAE 0x00000001 /* EEPROM access enable [5210] */
+ #define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */
+ #define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */
+ #define AR5K_PCICFG_EESIZE_S 3
+@@ -264,26 +188,118 @@ static const struct ath5k_srev_name ath5
+
+ #define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status (5210) */
+
+-#define AR5K_EEPROM_BASE 0x6000
++#define AR5K_EEPROM_BASE 0x6000
+
+-#define AR5K_EEPROM_MAGIC 0x003d /* Offset for EEPROM Magic number */
++/*
++ * Common AR5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE)
++ */
++#define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */
+ #define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */
+ #define AR5K_EEPROM_MAGIC_5212 0x0000145c /* 5212 */
+ #define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */
+ #define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */
+
++#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */
++#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */
++#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */
++#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */
++#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008
++#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */
++#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020
++#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */
++#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080
++#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */
++#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200
++#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */
++#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800
++#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */
++#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000
++#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */
++#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000
++#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */
++#define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */
++#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
++#define AR5K_EEPROM_INFO_CKSUM 0xffff
++#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n))
++
++#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */
++#define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */
++#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2GHz (AR5211_rfregs) */
++#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */
++#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
++#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain ee_cck_ofdm_power_delta (eeprom_read_modes) */
++#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc*, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
++#define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */
++#define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */
++#define AR5K_EEPROM_VERSION_4_3 0x4003
++#define AR5K_EEPROM_VERSION_4_4 0x4004
++#define AR5K_EEPROM_VERSION_4_5 0x4005
++#define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */
++#define AR5K_EEPROM_VERSION_4_7 0x3007
++
++#define AR5K_EEPROM_MODE_11A 0
++#define AR5K_EEPROM_MODE_11B 1
++#define AR5K_EEPROM_MODE_11G 2
++
++#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */
++#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1)
++#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1)
++#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1)
++#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2GHz (?) */
++#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */
++#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7)
++#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5GHz (?) */
++#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
++
++/* Misc values available since EEPROM 4.0 */
++#define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4)
++#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
++#define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1)
++#define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1)
++#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
++#define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5)
++#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
++#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1)
++
++#define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c
++#define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2
++#define AR5K_EEPROM_RFKILL_POLARITY 0x00000002
++#define AR5K_EEPROM_RFKILL_POLARITY_S 1
++
++/* Newer EEPROMs are using a different offset */
++#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \
++ (((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0)
++
++#define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3)
++#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((int8_t)(((_v) >> 8) & 0xff))
++#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((int8_t)((_v) & 0xff))
++
++/* calibration settings */
++#define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4)
++#define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2)
++#define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d)
++#define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */
++#define AR5K_EEPROM_CHANNELS_5GHZ(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* List of calibrated 5GHz chans */
++#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_CHANNELS_5GHZ(_v) + 0x0055, 0x0000)
++#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_CHANNELS_5GHZ(_v) + 0x0065, 0x0010)
++#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_CHANNELS_5GHZ(_v) + 0x0069, 0x0014)
++
++/* [3.1 - 3.3] */
++#define AR5K_EEPROM_OBDB0_2GHZ 0x00ec
++#define AR5K_EEPROM_OBDB1_2GHZ 0x00ed
++
+ /*
+ * EEPROM data register
+ */
+ #define AR5K_EEPROM_DATA_5211 0x6004
+ #define AR5K_EEPROM_DATA_5210 0x6800
+-#define AR5K_EEPROM_DATA (mac_version == AR5K_SREV_VER_AR5210 ? \
++#define AR5K_EEPROM_DATA (mac_version == AR5K_SREV_MAC_AR5210 ? \
+ AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211)
+
+ /*
+ * EEPROM command register
+ */
+-#define AR5K_EEPROM_CMD 0x6008 /* Register Addres */
++#define AR5K_EEPROM_CMD 0x6008 /* Register Address */
+ #define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */
+ #define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */
+ #define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */
+@@ -291,43 +307,163 @@ static const struct ath5k_srev_name ath5
+ /*
+ * EEPROM status register
+ */
+-#define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */
+-#define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */
+-#define AR5K_EEPROM_STATUS (mac_version == AR5K_SREV_VER_AR5210 ? \
++#define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */
++#define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */
++#define AR5K_EEPROM_STATUS (mac_version == AR5K_SREV_MAC_AR5210 ? \
+ AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211)
+ #define AR5K_EEPROM_STAT_RDERR 0x00000001 /* EEPROM read failed */
+ #define AR5K_EEPROM_STAT_RDDONE 0x00000002 /* EEPROM read successful */
+ #define AR5K_EEPROM_STAT_WRERR 0x00000004 /* EEPROM write failed */
+ #define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */
+
+-#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* Offset for EEPROM regulatory domain */
+-#define AR5K_EEPROM_INFO_BASE 0x00c0 /* Offset for EEPROM header */
+-#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
+-#define AR5K_EEPROM_INFO_CKSUM 0xffff
+-#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n))
+-#define AR5K_EEPROM_MODE_11A 0
+-#define AR5K_EEPROM_MODE_11B 1
+-#define AR5K_EEPROM_MODE_11G 2
++/*
++ * EEPROM config register (?)
++ */
++#define AR5K_EEPROM_CFG 0x6010
+
+-#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1)
++/* Some EEPROM defines */
++#define AR5K_EEPROM_EEP_SCALE 100
++#define AR5K_EEPROM_EEP_DELTA 10
++#define AR5K_EEPROM_N_MODES 3
++#define AR5K_EEPROM_N_5GHZ_CHAN 10
++#define AR5K_EEPROM_N_2GHZ_CHAN 3
++#define AR5K_EEPROM_MAX_CHAN 10
++#define AR5K_EEPROM_N_PCDAC 11
++#define AR5K_EEPROM_N_TEST_FREQ 8
++#define AR5K_EEPROM_N_EDGES 8
++#define AR5K_EEPROM_N_INTERCEPTS 11
++#define AR5K_EEPROM_FREQ_M(_v) AR5K_EEPROM_OFF(_v, 0x7f, 0xff)
++#define AR5K_EEPROM_PCDAC_M 0x3f
++#define AR5K_EEPROM_PCDAC_START 1
++#define AR5K_EEPROM_PCDAC_STOP 63
++#define AR5K_EEPROM_PCDAC_STEP 1
++#define AR5K_EEPROM_NON_EDGE_M 0x40
++#define AR5K_EEPROM_CHANNEL_POWER 8
++#define AR5K_EEPROM_N_OBDB 4
++#define AR5K_EEPROM_OBDB_DIS 0xffff
++#define AR5K_EEPROM_CHANNEL_DIS 0xff
++#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10)
++#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32)
++#define AR5K_EEPROM_MAX_CTLS 32
++#define AR5K_EEPROM_N_XPD_PER_CHANNEL 4
++#define AR5K_EEPROM_N_XPD0_POINTS 4
++#define AR5K_EEPROM_N_XPD3_POINTS 3
++#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35
++#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55
++#define AR5K_EEPROM_POWER_M 0x3f
++#define AR5K_EEPROM_POWER_MIN 0
++#define AR5K_EEPROM_POWER_MAX 3150
++#define AR5K_EEPROM_POWER_STEP 50
++#define AR5K_EEPROM_POWER_TABLE_SIZE 64
++#define AR5K_EEPROM_N_POWER_LOC_11B 4
++#define AR5K_EEPROM_N_POWER_LOC_11G 6
++#define AR5K_EEPROM_I_GAIN 10
++#define AR5K_EEPROM_CCK_OFDM_DELTA 15
++#define AR5K_EEPROM_N_IQ_CAL 2
++
++enum ath5k_ant_setting {
++ AR5K_ANT_VARIABLE = 0, /* variable by programming */
++ AR5K_ANT_FIXED_A = 1, /* fixed to 11a frequencies */
++ AR5K_ANT_FIXED_B = 2, /* fixed to 11b frequencies */
++ AR5K_ANT_MAX = 3,
++};
+
+-#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */
+-#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1) /* Device has a support */
+-#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1) /* Device has b support */
+-#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1) /* Device has g support */
+-#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */
+-#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */
+-#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7)
+-#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz (?) */
+-#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
++/* Per channel calibration data, used for power table setup */
++struct ath5k_chan_pcal_info {
++ u_int16_t freq; /* Frequency */
++ /* Power levels in dBm * 4 units */
++ int16_t pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
++ int16_t pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
++ /* PCDAC tables in dBm * 2 units */
++ u_int16_t pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
++ u_int16_t pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
++ /* Max available power */
++ u_int16_t max_pwr;
++};
+
+-/* Misc values available since EEPROM 4.0 */
+-#define AR5K_EEPROM_MISC0 0x00c4
+-#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
+-#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
+-#define AR5K_EEPROM_MISC1 0x00c5
+-#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
+-#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1)
++/* Per rate calibration data for each mode, used for power table setup */
++struct ath5k_rate_pcal_info {
++ u_int16_t freq; /* Frequency */
++ /* Power level for 6-24Mbit/s rates */
++ u_int16_t target_power_6to24;
++ /* Power level for 36Mbit rate */
++ u_int16_t target_power_36;
++ /* Power level for 48Mbit rate */
++ u_int16_t target_power_48;
++ /* Power level for 54Mbit rate */
++ u_int16_t target_power_54;
++};
++
++/* EEPROM calibration data */
++struct ath5k_eeprom_info {
++
++ /* Header information */
++ u_int16_t ee_magic;
++ u_int16_t ee_protect;
++ u_int16_t ee_regdomain;
++ u_int16_t ee_version;
++ u_int16_t ee_header;
++ u_int16_t ee_ant_gain;
++ u_int16_t ee_misc0;
++ u_int16_t ee_misc1;
++ u_int16_t ee_cck_ofdm_gain_delta;
++ u_int16_t ee_cck_ofdm_power_delta;
++ u_int16_t ee_scaled_cck_delta;
++
++ /* Used for tx thermal adjustment (eeprom_init, rfregs) */
++ u_int16_t ee_tx_clip;
++ u_int16_t ee_pwd_84;
++ u_int16_t ee_pwd_90;
++ u_int16_t ee_gain_select;
++
++ /* RF Calibration settings (reset, rfregs) */
++ u_int16_t ee_i_cal[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_q_cal[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_fixed_bias[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_turbo_max_power[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_xr_power[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_switch_settling[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_ant_tx_rx[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC];
++ u_int16_t ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
++ u_int16_t ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
++ u_int16_t ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_thr_62[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_xlna_gain[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_xpd[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_x_gain[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_i_gain[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_margin_tx_rx[AR5K_EEPROM_N_MODES];
++
++ /* Power calibration data */
++ u_int16_t ee_false_detect[AR5K_EEPROM_N_MODES];
++ u_int16_t ee_cal_piers_a;
++ struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
++ u_int16_t ee_cal_piers_b;
++ struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN];
++ u_int16_t ee_cal_piers_g;
++ struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN];
++ /* Per rate target power levels */
++ u_int16_t ee_rate_target_pwr_num_a;
++ struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
++ u_int16_t ee_rate_target_pwr_num_b;
++ struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN];
++ u_int16_t ee_rate_target_pwr_num_g;
++ struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN];
++
++ /* Conformance test limits (Unused) */
++ u_int16_t ee_ctls;
++ u_int16_t ee_ctl[AR5K_EEPROM_MAX_CTLS];
++
++ /* Noise Floor Calibration settings */
++ int16_t ee_noise_floor_thr[AR5K_EEPROM_N_MODES];
++ int8_t ee_adc_desired_size[AR5K_EEPROM_N_MODES];
++ int8_t ee_pga_desired_size[AR5K_EEPROM_N_MODES];
++
++ u_int32_t ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
++};
+
+ /*
+ * Read data by masking
+@@ -350,7 +486,6 @@ static const struct ath5k_srev_name ath5
+ (*((volatile u_int32_t *)(mem + (_reg))) = (_val))
+ #endif
+
+-
+ #define AR5K_REG_ENABLE_BITS(_reg, _flags) \
+ AR5K_REG_WRITE(_reg, AR5K_REG_READ(_reg) | (_flags))
+
+@@ -359,7 +494,12 @@ static const struct ath5k_srev_name ath5
+
+ #define AR5K_TUNE_REGISTER_TIMEOUT 20000
+
+-/* names for eeprom fields */
++#define AR5K_EEPROM_READ(_o, _v) do { \
++ if ((ret = ath5k_hw_eeprom_read(mem, (_o), &(_v), mac_version)) != 0) \
++ return (ret); \
++} while (0)
++
++/* Names for EEPROM fields */
+ struct eeprom_entry {
+ const char *name;
+ int addr;
+@@ -375,8 +515,6 @@ static const struct eeprom_entry eeprom_
+ {"regdomain", AR5K_EEPROM_REG_DOMAIN},
+ };
+
+-static const int eeprom_addr_len = sizeof(eeprom_addr) / sizeof(eeprom_addr[0]);
+-
+ static int force_write = 0;
+ static int verbose = 0;
+
+@@ -398,8 +536,8 @@ static u_int32_t ath5k_hw_bitswap(u_int3
+ /*
+ * Get the PHY Chip revision
+ */
+-static u_int16_t
+-ath5k_hw_radio_revision(u_int16_t mac_version, void *mem, u_int8_t chip)
++static u_int16_t ath5k_hw_radio_revision(u_int16_t mac_version, void *mem,
++ u_int8_t chip)
+ {
+ int i;
+ u_int32_t srev;
+@@ -427,7 +565,7 @@ ath5k_hw_radio_revision(u_int16_t mac_ve
+ for (i = 0; i < 8; i++)
+ AR5K_REG_WRITE(AR5K_PHY(0x20), 0x00010000);
+
+- if (mac_version == AR5K_SREV_VER_AR5210) {
++ if (mac_version == AR5K_SREV_MAC_AR5210) {
+ srev = AR5K_REG_READ(AR5K_PHY(256) >> 28) & 0xf;
+
+ ret = (u_int16_t)ath5k_hw_bitswap(srev, 4) + 1;
+@@ -447,9 +585,8 @@ ath5k_hw_radio_revision(u_int16_t mac_ve
+ /*
+ * Write to EEPROM
+ */
+-static int
+-ath5k_hw_eeprom_write(void *mem, u_int32_t offset, u_int16_t data,
+- u_int8_t mac_version)
++static int ath5k_hw_eeprom_write(void *mem, u_int32_t offset, u_int16_t data,
++ u_int8_t mac_version)
+ {
+ u_int32_t status, timeout;
+
+@@ -457,7 +594,7 @@ ath5k_hw_eeprom_write(void *mem, u_int32
+ * Initialize EEPROM access
+ */
+
+- if (mac_version == AR5K_SREV_VER_AR5210) {
++ if (mac_version == AR5K_SREV_MAC_AR5210) {
+
+ AR5K_REG_ENABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_EEAE);
+
+@@ -466,7 +603,7 @@ ath5k_hw_eeprom_write(void *mem, u_int32
+
+ } else {
+ /* not 5210 */
+- /* reset eeprom access */
++ /* reset EEPROM access */
+ AR5K_REG_WRITE(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_RESET);
+ usleep(5);
+
+@@ -484,7 +621,7 @@ ath5k_hw_eeprom_write(void *mem, u_int32
+ status = AR5K_REG_READ(AR5K_EEPROM_STATUS);
+ if (status & AR5K_EEPROM_STAT_WRDONE) {
+ if (status & AR5K_EEPROM_STAT_WRERR) {
+- err("eeprom write access to 0x%04x failed",
++ err("EEPROM write access to 0x%04x failed",
+ offset);
+ return 1;
+ }
+@@ -499,16 +636,15 @@ ath5k_hw_eeprom_write(void *mem, u_int32
+ /*
+ * Read from EEPROM
+ */
+-static int
+-ath5k_hw_eeprom_read(void *mem, u_int32_t offset, u_int16_t *data,
+- u_int8_t mac_version)
++static int ath5k_hw_eeprom_read(void *mem, u_int32_t offset, u_int16_t *data,
++ u_int8_t mac_version)
+ {
+ u_int32_t status, timeout;
+
+ /*
+ * Initialize EEPROM access
+ */
+- if (mac_version == AR5K_SREV_VER_AR5210) {
++ if (mac_version == AR5K_SREV_MAC_AR5210) {
+ AR5K_REG_ENABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_EEAE);
+ (void)AR5K_REG_READ(AR5K_EEPROM_BASE + (4 * offset));
+ } else {
+@@ -531,50 +667,701 @@ ath5k_hw_eeprom_read(void *mem, u_int32_
+ return 1;
+ }
+
+-static const char *ath5k_hw_get_part_name(enum ath5k_srev_type type,
+- u_int32_t val)
++/*
++ * Translate binary channel representation in EEPROM to frequency
++ */
++static u_int16_t ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee,
++ u_int16_t bin, unsigned int mode)
+ {
+- const char *name = "xxxxx";
+- int i;
++ u_int16_t val;
+
+- for (i = 0; i < ARRAY_SIZE(ath5k_srev_names); i++) {
+- if (ath5k_srev_names[i].sr_type != type ||
+- ath5k_srev_names[i].sr_val == AR5K_SREV_UNKNOWN)
+- continue;
+- if ((val & 0xff) < ath5k_srev_names[i + 1].sr_val) {
+- name = ath5k_srev_names[i].sr_name;
++ if (bin == AR5K_EEPROM_CHANNEL_DIS)
++ return bin;
++
++ if (mode == AR5K_EEPROM_MODE_11A) {
++ if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
++ val = (5 * bin) + 4800;
++ else
++ val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 :
++ (bin * 10) + 5100;
++ } else {
++ if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
++ val = bin + 2300;
++ else
++ val = bin + 2400;
++ }
++
++ return val;
++}
++
++/*
++ * Read antenna info from EEPROM
++ */
++static int ath5k_eeprom_read_ants(void *mem, u_int8_t mac_version,
++ struct ath5k_eeprom_info *ee,
++ u_int32_t *offset, unsigned int mode)
++{
++ u_int32_t o = *offset;
++ u_int16_t val;
++ int ret, i = 0;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_switch_settling[mode] = (val >> 8) & 0x7f;
++ ee->ee_ant_tx_rx[mode] = (val >> 2) & 0x3f;
++ ee->ee_ant_control[mode][i] = (val << 4) & 0x3f;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf;
++ ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f;
++ ee->ee_ant_control[mode][i++] = val & 0x3f;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f;
++ ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f;
++ ee->ee_ant_control[mode][i] = (val << 2) & 0x3f;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3;
++ ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f;
++ ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f;
++ ee->ee_ant_control[mode][i] = (val << 4) & 0x3f;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf;
++ ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f;
++ ee->ee_ant_control[mode][i++] = val & 0x3f;
++
++ /* Get antenna modes */
++ ee->ee_antenna[mode][0] =
++ (ee->ee_ant_control[mode][0] << 4) | 0x1;
++ ee->ee_antenna[mode][AR5K_ANT_FIXED_A] =
++ ee->ee_ant_control[mode][1] |
++ (ee->ee_ant_control[mode][2] << 6) |
++ (ee->ee_ant_control[mode][3] << 12) |
++ (ee->ee_ant_control[mode][4] << 18) |
++ (ee->ee_ant_control[mode][5] << 24);
++ ee->ee_antenna[mode][AR5K_ANT_FIXED_B] =
++ ee->ee_ant_control[mode][6] |
++ (ee->ee_ant_control[mode][7] << 6) |
++ (ee->ee_ant_control[mode][8] << 12) |
++ (ee->ee_ant_control[mode][9] << 18) |
++ (ee->ee_ant_control[mode][10] << 24);
++
++ /* return new offset */
++ *offset = o;
++
++ return 0;
++}
++
++/*
++ * Read supported modes from EEPROM
++ */
++static int ath5k_eeprom_read_modes(void *mem, u_int8_t mac_version,
++ struct ath5k_eeprom_info *ee,
++ u_int32_t *offset, unsigned int mode)
++{
++ u_int32_t o = *offset;
++ u_int16_t val;
++ int ret;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff;
++ ee->ee_thr_62[mode] = val & 0xff;
++
++ if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2)
++ ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff;
++ ee->ee_tx_frm2xpa_enable[mode] = val & 0xff;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff;
++
++ if ((val & 0xff) & 0x80)
++ ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1);
++ else
++ ee->ee_noise_floor_thr[mode] = val & 0xff;
++
++ if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2)
++ ee->ee_noise_floor_thr[mode] =
++ mode == AR5K_EEPROM_MODE_11A ? -54 : -1;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_xlna_gain[mode] = (val >> 5) & 0xff;
++ ee->ee_x_gain[mode] = (val >> 1) & 0xf;
++ ee->ee_xpd[mode] = val & 0x1;
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0)
++ ee->ee_fixed_bias[mode] = (val >> 13) & 0x1;
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_false_detect[mode] = (val >> 6) & 0x7f;
++
++ if (mode == AR5K_EEPROM_MODE_11A)
++ ee->ee_xr_power[mode] = val & 0x3f;
++ else {
++ ee->ee_ob[mode][0] = val & 0x7;
++ ee->ee_db[mode][0] = (val >> 3) & 0x7;
++ }
++ }
++
++ if (ee->ee_version < AR5K_EEPROM_VERSION_3_4) {
++ ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN;
++ ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA;
++ } else {
++ ee->ee_i_gain[mode] = (val >> 13) & 0x7;
++
++ AR5K_EEPROM_READ(o++, val);
++ ee->ee_i_gain[mode] |= (val << 3) & 0x38;
++
++ if (mode == AR5K_EEPROM_MODE_11G)
++ ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff;
++ }
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0 &&
++ mode == AR5K_EEPROM_MODE_11A) {
++ ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
++ ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
++ }
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_6 &&
++ mode == AR5K_EEPROM_MODE_11G)
++ ee->ee_scaled_cck_delta = (val >> 11) & 0x1f;
++
++ /* return new offset */
++ *offset = o;
++
++ return 0;
++}
++
++/*
++ * Read per channel calibration info from EEPROM
++ * This doesn't work on 2112+ chips (EEPROM versions >= 4.6),
++ * I only tested it on 5213 + 5112. This is still work in progress...
++ *
++ * This info is used to calibrate the baseband power table. Imagine
++ * that for each channel there is a power curve that's hw specific
++ * (depends on amplifier) and we try to "correct" this curve using offests
++ * we pass on to phy chip (baseband -> before amplifier) so that it can
++ * use acurate power values when setting tx power (takes amplifier's performance
++ * on each channel into account).
++ *
++ * EEPROM provides us with the offsets for some pre-calibrated channels
++ * and we have to scale (to create the full table for these channels) and
++ * interpolate (in order to create the table for any channel).
++ */
++static int ath5k_eeprom_read_pcal_info(void *mem, u_int8_t mac_version,
++ struct ath5k_eeprom_info *ee,
++ u_int32_t *offset, unsigned int mode)
++{
++ u_int32_t o = *offset;
++ unsigned int i, c;
++ int ret;
++ u_int16_t val;
++ struct ath5k_chan_pcal_info *chan_pcal_info;
++ u_int16_t cal_piers;
++
++ switch (mode) {
++ case AR5K_EEPROM_MODE_11A:
++ chan_pcal_info = ee->ee_pwr_cal_a;
++ cal_piers = ee->ee_cal_piers_a;
++ break;
++ case AR5K_EEPROM_MODE_11B:
++ chan_pcal_info = ee->ee_pwr_cal_b;
++ cal_piers = ee->ee_cal_piers_b;
++ break;
++ case AR5K_EEPROM_MODE_11G:
++ chan_pcal_info = ee->ee_pwr_cal_g;
++ cal_piers = ee->ee_cal_piers_g;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ for (i = 0; i < cal_piers; i++) {
++ /* Power values in dBm * 4 */
++ for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
++ AR5K_EEPROM_READ(o++, val);
++ chan_pcal_info[i].pwr_x0[c] = (val & 0xff);
++ chan_pcal_info[i].pwr_x0[++c] = ((val >> 8) & 0xff);
++ }
++
++ /* PCDAC steps (dBm * 2) */
++ AR5K_EEPROM_READ(o++, val);
++ chan_pcal_info[i].pcdac_x0[1] = (val & 0x1f);
++ chan_pcal_info[i].pcdac_x0[2] = ((val >> 5) & 0x1f);
++ chan_pcal_info[i].pcdac_x0[3] = ((val >> 10) & 0x1f);
++
++ /* No idea what these power levels are for (4 xpds ?)
++ I got zeroes on my card and the EEPROM info
++ dumps we found on the net also have weird values */
++ AR5K_EEPROM_READ(o++, val);
++ chan_pcal_info[i].pwr_x3[0] = (val & 0xff);
++ chan_pcal_info[i].pwr_x3[1] = ((val >> 8) & 0xff);
++
++ AR5K_EEPROM_READ(o++, val);
++ chan_pcal_info[i].pwr_x3[2] = (val & 0xff);
++ /* It's weird but they put it here, that's the
++ PCDAC starting step */
++ chan_pcal_info[i].pcdac_x0[0] = ((val >> 8) & 0xff);
++
++ /* Static values seen on EEPROM info dumps */
++ chan_pcal_info[i].pcdac_x3[0] = 20;
++ chan_pcal_info[i].pcdac_x3[1] = 35;
++ chan_pcal_info[i].pcdac_x3[2] = 63;
++
++ /* Last xpd0 power level is also channel maximum */
++ chan_pcal_info[i].max_pwr = chan_pcal_info[i].pwr_x0[3];
++
++ /* Recreate pcdac_x0 table for this channel using pcdac steps */
++ chan_pcal_info[i].pcdac_x0[1] += chan_pcal_info[i].pcdac_x0[0];
++ chan_pcal_info[i].pcdac_x0[2] += chan_pcal_info[i].pcdac_x0[1];
++ chan_pcal_info[i].pcdac_x0[3] += chan_pcal_info[i].pcdac_x0[2];
++ }
++
++ /* return new offset */
++ (*offset) = o;
++
++ return 0;
++}
++
++/*
++ * Read per rate target power (this is the maximum tx power
++ * supported by the card). This info is used when setting
++ * tx power, no matter the channel.
++ *
++ * This also works for v5 EEPROMs.
++ */
++static int ath5k_eeprom_read_target_rate_pwr_info(void *mem,
++ u_int8_t mac_version,
++ struct ath5k_eeprom_info *ee,
++ u_int32_t *offset,
++ unsigned int mode)
++{
++ u_int32_t o = *offset;
++ u_int16_t val;
++ struct ath5k_rate_pcal_info *rate_pcal_info;
++ u_int16_t *rate_target_pwr_num;
++ int ret, i;
++
++ switch (mode) {
++ case AR5K_EEPROM_MODE_11A:
++ rate_pcal_info = ee->ee_rate_tpwr_a;
++ ee->ee_rate_target_pwr_num_a = AR5K_EEPROM_N_5GHZ_CHAN;
++ rate_target_pwr_num = &ee->ee_rate_target_pwr_num_a;
++ break;
++ case AR5K_EEPROM_MODE_11B:
++ rate_pcal_info = ee->ee_rate_tpwr_b;
++ ee->ee_rate_target_pwr_num_b = 2; /* 3rd is g mode'ss 1st */
++ rate_target_pwr_num = &ee->ee_rate_target_pwr_num_b;
++ break;
++ case AR5K_EEPROM_MODE_11G:
++ rate_pcal_info = ee->ee_rate_tpwr_g;
++ ee->ee_rate_target_pwr_num_g = AR5K_EEPROM_N_2GHZ_CHAN;
++ rate_target_pwr_num = &ee->ee_rate_target_pwr_num_g;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* Different freq mask for older eeproms (<= v3.2) */
++ if(ee->ee_version <= 0x3002){
++ for (i = 0; i < (*rate_target_pwr_num); i++) {
++ AR5K_EEPROM_READ(o++, val);
++ rate_pcal_info[i].freq =
++ ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode);
++
++ rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f);
++ rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f;
++
++ AR5K_EEPROM_READ(o++, val);
++
++ if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
++ val == 0) {
++ (*rate_target_pwr_num) = i;
++ break;
++ }
++
++ rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7);
++ rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f);
++ rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f);
++ }
++ } else {
++ for (i = 0; i < (*rate_target_pwr_num); i++) {
++ AR5K_EEPROM_READ(o++, val);
++ rate_pcal_info[i].freq =
++ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
++
++ rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f);
++ rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f;
++
++ AR5K_EEPROM_READ(o++, val);
++
++ if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
++ val == 0) {
++ (*rate_target_pwr_num) = i;
++ break;
++ }
++
++ rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf;
++ rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f);
++ rate_pcal_info[i].target_power_54 = (val & 0x3f);
++ }
++ }
++ /* return new offset */
++ (*offset) = o;
++
++ return 0;
++}
++
++/*
++ * Initialize EEPROM & capabilities data
++ */
++static int ath5k_eeprom_init(void *mem, u_int8_t mac_version,
++ struct ath5k_eeprom_info *ee)
++{
++ unsigned int mode, i;
++ int ret;
++ u_int32_t offset;
++ u_int16_t val;
++
++ /* Initial TX thermal adjustment values */
++ ee->ee_tx_clip = 4;
++ ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
++ ee->ee_gain_select = 1;
++
++ /*
++ * Read values from EEPROM and store them in the capability structure
++ */
++ AR5K_EEPROM_READ(AR5K_EEPROM_MAGIC, ee->ee_magic);
++ AR5K_EEPROM_READ(AR5K_EEPROM_PROTECT, ee->ee_protect);
++ AR5K_EEPROM_READ(AR5K_EEPROM_REG_DOMAIN, ee->ee_regdomain);
++ AR5K_EEPROM_READ(AR5K_EEPROM_VERSION, ee->ee_version);
++ AR5K_EEPROM_READ(AR5K_EEPROM_HDR, ee->ee_header);
++
++ /* Return if we have an old EEPROM */
++ if (ee->ee_version < AR5K_EEPROM_VERSION_3_0)
++ return 0;
++
++#ifdef notyet
++ /*
++ * Validate the checksum of the EEPROM date. There are some
++ * devices with invalid EEPROMs.
++ */
++ for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) {
++ AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val);
++ cksum ^= val;
++ }
++ if (cksum != AR5K_EEPROM_INFO_CKSUM) {
++ AR5K_PRINTF("Invalid EEPROM checksum 0x%04x\n", cksum);
++ return -EIO;
++ }
++#endif
++
++ AR5K_EEPROM_READ(AR5K_EEPROM_ANT_GAIN(ee->ee_version), ee->ee_ant_gain);
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0) {
++ AR5K_EEPROM_READ(AR5K_EEPROM_MISC0, ee->ee_misc0);
++ AR5K_EEPROM_READ(AR5K_EEPROM_MISC1, ee->ee_misc1);
++ }
++
++ if (ee->ee_version < AR5K_EEPROM_VERSION_3_3) {
++ AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val);
++ ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7;
++ ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7;
++
++ AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val);
++ ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7;
++ ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7;
++ }
++
++ /*
++ * Get conformance test limit values
++ */
++ offset = AR5K_EEPROM_CTL(ee->ee_version);
++ ee->ee_ctls = 0;
++
++ for (i = 0; i < AR5K_EEPROM_N_CTLS(ee->ee_version); i++) {
++ AR5K_EEPROM_READ(offset++, val);
++
++ if (((val >> 8) & 0xff) == 0)
++ break;
++
++ ee->ee_ctl[i] = (val >> 8) & 0xff;
++ ee->ee_ctls++;
++
++ if ((val & 0xff) == 0)
+ break;
++
++ ee->ee_ctl[i + 1] = val & 0xff;
++ ee->ee_ctls++;
++ }
++
++ /*
++ * Get values for 802.11a (5GHz)
++ */
++ mode = AR5K_EEPROM_MODE_11A;
++
++ ee->ee_turbo_max_power[mode] =
++ AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header);
++
++ offset = AR5K_EEPROM_MODES_11A(ee->ee_version);
++
++ ret = ath5k_eeprom_read_ants(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_adc_desired_size[mode] = (int8_t)((val >> 8) & 0xff);
++ ee->ee_ob[mode][3] = (val >> 5) & 0x7;
++ ee->ee_db[mode][3] = (val >> 2) & 0x7;
++ ee->ee_ob[mode][2] = (val << 1) & 0x7;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
++ ee->ee_db[mode][2] = (val >> 12) & 0x7;
++ ee->ee_ob[mode][1] = (val >> 9) & 0x7;
++ ee->ee_db[mode][1] = (val >> 6) & 0x7;
++ ee->ee_ob[mode][0] = (val >> 3) & 0x7;
++ ee->ee_db[mode][0] = val & 0x7;
++
++ ret = ath5k_eeprom_read_modes(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_1) {
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_margin_tx_rx[mode] = val & 0x3f;
++ }
++
++ /*
++ * Get values for 802.11b (2.4GHz)
++ */
++ mode = AR5K_EEPROM_MODE_11B;
++ offset = AR5K_EEPROM_MODES_11B(ee->ee_version);
++
++ ret = ath5k_eeprom_read_ants(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_adc_desired_size[mode] = (int8_t)((val >> 8) & 0xff);
++ ee->ee_ob[mode][1] = (val >> 4) & 0x7;
++ ee->ee_db[mode][1] = val & 0x7;
++
++ ret = ath5k_eeprom_read_modes(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0) {
++ AR5K_EEPROM_READ(offset++, val);
++
++ ee->ee_cal_piers_b = 0;
++
++ ee->ee_pwr_cal_b[0].freq =
++ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
++ if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS)
++ ee->ee_cal_piers_b++;
++
++ ee->ee_pwr_cal_b[1].freq =
++ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
++ if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS)
++ ee->ee_cal_piers_b++;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_pwr_cal_b[2].freq =
++ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
++ if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS)
++ ee->ee_cal_piers_b++;
++ }
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_1)
++ ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
++
++ /*
++ * Get values for 802.11g (2.4GHz)
++ */
++ mode = AR5K_EEPROM_MODE_11G;
++ offset = AR5K_EEPROM_MODES_11G(ee->ee_version);
++
++ ret = ath5k_eeprom_read_ants(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_adc_desired_size[mode] = (signed short int)((val >> 8) & 0xff);
++ ee->ee_ob[mode][1] = (val >> 4) & 0x7;
++ ee->ee_db[mode][1] = val & 0x7;
++
++ ret = ath5k_eeprom_read_modes(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0) {
++ AR5K_EEPROM_READ(offset++, val);
++
++ ee->ee_cal_piers_g = 0;
++
++ ee->ee_pwr_cal_g[0].freq =
++ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
++ if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS)
++ ee->ee_cal_piers_g++;
++
++ ee->ee_pwr_cal_g[1].freq =
++ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
++ if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS)
++ ee->ee_cal_piers_g++;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_turbo_max_power[mode] = val & 0x7f;
++ ee->ee_xr_power[mode] = (val >> 7) & 0x3f;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_pwr_cal_g[2].freq =
++ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
++ if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS)
++ ee->ee_cal_piers_g++;
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_1)
++ ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
++
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
++ ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
++
++ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_2) {
++ AR5K_EEPROM_READ(offset++, val);
++ ee->ee_cck_ofdm_gain_delta = val & 0xff;
+ }
+ }
+
+- return (name);
++ /*
++ * Read 5GHz EEPROM channels
++ */
++ offset = AR5K_EEPROM_CHANNELS_5GHZ(ee->ee_version);
++ ee->ee_cal_piers_a = 0;
++ for (i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i++) {
++ AR5K_EEPROM_READ(offset++, val);
++
++ if ((val & 0xff) == 0)
++ break;
++
++ ee->ee_pwr_cal_a[i].freq =
++ ath5k_eeprom_bin2freq(ee, val & 0xff, AR5K_EEPROM_MODE_11A);
++ ee->ee_cal_piers_a++;
++
++ if (((val >> 8) & 0xff) == 0)
++ break;
++
++ ee->ee_pwr_cal_a[++i].freq =
++ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, AR5K_EEPROM_MODE_11A);
++ ee->ee_cal_piers_a++;
++
++ }
++
++ /*
++ * Read power calibration info
++ */
++ mode = AR5K_EEPROM_MODE_11A;
++ ret = ath5k_eeprom_read_pcal_info(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ mode = AR5K_EEPROM_MODE_11B;
++ ret = ath5k_eeprom_read_pcal_info(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ mode = AR5K_EEPROM_MODE_11G;
++ ret = ath5k_eeprom_read_pcal_info(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++
++ /*
++ * Read per rate target power info
++ */
++ offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) + AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version);
++ mode = AR5K_EEPROM_MODE_11A;
++ ret = ath5k_eeprom_read_target_rate_pwr_info(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) + AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version);
++ mode = AR5K_EEPROM_MODE_11B;
++ ret = ath5k_eeprom_read_target_rate_pwr_info(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) + AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version);
++ mode = AR5K_EEPROM_MODE_11G;
++ ret = ath5k_eeprom_read_target_rate_pwr_info(mem, mac_version, ee, &offset, mode);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static const char *ath5k_hw_get_mac_name(u_int8_t val)
++{
++ static char name[16];
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(ath5k_mac_names); i++) {
++ if (val <= ath5k_mac_names[i].sr_val)
++ break;
++ }
++
++ if (val == ath5k_mac_names[i].sr_val)
++ return ath5k_mac_names[i].sr_name;
++
++ snprintf(name, sizeof(name), "%s+", ath5k_mac_names[i - 1].sr_name);
++ return name;
++}
++
++static const char *ath5k_hw_get_phy_name(u_int8_t val)
++{
++ const char *name = "?????";
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(ath5k_phy_names); i++) {
++ if (val < ath5k_phy_names[i + 1].sr_val) {
++ name = ath5k_phy_names[i].sr_name;
++ break;
++ }
++ }
++
++ return name;
+ }
+
+ /* returns -1 on unknown name */
+ static int eeprom_name2addr(const char *name)
+ {
+- int i;
++ unsigned int i;
++
+ if (!name || !name[0])
+ return -1;
+- for (i = 0; i < eeprom_addr_len; i++)
++ for (i = 0; i < ARRAY_SIZE(eeprom_addr); i++)
+ if (!strcmp(name, eeprom_addr[i].name))
+ return eeprom_addr[i].addr;
+ return -1;
+-} /* eeprom_name2addr */
++}
+
+ /* returns "<unknown>" on unknown address */
+ static const char *eeprom_addr2name(int addr)
+ {
+- int i;
+- for (i = 0; i < eeprom_addr_len; i++)
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(eeprom_addr); i++)
+ if (eeprom_addr[i].addr == addr)
+ return eeprom_addr[i].name;
+ return "<unknown>";
+-} /* eeprom_addr2name */
++}
+
+-static int
+-do_write_pairs(int anr, int argc, char **argv, unsigned char *mem,
+- int mac_version)
++static int do_write_pairs(int anr, int argc, char **argv, unsigned char *mem,
++ int mac_version)
+ {
+ #define MAX_NR_WRITES 16
+ struct {
+@@ -635,7 +1422,7 @@ do_write_pairs(int anr, int argc, char *
+ }
+ anr++;
+ i++;
+- } /* while (anr < (argc-1)) */
++ }
+
+ if (!(wr_ops_len = i)) {
+ err("no (addr,val) pairs given");
+@@ -702,20 +1489,22 @@ do_write_pairs(int anr, int argc, char *
+ }
+
+ return errors ? 11 : 0;
+-} /* do_write_pairs */
++}
+
+ static void usage(const char *n)
+ {
+- int i;
++ unsigned int i;
+
+- fprintf(stderr, "%s [-w [-g N:M]] [-v] [-f] [-d] <base_address> "
++ fprintf(stderr, "%s [-w [-g N:M]] [-v] [-f] [-d] [-R addr] [-W addr val] <base_address> "
+ "[<name1> <val1> [<name2> <val2> ...]]\n\n", n);
+ fprintf(stderr,
+ "-w write values into EEPROM\n"
+ "-g N:M set GPIO N to level M (only used with -w)\n"
+ "-v verbose output\n"
+ "-f force; suppress question before writing\n"
+- "-d dump eeprom (file 'ath-eeprom-dump.bin' and screen)\n"
++ "-d dump EEPROM (file 'ath-eeprom-dump.bin' and screen)\n"
++ "-R <addr> read register at <addr> (hex)\n"
++ "-W <addr> <val> write <val> (hex) into register at <addr> (hex)\n"
+ "<base_address> device base address (see lspci output)\n\n");
+
+ fprintf(stderr,
+@@ -725,8 +1514,8 @@ static void usage(const char *n)
+ " %s -w <base_address> regdomain N\n\n"
+ "- set a PCI id field to value N:\n"
+ " %s -w <base_address> <field> N\n"
+- " where <field> is on of:\n ", n, n, n);
+- for (i = 0; i < eeprom_addr_len; i++)
++ " where <field> is one of:\n ", n, n, n);
++ for (i = 0; i < ARRAY_SIZE(eeprom_addr); i++)
+ fprintf(stderr, " %s", eeprom_addr[i].name);
+ fprintf(stderr, "\n\n");
+ fprintf(stderr,
+@@ -739,19 +1528,457 @@ static void usage(const char *n)
+ "unlawful radio transmissions!\n\n");
+ }
+
++static void dump_capabilities(struct ath5k_eeprom_info *ee)
++{
++ u_int8_t has_a, has_b, has_g, has_rfkill, turbog_dis, turboa_dis;
++ u_int8_t xr2_dis, xr5_dis, has_crystal;
++
++ has_a = AR5K_EEPROM_HDR_11A(ee->ee_header);
++ has_b = AR5K_EEPROM_HDR_11B(ee->ee_header);
++ has_g = AR5K_EEPROM_HDR_11G(ee->ee_header);
++ has_rfkill = AR5K_EEPROM_HDR_RFKILL(ee->ee_header);
++ has_crystal = AR5K_EEPROM_HAS32KHZCRYSTAL(ee->ee_misc1);
++ turbog_dis = AR5K_EEPROM_HDR_T_2GHZ_DIS(ee->ee_header);
++ turboa_dis = AR5K_EEPROM_HDR_T_5GHZ_DIS(ee->ee_header);
++ xr2_dis = AR5K_EEPROM_HDR_XR2_DIS(ee->ee_misc0);
++ xr5_dis = AR5K_EEPROM_HDR_XR5_DIS(ee->ee_misc0);
++
++ printf("|================= Capabilities ================|\n");
++
++ printf("| 802.11a Support: ");
++ if (has_a)
++ printf(" yes |");
++ else
++ printf(" no |");
++
++ printf(" Turbo-A disabled:");
++ if (turboa_dis)
++ printf(" yes |\n");
++ else
++ printf(" no |\n");
++
++ printf("| 802.11b Support: ");
++ if (has_b)
++ printf(" yes |");
++ else
++ printf(" no |");
++
++ printf(" Turbo-G disabled:");
++ if (turbog_dis)
++ printf(" yes |\n");
++ else
++ printf(" no |\n");
++
++ printf("| 802.11g Support: ");
++ if (has_g)
++ printf(" yes |");
++ else
++ printf(" no |");
++
++ printf(" 2GHz XR disabled:");
++ if (xr2_dis)
++ printf(" yes |\n");
++ else
++ printf(" no |\n");
++
++ printf("| RFKill Support: ");
++ if (has_rfkill)
++ printf(" yes |");
++ else
++ printf(" no |");
++
++ printf(" 5GHz XR disabled:");
++ if (xr5_dis)
++ printf(" yes |\n");
++ else
++ printf(" no |\n");
++
++ if (has_crystal != 2) {
++ printf("| 32kHz Crystal: ");
++ if (has_crystal)
++ printf(" yes |");
++ else
++ printf(" no |");
++
++ printf(" |\n");
++ }
++
++ printf("\\===============================================/\n");
++}
++
++static void dump_calinfo_for_mode(int mode, struct ath5k_eeprom_info *ee)
++{
++ int i;
++
++ printf("|=========================================================|\n");
++ printf("| I power: 0x%02x |", ee->ee_i_cal[mode]);
++ printf(" Q power: 0x%02x |\n", ee->ee_q_cal[mode]);
++ printf("| Use fixed bias: 0x%02x |", ee->ee_fixed_bias[mode]);
++ printf(" Max turbo power: 0x%02x |\n", ee->ee_turbo_max_power[mode]);
++ printf("| Max XR power: 0x%02x |", ee->ee_xr_power[mode]);
++ printf(" Switch Settling Time: 0x%02x |\n", ee->ee_switch_settling[mode]);
++ printf("| Tx/Rx attenuation: 0x%02x |", ee->ee_ant_tx_rx[mode]);
++ printf(" TX end to XLNA On: 0x%02x |\n", ee->ee_tx_end2xlna_enable[mode]);
++ printf("| TX end to XPA Off: 0x%02x |", ee->ee_tx_end2xpa_disable[mode]);
++ printf(" TX end to XPA On: 0x%02x |\n", ee->ee_tx_frm2xpa_enable[mode]);
++ printf("| 62db Threshold: 0x%02x |", ee->ee_thr_62[mode]);
++ printf(" XLNA gain: 0x%02x |\n", ee->ee_xlna_gain[mode]);
++ printf("| XPD: 0x%02x |", ee->ee_xpd[mode]);
++ printf(" XPD gain: 0x%02x |\n", ee->ee_x_gain[mode]);
++ printf("| I gain: 0x%02x |", ee->ee_i_gain[mode]);
++ printf(" Tx/Rx margin: 0x%02x |\n", ee->ee_margin_tx_rx[mode]);
++ printf("| False detect backoff: 0x%02x |", ee->ee_false_detect[mode]);
++ printf(" Noise Floor Threshold: %3d |\n", ee->ee_noise_floor_thr[mode]);
++ printf("| ADC desired size: %3d |", ee->ee_adc_desired_size[mode]);
++ printf(" PGA desired size: %3d |\n", ee->ee_pga_desired_size[mode]);
++ printf("|=========================================================|\n");
++ for (i = 0; i < AR5K_EEPROM_N_PCDAC; i++) {
++ printf("| Antenna control %2i: 0x%02x |", i, ee->ee_ant_control[mode][i]);
++ i++;
++ printf(" Antenna control %2i: 0x%02x |\n", i, ee->ee_ant_control[mode][i]);
++ }
++ printf("|=========================================================|\n");
++ for (i = 0; i < AR5K_EEPROM_N_OBDB; i++) {
++ printf("| Octave Band %i: %2i |", i, ee->ee_ob[mode][i]);
++ printf(" db %i: %2i |\n", i, ee->ee_db[mode][i]);
++ }
++ printf("\\=========================================================/\n");
++}
++
++static void dump_power_calinfo_for_mode(int mode, struct ath5k_eeprom_info *ee)
++{
++ struct ath5k_chan_pcal_info *chan_pcal_info;
++ u_int16_t cal_piers;
++ int i, c;
++
++ switch (mode) {
++ case AR5K_EEPROM_MODE_11A:
++ chan_pcal_info = ee->ee_pwr_cal_a;
++ cal_piers = ee->ee_cal_piers_a;
++ break;
++ case AR5K_EEPROM_MODE_11B:
++ chan_pcal_info = ee->ee_pwr_cal_b;
++ cal_piers = ee->ee_cal_piers_b;
++ break;
++ case AR5K_EEPROM_MODE_11G:
++ chan_pcal_info = ee->ee_pwr_cal_g;
++ cal_piers = ee->ee_cal_piers_g;
++ break;
++ default:
++ return;
++ }
++
++ printf("/=================== Per channel power calibration ====================\\\n");
++ printf("| Freq | pwr_0 | pwr_1 | pwr_2 | pwr_3 |pwrx3_0|pwrx3_1|pwrx3_2|max_pwr|\n");
++ printf("| | pcdac | pcdac | pcdac | pcdac | pcdac | pcdac | pcdac | |\n");
++
++ for (i = 0; i < cal_piers; i++) {
++ char buf[16];
++
++ printf("|======|=======|=======|=======|=======|=======|=======|=======|=======|\n");
++ printf("| %4i |", chan_pcal_info[i].freq);
++ for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
++ printf(" %2i.%02i |", chan_pcal_info[i].pwr_x0[c] / 4,
++ chan_pcal_info[i].pwr_x0[c] % 4);
++ }
++ for (c = 0; c < AR5K_EEPROM_N_XPD3_POINTS; c++) {
++ printf(" %2i.%02i |", chan_pcal_info[i].pwr_x3[c] / 4,
++ chan_pcal_info[i].pwr_x3[c] % 4);
++ }
++ printf(" %2i.%02i |\n", chan_pcal_info[i].max_pwr / 4,
++ chan_pcal_info[i].max_pwr % 4);
++
++ printf("| |");
++ for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
++ snprintf(buf, sizeof(buf), "[%i]",
++ chan_pcal_info[i].pcdac_x0[c]);
++ printf("%6s |", buf);
++ }
++ for (c = 0; c < AR5K_EEPROM_N_XPD3_POINTS; c++) {
++ snprintf(buf, sizeof(buf), "[%i]",
++ chan_pcal_info[i].pcdac_x3[c]);
++ printf("%6s |", buf);
++ }
++ printf(" |\n");
++
++ }
++ printf("\\======================================================================/\n");
++}
++
++static void dump_rate_calinfo_for_mode(int mode, struct ath5k_eeprom_info *ee)
++{
++ int i;
++ struct ath5k_rate_pcal_info *rate_pcal_info;
++ u_int16_t rate_target_pwr_num;
++
++ switch (mode) {
++ case AR5K_EEPROM_MODE_11A:
++ rate_pcal_info = ee->ee_rate_tpwr_a;
++ rate_target_pwr_num = ee->ee_rate_target_pwr_num_a;
++ break;
++ case AR5K_EEPROM_MODE_11B:
++ rate_pcal_info = ee->ee_rate_tpwr_b;
++ rate_target_pwr_num = ee->ee_rate_target_pwr_num_b;
++ break;
++ case AR5K_EEPROM_MODE_11G:
++ rate_pcal_info = ee->ee_rate_tpwr_g;
++ rate_target_pwr_num = ee->ee_rate_target_pwr_num_g;
++ break;
++ default:
++ return;
++ }
++
++ printf("/============== Per rate power calibration ===========\\\n");
++ if (mode == AR5K_EEPROM_MODE_11B)
++ printf("| Freq | 1Mbit/s | 2Mbit/s | 5.5Mbit/s | 11Mbit/s |\n");
++ else
++ printf("| Freq | 6-24Mbit/s | 36Mbit/s | 48Mbit/s | 54Mbit/s |\n");
++
++ for (i = 0; i < rate_target_pwr_num; i++) {
++
++ printf("|======|============|==========|===========|==========|\n");
++ printf("| %4i |", rate_pcal_info[i].freq);
++ printf(" %2i.%02i |",rate_pcal_info[i].target_power_6to24 /2,
++ rate_pcal_info[i].target_power_6to24 % 2);
++ printf(" %2i.%02i |",rate_pcal_info[i].target_power_36 /2,
++ rate_pcal_info[i].target_power_36 % 2);
++ printf(" %2i.%02i |",rate_pcal_info[i].target_power_48 /2,
++ rate_pcal_info[i].target_power_48 % 2);
++ printf(" %2i.%02i |\n",rate_pcal_info[i].target_power_54 /2,
++ rate_pcal_info[i].target_power_54 % 2);
++ }
++ printf("\\=====================================================/\n");
++}
++
++static u_int32_t extend_tu(u_int32_t base_tu, u_int32_t val, u_int32_t mask)
++{
++ u_int32_t result;
++
++ result = (base_tu & ~mask) | (val & mask);
++ if ((base_tu & mask) > (val & mask))
++ result += mask + 1;
++ return result;
++}
++
++static void dump_timers_register(void *mem, u_int16_t mac_version)
++{
++#define AR5K_TIMER0_5210 0x802c /* next TBTT */
++#define AR5K_TIMER0_5211 0x8028
++#define AR5K_TIMER0 (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_TIMER0_5210 : AR5K_TIMER0_5211)
++
++#define AR5K_TIMER1_5210 0x8030 /* next DMA beacon */
++#define AR5K_TIMER1_5211 0x802c
++#define AR5K_TIMER1 (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_TIMER1_5210 : AR5K_TIMER1_5211)
++
++#define AR5K_TIMER2_5210 0x8034 /* next SWBA interrupt */
++#define AR5K_TIMER2_5211 0x8030
++#define AR5K_TIMER2 (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_TIMER2_5210 : AR5K_TIMER2_5211)
++
++#define AR5K_TIMER3_5210 0x8038 /* next ATIM window */
++#define AR5K_TIMER3_5211 0x8034
++#define AR5K_TIMER3 (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_TIMER3_5210 : AR5K_TIMER3_5211)
++
++#define AR5K_TSF_L32_5210 0x806c /* TSF (lower 32 bits) */
++#define AR5K_TSF_L32_5211 0x804c
++#define AR5K_TSF_L32 (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211)
++
++#define AR5K_TSF_U32_5210 0x8070
++#define AR5K_TSF_U32_5211 0x8050
++#define AR5K_TSF_U32 (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211)
++
++#define AR5K_BEACON_5210 0x8024
++#define AR5K_BEACON_5211 0x8020
++#define AR5K_BEACON (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_BEACON_5210 : AR5K_BEACON_5211)
++
++#define AR5K_LAST_TSTP 0x8080
++
++ const int timer_mask = 0xffff;
++
++ u_int32_t timer0, timer1, timer2, timer3, now_tu;
++ u_int32_t timer0_tu, timer1_tu, timer2_tu, timer3_tu;
++ u_int64_t now_tsf;
++
++ timer0 = AR5K_REG_READ(AR5K_TIMER0); /* 0x0000ffff */
++ timer1 = AR5K_REG_READ(AR5K_TIMER1_5211); /* 0x0007ffff */
++ timer2 = AR5K_REG_READ(AR5K_TIMER2_5211); /* 0x?1ffffff */
++ timer3 = AR5K_REG_READ(AR5K_TIMER3_5211); /* 0x0000ffff */
++
++ now_tsf = ((u_int64_t)AR5K_REG_READ(AR5K_TSF_U32_5211) << 32)
++ | (u_int64_t)AR5K_REG_READ(AR5K_TSF_L32_5211);
++
++ now_tu = now_tsf >> 10;
++
++ timer0_tu = extend_tu(now_tu, timer0, 0xffff);
++ printf("TIMER0: 0x%08x, TBTT: %5u, TU: 0x%08x\n", timer0,
++ timer0 & timer_mask, timer0_tu);
++ timer1_tu = extend_tu(now_tu, timer1 >> 3, 0x7ffff >> 3);
++ printf("TIMER1: 0x%08x, DMAb: %5u, TU: 0x%08x (%+d)\n", timer1,
++ (timer1 >> 3) & timer_mask, timer1_tu, timer1_tu - timer0_tu);
++ timer2_tu = extend_tu(now_tu, timer2 >> 3, 0x1ffffff >> 3);
++ printf("TIMER2: 0x%08x, SWBA: %5u, TU: 0x%08x (%+d)\n", timer2,
++ (timer2 >> 3) & timer_mask, timer2_tu, timer2_tu - timer0_tu);
++ timer3_tu = extend_tu(now_tu, timer3, 0xffff);
++ printf("TIMER3: 0x%08x, ATIM: %5u, TU: 0x%08x (%+d)\n", timer3,
++ timer3 & timer_mask, timer3_tu, timer3_tu - timer0_tu);
++ printf("TSF: 0x%016llx, TSFTU: %5u, TU: 0x%08x\n",
++ (unsigned long long)now_tsf, now_tu & timer_mask, now_tu);
++
++ printf("BEACON: 0x%08x\n", AR5K_REG_READ(AR5K_BEACON));
++ printf("LAST_TSTP: 0x%08x\n", AR5K_REG_READ(AR5K_LAST_TSTP));
++}
++
++#define AR5K_KEYTABLE_0_5210 0x9000
++#define AR5K_KEYTABLE_0_5211 0x8800
++#define AR5K_KEYTABLE_0 (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_KEYTABLE_0_5210 : \
++ AR5K_KEYTABLE_0_5211)
++
++#define AR5K_KEYTABLE(_n) (AR5K_KEYTABLE_0_5211 + ((_n) << 5))
++#define AR5K_KEYTABLE_OFF(_n, x) (AR5K_KEYTABLE(_n) + ((x) << 2))
++#define AR5K_KEYTABLE_VALID 0x00008000
++
++#define AR5K_KEYTABLE_SIZE_5210 64
++#define AR5K_KEYTABLE_SIZE_5211 128
++#define AR5K_KEYTABLE_SIZE (mac_version == AR5K_SREV_MAC_AR5210 ? \
++ AR5K_KEYTABLE_SIZE_5210 : \
++ AR5K_KEYTABLE_SIZE_5211)
++
++static void keycache_dump(void *mem, u_int16_t mac_version)
++{
++ int i, keylen;
++ u_int32_t val0, val1, val2, val3, val4, keytype, ant, mac0, mac1;
++
++ /* dump all 128 entries */
++ printf("Dumping keycache entries...\n");
++ for (i = 0; i < AR5K_KEYTABLE_SIZE; i++) {
++ mac1 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 7));
++ if (mac1 & AR5K_KEYTABLE_VALID) {
++ val0 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 0));
++ val1 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 1));
++ val2 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 2));
++ val3 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 3));
++ val4 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 4));
++ keytype = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 5));
++ ant = keytype & 8;
++ keytype &= ~8;
++ switch (keytype) {
++ case 0: /* WEP40 */ keylen = 40 / 8; break;
++ case 1: /* WEP104 */ keylen = 104 / 8; break;
++ case 3: /* WEP128 */ keylen = 128 / 8; break;
++ case 4: /* TKIP */ keylen = 128 / 8; break;
++ case 5: /* AES */ keylen = 128 / 8; break;
++ case 6: /* CCM */ keylen = 128 / 8; break;
++ default: keylen = 0; break;
++ }
++ mac0 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(i, 6));
++
++ printf("[%3u] keytype %d [%s%s%s%s%s%s%s%s] mac %02x:%02x:%02x:%02x:%02x:%02x key:%08x-%08x-%08x-%08x-%08x\n",
++ i,
++ keytype,
++ keytype == 0 ? "WEP40 " : "",
++ keytype == 1 ? "WEP104" : "",
++ keytype == 3 ? "WEP128" : "",
++ keytype == 4 ? "TKIP " : "",
++ keytype == 5 ? "AES " : "",
++ keytype == 6 ? "CCM " : "",
++ keytype == 7 ? "NULL " : "",
++ ant == 8 ? "+ANT" : "",
++ ((mac0 << 1) & 0xff),
++ ((mac0 >> 7) & 0xff),
++ ((mac0 >> 15) & 0xff),
++ ((mac0 >> 23) & 0xff),
++ ((mac1 << 1) & 0xff) | (mac0 >> 31),
++ ((mac1 >> 7) & 0xff),
++ val0, val1, val2, val3, val4);
++ }
++ }
++}
++
++/* copy key index (0) to key index (idx) */
++
++static void keycache_copy(void *mem, u_int16_t mac_version, int idx)
++{
++ u_int32_t val0, val1, val2, val3, val4, keytype, mac0, mac1;
++
++ printf("Copying keycache entry 0 to %d\n", idx);
++ if (idx < 0 || idx >= AR5K_KEYTABLE_SIZE) {
++ printf("invalid keycache index\n");
++ return;
++ }
++
++ val0 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 0));
++ val1 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 1));
++ val2 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 2));
++ val3 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 3));
++ val4 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 4));
++ keytype = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 5));
++ mac0 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 6));
++ mac1 = AR5K_REG_READ(AR5K_KEYTABLE_OFF(0, 7));
++
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 0), val0);
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 1), val1);
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 2), val2);
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 3), val3);
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 4), val4);
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 5), keytype);
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 6), mac0);
++ AR5K_REG_WRITE(AR5K_KEYTABLE_OFF(idx, 7), mac1);
++}
++
++static void sta_id0_id1_dump(void *mem)
++{
++#define AR5K_STA_ID0 0x8000
++#define AR5K_STA_ID1 0x8004
++#define AR5K_STA_ID1_AP 0x00010000
++#define AR5K_STA_ID1_ADHOC 0x00020000
++#define AR5K_STA_ID1_NO_KEYSRCH 0x00080000
++
++ u_int32_t sta_id0, sta_id1;
++
++ sta_id0 = AR5K_REG_READ(AR5K_STA_ID0);
++ sta_id1 = AR5K_REG_READ(AR5K_STA_ID1);
++ printf("STA_ID0: %02x:%02x:%02x:%02x:%02x:%02x\n",
++ (sta_id0 >> 0) & 0xff,
++ (sta_id0 >> 8) & 0xff,
++ (sta_id0 >> 16) & 0xff,
++ (sta_id0 >> 24) & 0xff,
++ (sta_id1 >> 0) & 0xff,
++ (sta_id1 >> 8) & 0xff);
++ printf("STA_ID1: 0x%08x, AP: %d, IBSS: %d, KeyCache Disable: %d\n",
++ sta_id1,
++ sta_id1 & AR5K_STA_ID1_AP ? 1 : 0,
++ sta_id1 & AR5K_STA_ID1_ADHOC ? 1 : 0,
++ sta_id1 & AR5K_STA_ID1_NO_KEYSRCH ? 1 : 0);
++}
++
+ int
+ CMD(athinfo)(int argc, char *argv[])
+ {
+- u_int32_t dev_addr;
+- u_int16_t eeprom_header, srev, phy_rev_5ghz, phy_rev_2ghz;
+- u_int16_t eeprom_version, mac_version, regdomain, has_crystal, ee_magic;
+- u_int8_t error, has_a, has_b, has_g, has_rfkill, eeprom_size;
+- int byte_size = 0;
++ unsigned long long dev_addr;
++ u_int16_t srev, phy_rev_5ghz, phy_rev_2ghz, ee_magic;
++ u_int8_t mac_version, mac_revision;
++ u_int8_t error, eeprom_size, dev_type, eemap;
++ struct ath5k_eeprom_info *ee;
++ unsigned int byte_size = 0;
+ void *mem;
+ int fd;
+- int i, anr = 1;
++ unsigned int i;
++ int anr = 1;
+ int do_write = 0; /* default: read only */
+ int do_dump = 0;
++ int reg_read = 0;
++ int reg_write = 0;
++ unsigned int reg_write_val = 0;
++ unsigned int timer_count = 1;
++ int do_keycache_dump = 0;
++ int keycache_copy_idx = 0;
+
+ struct {
+ int valid;
+@@ -759,7 +1986,7 @@ CMD(athinfo)(int argc, char *argv[])
+ } gpio_set[AR5K_NUM_GPIO];
+ int nr_gpio_set = 0;
+
+- for (i = 0; i < sizeof(gpio_set) / sizeof(gpio_set[0]); i++)
++ for (i = 0; i < ARRAY_SIZE(gpio_set); i++)
+ gpio_set[i].valid = 0;
+
+ if (argc < 2) {
+@@ -769,6 +1996,15 @@ CMD(athinfo)(int argc, char *argv[])
+
+ while (anr < argc && argv[anr][0] == '-') {
+ switch (argv[anr][1]) {
++ case 't':
++ if (++anr < argc) {
++ timer_count = atoi(argv[anr]);
++ printf("timer_count:%d\n", timer_count);
++ } else {
++ usage(argv[0]);
++ return 0;
++ }
++ break;
+ case 'w':
+ do_write = 1;
+ break;
+@@ -777,7 +2013,7 @@ CMD(athinfo)(int argc, char *argv[])
+ if (strlen(argv[anr]) != 3 || argv[anr][1] != ':' ||
+ argv[anr][0] < '0' || argv[anr][0] > '5' ||
+ (argv[anr][2] != '0' && argv[anr][2] != '1')) {
+- err("invalid gpio spec. %s", argv[anr]);
++ err("invalid GPIO spec. %s", argv[anr]);
+ return 2;
+ }
+ gpio_set[argv[anr][0] - '0'].valid = 1;
+@@ -797,6 +2033,25 @@ CMD(athinfo)(int argc, char *argv[])
+ do_dump = 1;
+ break;
+
++ case 'R':
++ anr++;
++ reg_read = strtoul(argv[anr], NULL, 16);
++ break;
++
++ case 'W':
++ anr++;
++ reg_write = strtoul(argv[anr++], NULL, 16);
++ reg_write_val = strtoul(argv[anr], NULL, 16);
++ break;
++
++ case 'k':
++ do_keycache_dump = 1;
++ break;
++
++ case 'K':
++ keycache_copy_idx = atoi(argv[++anr]);
++ break;
++
+ case 'h':
+ usage(argv[0]);
+ return 0;
+@@ -805,10 +2060,10 @@ CMD(athinfo)(int argc, char *argv[])
+ default:
+ err("unknown option %s", argv[anr]);
+ return 2;
+- } /* switch (argv[anr][1]) */
++ }
+
+ anr++;
+- } /* while (anr < argc && ...) */
++ }
+
+ if (anr >= argc) {
+ err("missing device address");
+@@ -816,7 +2071,7 @@ CMD(athinfo)(int argc, char *argv[])
+ return 3;
+ }
+
+- dev_addr = strtoul(argv[anr], NULL, 16);
++ dev_addr = strtoull(argv[anr], NULL, 16);
+
+ fd = open("/dev/mem", O_RDWR);
+ if (fd < 0) {
+@@ -828,7 +2083,7 @@ CMD(athinfo)(int argc, char *argv[])
+ MAP_SHARED | MAP_FILE, fd, dev_addr);
+
+ if (mem == MAP_FAILED) {
+- printf("Mmap of device at 0x%08X for 0x%X bytes failed - "
++ printf("mmap of device at 0x%08llX for 0x%X bytes failed - "
+ "%s\n", dev_addr, AR5K_PCI_MEM_SIZE, strerror(errno));
+ return -3;
+ }
+@@ -856,10 +2111,31 @@ CMD(athinfo)(int argc, char *argv[])
+ AR5K_REG_DISABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_SPWR_DN);
+ usleep(500);
+
++ if (reg_read) {
++ printf("READ %04x = %08x\n", reg_read, AR5K_REG_READ(reg_read));
++ return 0;
++ }
++
++ if (reg_write) {
++ printf("WRITE %04x = %08x\n", reg_write, reg_write_val);
++ AR5K_REG_WRITE(reg_write, reg_write_val);
++ return 0;
++ }
++
+ srev = AR5K_REG_READ(AR5K_SREV);
+- mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER) << 4;
++ if (srev >= 0x0100) {
++ printf("MAC revision 0x%04x is not supported!\n", srev);
++ return -1;
++ }
++ mac_version = srev & AR5K_SREV_VER;
++ mac_revision = srev & AR5K_SREV_REV;
+
+- /* Verify eeprom magic value first */
++ printf(" -==Device Information==-\n");
++
++ printf("MAC Revision: %-5s (0x%02x)\n",
++ ath5k_hw_get_mac_name(mac_revision), mac_revision);
++
++ /* Verify EEPROM magic value first */
+ error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_MAGIC, &ee_magic,
+ mac_version);
+
+@@ -872,157 +2148,114 @@ CMD(athinfo)(int argc, char *argv[])
+ printf("Warning: Invalid EEPROM Magic number!\n");
+ }
+
+- error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_HDR, &eeprom_header,
+- mac_version);
+-
+- if (error) {
+- printf("Unable to read EEPROM Header!\n");
+- return -1;
+- }
+-
+- error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_VERSION, &eeprom_version,
+- mac_version);
+-
+- if (error) {
+- printf("Unable to read EEPROM version!\n");
++ ee = calloc(sizeof(struct ath5k_eeprom_info), 1);
++ if (!ee) {
++ printf("Cannot allocate memory for EEPROM information\n");
+ return -1;
+ }
+
+- error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_REG_DOMAIN, &regdomain,
+- mac_version);
+-
+- if (error) {
+- printf("Unable to read Regdomain!\n");
++ if (ath5k_eeprom_init(mem, mac_version, ee)) {
++ printf("EEPROM init failed\n");
+ return -1;
+ }
+
+- if (eeprom_version >= 0x4000) {
+- error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_MISC0,
+- &has_crystal, mac_version);
+-
+- if (error) {
+- printf("Unable to read EEPROM Misc data!\n");
+- return -1;
+- }
+-
+- has_crystal = AR5K_EEPROM_HAS32KHZCRYSTAL(has_crystal);
+- } else {
+- has_crystal = 2;
+- }
+-
+ eeprom_size = AR5K_REG_MS(AR5K_REG_READ(AR5K_PCICFG),
+ AR5K_PCICFG_EESIZE);
+
+- has_a = AR5K_EEPROM_HDR_11A(eeprom_header);
+- has_b = AR5K_EEPROM_HDR_11B(eeprom_header);
+- has_g = AR5K_EEPROM_HDR_11G(eeprom_header);
+- has_rfkill = AR5K_EEPROM_HDR_RFKILL(eeprom_header);
++ dev_type = AR5K_EEPROM_HDR_DEVICE(ee->ee_header);
++ eemap = AR5K_EEPROM_EEMAP(ee->ee_misc0);
+
+- if (has_a)
++ /* 1 = ?? 2 = ?? 3 = card 4 = wmac */
++ printf("Device type: %1i\n", dev_type);
++
++ if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+ phy_rev_5ghz = ath5k_hw_radio_revision(mac_version, mem, 1);
+ else
+ phy_rev_5ghz = 0;
+
+- if (has_b)
++ if (AR5K_EEPROM_HDR_11B(ee->ee_header))
+ phy_rev_2ghz = ath5k_hw_radio_revision(mac_version, mem, 0);
+ else
+ phy_rev_2ghz = 0;
+
+- printf(" -==Device Information==-\n");
+-
+- printf("MAC Version: %-5s (0x%02x)\n",
+- ath5k_hw_get_part_name(AR5K_VERSION_VER, mac_version),
+- mac_version);
+-
+- printf("MAC Revision: %-5s (0x%02x)\n",
+- ath5k_hw_get_part_name(AR5K_VERSION_VER, srev), srev);
+-
+- /* Single-chip PHY with a/b/g support */
+- if (has_b && !phy_rev_2ghz) {
+- printf("PHY Revision: %-5s (0x%02x)\n",
+- ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_5ghz),
+- phy_rev_5ghz);
+- phy_rev_5ghz = 0;
+- }
+-
+- /* Single-chip PHY with b/g support */
+- if (!has_a) {
+- printf("PHY Revision: %-5s (0x%02x)\n",
+- ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_2ghz),
+- phy_rev_2ghz);
+- phy_rev_2ghz = 0;
+- }
+-
+- /* Different chip for 5Ghz and 2Ghz */
+ if (phy_rev_5ghz) {
+- printf("5Ghz PHY Revision: %-5s (0x%2x)\n",
+- ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_5ghz),
+- phy_rev_5ghz);
++ printf("5GHz PHY Revision: %-5s (0x%02x)\n",
++ ath5k_hw_get_phy_name(phy_rev_5ghz), phy_rev_5ghz);
+ }
+ if (phy_rev_2ghz) {
+- printf("2Ghz PHY Revision: %-5s (0x%2x)\n",
+- ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_2ghz),
+- phy_rev_2ghz);
++ printf("2GHz PHY Revision: %-5s (0x%02x)\n",
++ ath5k_hw_get_phy_name(phy_rev_2ghz), phy_rev_2ghz);
+ }
+
+- printf(" -==EEPROM Information==-\n");
+-
+- printf("EEPROM Version: %x.%x\n",
+- (eeprom_version & 0xF000) >> 12, eeprom_version & 0xFFF);
++ printf("\n");
++ printf("/============== EEPROM Information =============\\\n");
++ printf("| EEPROM Version: %1x.%1x |",
++ (ee->ee_version & 0xF000) >> 12, ee->ee_version & 0xFFF);
+
+- printf("EEPROM Size: ");
++ printf(" EEPROM Size: ");
+
+ if (eeprom_size == 0) {
+- printf(" 4K\n");
+- byte_size = 4096;
++ printf(" 4 kbit |\n");
++ byte_size = 4096 / 8;
+ } else if (eeprom_size == 1) {
+- printf(" 8K\n");
+- byte_size = 8192;
++ printf(" 8 kbit |\n");
++ byte_size = 8192 / 8;
+ } else if (eeprom_size == 2) {
+- printf(" 16K\n");
+- byte_size = 16384;
++ printf(" 16 kbit |\n");
++ byte_size = 16384 / 8;
+ } else
+- printf(" ??\n");
++ printf(" unknown |\n");
+
+- printf("Regulatory Domain: 0x%X\n", regdomain);
+-
+- printf(" -==== Capabilities ====-\n");
+-
+- printf("| 802.11a Support: ");
+- if (has_a)
+- printf("yes |\n");
+- else
+- printf("no |\n");
+-
+- printf("| 802.11b Support: ");
+- if (has_b)
+- printf("yes |\n");
+- else
+- printf("no |\n");
++ printf("| EEMAP: %i |", eemap);
+
+- printf("| 802.11g Support: ");
+- if (has_g)
+- printf("yes |\n");
+- else
+- printf("no |\n");
++ printf(" Reg. Domain: 0x%02X |\n", ee->ee_regdomain);
+
+- printf("| RFKill Support: ");
+- if (has_rfkill)
+- printf("yes |\n");
+- else
+- printf("no |\n");
++ dump_capabilities(ee);
++ printf("\n");
+
+- if (has_crystal != 2) {
+- printf("| 32KHz Crystal: ");
+- if (has_crystal)
+- printf("yes |\n");
+- else
+- printf("no |\n");
++ printf("/=========================================================\\\n");
++ printf("| Calibration data common for all modes |\n");
++ printf("|=========================================================|\n");
++ printf("| CCK/OFDM gain delta: %2i |\n", ee->ee_cck_ofdm_gain_delta);
++ printf("| CCK/OFDM power delta: %2i |\n", ee->ee_cck_ofdm_power_delta);
++ printf("| Scaled CCK delta: %2i |\n", ee->ee_scaled_cck_delta);
++ printf("| 2GHz Antenna gain: %2i |\n", AR5K_EEPROM_ANT_GAIN_2GHZ(ee->ee_ant_gain));
++ printf("| 5GHz Antenna gain: %2i |\n", AR5K_EEPROM_ANT_GAIN_5GHZ(ee->ee_ant_gain));
++ printf("| Turbo 2W maximum dBm: %2i |\n", AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header));
++ printf("| Target power start: 0x%03x |\n", AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1));
++ printf("| EAR Start: 0x%03x |\n", AR5K_EEPROM_EARSTART(ee->ee_misc0));
++ printf("\\=========================================================/\n");
++
++ printf("\n");
++ if (AR5K_EEPROM_HDR_11A(ee->ee_header)) {
++ printf("/=========================================================\\\n");
++ printf("| Calibration data for 802.11a operation |\n");
++ dump_calinfo_for_mode(AR5K_EEPROM_MODE_11A, ee);
++ dump_rate_calinfo_for_mode(AR5K_EEPROM_MODE_11A, ee);
++ dump_power_calinfo_for_mode(AR5K_EEPROM_MODE_11A, ee);
++ printf("\n");
++ }
++
++ if (AR5K_EEPROM_HDR_11B(ee->ee_header)) {
++ printf("/=========================================================\\\n");
++ printf("| Calibration data for 802.11b operation |\n");
++ dump_calinfo_for_mode(AR5K_EEPROM_MODE_11B, ee);
++ dump_rate_calinfo_for_mode(AR5K_EEPROM_MODE_11B, ee);
++ dump_power_calinfo_for_mode(AR5K_EEPROM_MODE_11B, ee);
++ printf("\n");
++ }
++
++ if (AR5K_EEPROM_HDR_11G(ee->ee_header)) {
++ printf("/=========================================================\\\n");
++ printf("| Calibration data for 802.11g operation |\n");
++ dump_calinfo_for_mode(AR5K_EEPROM_MODE_11G, ee);
++ dump_rate_calinfo_for_mode(AR5K_EEPROM_MODE_11G, ee);
++ dump_power_calinfo_for_mode(AR5K_EEPROM_MODE_11G, ee);
++ printf("\n");
+ }
+- printf(" ========================\n");
+
+ /* print current GPIO settings */
+- printf("GPIO registers: CR %08x DO %08x DI %08x\n",
++ printf("GPIO registers: CR 0x%08x, DO 0x%08x, DI 0x%08x\n",
+ AR5K_REG_READ(AR5K_GPIOCR), AR5K_REG_READ(AR5K_GPIODO),
+ AR5K_REG_READ(AR5K_GPIODI));
+
+@@ -1030,18 +2263,18 @@ CMD(athinfo)(int argc, char *argv[])
+ u_int16_t data;
+ FILE *dumpfile = fopen("ath-eeprom-dump.bin", "w");
+
+- printf("\nEEPROM dump (%d byte)\n", byte_size);
++ printf("\nEEPROM dump (%d bytes)\n", byte_size);
+ printf("==============================================");
+- for (i = 1; i <= (byte_size / 2); i++) {
++ for (i = 0; i < byte_size / 2; i++) {
+ error =
+ ath5k_hw_eeprom_read(mem, i, &data, mac_version);
+ if (error) {
+ printf("\nUnable to read at %04x\n", i);
+ continue;
+ }
+- if (!((i - 1) % 8))
+- printf("\n%04x: ", i);
+- printf("%04x ", data);
++ if (!(i % 8))
++ printf("\n%04x: ", i);
++ printf(" %04x", data);
+ fwrite(&data, 2, 1, dumpfile);
+ }
+ printf("\n==============================================\n");
+@@ -1054,18 +2287,18 @@ CMD(athinfo)(int argc, char *argv[])
+ u_int32_t old_cr = rcr, old_do = rdo;
+ int rc;
+
+- if (mac_version >= AR5K_SREV_VER_AR5213 && !nr_gpio_set) {
+- dbg("new MAC %x (>= AR5213) set gpio4 to low",
++ if (mac_version >= AR5K_SREV_MAC_AR5213 && !nr_gpio_set) {
++ dbg("new MAC %x (>= AR5213) set GPIO4 to low",
+ mac_version);
+ gpio_set[4].valid = 1;
+ gpio_set[4].value = 0;
+ }
+
+- /* set gpios */
++ /* set GPIOs */
+ dbg("old GPIO CR %08x DO %08x DI %08x",
+ rcr, rdo, AR5K_REG_READ(AR5K_GPIODI));
+
+- for (i = 0; i < sizeof(gpio_set) / sizeof(gpio_set[0]); i++) {
++ for (i = 0; i < ARRAY_SIZE(gpio_set); i++) {
+ if (gpio_set[i].valid) {
+ rcr |= AR5K_GPIOCR_OUT(i); /* we use mode 3 */
+ rcr &= ~AR5K_GPIOCR_INT_SEL(i);
+@@ -1111,5 +2344,17 @@ CMD(athinfo)(int argc, char *argv[])
+
+ return rc;
+ }
++
++ sta_id0_id1_dump(mem);
++
++ for (i = 0; i < timer_count; i++)
++ dump_timers_register(mem, mac_version);
++
++ if (do_keycache_dump)
++ keycache_dump(mem, mac_version);
++
++ if (keycache_copy_idx > 0)
++ keycache_copy(mem, mac_version, keycache_copy_idx);
++
+ return 0;
+ }
diff --git a/package/madwifi/patches/408-changeset_r3337.patch b/package/madwifi/patches/408-changeset_r3337.patch
new file mode 100644
index 000000000..53e76a9e8
--- /dev/null
+++ b/package/madwifi/patches/408-changeset_r3337.patch
@@ -0,0 +1,34 @@
+--- a/THANKS
++++ b/THANKS
+@@ -129,6 +129,7 @@ Derek J Smithies
+ jhansen
+ Benoit Papillault
+ Russell Harmon
++Alessandro Erta
+
+ Apologies to anyone whose name was unintentionally left off.
+ Please let us know if you think your name should be mentioned here!
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -3147,7 +3147,7 @@ ath_tx_startraw(struct net_device *dev,
+ struct ath_softc *sc = dev->priv;
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211_phy_params *ph = (struct ieee80211_phy_params *)
+- (SKB_CB(skb) + sizeof(struct ieee80211_cb));
++ (SKB_CB(skb) + 1); /* NB: SKB_CB casts to CB struct*. */
+ const HAL_RATE_TABLE *rt;
+ unsigned int pktlen, hdrlen, try0, power;
+ HAL_PKT_TYPE atype;
+--- a/net80211/ieee80211_monitor.c
++++ b/net80211/ieee80211_monitor.c
+@@ -128,8 +128,8 @@ struct ar5212_openbsd_desc {
+ void
+ ieee80211_monitor_encap(struct ieee80211vap *vap, struct sk_buff *skb)
+ {
+- struct ieee80211_phy_params *ph =
+- (struct ieee80211_phy_params *) (SKB_CB(skb) + sizeof(struct ieee80211_cb));
++ struct ieee80211_phy_params *ph = (struct ieee80211_phy_params *)
++ (SKB_CB(skb) + 1); /* NB: SKB_CB casts to CB struct*. */
+ SKB_CB(skb)->flags = M_RAW;
+ SKB_CB(skb)->ni = NULL;
+ SKB_CB(skb)->next = NULL;
diff --git a/package/madwifi/patches/409-wext_compat.patch b/package/madwifi/patches/409-wext_compat.patch
new file mode 100644
index 000000000..449709232
--- /dev/null
+++ b/package/madwifi/patches/409-wext_compat.patch
@@ -0,0 +1,133 @@
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -73,6 +73,13 @@
+ (_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+ #define RESCAN 1
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
++#define IWE(func, ...) func(&iweinfo, __VA_ARGS__)
++static struct iw_request_info iweinfo = { 0, 0 };
++#else
++#define IWE(func, ...) func(__VA_ARGS__)
++#endif
++
+ static void
+ pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt);
+
+@@ -1800,7 +1807,7 @@ giwscan_cb(void *arg, const struct ieee8
+ IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, se->se_macaddr);
+ else
+ IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, se->se_bssid);
+- current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
++ current_ev = IWE(iwe_stream_add_event, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+ /* We ran out of space in the buffer. */
+ if (last_ev == current_ev)
+@@ -1811,7 +1818,7 @@ giwscan_cb(void *arg, const struct ieee8
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ iwe.u.data.length = se->se_ssid[1];
+- current_ev = iwe_stream_add_point(current_ev,
++ current_ev = IWE(iwe_stream_add_point, current_ev,
+ end_buf, &iwe, (char *) se->se_ssid+2);
+
+ /* We ran out of space in the buffer. */
+@@ -1824,7 +1831,7 @@ giwscan_cb(void *arg, const struct ieee8
+ iwe.cmd = SIOCGIWMODE;
+ iwe.u.mode = se->se_capinfo & IEEE80211_CAPINFO_ESS ?
+ IW_MODE_MASTER : IW_MODE_ADHOC;
+- current_ev = iwe_stream_add_event(current_ev,
++ current_ev = IWE(iwe_stream_add_event, current_ev,
+ end_buf, &iwe, IW_EV_UINT_LEN);
+
+ /* We ran out of space in the buffer. */
+@@ -1837,7 +1844,7 @@ giwscan_cb(void *arg, const struct ieee8
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = se->se_chan->ic_freq * 100000;
+ iwe.u.freq.e = 1;
+- current_ev = iwe_stream_add_event(current_ev,
++ current_ev = IWE(iwe_stream_add_event, current_ev,
+ end_buf, &iwe, IW_EV_FREQ_LEN);
+
+ /* We ran out of space in the buffer. */
+@@ -1848,7 +1855,7 @@ giwscan_cb(void *arg, const struct ieee8
+ last_ev = current_ev;
+ iwe.cmd = IWEVQUAL;
+ set_quality(&iwe.u.qual, se->se_rssi, ATH_DEFAULT_NOISE);
+- current_ev = iwe_stream_add_event(current_ev,
++ current_ev = IWE(iwe_stream_add_event, current_ev,
+ end_buf, &iwe, IW_EV_QUAL_LEN);
+
+ /* We ran out of space in the buffer */
+@@ -1863,7 +1870,7 @@ giwscan_cb(void *arg, const struct ieee8
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
++ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf, &iwe, "");
+
+ /* We ran out of space in the buffer. */
+ if (last_ev == current_ev)
+@@ -1878,7 +1885,7 @@ giwscan_cb(void *arg, const struct ieee8
+ int r = se->se_rates[2 + j] & IEEE80211_RATE_VAL;
+ if (r != 0) {
+ iwe.u.bitrate.value = r * (1000000 / 2);
+- current_val = iwe_stream_add_value(current_ev,
++ current_val = IWE(iwe_stream_add_value, current_ev,
+ current_val, end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+@@ -1887,7 +1894,7 @@ giwscan_cb(void *arg, const struct ieee8
+ int r = se->se_xrates[2+j] & IEEE80211_RATE_VAL;
+ if (r != 0) {
+ iwe.u.bitrate.value = r * (1000000 / 2);
+- current_val = iwe_stream_add_value(current_ev,
++ current_val = IWE(iwe_stream_add_value, current_ev,
+ current_val, end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+@@ -1906,7 +1913,7 @@ giwscan_cb(void *arg, const struct ieee8
+ iwe.cmd = IWEVCUSTOM;
+ snprintf(buf, sizeof(buf), "bcn_int=%d", se->se_intval);
+ iwe.u.data.length = strlen(buf);
+- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
++ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf, &iwe, buf);
+
+ /* We ran out of space in the buffer. */
+ if (last_ev == current_ev)
+@@ -1930,7 +1937,7 @@ giwscan_cb(void *arg, const struct ieee8
+ rsn_leader, sizeof(rsn_leader) - 1);
+ #endif
+ if (iwe.u.data.length != 0) {
+- current_ev = iwe_stream_add_point(current_ev, end_buf,
++ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
+ &iwe, buf);
+
+ /* We ran out of space in the buffer */
+@@ -1956,7 +1963,7 @@ giwscan_cb(void *arg, const struct ieee8
+ wpa_leader, sizeof(wpa_leader) - 1);
+ #endif
+ if (iwe.u.data.length != 0) {
+- current_ev = iwe_stream_add_point(current_ev, end_buf,
++ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
+ &iwe, buf);
+
+ /* We ran out of space in the buffer. */
+@@ -1975,7 +1982,7 @@ giwscan_cb(void *arg, const struct ieee8
+ se->se_wme_ie, se->se_wme_ie[1] + 2,
+ wme_leader, sizeof(wme_leader) - 1);
+ if (iwe.u.data.length != 0) {
+- current_ev = iwe_stream_add_point(current_ev, end_buf,
++ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
+ &iwe, buf);
+
+ /* We ran out of space in the buffer. */
+@@ -1993,7 +2000,7 @@ giwscan_cb(void *arg, const struct ieee8
+ se->se_ath_ie, se->se_ath_ie[1] + 2,
+ ath_leader, sizeof(ath_leader) - 1);
+ if (iwe.u.data.length != 0) {
+- current_ev = iwe_stream_add_point(current_ev, end_buf,
++ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
+ &iwe, buf);
+
+ /* We ran out of space in the buffer. */
diff --git a/package/madwifi/patches/410-ar231x_2.6.28.patch b/package/madwifi/patches/410-ar231x_2.6.28.patch
new file mode 100644
index 000000000..30d1c56a4
--- /dev/null
+++ b/package/madwifi/patches/410-ar231x_2.6.28.patch
@@ -0,0 +1,281 @@
+--- a/ath/if_ath_ahb.c
++++ b/ath/if_ath_ahb.c
+@@ -33,20 +33,15 @@
+ #include "if_ath_ahb.h"
+ #include "ah_soc.h"
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+-#error "Kernel versions older than 2.6.19 are not supported!"
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
++#include <ar231x_platform.h>
+ #endif
+
+ struct ath_ahb_softc {
+ struct ath_softc aps_sc;
+-#ifdef CONFIG_PM
+- u32 aps_pmstate[16];
+-#endif
++ struct ar531x_config aps_config;
+ };
+
+-static struct ath_ahb_softc *sclist[2] = {NULL, NULL};
+-static u_int8_t num_activesc = 0;
+-
+ /*
+ * Module glue.
+ */
+@@ -101,13 +96,13 @@ ahb_enable_wmac(u_int16_t devid, u_int16
+ while (REG_READ(AR5315_PCI_MAC_PCICFG) & AR5315_PCI_MAC_PCICFG_SPWR_DN);
+ } else {
+ switch (wlanNum) {
+- case AR531X_WLAN0_NUM:
++ case 0:
+ reset = (AR531X_RESET_WLAN0 |
+ AR531X_RESET_WARM_WLAN0_MAC |
+ AR531X_RESET_WARM_WLAN0_BB);
+ enable = AR531X_ENABLE_WLAN0;
+ break;
+- case AR531X_WLAN1_NUM:
++ case 1:
+ reset = (AR531X_RESET_WLAN1 |
+ AR531X_RESET_WARM_WLAN1_MAC |
+ AR531X_RESET_WARM_WLAN1_BB);
+@@ -144,10 +139,10 @@ ahb_disable_wmac(u_int16_t devid, u_int1
+ *en &= ~AR5315_ARB_WLAN;
+ } else {
+ switch (wlanNum) {
+- case AR531X_WLAN0_NUM:
++ case 0:
+ enable = AR531X_ENABLE_WLAN0;
+ break;
+- case AR531X_WLAN1_NUM:
++ case 1:
+ enable = AR531X_ENABLE_WLAN1;
+ break;
+ default:
+@@ -159,29 +154,6 @@ ahb_disable_wmac(u_int16_t devid, u_int1
+ }
+
+
+-static int
+-exit_ath_wmac(u_int16_t wlanNum, struct ar531x_config *config)
+-{
+- struct ath_ahb_softc *sc = sclist[wlanNum];
+- struct net_device *dev;
+- u_int16_t devid;
+-
+- if (sc == NULL)
+- return -ENODEV; /* XXX: correct return value? */
+-
+- dev = sc->aps_sc.sc_dev;
+- ath_detach(dev);
+- if (dev->irq)
+- free_irq(dev->irq, dev);
+- devid = sc->aps_sc.devid;
+- config->tag = (void *)((unsigned long) devid);
+-
+- ahb_disable_wmac(devid, wlanNum);
+- free_netdev(dev);
+- sclist[wlanNum] = NULL;
+- return 0;
+-}
+-
+ static const char ubnt[] = "Ubiquiti Networks";
+ /* { vendorname, cardname, vendorid, cardid, subsys vendorid, subsys id, poweroffset } */
+ static const struct ath_hw_detect cards[] = {
+@@ -201,6 +173,114 @@ static const struct ath_hw_detect cards[
+ { ubnt, "Bullet5", PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0xc205 },
+ };
+
++static void
++ahb_hw_detect(struct ath_ahb_softc *sc, const char *radio)
++{
++ u16 *radio_data = (u16 *) radio;
++ if (radio_data) {
++ u16 vendor, id, subvendor, subid;
++ vendor = radio_data[1];
++ id = radio_data[0];
++ subvendor = radio_data[8];
++ subid = radio_data[7];
++ ath_hw_detect(&sc->aps_sc, cards, ARRAY_SIZE(cards), vendor, id, subvendor, subid);
++ }
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
++
++static int ahb_wmac_probe(struct platform_device *pdev)
++{
++ struct ar231x_board_config *bcfg = pdev->dev.platform_data;
++ struct ath_ahb_softc *sc;
++ struct net_device *dev;
++ struct resource *res;
++ const char *athname;
++ int err;
++
++ ahb_enable_wmac(bcfg->devid, pdev->id);
++ dev = alloc_netdev(sizeof(struct ath_ahb_softc), "wifi%d", ether_setup);
++ if (!dev)
++ return -ENOMEM;
++
++ sc = dev->priv;
++ sc->aps_sc.sc_dev = dev;
++
++ dev->irq = platform_get_irq(pdev, 0);
++ if (dev->irq <= 0) {
++ printk("%s: Cannot find IRQ resource\n", dev->name);
++ goto error;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res) {
++ printk("%s: Cannot find MMIO resource\n", dev->name);
++ goto error;
++ }
++
++ dev->mem_start = KSEG1ADDR(res->start);
++ dev->mem_end = KSEG1ADDR(res->end);
++ sc->aps_sc.sc_iobase = (void __iomem *) dev->mem_start;
++ sc->aps_sc.sc_bdev = NULL;
++
++ /* bus information for the HAL */
++ sc->aps_config.board = (const struct ar531x_boarddata *) bcfg->config;
++ sc->aps_config.radio = bcfg->radio;
++ sc->aps_config.unit = pdev->id;
++ sc->aps_config.tag = NULL;
++
++ err = ath_attach(bcfg->devid, dev, &sc->aps_config);
++ if (err != 0) {
++ printk("%s: ath_attach failed: %d\n", dev->name, err);
++ goto error;
++ }
++
++ athname = ath_hal_probe(ATHEROS_VENDOR_ID, bcfg->devid);
++ printk(KERN_INFO "%s: %s: %s: mem=0x%lx, irq=%d\n",
++ dev_info, dev->name, athname ? athname : "Atheros ???", dev->mem_start, dev->irq);
++
++ if (request_irq(dev->irq, ath_intr, IRQF_SHARED|IRQF_DISABLED, dev->name, dev)) {
++ printk(KERN_WARNING "%s: %s: request_irq failed\n", dev_info, dev->name);
++ goto error;
++ }
++
++ sc->aps_sc.sc_softled = 1; /* SoftLED over GPIO */
++ sc->aps_sc.sc_ledpin = bcfg->config->sysLedGpio;
++ sc->aps_sc.sc_invalid = 0;
++ ahb_hw_detect(sc, bcfg->radio);
++ platform_set_drvdata(pdev, dev);
++ return 0;
++
++error_dev:
++ free_irq(dev->irq, dev);
++error:
++ free_netdev(dev);
++
++ return -ENODEV;
++}
++
++
++static int ahb_wmac_remove(struct platform_device *pdev)
++{
++ struct ar231x_board_config *bcfg = pdev->dev.platform_data;
++ struct net_device *dev;
++
++ dev = platform_get_drvdata(pdev);
++ ath_detach(dev);
++
++ if (dev->irq)
++ free_irq(dev->irq, dev);
++
++ ahb_disable_wmac(bcfg->devid, pdev->id);
++ free_netdev(dev);
++
++ return 0;
++}
++
++#else
++
++static struct ath_ahb_softc *sclist[2] = {NULL, NULL};
++
+ static int
+ init_ath_wmac(u_int16_t devid, u_int16_t wlanNum, struct ar531x_config *config)
+ {
+@@ -253,7 +333,7 @@ init_ath_wmac(u_int16_t devid, u_int16_t
+ sc->aps_sc.sc_iobase = (void __iomem *) dev->mem_start;
+ sc->aps_sc.sc_bdev = NULL;
+
+- if (request_irq(dev->irq, ath_intr, IRQF_SHARED, dev->name, dev)) {
++ if (request_irq(dev->irq, ath_intr, IRQF_SHARED|IRQF_DISABLED, dev->name, dev)) {
+ printk(KERN_WARNING "%s: %s: request_irq failed\n", dev_info, dev->name);
+ goto bad3;
+ }
+@@ -263,21 +343,12 @@ init_ath_wmac(u_int16_t devid, u_int16_t
+ athname = ath_hal_probe(ATHEROS_VENDOR_ID, devid);
+ printk(KERN_INFO "%s: %s: %s: mem=0x%lx, irq=%d\n",
+ dev_info, dev->name, athname ? athname : "Atheros ???", dev->mem_start, dev->irq);
+- num_activesc++;
+ /* Ready to process interrupts */
+
+ sc->aps_sc.sc_softled = 1; /* SoftLED over GPIO */
+ sc->aps_sc.sc_ledpin = config->board->sysLedGpio;
+ sc->aps_sc.sc_invalid = 0;
+- radio_data = (u16 *) config->radio;
+- if (radio_data) {
+- u16 vendor, id, subvendor, subid;
+- vendor = radio_data[1];
+- id = radio_data[0];
+- subvendor = radio_data[8];
+- subid = radio_data[7];
+- ath_hw_detect(&sc->aps_sc, cards, ARRAY_SIZE(cards), vendor, id, subvendor, subid);
+- }
++ ahb_hw_detect(sc, config->radio);
+
+ return 0;
+
+@@ -292,6 +363,29 @@ init_ath_wmac(u_int16_t devid, u_int16_t
+ return -ENODEV;
+ }
+
++static int
++exit_ath_wmac(u_int16_t wlanNum, struct ar531x_config *config)
++{
++ struct ath_ahb_softc *sc = sclist[wlanNum];
++ struct net_device *dev;
++ u_int16_t devid;
++
++ if (sc == NULL)
++ return -ENODEV; /* XXX: correct return value? */
++
++ dev = sc->aps_sc.sc_dev;
++ ath_detach(dev);
++ if (dev->irq)
++ free_irq(dev->irq, dev);
++ devid = sc->aps_sc.devid;
++ config->tag = (void *)((unsigned long) devid);
++
++ ahb_disable_wmac(devid, wlanNum);
++ free_netdev(dev);
++ sclist[wlanNum] = NULL;
++ return 0;
++}
++
+ static int ahb_wmac_probe(struct platform_device *pdev)
+ {
+ u_int16_t devid;
+@@ -312,11 +406,18 @@ static int ahb_wmac_remove(struct platfo
+ return 0;
+ }
+
++#endif
++
+ static struct platform_driver ahb_wmac_driver = {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
++ .driver.name = "ar231x-wmac",
++#else
+ .driver.name = "ar531x-wmac",
++#endif
+ .probe = ahb_wmac_probe,
+ .remove = ahb_wmac_remove
+ };
++
+ int
+ ath_ioctl_ethtool(struct ath_softc *sc, int cmd, void __user *addr)
+ {
diff --git a/package/madwifi/patches/411-autochannel_multi.patch b/package/madwifi/patches/411-autochannel_multi.patch
new file mode 100644
index 000000000..d05c447f7
--- /dev/null
+++ b/package/madwifi/patches/411-autochannel_multi.patch
@@ -0,0 +1,347 @@
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -97,6 +97,123 @@ struct scan_state {
+ static void scan_restart_pwrsav(unsigned long);
+ static void scan_next(unsigned long);
+
++spinlock_t channel_lock = SPIN_LOCK_UNLOCKED;
++static LIST_HEAD(channels_inuse);
++
++struct channel_inuse {
++ struct list_head list;
++ struct ieee80211com *ic;
++ u16 freq;
++ u8 bw;
++};
++
++static inline u32
++get_signal(u8 bw, u8 distance)
++{
++ u32 v;
++
++ /* signal = 1 - (distance / bw)^2 [scale: 100] */
++ v = 100 * distance / bw;
++ v = (100 - ((v * v) / 100));
++ return v;
++}
++
++static u32
++get_overlap(u16 f1, u16 f2, u8 b1, u8 b2)
++{
++ u32 v;
++ u16 d, c;
++
++ /* add offsets for sidechannel interference */
++ b1 += (b1 / 5);
++ b2 += (b2 / 5);
++
++ /* use only one direction */
++ b1 /= 2;
++ b2 /= 2;
++
++ if (f1 + b1 < f2 - b2)
++ return 0;
++
++ d = f2 - f1;
++ c = d * b1 / (b1 + b2);
++ v = get_signal(b1, c);
++
++ return v * v / 100;
++}
++
++static u8
++get_channel_bw(struct ieee80211_channel *c)
++{
++ switch(c->ic_flags & (
++ IEEE80211_CHAN_HALF |
++ IEEE80211_CHAN_QUARTER |
++ IEEE80211_CHAN_TURBO |
++ IEEE80211_CHAN_STURBO)) {
++ case IEEE80211_CHAN_QUARTER:
++ return 5;
++ case IEEE80211_CHAN_HALF:
++ return 10;
++ case IEEE80211_CHAN_TURBO:
++ case IEEE80211_CHAN_STURBO:
++ return 40;
++ default:
++ return 20;
++ }
++}
++
++/* must be called with channel_lock held */
++u32
++ieee80211_scan_get_bias(struct ieee80211_channel *c)
++{
++ struct channel_inuse *ch;
++ u8 bw = get_channel_bw(c);
++ u32 bias = 0;
++
++ list_for_each_entry(ch, &channels_inuse, list) {
++ if (ch->freq == c->ic_freq) {
++ bias += 50;
++ continue;
++ }
++ if (c->ic_freq < ch->freq)
++ bias += get_overlap(c->ic_freq, ch->freq, bw, ch->bw);
++ else
++ bias += get_overlap(ch->freq, c->ic_freq, ch->bw, bw);
++ }
++ return bias;
++}
++EXPORT_SYMBOL(ieee80211_scan_get_bias);
++
++/* must be called with channel_lock held */
++void
++ieee80211_scan_set_bss_channel(struct ieee80211com *ic, struct ieee80211_channel *c)
++{
++ unsigned long flags;
++ struct channel_inuse *ch;
++
++ list_for_each_entry(ch, &channels_inuse, list) {
++ if (ch->ic == ic)
++ goto found;
++ }
++ ch = NULL;
++found:
++ if (c && (c != IEEE80211_CHAN_ANYC)) {
++ if (!ch) {
++ ch = kmalloc(sizeof(struct channel_inuse), GFP_ATOMIC);
++ ch->ic = ic;
++ INIT_LIST_HEAD(&ch->list);
++ list_add(&ch->list, &channels_inuse);
++ }
++ ch->freq = c->ic_freq;
++ ch->bw = get_channel_bw(c);
++ } else if (ch) {
++ list_del(&ch->list);
++ kfree(ch);
++ }
++}
++EXPORT_SYMBOL(ieee80211_scan_set_bss_channel);
++
++
+ void
+ ieee80211_scan_attach(struct ieee80211com *ic)
+ {
+@@ -1169,7 +1286,7 @@ ieee80211_scan_dfs_action(struct ieee802
+ IEEE80211_RADAR_CHANCHANGE_TBTT_COUNT;
+ ic->ic_flags |= IEEE80211_F_CHANSWITCH;
+ } else {
+-
++ unsigned long flags;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
+ "%s: directly switching to channel "
+ "%3d (%4d MHz)\n", __func__,
+@@ -1180,6 +1297,9 @@ ieee80211_scan_dfs_action(struct ieee802
+ * change the channel here. */
+ change_channel(ic, new_channel);
+ ic->ic_bsschan = new_channel;
++ spin_lock_irqsave(&channel_lock, flags);
++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
++ spin_unlock_irqrestore(&channel_lock, flags);
+ if (vap->iv_bss)
+ vap->iv_bss->ni_chan = new_channel;
+ }
+--- a/net80211/ieee80211_scan.h
++++ b/net80211/ieee80211_scan.h
+@@ -35,6 +35,7 @@
+
+ #define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX
+
++extern spinlock_t channel_lock;
+ struct ieee80211_scanner;
+ struct ieee80211_scan_entry;
+
+@@ -116,6 +117,8 @@ void ieee80211_scan_flush(struct ieee802
+ struct ieee80211_scan_entry;
+ typedef int ieee80211_scan_iter_func(void *, const struct ieee80211_scan_entry *);
+ int ieee80211_scan_iterate(struct ieee80211com *, ieee80211_scan_iter_func *, void *);
++u32 ieee80211_scan_get_bias(struct ieee80211_channel *c);
++void ieee80211_scan_set_bss_channel(struct ieee80211com *ic, struct ieee80211_channel *c);
+
+ /*
+ * Parameters supplied when adding/updating an entry in a
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -373,8 +373,16 @@ void
+ ieee80211_ifdetach(struct ieee80211com *ic)
+ {
+ struct ieee80211vap *vap;
++ unsigned long flags;
+ int count;
+
++ /* mark the channel as no longer in use */
++ ic->ic_bsschan = IEEE80211_CHAN_ANYC;
++ spin_lock_irqsave(&channel_lock, flags);
++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
++ spin_unlock_irqrestore(&channel_lock, flags);
++
++
+ /* bring down all vaps */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ieee80211_stop(vap->iv_dev);
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -2775,6 +2775,7 @@ static void
+ ieee80211_doth_switch_channel(struct ieee80211vap *vap)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
++ unsigned long flags;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
+ "%s: Channel switch to %3d (%4d MHz) NOW!\n",
+@@ -2797,6 +2798,9 @@ ieee80211_doth_switch_channel(struct iee
+
+ ic->ic_curchan = ic->ic_bsschan = vap->iv_csa_chan;
+ ic->ic_set_channel(ic);
++ spin_lock_irqsave(&channel_lock, flags);
++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
++ spin_unlock_irqrestore(&channel_lock, flags);
+ }
+
+ static void
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -308,6 +308,7 @@ ieee80211_create_ibss(struct ieee80211va
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
++ unsigned long flags;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: creating ibss on channel %u\n", __func__,
+@@ -386,6 +387,9 @@ ieee80211_create_ibss(struct ieee80211va
+ ic->ic_bsschan = chan;
+ ieee80211_node_set_chan(ic, ni);
+ ic->ic_curmode = ieee80211_chan2mode(chan);
++ spin_lock_irqsave(&channel_lock, flags);
++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
++ spin_unlock_irqrestore(&channel_lock, flags);
+
+ /* Update country ie information */
+ ieee80211_build_countryie(ic);
+@@ -622,6 +626,7 @@ ieee80211_sta_join1(struct ieee80211_nod
+ struct ieee80211vap *vap = selbs->ni_vap;
+ struct ieee80211com *ic = selbs->ni_ic;
+ struct ieee80211_node *obss;
++ unsigned long flags;
+ int canreassoc;
+
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+@@ -650,6 +655,9 @@ ieee80211_sta_join1(struct ieee80211_nod
+ ic->ic_curchan = ic->ic_bsschan;
+ ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
+ ic->ic_set_channel(ic);
++ spin_lock_irqsave(&channel_lock, flags);
++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
++ spin_unlock_irqrestore(&channel_lock, flags);
+ /*
+ * Set the erp state (mostly the slot time) to deal with
+ * the auto-select case; this should be redundant if the
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -1231,6 +1231,7 @@ ieee80211_dturbo_switch(struct ieee80211
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ #endif
+ struct ieee80211_channel *chan;
++ unsigned long flags;
+
+ chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags);
+ if (chan == NULL) { /* XXX should not happen */
+@@ -1249,6 +1250,9 @@ ieee80211_dturbo_switch(struct ieee80211
+ ic->ic_bsschan = chan;
+ ic->ic_curchan = chan;
+ ic->ic_set_channel(ic);
++ spin_lock_irqsave(&channel_lock, flags);
++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
++ spin_unlock_irqrestore(&channel_lock, flags);
+ /* NB: do not need to reset ERP state because in sta mode */
+ }
+ EXPORT_SYMBOL(ieee80211_dturbo_switch);
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -4076,8 +4076,13 @@ ieee80211_ioctl_setchanlist(struct net_d
+ if (nchan == 0) /* no valid channels, disallow */
+ return -EINVAL;
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */
+- isclr(chanlist, ic->ic_bsschan->ic_ieee))
++ isclr(chanlist, ic->ic_bsschan->ic_ieee)) {
++ unsigned long flags;
+ ic->ic_bsschan = IEEE80211_CHAN_ANYC; /* invalidate */
++ spin_lock_irqsave(&channel_lock, flags);
++ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
++ spin_unlock_irqrestore(&channel_lock, flags);
++ }
+
+ memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
+ /* update Supported Channels information element */
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -208,9 +208,15 @@ ap_start(struct ieee80211_scan_state *ss
+ struct ieee80211com *ic = NULL;
+ int i;
+ unsigned int mode = 0;
++ unsigned long sflags;
+
+ SCAN_AP_LOCK_IRQ(as);
+ ic = vap->iv_ic;
++
++ spin_lock_irqsave(&channel_lock, sflags);
++ ieee80211_scan_set_bss_channel(ic, NULL);
++ spin_unlock_irqrestore(&channel_lock, sflags);
++
+ /* Determine mode flags to match, or leave zero for auto mode */
+ ss->ss_last = 0;
+ ieee80211_scan_add_channels(ic, ss, vap->iv_des_mode);
+@@ -423,8 +429,10 @@ pc_cmp_idletime(struct ieee80211_channel
+ if (!a->ic_idletime || !b->ic_idletime)
+ return 0;
+
+- /* a is better than b (return < 0) when a has more idle time than b */
+- return b->ic_idletime - a->ic_idletime;
++ /* a is better than b (return < 0) when a has more idle and less bias time than b */
++ return
++ ((100 - (u32) a->ic_idletime) + ieee80211_scan_get_bias(a)) -
++ ((100 - (u32) b->ic_idletime) + ieee80211_scan_get_bias(b));
+ }
+
+
+@@ -575,6 +583,7 @@ ap_end(struct ieee80211_scan_state *ss,
+ struct ap_state *as = ss->ss_priv;
+ struct ieee80211_channel *bestchan = NULL;
+ struct ieee80211com *ic = NULL;
++ unsigned long sflags;
+ int res = 1;
+
+ SCAN_AP_LOCK_IRQ(as);
+@@ -586,8 +595,11 @@ ap_end(struct ieee80211_scan_state *ss,
+
+ /* record stats for the channel that was scanned last */
+ ic->ic_set_channel(ic);
++ spin_lock_irqsave(&channel_lock, sflags);
++ ieee80211_scan_set_bss_channel(ic, NULL);
+ bestchan = pick_channel(ss, vap, flags);
+ if (bestchan == NULL) {
++ spin_unlock_irqrestore(&channel_lock, sflags);
+ if (ss->ss_last > 0) {
+ /* no suitable channel, should not happen */
+ printk(KERN_ERR "%s: %s: no suitable channel! "
+@@ -606,6 +618,7 @@ ap_end(struct ieee80211_scan_state *ss,
+ bestchan->ic_freq, bestchan->ic_flags &
+ ~IEEE80211_CHAN_TURBO)) == NULL) {
+ /* should never happen ?? */
++ spin_unlock_irqrestore(&channel_lock, sflags);
+ SCAN_AP_UNLOCK_IRQ_EARLY(as);
+ return 0;
+ }
+@@ -618,6 +631,9 @@ ap_end(struct ieee80211_scan_state *ss,
+ as->as_action = action;
+ as->as_selbss = se;
+
++ ieee80211_scan_set_bss_channel(ic, bestchan);
++ spin_unlock_irqrestore(&channel_lock, sflags);
++
+ /* Must defer action to avoid possible recursive call through
+ * 80211 state machine, which would result in recursive
+ * locking. */
diff --git a/package/madwifi/patches/412-fragmentation_fix.patch b/package/madwifi/patches/412-fragmentation_fix.patch
new file mode 100644
index 000000000..92c411b2b
--- /dev/null
+++ b/package/madwifi/patches/412-fragmentation_fix.patch
@@ -0,0 +1,10 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -3684,6 +3684,7 @@ ff_bypass:
+ * already alloc'd
+ */
+ ATH_TXBUF_LOCK_IRQ(sc);
++ STAILQ_INSERT_TAIL(&bf_head, bf, bf_list);
+ for (bfcnt = 1; bfcnt < framecnt; ++bfcnt) {
+ tbf = ath_take_txbuf_locked(sc);
+ if (tbf == NULL)
diff --git a/package/madwifi/patches/413-rxorn.patch b/package/madwifi/patches/413-rxorn.patch
new file mode 100644
index 000000000..0e8d885da
--- /dev/null
+++ b/package/madwifi/patches/413-rxorn.patch
@@ -0,0 +1,31 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -2308,6 +2308,17 @@ ath_intr(int irq, void *dev_id, struct p
+
+ sc->sc_isr = status;
+ status &= sc->sc_imask; /* discard unasked for bits */
++
++ /* Treat RXORN as non-fatal. Either the bus is busy or the CPU
++ * is not fast enough to process all frames. Treat it like
++ * an Rx interrupt
++ */
++ if (status & HAL_INT_RXORN) {
++ sc->sc_stats.ast_rxorn++;
++ status &= ~HAL_INT_RXORN;
++ status |= HAL_INT_RX;
++ }
++
+ /* As soon as we know we have a real interrupt we intend to service,
+ * we will check to see if we need an initial hardware TSF reading.
+ * Normally we would just populate this all the time to keep things
+@@ -2320,10 +2331,6 @@ ath_intr(int irq, void *dev_id, struct p
+ sc->sc_stats.ast_hardware++;
+ ath_hal_intrset(ah, 0); /* disable intr's until reset */
+ ATH_SCHEDULE_TQUEUE(&sc->sc_fataltq, &needmark);
+- } else if (status & HAL_INT_RXORN) {
+- sc->sc_stats.ast_rxorn++;
+- ath_hal_intrset(ah, 0); /* disable intr's until reset */
+- ATH_SCHEDULE_TQUEUE(&sc->sc_rxorntq, &needmark);
+ } else {
+ if (status & HAL_INT_SWBA) {
+ struct ieee80211vap * vap;
diff --git a/package/madwifi/patches/414-txpower.patch b/package/madwifi/patches/414-txpower.patch
new file mode 100644
index 000000000..3c18bd6fe
--- /dev/null
+++ b/package/madwifi/patches/414-txpower.patch
@@ -0,0 +1,262 @@
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -270,6 +270,7 @@ ieee80211_ifattach(struct ieee80211com *
+ ("invalid number of channels specified: %u", ic->ic_nchans));
+ memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+ ic->ic_modecaps |= 1 << IEEE80211_MODE_AUTO;
++ ic->ic_max_txpower = IEEE80211_TXPOWER_MIN;
+
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+@@ -277,6 +278,7 @@ ieee80211_ifattach(struct ieee80211com *
+ KASSERT(c->ic_ieee < IEEE80211_CHAN_MAX,
+ ("channel with bogus ieee number %u", c->ic_ieee));
+ setbit(ic->ic_chan_avail, c->ic_ieee);
++ ic->ic_max_txpower = max(ic->ic_max_txpower, (u16) (c->ic_maxpower * 2));
+
+ if (c->ic_scanflags & IEEE80211_NOSCAN_DEFAULT)
+ c->ic_scanflags |= IEEE80211_NOSCAN_SET;
+@@ -346,8 +348,6 @@ ieee80211_ifattach(struct ieee80211com *
+ TAILQ_INIT(&ic->ic_vaps);
+
+ ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
+- ic->ic_txpowlimit = IEEE80211_TXPOWER_MIN;
+- ic->ic_newtxpowlimit = IEEE80211_TXPOWER_MAX;
+
+ init_timer(&ic->ic_dfs_excl_timer);
+ ic->ic_dfs_excl_timer.function =
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -1125,7 +1125,7 @@ ieee80211_alloc_node(struct ieee80211vap
+
+ ni->ni_chan = IEEE80211_CHAN_ANYC;
+ ni->ni_authmode = IEEE80211_AUTH_OPEN;
+- ni->ni_txpower = ic->ic_txpowlimit;
++ ni->ni_txpower = IEEE80211_TXPOWER_MAX;
+
+ ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey,
+ IEEE80211_KEYIX_NONE);
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -343,8 +343,8 @@ struct ieee80211com {
+ u_int16_t ic_holdover; /* PM hold over duration */
+ u_int16_t ic_bmissthreshold; /* beacon miss threshold (# beacons) */
+ unsigned long ic_bmiss_guard; /* when to cease ignoring bmiss (jiffies) */
+- u_int16_t ic_txpowlimit; /* global tx power limit (in 0.5 dBm) */
+- u_int16_t ic_newtxpowlimit; /* tx power limit to change to (in 0.5 dBm) */
++ u_int16_t ic_txpowlimit; /* configured global tx power limit (in 0.5 dBm) */
++ u_int16_t ic_max_txpower; /* global hardware tx power limit */
+ u_int16_t ic_uapsdmaxtriggers; /* max triggers that could arrive */
+ u_int8_t ic_coverageclass; /* coverage class */
+ u_int8_t ic_protmode_rssi; /* rssi threshold for protection mode */
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -908,6 +908,21 @@ ieee80211_ioctl_giwessid(struct net_devi
+ return 0;
+ }
+
++static u16
++ieee80211_get_maxtxpow(struct ieee80211com *ic)
++{
++ u_int16_t txp = IEEE80211_TXPOWER_MAX;
++
++ if (ic->ic_bsschan && (ic->ic_bsschan != IEEE80211_CHAN_ANYC))
++ txp = min(txp, (u16) ic->ic_bsschan->ic_maxpower);
++
++ if (ic->ic_max_txpower > 0)
++ txp = min(txp, ic->ic_max_txpower);
++
++ return txp;
++}
++
++
+ static int
+ ieee80211_ioctl_giwrange(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+@@ -920,17 +935,21 @@ ieee80211_ioctl_giwrange(struct net_devi
+ u_int8_t reported[IEEE80211_CHAN_BYTES]; /* XXX stack usage? */
+ int i, r;
+ int step = 0;
++ u_int16_t power;
+
+ data->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(struct iw_range));
+
++ power = ieee80211_get_maxtxpow(ic);
++
+ /* txpower (128 values, but will print out only IW_MAX_TXPOWER) */
+- range->num_txpower = (ic->ic_txpowlimit >= 8) ? IW_MAX_TXPOWER : ic->ic_txpowlimit;
+- step = ic->ic_txpowlimit / (2 * (IW_MAX_TXPOWER - 1));
++ power /= 2; /* Unit: 0.5 dBm */
++ range->num_txpower = (power >= 8) ? IW_MAX_TXPOWER : power;
++ step = power / (IW_MAX_TXPOWER - 1);
+
+ range->txpower[0] = 0;
+ for (i = 1; i < IW_MAX_TXPOWER; i++)
+- range->txpower[i] = (ic->ic_txpowlimit/2)
++ range->txpower[i] = power
+ - (IW_MAX_TXPOWER - i - 1) * step;
+
+ range->txpower_capa = IW_TXPOW_DBM;
+@@ -1379,13 +1398,11 @@ ieee80211_ioctl_siwtxpow(struct net_devi
+ int fixed, disabled;
+
+ fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED);
+- disabled = (fixed && vap->iv_bss->ni_txpower == 0);
++ disabled = (fixed && ic->ic_txpowlimit == 0);
+ if (rrq->disabled) {
+ if (!disabled) {
+- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+- return -EOPNOTSUPP;
+ ic->ic_flags |= IEEE80211_F_TXPOW_FIXED;
+- vap->iv_bss->ni_txpower = 0;
++ ic->ic_txpowlimit = 0;
+ goto done;
+ }
+ return 0;
+@@ -1396,30 +1413,12 @@ ieee80211_ioctl_siwtxpow(struct net_devi
+ return -EOPNOTSUPP;
+ if (rrq->flags != IW_TXPOW_DBM)
+ return -EINVAL;
+- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) {
+- if ((ic->ic_bsschan->ic_maxregpower >= rrq->value) &&
+- (ic->ic_txpowlimit/2 >= rrq->value)) {
+- vap->iv_bss->ni_txpower = 2 * rrq->value;
+- ic->ic_newtxpowlimit = 2 * rrq->value;
+- ic->ic_flags |= IEEE80211_F_TXPOW_FIXED;
+- } else
+- return -EINVAL;
+- } else {
+- /*
+- * No channel set yet
+- */
+- if (ic->ic_txpowlimit/2 >= rrq->value) {
+- vap->iv_bss->ni_txpower = 2 * rrq->value;
+- ic->ic_newtxpowlimit = 2 * rrq->value;
+- ic->ic_flags |= IEEE80211_F_TXPOW_FIXED;
+- }
+- else
+- return -EINVAL;
+- }
++ ic->ic_txpowlimit = 2 * rrq->value;
++ ic->ic_flags |= IEEE80211_F_TXPOW_FIXED;
+ } else {
+ if (!fixed) /* no change */
+ return 0;
+- ic->ic_newtxpowlimit = IEEE80211_TXPOWER_MAX;
++ ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
+ ic->ic_flags &= ~IEEE80211_F_TXPOW_FIXED;
+ }
+ done:
+@@ -1588,9 +1587,18 @@ ieee80211_ioctl_giwtxpow(struct net_devi
+ {
+ struct ieee80211vap *vap = dev->priv;
+ struct ieee80211com *ic = vap->iv_ic;
+-
+- rrq->value = vap->iv_bss->ni_txpower / 2;
+- rrq->fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED) != 0;
++ unsigned int power = ic->ic_txpowlimit;
++ struct ieee80211_channel *c;
++ u_int16_t txp;
++
++ txp = ieee80211_get_maxtxpow(ic);
++ if (ic->ic_flags & IEEE80211_F_TXPOW_FIXED) {
++ txp = min(txp, ic->ic_txpowlimit);
++ rrq->fixed = 1;
++ } else {
++ rrq->fixed = 0;
++ }
++ rrq->value = txp / 2;
+ rrq->disabled = (rrq->fixed && rrq->value == 0);
+ rrq->flags = IW_TXPOW_DBM;
+ return 0;
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -380,7 +380,6 @@ static unsigned int ath_dump_hal_map(str
+ static u_int32_t ath_get_clamped_maxtxpower(struct ath_softc *sc);
+ static u_int32_t ath_set_clamped_maxtxpower(struct ath_softc *sc,
+ u_int32_t new_clamped_maxtxpower);
+-static u_int32_t ath_get_real_maxtxpower(struct ath_softc *sc);
+
+ static void ath_poll_disable(struct net_device *dev);
+ static void ath_poll_enable(struct net_device *dev);
+@@ -3168,7 +3167,7 @@ ath_tx_startraw(struct net_device *dev,
+ try0 = ph->try0;
+ rt = sc->sc_currates;
+ txrate = dot11_to_ratecode(sc, rt, ph->rate0);
+- power = ph->power > 60 ? 60 : ph->power;
++ power = ph->power > 63 ? 63 : ph->power;
+ hdrlen = ieee80211_anyhdrsize(wh);
+ pktlen = skb->len + IEEE80211_CRC_LEN;
+
+@@ -8394,7 +8393,7 @@ ath_tx_start(struct net_device *dev, str
+ pktlen, /* packet length */
+ hdrlen, /* header length */
+ atype, /* Atheros packet type */
+- MIN(ni->ni_txpower, 60), /* txpower */
++ MIN(ni->ni_txpower, 63), /* txpower */
+ txrate, try0, /* series 0 rate/tries */
+ keyix, /* key cache index */
+ antenna, /* antenna mode */
+@@ -10387,59 +10386,16 @@ ath_get_clamped_maxtxpower(struct ath_so
+
+ /* XXX: this function needs some locking to avoid being called
+ * twice/interrupted */
+-/* 1. Save the currently specified maximum txpower (as clamped by madwifi)
+- * 2. Determine the real maximum txpower the card can support by
+- * setting a value that exceeds the maximum range (by one) and
+- * finding out what it limits us to.
+- * 3. Restore the saved maxtxpower value we had previously specified */
+-static u_int32_t
+-ath_get_real_maxtxpower(struct ath_softc *sc)
+-{
+- u_int32_t saved_clamped_maxtxpower;
+- u_int32_t real_maxtxpower;
+-
+- saved_clamped_maxtxpower = ath_get_clamped_maxtxpower(sc);
+- real_maxtxpower =
+- ath_set_clamped_maxtxpower(sc, IEEE80211_TXPOWER_MAX + 1);
+- ath_set_clamped_maxtxpower(sc, saved_clamped_maxtxpower);
+- return real_maxtxpower;
+-}
+-
+-
+-/* XXX: this function needs some locking to avoid being called
+- * twice/interrupted */
+ static void
+ ath_update_txpow(struct ath_softc *sc)
+ {
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = NULL;
+ struct ath_hal *ah = sc->sc_ah;
+- u_int32_t prev_clamped_maxtxpower = 0;
+- u_int32_t new_clamped_maxtxpower = 0;
+
+ /* Determine the previous value of maxtxpower */
+- prev_clamped_maxtxpower = ath_get_clamped_maxtxpower(sc);
+- /* Determine the real maximum txpower the card can support */
+- ic->ic_txpowlimit = ath_get_real_maxtxpower(sc);
+- /* Grab the new maxtxpower setting (which may have changed) */
+- new_clamped_maxtxpower = ic->ic_newtxpowlimit;
+- /* Make sure the change is within limits, clamp it otherwise */
+- if (ic->ic_newtxpowlimit > ic->ic_txpowlimit)
+- new_clamped_maxtxpower = ic->ic_txpowlimit;
+- /* Search for the VAP that needs a txpow change, if any */
+- TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+- if (!tpc || ic->ic_newtxpowlimit != vap->iv_bss->ni_txpower) {
+- vap->iv_bss->ni_txpower = new_clamped_maxtxpower;
+- ieee80211_iterate_nodes(&vap->iv_ic->ic_sta,
+- set_node_txpower,
+- &new_clamped_maxtxpower);
+- }
+- }
+-
+- /* Store the assigned (clamped) maximum txpower and update the HAL */
+- sc->sc_curtxpow = new_clamped_maxtxpower;
+- if (new_clamped_maxtxpower != prev_clamped_maxtxpower)
+- ath_hal_settxpowlimit(ah, new_clamped_maxtxpower);
++ ath_set_clamped_maxtxpower(sc, ic->ic_txpowlimit);
++ ic->ic_max_txpower = ath_get_clamped_maxtxpower(sc);
+ }
+
+ #ifdef ATH_SUPERG_XR
diff --git a/package/madwifi/patches/415-chan_switch.patch b/package/madwifi/patches/415-chan_switch.patch
new file mode 100644
index 000000000..6dc5da838
--- /dev/null
+++ b/package/madwifi/patches/415-chan_switch.patch
@@ -0,0 +1,187 @@
+--- a/net80211/ieee80211_beacon.c
++++ b/net80211/ieee80211_beacon.c
+@@ -224,18 +224,18 @@ ieee80211_beacon_alloc(struct ieee80211_
+ pktlen = 8 /* time stamp */
+ + sizeof(u_int16_t) /* beacon interval */
+ + sizeof(u_int16_t) /* capability information */
+- + 2 + ni->ni_esslen /* ssid */
++ + 2 + IEEE80211_NWID_LEN /* ssid */
+ + 2 + IEEE80211_RATE_SIZE /* supported rates */
+ + 7 /* FH/DS parameters max(7,3) */
+- + 2 + 4 + vap->iv_tim_len /* IBSS/TIM parameter set*/
++ + sizeof(struct ieee80211_tim_ie) + 128 /* IBSS/TIM parameter set*/
+ + ic->ic_country_ie.country_len + 2 /* country code */
+ + 3 /* power constraint */
+ + 5 /* channel switch announcement */
+ + 3 /* ERP */
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) /* Ext. Supp. Rates */
+- + (vap->iv_caps & IEEE80211_C_WME ? /* WME */
++ + (ic->ic_caps & IEEE80211_C_WME ? /* WME */
+ sizeof(struct ieee80211_wme_param) : 0)
+- + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
++ + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
+ 2 * sizeof(struct ieee80211_ie_wpa) : 0)
+ + sizeof(struct ieee80211_ie_athAdvCap)
+ #ifdef ATH_SUPERG_XR
+@@ -290,17 +290,26 @@ ieee80211_beacon_update(struct ieee80211
+ IEEE80211_LOCK_IRQ(ic);
+
+ /* Check if we need to change channel right now */
+- if ((ic->ic_flags & IEEE80211_F_DOTH) &&
+- (vap->iv_flags & IEEE80211_F_CHANSWITCH)) {
+- struct ieee80211_channel *c =
++ if (ic->ic_flags & IEEE80211_F_CHANSWITCH) {
++ struct ieee80211_channel *c =
+ ieee80211_doth_findchan(vap, ic->ic_chanchange_chan);
+-
+- if (!vap->iv_chanchange_count && !c) {
+- vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
+- ic->ic_flags &= ~IEEE80211_F_CHANSWITCH;
+- } else if (vap->iv_chanchange_count &&
+- ((!ic->ic_chanchange_tbtt) ||
+- (vap->iv_chanchange_count == ic->ic_chanchange_tbtt))) {
++ struct ieee80211vap *avp;
++ int do_switch = 1;
++
++ TAILQ_FOREACH(avp, &ic->ic_vaps, iv_next) {
++ if (!(avp->iv_flags & IEEE80211_F_CHANSWITCH))
++ continue;
++
++ do_switch = 0;
++ break;
++ }
++ if (vap->iv_flags & IEEE80211_F_CHANSWITCH) {
++ if (vap->iv_chanchange_count-- <= 1) {
++ vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
++ vap->iv_chanchange_count = 0;
++ }
++ }
++ if (do_switch) {
+ u_int8_t *frm;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
+@@ -316,16 +325,7 @@ ieee80211_beacon_update(struct ieee80211
+ } else
+ ic->ic_bsschan = c;
+
+- skb_pull(skb, sizeof(struct ieee80211_frame));
+- skb_trim(skb, 0);
+- frm = skb->data;
+- skb_put(skb, ieee80211_beacon_init(ni, bo, frm) - frm);
+- skb_push(skb, sizeof(struct ieee80211_frame));
+-
+- vap->iv_chanchange_count = 0;
+- vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
+ ic->ic_flags &= ~IEEE80211_F_CHANSWITCH;
+-
+ /* NB: Only for the first VAP to get here, and when we
+ * have a valid channel to which to change. */
+ if (c && (ic->ic_curchan != c)) {
+@@ -488,22 +488,20 @@ ieee80211_beacon_update(struct ieee80211
+
+ if (IEEE80211_IS_MODE_BEACON(vap->iv_opmode)) {
+
+- if ((ic->ic_flags & IEEE80211_F_DOTH) &&
+- (ic->ic_flags & IEEE80211_F_CHANSWITCH)) {
++ if (ic->ic_flags & IEEE80211_F_CHANSWITCH) {
+ struct ieee80211_ie_csa *csa_ie =
+ (struct ieee80211_ie_csa *)bo->bo_chanswitch;
+
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
++ if (csa_ie->csa_len == 0) {
++ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
+ "%s: Sending 802.11h chanswitch IE: "
+ "%d/%d\n", __func__,
+ ic->ic_chanchange_chan,
+ ic->ic_chanchange_tbtt);
+- if (!vap->iv_chanchange_count) {
+- vap->iv_flags |= IEEE80211_F_CHANSWITCH;
+
+ /* copy out trailer to open up a slot */
+ memmove(bo->bo_chanswitch + sizeof(*csa_ie),
+- bo->bo_chanswitch,
++ bo->bo_chanswitch,
+ bo->bo_chanswitch_trailerlen);
+
+ /* add ie in opened slot */
+@@ -523,17 +521,15 @@ ieee80211_beacon_update(struct ieee80211
+ bo->bo_ath_caps += sizeof(*csa_ie);
+ bo->bo_xr += sizeof(*csa_ie);
+
+- /* indicate new beacon length so other layers
++ /* indicate new beacon length so other layers
+ * may manage memory */
+ skb_put(skb, sizeof(*csa_ie));
+ len_changed = 1;
+- } else if(csa_ie->csa_count)
+- csa_ie->csa_count--;
+-
+- vap->iv_chanchange_count++;
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
+- "%s: CHANSWITCH IE, change in %d TBTT\n",
+- __func__, csa_ie->csa_count);
++
++ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
++ "%s: CHANSWITCH IE, change in %d TBTT\n",
++ __func__, csa_ie->csa_count);
++ }
+ }
+ #ifdef ATH_SUPERG_XR
+ if (vap->iv_flags & IEEE80211_F_XRUPDATE) {
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -699,39 +699,11 @@ ieee80211_ioctl_siwfreq(struct net_devic
+ if (c == NULL) /* no channel */
+ return -EINVAL;
+ }
+- /*
+- * Fine tune channel selection based on desired mode:
+- * if 11b is requested, find the 11b version of any
+- * 11g channel returned,
+- * if static turbo, find the turbo version of any
+- * 11a channel return,
+- * otherwise we should be ok with what we've got.
+- */
+- switch (vap->iv_des_mode) {
+- case IEEE80211_MODE_11B:
+- if (IEEE80211_IS_CHAN_ANYG(c)) {
+- c2 = findchannel(ic, i, IEEE80211_MODE_11B);
+- /* NB: should not happen, =>'s 11g w/o 11b */
+- if (c2 != NULL)
+- c = c2;
+- }
+- break;
+- case IEEE80211_MODE_TURBO_A:
+- if (IEEE80211_IS_CHAN_A(c)) {
+- c2 = findchannel(ic, i, IEEE80211_MODE_TURBO_A);
+- if (c2 != NULL)
+- c = c2;
+- }
+- break;
+- default: /* NB: no static turboG */
+- break;
+- }
++
+ if (ieee80211_check_mode_consistency(ic, vap->iv_des_mode, c)) {
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ return -EINVAL;
+ }
+- if ((vap->iv_state == IEEE80211_S_RUN) && (c == vap->iv_des_chan))
+- return 0; /* no change, return */
+
+ /* Don't allow to change to channel with radar found */
+ if (c->ic_flags & IEEE80211_CHAN_RADAR)
+@@ -4634,7 +4606,13 @@ static void
+ pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt) {
+ struct ieee80211vap *vap = dev->priv;
+ struct ieee80211com *ic = vap->iv_ic;
++ struct ieee80211vap *avp;
++
+ /* now flag the beacon update to include the channel switch IE */
++ TAILQ_FOREACH(avp, &ic->ic_vaps, iv_next) {
++ avp->iv_flags |= IEEE80211_F_CHANSWITCH;
++ avp->iv_chanchange_count = tbtt;
++ }
+ ic->ic_flags |= IEEE80211_F_CHANSWITCH;
+ ic->ic_chanchange_chan = channel;
+ ic->ic_chanchange_tbtt = tbtt;
diff --git a/package/madwifi/patches/416-wprobe.patch b/package/madwifi/patches/416-wprobe.patch
new file mode 100644
index 000000000..0b378d7e2
--- /dev/null
+++ b/package/madwifi/patches/416-wprobe.patch
@@ -0,0 +1,549 @@
+--- /dev/null
++++ b/ath/ath_wprobe.c
+@@ -0,0 +1,433 @@
++#include <net80211/ieee80211_node.h>
++#include <linux/wprobe.h>
++
++atomic_t cleanup_tasks = ATOMIC_INIT(0);
++
++enum wp_node_val {
++ WP_NODE_RSSI,
++ WP_NODE_SIGNAL,
++ WP_NODE_RX_RATE,
++ WP_NODE_TX_RATE,
++ WP_NODE_RETRANSMIT_200,
++ WP_NODE_RETRANSMIT_400,
++ WP_NODE_RETRANSMIT_800,
++ WP_NODE_RETRANSMIT_1600,
++};
++
++enum wp_global_val {
++ WP_GLOBAL_NOISE,
++ WP_GLOBAL_PHY_BUSY,
++ WP_GLOBAL_PHY_RX,
++ WP_GLOBAL_PHY_TX,
++ WP_GLOBAL_FRAMES,
++ WP_GLOBAL_PROBEREQ,
++};
++
++static struct wprobe_item ath_wprobe_globals[] = {
++ [WP_GLOBAL_NOISE] = {
++ .name = "noise",
++ .type = WPROBE_VAL_S16,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_GLOBAL_PHY_BUSY] = {
++ .name = "phy_busy",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_GLOBAL_PHY_RX] = {
++ .name = "phy_rx",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_GLOBAL_PHY_TX] = {
++ .name = "phy_tx",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_GLOBAL_FRAMES] = {
++ .name = "frames",
++ .type = WPROBE_VAL_U32,
++ },
++ [WP_GLOBAL_PROBEREQ] = {
++ .name = "probereq",
++ .type = WPROBE_VAL_U32,
++ },
++};
++
++static struct wprobe_item ath_wprobe_link[] = {
++ [WP_NODE_RSSI] = {
++ .name = "rssi",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_NODE_SIGNAL] = {
++ .name = "signal",
++ .type = WPROBE_VAL_S16,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_NODE_RX_RATE] = {
++ .name = "rx_rate",
++ .type = WPROBE_VAL_U16,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_NODE_TX_RATE] = {
++ .name = "tx_rate",
++ .type = WPROBE_VAL_U16,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_NODE_RETRANSMIT_200] = {
++ .name = "retransmit_200",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_NODE_RETRANSMIT_400] = {
++ .name = "retransmit_400",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_NODE_RETRANSMIT_800] = {
++ .name = "retransmit_800",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++ [WP_NODE_RETRANSMIT_1600] = {
++ .name = "retransmit_1600",
++ .type = WPROBE_VAL_U8,
++ .flags = WPROBE_F_KEEPSTAT
++ },
++};
++
++#define AR5K_MIBC 0x0040
++#define AR5K_MIBC_FREEZE (1 << 1)
++#define AR5K_TXFC 0x80ec
++#define AR5K_RXFC 0x80f0
++#define AR5K_RXCLEAR 0x80f4
++#define AR5K_CYCLES 0x80f8
++
++#define READ_CLR(_ah, _reg) \
++ ({ u32 __val = OS_REG_READ(_ah, _reg); OS_REG_WRITE(_ah, _reg, 0); __val; })
++
++static bool
++wprobe_disabled(void)
++{
++ return (!wprobe_add_iface || IS_ERR(wprobe_add_iface));
++}
++
++static int
++ath_wprobe_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure)
++{
++ struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif);
++ struct ieee80211vap *vap = &avp->av_vap;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_hal *ah = sc->sc_ah;
++ u32 cc, busy, rx, tx;
++ s16 noise;
++
++ if (l)
++ goto out;
++
++ OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
++ cc = READ_CLR(ah, AR5K_CYCLES);
++ busy = READ_CLR(ah, AR5K_RXCLEAR);
++ rx = READ_CLR(ah, AR5K_RXFC);
++ tx = READ_CLR(ah, AR5K_TXFC);
++ OS_REG_WRITE(ah, AR5K_MIBC, 0);
++ noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
++ ic->ic_channoise = noise;
++
++ WPROBE_FILL_BEGIN(val, ath_wprobe_globals);
++ if (cc & 0xf0000000) {
++ /* scale down if the counters are near max */
++ cc >>= 8;
++ busy >>= 8;
++ rx >>= 8;
++ tx >>= 8;
++ }
++ if (ah->ah_macType < 5212)
++ goto phy_skip;
++ if (!cc)
++ goto phy_skip;
++ if (busy > cc)
++ goto phy_skip;
++ if (rx > cc)
++ goto phy_skip;
++ if (tx > cc)
++ goto phy_skip;
++ busy = (busy * 100) / cc;
++ rx = (rx * 100) / cc;
++ tx = (tx * 100) / cc;
++ WPROBE_SET(WP_GLOBAL_PHY_BUSY, U8, busy);
++ WPROBE_SET(WP_GLOBAL_PHY_RX, U8, rx);
++ WPROBE_SET(WP_GLOBAL_PHY_TX, U8, tx);
++ WPROBE_SET(WP_GLOBAL_FRAMES, U32, avp->av_rxframes);
++ WPROBE_SET(WP_GLOBAL_PROBEREQ, U32, avp->av_rxprobereq);
++
++phy_skip:
++ WPROBE_SET(WP_GLOBAL_NOISE, S16, noise);
++ WPROBE_FILL_END();
++
++out:
++ return 0;
++}
++
++#undef AR5K_TXFC
++#undef AR5K_RXFC
++#undef AR5K_RXCLEAR
++#undef AR5K_CYCLES
++#undef AR5K_MIBC
++#undef AR5K_MIBC_FREEZE
++#undef READ_CLR
++
++static const struct wprobe_iface ath_wprobe_dev = {
++ .link_items = ath_wprobe_link,
++ .n_link_items = ARRAY_SIZE(ath_wprobe_link),
++ .global_items = ath_wprobe_globals,
++ .n_global_items = ARRAY_SIZE(ath_wprobe_globals),
++ .sync_data = ath_wprobe_sync,
++};
++
++static int
++ath_lookup_rateval(struct ieee80211_node *ni, int rate)
++{
++ struct ieee80211vap *vap = ni->ni_vap;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct ath_softc *sc = ic->ic_dev->priv;
++ const HAL_RATE_TABLE *rt = sc->sc_currates;
++
++ if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap)))
++ return -1;
++
++ rate = sc->sc_hwmap[rate].ieeerate;
++ rate = sc->sc_rixmap[rate & IEEE80211_RATE_VAL];
++ if ((rate < 0) || (rate >= rt->rateCount))
++ return -1;
++
++ return rt->info[rate].rateKbps;
++}
++
++static void
++ath_wprobe_report_rx(struct ieee80211vap *vap, struct ath_rx_status *rs, struct sk_buff *skb)
++{
++ const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
++ struct wprobe_wlan_hdr hdr;
++ struct ath_vap *avp;
++ int hdrsize;
++
++ if (wprobe_disabled())
++ return;
++
++ avp = ATH_VAP(vap);
++ avp->av_rxframes++;
++ if (wh->i_fc[0] == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ))
++ avp->av_rxprobereq++;
++
++ memset(&hdr, 0, sizeof(hdr));
++ hdr.len = skb->len;
++ hdr.snr = rs->rs_rssi;
++ hdr.type = WPROBE_PKT_RX;
++ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
++ hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
++ else
++ hdrsize = ieee80211_hdrsize(skb->data);
++ wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
++}
++
++
++static void
++ath_node_sample_rx(struct ieee80211_node *ni, struct ath_rx_status *rs)
++{
++ struct ath_node *an = ATH_NODE(ni);
++ struct ieee80211vap *vap = ni->ni_vap;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct wprobe_link *l = &an->an_wplink;
++ struct wprobe_value *v = l->val;
++ unsigned long flags;
++ int rate;
++
++ if (wprobe_disabled() || !an->an_wplink_active || !l->val)
++ return;
++
++ rate = ath_lookup_rateval(ni, rs->rs_rate);
++
++ spin_lock_irqsave(&l->iface->lock, flags);
++ WPROBE_FILL_BEGIN(v, ath_wprobe_link);
++ WPROBE_SET(WP_NODE_RSSI, U8, rs->rs_rssi);
++ WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + rs->rs_rssi);
++ if ((rate > 0) && (rate <= 600000))
++ WPROBE_SET(WP_NODE_RX_RATE, U16, rate);
++ WPROBE_FILL_END();
++ wprobe_update_stats(l->iface, l);
++ spin_unlock_irqrestore(&l->iface->lock, flags);
++}
++
++static void
++ath_wprobe_report_tx(struct ieee80211vap *vap, struct ath_tx_status *ts, struct sk_buff *skb)
++{
++ const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
++ struct wprobe_wlan_hdr hdr;
++ struct ath_vap *avp;
++ int hdrsize;
++
++ if (wprobe_disabled())
++ return;
++
++ avp = ATH_VAP(vap);
++
++ memset(&hdr, 0, sizeof(hdr));
++ hdr.len = skb->len;
++ hdr.snr = ts->ts_rssi;
++ hdr.type = WPROBE_PKT_TX;
++ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
++ hdrsize = sizeof(struct ieee80211_ctlframe_addr2);
++ else
++ hdrsize = ieee80211_hdrsize(skb->data);
++ wprobe_add_frame(&avp->av_wpif, &hdr, skb->data, hdrsize + 0x42);
++}
++
++
++
++static void
++ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, struct sk_buff *skb)
++{
++ struct ath_node *an = ATH_NODE(ni);
++ struct ieee80211vap *vap = ni->ni_vap;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct wprobe_link *l = &an->an_wplink;
++ struct wprobe_value *v = l->val;
++ unsigned long flags;
++ int rate, rexmit_counter;
++ int len = skb->len;
++
++ if (wprobe_disabled() || !an->an_wplink_active || !l->val)
++ return;
++
++ ath_wprobe_report_tx(vap, ts, skb);
++ rate = ath_lookup_rateval(ni, ts->ts_rate);
++
++ spin_lock_irqsave(&l->iface->lock, flags);
++ WPROBE_FILL_BEGIN(v, ath_wprobe_link);
++ WPROBE_SET(WP_NODE_RSSI, U8, ts->ts_rssi);
++ WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + ts->ts_rssi);
++
++ if (len <= 200)
++ rexmit_counter = WP_NODE_RETRANSMIT_200;
++ else if (len <= 400)
++ rexmit_counter = WP_NODE_RETRANSMIT_400;
++ else if (len <= 800)
++ rexmit_counter = WP_NODE_RETRANSMIT_800;
++ else
++ rexmit_counter = WP_NODE_RETRANSMIT_1600;
++ WPROBE_SET(rexmit_counter, U8, ts->ts_longretry);
++
++ if ((rate > 0) && (rate <= 600000))
++ WPROBE_SET(WP_NODE_TX_RATE, U16, rate);
++ WPROBE_FILL_END();
++ wprobe_update_stats(l->iface, l);
++ spin_unlock_irqrestore(&l->iface->lock, flags);
++}
++
++static void
++ath_wprobe_node_join(struct ieee80211vap *vap, struct ieee80211_node *ni)
++{
++ struct wprobe_iface *dev;
++ struct wprobe_link *l;
++ struct ath_vap *avp;
++ struct ath_node *an = ATH_NODE(ni);
++
++ if (wprobe_disabled() || an->an_wplink_active)
++ return;
++
++ avp = ATH_VAP(vap);
++ dev = &avp->av_wpif;
++ l = &an->an_wplink;
++
++ ieee80211_ref_node(ni);
++ wprobe_add_link(dev, l, ni->ni_macaddr);
++ an->an_wplink_active = 1;
++}
++
++static void
++ath_wprobe_do_node_leave(struct work_struct *work)
++{
++ struct ath_node *an = container_of(work, struct ath_node, an_destroy);
++ struct ieee80211_node *ni = &an->an_node;
++ struct ieee80211vap *vap = ni->ni_vap;
++ struct wprobe_iface *dev;
++ struct wprobe_link *l;
++ struct ath_vap *avp;
++
++ avp = ATH_VAP(vap);
++ dev = &avp->av_wpif;
++ l = &an->an_wplink;
++
++ wprobe_remove_link(dev, l);
++ ieee80211_unref_node(&ni);
++ atomic_dec(&cleanup_tasks);
++}
++
++static void
++ath_wprobe_node_leave(struct ieee80211vap *vap, struct ieee80211_node *ni)
++{
++ struct ath_node *an = ATH_NODE(ni);
++
++ if (wprobe_disabled() || !an->an_wplink_active)
++ return;
++
++ atomic_inc(&cleanup_tasks);
++ an->an_wplink_active = 0;
++ IEEE80211_INIT_WORK(&an->an_destroy, ath_wprobe_do_node_leave);
++ schedule_work(&an->an_destroy);
++}
++
++static void
++ath_init_wprobe_dev(struct ath_vap *avp)
++{
++ struct ieee80211vap *vap = &avp->av_vap;
++ struct wprobe_iface *dev = &avp->av_wpif;
++
++ if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
++ return;
++
++ memcpy(dev, &ath_wprobe_dev, sizeof(struct wprobe_iface));
++ dev->addr = vap->iv_myaddr;
++ dev->name = vap->iv_dev->name;
++ wprobe_add_iface(dev);
++}
++
++static void
++ath_remove_wprobe_dev(struct ath_vap *avp)
++{
++ struct ieee80211vap *vap = &avp->av_vap;
++ struct ieee80211com *ic = vap->iv_ic;
++ struct ieee80211_node *ni;
++ struct wprobe_iface *dev = &avp->av_wpif;
++ struct wprobe_link *l;
++ struct ath_node *an;
++ unsigned long flags;
++
++ if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
++ return;
++
++restart:
++ rcu_read_lock();
++ list_for_each_entry_rcu(l, &dev->links, list) {
++ an = container_of(l, struct ath_node, an_wplink);
++
++ if (!an->an_wplink_active)
++ continue;
++
++ ni = &an->an_node;
++ ath_wprobe_node_leave(vap, ni);
++ rcu_read_unlock();
++ goto restart;
++ }
++ rcu_read_unlock();
++
++ /* wait for the cleanup tasks to finish */
++ while (atomic_read(&cleanup_tasks) != 0) {
++ schedule();
++ }
++
++ wprobe_remove_iface(dev);
++}
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -400,6 +400,7 @@ static int countrycode = -1;
+ static int maxvaps = -1;
+ static int outdoor = -1;
+ static int xchanmode = -1;
++#include "ath_wprobe.c"
+ static int beacon_cal = 1;
+
+ static const struct ath_hw_detect generic_hw_info = {
+@@ -1525,6 +1526,7 @@ ath_vap_create(struct ieee80211com *ic,
+ ath_hal_intrset(ah, sc->sc_imask);
+ }
+
++ ath_init_wprobe_dev(avp);
+ return vap;
+ }
+
+@@ -1606,6 +1608,7 @@ ath_vap_delete(struct ieee80211vap *vap)
+ decrease = 0;
+
+ ieee80211_vap_detach(vap);
++ ath_remove_wprobe_dev(ATH_VAP(vap));
+ /* NB: memory is reclaimed through dev->destructor callback */
+ if (decrease)
+ sc->sc_nvaps--;
+@@ -5940,6 +5943,7 @@ ath_node_cleanup(struct ieee80211_node *
+ /* Clean up node-specific rate things - this currently appears to
+ * always be a no-op */
+ sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
++ ath_wprobe_node_leave(ni->ni_vap, ni);
+
+ ATH_NODE_UAPSD_LOCK_IRQ(an);
+ #ifdef IEEE80211_DEBUG_REFCNT
+@@ -7010,6 +7014,8 @@ drop_micfail:
+ goto lookup_slowpath;
+ }
+ ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
++ ath_node_sample_rx(ni, rs);
++ ath_wprobe_report_rx(ni->ni_vap, rs, skb);
+ type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+ ieee80211_unref_node(&ni);
+ } else {
+@@ -7024,15 +7030,21 @@ lookup_slowpath:
+ else
+ vap = ieee80211_find_rxvap(ic, wh->i_addr1);
+
+- if (vap)
++ if (vap) {
++ ath_wprobe_report_rx(vap, rs, skb);
+ ni = ieee80211_find_rxnode(ic, vap, wh);
+- else
++ } else {
++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ ath_wprobe_report_rx(vap, rs, skb);
++ }
+ ni = NULL;
++ }
+
+ if (ni != NULL) {
+ ieee80211_keyix_t keyix;
+
+ ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
++ ath_node_sample_rx(ni, rs);
+ type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+ /*
+ * If the station has a key cache slot assigned
+@@ -8612,6 +8624,7 @@ ath_tx_processq(struct ath_softc *sc, st
+ sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
+ ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
+ ts->ts_rssi);
++ ath_node_sample_tx(&an->an_node, ts, bf->bf_skb);
+ if (bf->bf_skb->priority == WME_AC_VO ||
+ bf->bf_skb->priority == WME_AC_VI)
+ ni->ni_ic->ic_wme.wme_hipri_traffic++;
+@@ -10111,6 +10124,7 @@ ath_newassoc(struct ieee80211_node *ni,
+ struct ath_softc *sc = ic->ic_dev->priv;
+
+ sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
++ ath_wprobe_node_join(ni->ni_vap, ni);
+
+ /* are we supporting compression? */
+ if (!(vap->iv_ath_cap & ni->ni_ath_flags & IEEE80211_NODE_COMP))
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -46,6 +46,7 @@
+ #include "ah_desc.h"
+ #include "ah_os.h"
+ #include "if_athioctl.h"
++#include <linux/wprobe.h>
+ #include "net80211/ieee80211.h" /* XXX for WME_NUM_AC */
+ #include <asm/io.h>
+ #include <linux/list.h>
+@@ -352,6 +353,9 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhe
+ /* driver-specific node state */
+ struct ath_node {
+ struct ieee80211_node an_node; /* base class */
++ struct wprobe_link an_wplink;
++ uint8_t an_wplink_active;
++ struct work_struct an_destroy;
+ u_int16_t an_decomp_index; /* decompression mask index */
+ u_int32_t an_avgrssi; /* average rssi over all rx frames */
+ u_int8_t an_prevdatarix; /* rate ix of last data frame */
+@@ -521,6 +525,9 @@ struct ath_vap {
+ #else
+ unsigned int av_beacon_alloc;
+ #endif
++ struct wprobe_iface av_wpif;
++ u32 av_rxframes;
++ u32 av_rxprobereq;
+ };
+ #define ATH_VAP(_v) ((struct ath_vap *)(_v))
+
diff --git a/package/madwifi/patches/417-beacon_txpower.patch b/package/madwifi/patches/417-beacon_txpower.patch
new file mode 100644
index 000000000..8a59c1211
--- /dev/null
+++ b/package/madwifi/patches/417-beacon_txpower.patch
@@ -0,0 +1,81 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -395,7 +395,7 @@ static int bstuck_thresh = BSTUCK_THRESH
+ static char *autocreate = NULL;
+ static char *ratectl = DEF_RATE_CTL;
+ static int rfkill = 0;
+-static int tpc = 0;
++static int tpc = 1;
+ static int countrycode = -1;
+ static int maxvaps = -1;
+ static int outdoor = -1;
+@@ -4932,6 +4932,7 @@ ath_beacon_setup(struct ath_softc *sc, s
+ (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
+ == IEEE80211_F_SHPREAMBLE)
+ struct ieee80211com *ic = bf->bf_node->ni_ic;
++ struct ieee80211vap *vap = bf->bf_node->ni_vap;
+ struct sk_buff *skb = bf->bf_skb;
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_desc *ds;
+@@ -4999,7 +5000,7 @@ ath_beacon_setup(struct ath_softc *sc, s
+ skb->len + IEEE80211_CRC_LEN, /* frame length */
+ sizeof(struct ieee80211_frame), /* header length */
+ HAL_PKT_TYPE_BEACON, /* Atheros packet type */
+- bf->bf_node->ni_txpower, /* txpower XXX */
++ (vap->iv_beacon_txpow ? vap->iv_beacon_txpow : 63),
+ rate, 1, /* series 0 rate/tries */
+ HAL_TXKEYIX_INVALID, /* no encryption */
+ antenna, /* antenna mode */
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -652,6 +652,7 @@ enum {
+ IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */
+ IEEE80211_PARAM_MAXASSOC = 83, /* maximum associated stations */
+ IEEE80211_PARAM_PROBEREQ = 84, /* enable handling of probe requests */
++ IEEE80211_PARAM_BEACON_TXP = 85, /* set beacon tx power */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -254,6 +254,7 @@ struct ieee80211vap {
+ u_int8_t iv_dtim_period; /* DTIM period */
+ u_int8_t iv_dtim_count; /* DTIM count from last bcn */
+ /* set/unset aid pwrsav state */
++ u_int8_t iv_beacon_txpow; /* beacon tx power */
+ void (*iv_set_tim)(struct ieee80211_node *, int);
+ u_int8_t iv_uapsdinfo; /* sta mode QoS Info flags */
+ struct ieee80211_node *iv_bss; /* information for this node */
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2871,6 +2871,9 @@ ieee80211_ioctl_setparam(struct net_devi
+ case IEEE80211_PARAM_PROBEREQ:
+ vap->iv_no_probereq = !value;
+ break;
++ case IEEE80211_PARAM_BEACON_TXP:
++ vap->iv_beacon_txpow = value;
++ break;
+ #ifdef ATH_REVERSE_ENGINEERING
+ case IEEE80211_PARAM_DUMPREGS:
+ ieee80211_dump_registers(dev, info, w, extra);
+@@ -3236,6 +3239,9 @@ ieee80211_ioctl_getparam(struct net_devi
+ case IEEE80211_PARAM_PROBEREQ:
+ param[0] = !vap->iv_no_probereq;
+ break;
++ case IEEE80211_PARAM_BEACON_TXP:
++ param[0] = vap->iv_beacon_txpow;
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -5810,6 +5816,10 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "probereq"},
+ { IEEE80211_PARAM_PROBEREQ,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_probereq"},
++ { IEEE80211_PARAM_BEACON_TXP,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_pwr"},
++ { IEEE80211_PARAM_BEACON_TXP,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_beacon_pwr"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
diff --git a/package/madwifi/patches/419-skb_unmap_crash.patch b/package/madwifi/patches/419-skb_unmap_crash.patch
new file mode 100644
index 000000000..37602e491
--- /dev/null
+++ b/package/madwifi/patches/419-skb_unmap_crash.patch
@@ -0,0 +1,20 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -13499,7 +13499,7 @@ cleanup_ath_buf(struct ath_softc *sc, st
+ if (bf == NULL)
+ return bf;
+
+- if (bf->bf_skbaddr) {
++ if (bf->bf_skb && bf->bf_skbaddr) {
+ bus_unmap_single(
+ sc->sc_bdev,
+ bf->bf_skbaddr,
+@@ -13507,8 +13507,6 @@ cleanup_ath_buf(struct ath_softc *sc, st
+ sc->sc_rxbufsize : bf->bf_skb->len),
+ direction);
+ bf->bf_skbaddr = 0;
+- bf->bf_desc->ds_link = 0;
+- bf->bf_desc->ds_data = 0;
+ }
+
+ #ifdef ATH_SUPERG_FF
diff --git a/package/madwifi/patches/420-diversity_fix.patch b/package/madwifi/patches/420-diversity_fix.patch
new file mode 100644
index 000000000..90bcd0e16
--- /dev/null
+++ b/package/madwifi/patches/420-diversity_fix.patch
@@ -0,0 +1,86 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -797,7 +797,6 @@ ath_attach(u_int16_t devid, struct net_d
+ break;
+ }
+
+- sc->sc_setdefantenna = ath_setdefantenna;
+ sc->sc_rc = ieee80211_rate_attach(sc, ratectl);
+ if (sc->sc_rc == NULL) {
+ error = EIO;
+@@ -2624,9 +2623,6 @@ ath_init(struct net_device *dev)
+ ath_radar_update(sc);
+ ath_rp_flush(sc);
+
+- /* Set the default RX antenna; it may get lost on reset. */
+- ath_setdefantenna(sc, sc->sc_defant);
+-
+ /*
+ * Setup the hardware after reset: the key cache
+ * is filled as needed and the receive engine is
+@@ -3019,7 +3015,6 @@ ath_reset(struct net_device *dev)
+ ath_setintmit(sc);
+ ath_update_txpow(sc); /* update tx power state */
+ ath_radar_update(sc);
+- ath_setdefantenna(sc, sc->sc_defant);
+ if (ath_startrecv(sc) != 0) /* restart recv */
+ EPRINTF(sc, "Unable to start receive logic.\n");
+ if (sc->sc_softled)
+@@ -5353,27 +5348,6 @@ ath_beacon_send(struct ath_softc *sc, in
+ } else if ((sc->sc_updateslot == COMMIT) && (sc->sc_slotupdate == slot))
+ ath_setslottime(sc); /* commit change to hardware */
+
+- if ((!sc->sc_stagbeacons || slot == 0) && (!sc->sc_diversity)) {
+- unsigned int otherant;
+- /*
+- * Check recent per-antenna transmit statistics and flip
+- * the default rx antenna if noticeably more frames went out
+- * on the non-default antenna. Only do this if rx diversity
+- * is off.
+- * XXX assumes 2 antennae
+- */
+- otherant = sc->sc_defant & 1 ? 2 : 1;
+- if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] +
+- ATH_ANTENNA_DIFF) {
+- DPRINTF(sc, ATH_DEBUG_BEACON,
+- "Flip default antenna to %u, %u > %u\n",
+- otherant, sc->sc_ant_tx[otherant],
+- sc->sc_ant_tx[sc->sc_defant]);
+- ath_setdefantenna(sc, otherant);
+- }
+- sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+- }
+-
+ if (bfaddr != 0) {
+ /*
+ * Stop any current DMA and put the new frame(s) on the queue.
+@@ -6734,9 +6708,8 @@ ath_setdefantenna(struct ath_softc *sc,
+ {
+ struct ath_hal *ah = sc->sc_ah;
+
+- /* XXX block beacon interrupts */
+- ath_hal_setdiversity(ah, (sc->sc_diversity != 0));
+ ath_hal_setdefantenna(ah, antenna);
++ ath_hal_setantennaswitch(ah, sc->sc_diversity ? 0 : antenna);
+ if (sc->sc_defant != antenna)
+ sc->sc_stats.ast_ant_defswitch++;
+ sc->sc_defant = antenna;
+@@ -11160,7 +11133,7 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ break;
+ }
+ sc->sc_diversity = val;
+- ath_hal_setdiversity(ah, val);
++ ath_setdefantenna(sc, sc->sc_defant);
+ break;
+ case ATH_TXINTRPERIOD:
+ /* XXX: validate? */
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -640,7 +640,6 @@ struct ath_softc {
+ spinlock_t sc_hal_lock; /* hardware access lock */
+ struct ath_ratectrl *sc_rc; /* tx rate control support */
+ struct ath_tx99 *sc_tx99; /* tx99 support */
+- void (*sc_setdefantenna)(struct ath_softc *, u_int);
+ const struct ath_hw_detect *sc_hwinfo;
+
+ unsigned int sc_invalid:1; /* being detached */
diff --git a/package/madwifi/patches/421-channel_handling.patch b/package/madwifi/patches/421-channel_handling.patch
new file mode 100644
index 000000000..2a8ec27ce
--- /dev/null
+++ b/package/madwifi/patches/421-channel_handling.patch
@@ -0,0 +1,1351 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -148,7 +148,6 @@ static int ath_key_set(struct ieee80211v
+ static void ath_key_update_begin(struct ieee80211vap *);
+ static void ath_key_update_end(struct ieee80211vap *);
+ static void ath_mode_init(struct net_device *);
+-static void ath_setslottime(struct ath_softc *);
+ static void ath_updateslot(struct net_device *);
+ static int ath_beaconq_setup(struct ath_softc *);
+ static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
+@@ -240,7 +239,7 @@ static void ath_setup_stationkey(struct
+ static void ath_setup_stationwepkey(struct ieee80211_node *);
+ static void ath_setup_keycacheslot(struct ath_softc *, struct ieee80211_node *);
+ static void ath_newassoc(struct ieee80211_node *, int);
+-static int ath_getchannels(struct net_device *, u_int, HAL_BOOL, HAL_BOOL);
++static int ath_getchannels(struct net_device *);
+ static void ath_led_event(struct ath_softc *, int);
+ static void ath_update_txpow(struct ath_softc *);
+
+@@ -265,7 +264,6 @@ static int ath_change_mtu(struct net_dev
+ static int ath_ioctl(struct net_device *, struct ifreq *, int);
+
+ static int ath_rate_setup(struct net_device *, u_int);
+-static void ath_setup_subrates(struct net_device *);
+ #ifdef ATH_SUPERG_XR
+ static int ath_xr_rate_setup(struct net_device *);
+ static void ath_grppoll_txq_setup(struct ath_softc *, int, int);
+@@ -387,8 +385,6 @@ static void ath_fetch_idle_time(struct a
+
+ /* calibrate every 30 secs in steady state but check every second at first. */
+ static int ath_calinterval = ATH_SHORT_CALINTERVAL;
+-static int ath_countrycode = CTRY_DEFAULT; /* country code */
+-static int ath_outdoor = AH_FALSE; /* enable outdoor use */
+ static int ath_xchanmode = AH_TRUE; /* enable extended channels */
+ static int ath_maxvaps = ATH_MAXVAPS_DEFAULT; /* set default maximum vaps */
+ static int bstuck_thresh = BSTUCK_THRESH; /* Stuck beacon count required for reset */
+@@ -396,9 +392,7 @@ static char *autocreate = NULL;
+ static char *ratectl = DEF_RATE_CTL;
+ static int rfkill = 0;
+ static int tpc = 1;
+-static int countrycode = -1;
+ static int maxvaps = -1;
+-static int outdoor = -1;
+ static int xchanmode = -1;
+ #include "ath_wprobe.c"
+ static int beacon_cal = 1;
+@@ -437,9 +431,7 @@ static struct notifier_block ath_event_b
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+ MODULE_PARM(beacon_cal, "i");
+-MODULE_PARM(countrycode, "i");
+ MODULE_PARM(maxvaps, "i");
+-MODULE_PARM(outdoor, "i");
+ MODULE_PARM(xchanmode, "i");
+ MODULE_PARM(rfkill, "i");
+ #ifdef ATH_CAP_TPC
+@@ -451,9 +443,7 @@ MODULE_PARM(ratectl, "s");
+ #else
+ #include <linux/moduleparam.h>
+ module_param(beacon_cal, int, 0600);
+-module_param(countrycode, int, 0600);
+ module_param(maxvaps, int, 0600);
+-module_param(outdoor, int, 0600);
+ module_param(xchanmode, int, 0600);
+ module_param(rfkill, int, 0600);
+ #ifdef ATH_CAP_TPC
+@@ -463,9 +453,7 @@ module_param(bstuck_thresh, int, 0600);
+ module_param(autocreate, charp, 0600);
+ module_param(ratectl, charp, 0600);
+ #endif
+-MODULE_PARM_DESC(countrycode, "Override default country code");
+ MODULE_PARM_DESC(maxvaps, "Maximum VAPs");
+-MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use");
+ MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode");
+ MODULE_PARM_DESC(rfkill, "Enable/disable RFKILL capability");
+ #ifdef ATH_CAP_TPC
+@@ -531,6 +519,50 @@ MODULE_PARM_DESC(ieee80211_debug, "Load-
+ (bssid)[0] |= (((id) << 2) | 0x02); \
+ } while (0)
+
++static inline int ath_chan2mode(struct ieee80211_channel *c)
++{
++ if (IEEE80211_IS_CHAN_HALF(c))
++ return ATH_MODE_HALF;
++ else if (IEEE80211_IS_CHAN_QUARTER(c))
++ return ATH_MODE_QUARTER;
++ else
++ return ieee80211_chan2mode(c);
++}
++
++static inline int rate_hal2ieee(int dot11Rate, int f)
++{
++ int flag = dot11Rate & ~(IEEE80211_RATE_VAL);
++ dot11Rate &= IEEE80211_RATE_VAL;
++
++ if (f == 4) { /* Quarter */
++ if (dot11Rate == 4)
++ return 18 | flag;
++ }
++ return (dot11Rate * f) | flag;
++}
++
++static inline int rate_factor(int mode)
++{
++ int f;
++
++ /*
++ * NB: Fix up rates. HAL returns half or quarter dot11Rates,
++ * while the stack deals with full rates only
++ */
++ switch(mode) {
++ case ATH_MODE_HALF:
++ f = 2;
++ break;
++ case ATH_MODE_QUARTER:
++ f = 4;
++ break;
++ default:
++ f = 1;
++ break;
++ }
++ return f;
++}
++
+ /* Initialize ath_softc structure */
+
+ int
+@@ -647,14 +679,6 @@ ath_attach(u_int16_t devid, struct net_d
+ for (i = 0; i < sc->sc_keymax; i++)
+ ath_hal_keyreset(ah, i);
+
+- /*
+- * Collect the channel list using the default country
+- * code and including outdoor channels. The 802.11 layer
+- * is responsible for filtering this list based on settings
+- * like the phy mode.
+- */
+- if (countrycode != -1)
+- ath_countrycode = countrycode;
+ if (maxvaps != -1) {
+ ath_maxvaps = maxvaps;
+ if (ath_maxvaps < ATH_MAXVAPS_MIN)
+@@ -662,17 +686,14 @@ ath_attach(u_int16_t devid, struct net_d
+ else if (ath_maxvaps > ATH_MAXVAPS_MAX)
+ ath_maxvaps = ATH_MAXVAPS_MAX;
+ }
+- if (outdoor != -1)
+- ath_outdoor = outdoor;
+ if (xchanmode != -1)
+ ath_xchanmode = xchanmode;
+- error = ath_getchannels(dev, ath_countrycode,
+- ath_outdoor, ath_xchanmode);
++ error = ath_getchannels(dev);
+ if (error != 0)
+ goto bad;
+
+- ic->ic_country_code = ath_countrycode;
+- ic->ic_country_outdoor = ath_outdoor;
++ ic->ic_country_code = CTRY_DEFAULT;
++ ic->ic_country_outdoor = 0;
+
+ IPRINTF(sc, "Switching rfkill capability %s\n",
+ rfkill ? "on" : "off");
+@@ -686,9 +707,8 @@ ath_attach(u_int16_t devid, struct net_d
+ ath_rate_setup(dev, IEEE80211_MODE_11G);
+ ath_rate_setup(dev, IEEE80211_MODE_TURBO_A);
+ ath_rate_setup(dev, IEEE80211_MODE_TURBO_G);
+-
+- /* Setup for half/quarter rates */
+- ath_setup_subrates(dev);
++ ath_rate_setup(dev, ATH_MODE_HALF);
++ ath_rate_setup(dev, ATH_MODE_QUARTER);
+
+ /* NB: setup here so ath_rate_update is happy */
+ ath_setcurmode(sc, IEEE80211_MODE_11A);
+@@ -908,10 +928,6 @@ ath_attach(u_int16_t devid, struct net_d
+ IEEE80211_ATHC_COMP : 0);
+ #endif
+
+-#ifdef ATH_SUPERG_DYNTURBO
+- ic->ic_ath_cap |= (ath_hal_turboagsupported(ah, ath_countrycode) ?
+- (IEEE80211_ATHC_TURBOP | IEEE80211_ATHC_AR) : 0);
+-#endif
+ #ifdef ATH_SUPERG_XR
+ ic->ic_ath_cap |= (ath_hal_xrsupported(ah) ? IEEE80211_ATHC_XR : 0);
+ #endif
+@@ -4470,17 +4486,17 @@ ath_mode_init(struct net_device *dev)
+ * Set the slot time based on the current setting.
+ */
+ static void
+-ath_setslottime(struct ath_softc *sc)
++ath_settiming(struct ath_softc *sc)
+ {
+- struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
++ u_int offset = getTimingOffset(sc);
+
+- if (sc->sc_slottimeconf > 0) /* manual override */
+- ath_hal_setslottime(ah, sc->sc_slottimeconf);
+- else if (ic->ic_flags & IEEE80211_F_SHSLOT)
+- ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
+- else
+- ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
++ if (sc->sc_slottimeconf > 0)
++ ath_hal_setslottime(ah, offset + sc->sc_slottimeconf);
++ if (sc->sc_acktimeconf > 0)
++ ath_hal_setacktimeout(ah, 2 * offset + sc->sc_acktimeconf);
++ if (sc->sc_ctstimeconf > 0)
++ ath_hal_setctstimeout(ah, 2 * offset + sc->sc_ctstimeconf);
+ sc->sc_updateslot = OK;
+ }
+
+@@ -4502,7 +4518,7 @@ ath_updateslot(struct net_device *dev)
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ sc->sc_updateslot = UPDATE;
+ else if (dev->flags & IFF_RUNNING)
+- ath_setslottime(sc);
++ ath_settiming(sc);
+ }
+
+ #ifdef ATH_SUPERG_DYNTURBO
+@@ -5346,7 +5362,7 @@ ath_beacon_send(struct ath_softc *sc, in
+ sc->sc_updateslot = COMMIT; /* commit next beacon */
+ sc->sc_slotupdate = slot;
+ } else if ((sc->sc_updateslot == COMMIT) && (sc->sc_slotupdate == slot))
+- ath_setslottime(sc); /* commit change to hardware */
++ ath_settiming(sc); /* commit change to hardware */
+
+ if (bfaddr != 0) {
+ /*
+@@ -7802,12 +7818,14 @@ ath_get_ivlen(struct ieee80211_key *k)
+ * Get transmit rate index using rate in Kbps
+ */
+ static __inline int
+-ath_tx_findindex(const HAL_RATE_TABLE *rt, int rate)
++ath_tx_findindex(struct ath_softc *sc, const HAL_RATE_TABLE *rt, int rate)
+ {
+ unsigned int i, ndx = 0;
++ int f;
+
++ f = rate_factor(sc->sc_curmode);
+ for (i = 0; i < rt->rateCount; i++) {
+- if (rt->info[i].rateKbps == rate) {
++ if ((rt->info[i].rateKbps * f) == rate) {
+ ndx = i;
+ break;
+ }
+@@ -8100,7 +8118,7 @@ ath_tx_start(struct net_device *dev, str
+ atype = HAL_PKT_TYPE_NORMAL; /* default */
+
+ if (ismcast) {
+- rix = ath_tx_findindex(rt, vap->iv_mcast_rate);
++ rix = ath_tx_findindex(sc, rt, vap->iv_mcast_rate);
+ txrate = rt->info[rix].rateCode;
+ if (shortPreamble)
+ txrate |= rt->info[rix].shortPreamble;
+@@ -9067,7 +9085,7 @@ ath_chan_change(struct ath_softc *sc, st
+ struct net_device *dev = sc->sc_dev;
+ enum ieee80211_phymode mode;
+
+- mode = ieee80211_chan2mode(chan);
++ mode = ath_chan2mode(chan);
+
+ ath_rate_setup(dev, mode);
+ ath_setcurmode(sc, mode);
+@@ -10124,8 +10142,7 @@ ath_newassoc(struct ieee80211_node *ni,
+ }
+
+ static int
+-ath_getchannels(struct net_device *dev, u_int cc,
+- HAL_BOOL outdoor, HAL_BOOL xchanmode)
++ath_getchannels(struct net_device *dev)
+ {
+ struct ath_softc *sc = dev->priv;
+ struct ieee80211com *ic = &sc->sc_ic;
+@@ -10139,17 +10156,31 @@ ath_getchannels(struct net_device *dev,
+ EPRINTF(sc, "Insufficient memory for channel table!\n");
+ return -ENOMEM;
+ }
++
++restart:
+ if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
+ ic->ic_regclassids, IEEE80211_REGCLASSIDS_MAX, &ic->ic_nregclass,
+- cc, HAL_MODE_ALL, outdoor, xchanmode)) {
++ ic->ic_country_code, HAL_MODE_ALL, ic->ic_country_outdoor, ath_xchanmode)) {
+ u_int32_t rd;
+
+ ath_hal_getregdomain(ah, &rd);
+ EPRINTF(sc, "Unable to collect channel list from HAL; "
+- "regdomain likely %u country code %u\n", rd, cc);
++ "regdomain likely %u country code %u\n", rd, ic->ic_country_code);
++ if ((ic->ic_country_code != CTRY_DEFAULT) ||
++ (ic->ic_country_outdoor != 0)) {
++ EPRINTF(sc, "Reverting to defaults\n");
++ ic->ic_country_code = CTRY_DEFAULT;
++ ic->ic_country_outdoor = 0;
++ goto restart;
++ }
+ kfree(chans);
+ return -EINVAL;
+ }
++#ifdef ATH_SUPERG_DYNTURBO
++ ic->ic_ath_cap &= ~(IEEE80211_ATHC_TURBOP | IEEE80211_ATHC_AR);
++ ic->ic_ath_cap |= (ath_hal_turboagsupported(ah, ic->ic_country_code) ?
++ (IEEE80211_ATHC_TURBOP | IEEE80211_ATHC_AR) : 0);
++#endif
+ /*
+ * Convert HAL channels to ieee80211 ones.
+ */
+@@ -10395,7 +10426,7 @@ ath_xr_rate_setup(struct net_device *dev
+ struct ieee80211com *ic = &sc->sc_ic;
+ const HAL_RATE_TABLE *rt;
+ struct ieee80211_rateset *rs;
+- unsigned int i, maxrates;
++ unsigned int i, j, maxrates;
+ sc->sc_xr_rates = ath_hal_getratetable(ah, HAL_MODE_XR);
+ rt = sc->sc_xr_rates;
+ if (rt == NULL)
+@@ -10408,57 +10439,16 @@ ath_xr_rate_setup(struct net_device *dev
+ } else
+ maxrates = rt->rateCount;
+ rs = &ic->ic_sup_xr_rates;
+- for (i = 0; i < maxrates; i++)
+- rs->rs_rates[i] = rt->info[i].dot11Rate;
+- rs->rs_nrates = maxrates;
++ for (j = 0, i = 0; i < maxrates; i++) {
++ if (!rt->info[i].valid)
++ continue;
++ rs->rs_rates[j++] = rt->info[i].dot11Rate;
++ }
++ rs->rs_nrates = j;
+ return 1;
+ }
+ #endif
+
+-/* Setup half/quarter rate table support */
+-static void
+-ath_setup_subrates(struct net_device *dev)
+-{
+- struct ath_softc *sc = dev->priv;
+- struct ath_hal *ah = sc->sc_ah;
+- struct ieee80211com *ic = &sc->sc_ic;
+- const HAL_RATE_TABLE *rt;
+- struct ieee80211_rateset *rs;
+- unsigned int i, maxrates;
+-
+- sc->sc_half_rates = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE);
+- rt = sc->sc_half_rates;
+- if (rt != NULL) {
+- if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
+- DPRINTF(sc, ATH_DEBUG_ANY,
+- "The rate table is too small (%u > %u)\n",
+- rt->rateCount, IEEE80211_RATE_MAXSIZE);
+- maxrates = IEEE80211_RATE_MAXSIZE;
+- } else
+- maxrates = rt->rateCount;
+- rs = &ic->ic_sup_half_rates;
+- for (i = 0; i < maxrates; i++)
+- rs->rs_rates[i] = rt->info[i].dot11Rate;
+- rs->rs_nrates = maxrates;
+- }
+-
+- sc->sc_quarter_rates = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE);
+- rt = sc->sc_quarter_rates;
+- if (rt != NULL) {
+- if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
+- DPRINTF(sc, ATH_DEBUG_ANY,
+- "The rate table is too small (%u > %u)\n",
+- rt->rateCount, IEEE80211_RATE_MAXSIZE);
+- maxrates = IEEE80211_RATE_MAXSIZE;
+- } else
+- maxrates = rt->rateCount;
+- rs = &ic->ic_sup_quarter_rates;
+- for (i = 0; i < maxrates; i++)
+- rs->rs_rates[i] = rt->info[i].dot11Rate;
+- rs->rs_nrates = maxrates;
+- }
+-}
+-
+ static int
+ ath_rate_setup(struct net_device *dev, u_int mode)
+ {
+@@ -10467,7 +10457,7 @@ ath_rate_setup(struct net_device *dev, u
+ struct ieee80211com *ic = &sc->sc_ic;
+ const HAL_RATE_TABLE *rt;
+ struct ieee80211_rateset *rs;
+- unsigned int i, maxrates;
++ unsigned int i, j, maxrates, f;
+
+ switch (mode) {
+ case IEEE80211_MODE_11A:
+@@ -10485,6 +10475,12 @@ ath_rate_setup(struct net_device *dev, u
+ case IEEE80211_MODE_TURBO_G:
+ sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_108G);
+ break;
++ case ATH_MODE_HALF:
++ sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE);
++ break;
++ case ATH_MODE_QUARTER:
++ sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE);
++ break;
+ default:
+ DPRINTF(sc, ATH_DEBUG_ANY, "Invalid mode %u\n", mode);
+ return 0;
+@@ -10499,10 +10495,16 @@ ath_rate_setup(struct net_device *dev, u
+ maxrates = IEEE80211_RATE_MAXSIZE;
+ } else
+ maxrates = rt->rateCount;
++
++ /* NB: quarter/half rate channels hijack the 11A rateset */
++ if (mode >= IEEE80211_MODE_MAX)
++ return 1;
++
+ rs = &ic->ic_sup_rates[mode];
+ for (i = 0; i < maxrates; i++)
+ rs->rs_rates[i] = rt->info[i].dot11Rate;
+ rs->rs_nrates = maxrates;
++
+ return 1;
+ }
+
+@@ -10531,13 +10533,18 @@ ath_setcurmode(struct ath_softc *sc, enu
+ { 0, 500, 130 },
+ };
+ const HAL_RATE_TABLE *rt;
+- unsigned int i, j;
++ unsigned int i, j, f;
+
++ /*
++ * NB: Fix up rixmap. HAL returns half or quarter dot11Rates,
++ * while the stack deals with full rates only
++ */
++ f = rate_factor(mode);
+ memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
+ rt = sc->sc_rates[mode];
+ KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode));
+ for (i = 0; i < rt->rateCount; i++)
+- sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
++ sc->sc_rixmap[rate_hal2ieee(rt->info[i].dot11Rate, f) & IEEE80211_RATE_VAL] = i;
+ memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
+ for (i = 0; i < 32; i++) {
+ u_int8_t ix = rt->rateCodeToIndex[i];
+@@ -10547,7 +10554,7 @@ ath_setcurmode(struct ath_softc *sc, enu
+ continue;
+ }
+ sc->sc_hwmap[i].ieeerate =
+- rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
++ rate_hal2ieee(rt->info[ix].dot11Rate, f) & IEEE80211_RATE_VAL;
+ if (rt->info[ix].shortPreamble ||
+ rt->info[ix].phy == IEEE80211_T_OFDM)
+ sc->sc_hwmap[i].flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+@@ -10948,9 +10955,106 @@ enum {
+ ATH_MAXVAPS = 26,
+ ATH_INTMIT = 27,
+ ATH_NOISE_IMMUNITY = 28,
+- ATH_OFDM_WEAK_DET = 29
++ ATH_OFDM_WEAK_DET = 29,
++ ATH_CHANBW = 30,
++ ATH_OUTDOOR = 31,
+ };
+
++/*
++ * perform the channel related sysctl, reload the channel list
++ * and try to stay on the current frequency
++ */
++static int ath_sysctl_setchanparam(struct ath_softc *sc, unsigned long ctl, u_int val)
++{
++ struct ieee80211com *ic = &sc->sc_ic;
++ struct ath_hal *ah = sc->sc_ah;
++ struct ieee80211_channel *c = NULL;
++ struct ieee80211vap *vap;
++ u_int16_t freq = 0;
++ struct ifreq ifr;
++
++ if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
++ freq = ic->ic_curchan->ic_freq;
++
++ switch(ctl) {
++ case ATH_COUNTRYCODE:
++ ic->ic_country_code = val;
++ break;
++ case ATH_OUTDOOR:
++ ic->ic_country_outdoor = val;
++ break;
++ case ATH_CHANBW:
++ switch(val) {
++ case 0:
++ case 5:
++ case 10:
++ case 20:
++ case 40:
++ if (ath_hal_setcapability(ah, HAL_CAP_CHANBW, 1, val, NULL) == AH_TRUE) {
++ sc->sc_chanbw = val;
++ break;
++ }
++ default:
++ return -EINVAL;
++ }
++ break;
++ }
++
++ if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
++ freq = ic->ic_curchan->ic_freq;
++
++ /* clear out any old state */
++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ vap->iv_des_mode = IEEE80211_MODE_AUTO;
++ vap->iv_des_chan = IEEE80211_CHAN_ANYC;
++ }
++ ieee80211_scan_flush(ic);
++
++ IEEE80211_LOCK_IRQ(ic);
++ ath_getchannels(sc->sc_dev);
++ ieee80211_update_channels(ic, 0);
++ if (freq)
++ c = ieee80211_find_channel(ic, freq, IEEE80211_MODE_AUTO);
++ if (!c)
++ c = &ic->ic_channels[0];
++ ic->ic_curchan = c;
++ ic->ic_bsschan = c;
++ ic->ic_curmode = IEEE80211_MODE_AUTO;
++ IEEE80211_UNLOCK_IRQ(ic);
++
++ if (!(sc->sc_dev->flags & IFF_RUNNING)) {
++ ic->ic_bsschan = IEEE80211_CHAN_ANYC;
++ return 0;
++ }
++
++#ifndef ifr_media
++#define ifr_media ifr_ifru.ifru_ivalue
++#endif
++ memset(&ifr, 0, sizeof(ifr));
++ ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media & ~IFM_MMASK;
++ ifr.ifr_media |= IFM_MAKEMODE(IEEE80211_MODE_AUTO);
++ ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA);
++
++ /* apply the channel to the hw */
++ ath_set_channel(ic);
++
++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ struct net_device *dev = vap->iv_dev;
++
++ /* reactivate all active vaps */
++ vap->iv_state = IEEE80211_S_SCAN;
++ if ((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
++ (vap->iv_opmode == IEEE80211_M_MONITOR) ||
++ (vap->iv_opmode == IEEE80211_M_WDS))
++ ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
++ else
++ ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
++ }
++
++ return 0;
++}
++
++
+ static int
+ ath_sysctl_set_intmit(struct ath_softc *sc, long ctl, u_int val)
+ {
+@@ -11029,6 +11133,7 @@ static int
+ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos)
+ {
+ struct ath_softc *sc = ctl->extra1;
++ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int val;
+ u_int tab_3_val[3];
+@@ -11052,25 +11157,34 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ lenp, ppos);
+ if (ret == 0) {
+ switch ((long)ctl->extra2) {
++ case ATH_REGDOMAIN:
++ ath_hal_setregdomain(ah, val);
++ break;
++ case ATH_OUTDOOR:
++ case ATH_COUNTRYCODE:
++ case ATH_CHANBW:
++ ret = ath_sysctl_setchanparam(sc, (long) ctl->extra2, val);
++ break;
+ case ATH_SLOTTIME:
+- if (val > 0) {
+- if (!ath_hal_setslottime(ah, val))
+- ret = -EINVAL;
+- else
+- sc->sc_slottimeconf = val;
+- } else {
+- /* disable manual override */
++ if (val > 0)
++ sc->sc_slottimeconf = val;
++ else
+ sc->sc_slottimeconf = 0;
+- ath_setslottime(sc);
+- }
++ ath_settiming(sc);
+ break;
+ case ATH_ACKTIMEOUT:
+- if (!ath_hal_setacktimeout(ah, val))
+- ret = -EINVAL;
++ if (val > 0)
++ sc->sc_acktimeconf = val;
++ else
++ sc->sc_acktimeconf = 0;
++ ath_settiming(sc);
+ break;
+ case ATH_CTSTIMEOUT:
+- if (!ath_hal_setctstimeout(ah, val))
+- ret = -EINVAL;
++ if (val > 0)
++ sc->sc_ctstimeconf = val;
++ else
++ sc->sc_ctstimeconf = 0;
++ ath_settiming(sc);
+ break;
+ case ATH_SOFTLED:
+ if (val != sc->sc_softled) {
+@@ -11223,6 +11337,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ }
+ } else {
+ switch ((long)ctl->extra2) {
++ case ATH_CHANBW:
++ val = sc->sc_chanbw ?: 20;
++ break;
+ case ATH_SLOTTIME:
+ val = ath_hal_getslottime(ah);
+ break;
+@@ -11241,6 +11358,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_COUNTRYCODE:
+ ath_hal_getcountrycode(ah, &val);
+ break;
++ case ATH_OUTDOOR:
++ val = ic->ic_country_outdoor;
++ break;
+ case ATH_MAXVAPS:
+ val = ath_maxvaps;
+ break;
+@@ -11354,11 +11474,17 @@ static const ctl_table ath_sysctl_templa
+ },
+ { .ctl_name = CTL_AUTO,
+ .procname = "countrycode",
+- .mode = 0444,
++ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_COUNTRYCODE,
+ },
+ { .ctl_name = CTL_AUTO,
++ .procname = "outdoor",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_OUTDOOR,
++ },
++ { .ctl_name = CTL_AUTO,
+ .procname = "maxvaps",
+ .mode = 0444,
+ .proc_handler = ath_sysctl_halparam,
+@@ -11366,7 +11492,7 @@ static const ctl_table ath_sysctl_templa
+ },
+ { .ctl_name = CTL_AUTO,
+ .procname = "regdomain",
+- .mode = 0444,
++ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_REGDOMAIN,
+ },
+@@ -11429,6 +11555,12 @@ static const ctl_table ath_sysctl_templa
+ .extra2 = (void *)ATH_ACKRATE,
+ },
+ { .ctl_name = CTL_AUTO,
++ .procname = "channelbw",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_CHANBW,
++ },
++ { .ctl_name = CTL_AUTO,
+ .procname = "rp",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+@@ -11669,13 +11801,6 @@ static ctl_table ath_static_sysctls[] =
+ },
+ #endif
+ { .ctl_name = CTL_AUTO,
+- .procname = "countrycode",
+- .mode = 0444,
+- .data = &ath_countrycode,
+- .maxlen = sizeof(ath_countrycode),
+- .proc_handler = proc_dointvec
+- },
+- { .ctl_name = CTL_AUTO,
+ .procname = "maxvaps",
+ .mode = 0444,
+ .data = &ath_maxvaps,
+@@ -11683,13 +11808,6 @@ static ctl_table ath_static_sysctls[] =
+ .proc_handler = proc_dointvec
+ },
+ { .ctl_name = CTL_AUTO,
+- .procname = "outdoor",
+- .mode = 0444,
+- .data = &ath_outdoor,
+- .maxlen = sizeof(ath_outdoor),
+- .proc_handler = proc_dointvec
+- },
+- { .ctl_name = CTL_AUTO,
+ .procname = "xchanmode",
+ .mode = 0444,
+ .data = &ath_xchanmode,
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -688,17 +688,18 @@ struct ath_softc {
+ int8_t sc_ofdm_weak_det; /* OFDM weak frames detection, -1 == auto */
+
+ /* rate tables */
+- const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX];
++#define ATH_MODE_HALF (IEEE80211_MODE_MAX)
++#define ATH_MODE_QUARTER (IEEE80211_MODE_MAX + 1)
++ const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX + 2];
+ const HAL_RATE_TABLE *sc_currates; /* current rate table */
+ const HAL_RATE_TABLE *sc_xr_rates; /* XR rate table */
+- const HAL_RATE_TABLE *sc_half_rates; /* half rate table */
+- const HAL_RATE_TABLE *sc_quarter_rates; /* quarter rate table */
+ HAL_OPMODE sc_opmode; /* current hal operating mode */
+ enum ieee80211_phymode sc_curmode; /* current phy mode */
+ u_int sc_poweroffset; /* hardware power offset */
+ u_int16_t sc_curtxpow; /* current tx power limit */
+ u_int16_t sc_curaid; /* current association id */
+ HAL_CHANNEL sc_curchan; /* current h/w channel */
++ u_int8_t sc_chanbw; /* channel bandwidth */
+ u_int8_t sc_curbssid[IEEE80211_ADDR_LEN];
+ u_int8_t sc_rixmap[256]; /* IEEE to h/w rate table ix */
+ struct {
+@@ -809,6 +810,8 @@ struct ath_softc {
+ u_int32_t sc_dturbo_bw_turbo; /* bandwidth threshold */
+ #endif
+ u_int sc_slottimeconf; /* manual override for slottime */
++ u_int sc_acktimeconf; /* manual override for acktime */
++ u_int sc_ctstimeconf; /* manual override for ctstime */
+
+ struct timer_list sc_dfs_excl_timer; /* mark expiration timer task */
+ struct timer_list sc_dfs_cac_timer; /* dfs wait timer */
+@@ -827,6 +830,7 @@ struct ath_softc {
+ int sc_rp_num;
+ int sc_rp_min;
+ HAL_BOOL (*sc_rp_analyse)(struct ath_softc *sc);
++ struct ATH_TQ_STRUCT sc_refresh_tq;
+ struct ATH_TQ_STRUCT sc_rp_tq;
+
+ int sc_rp_ignored; /* if set, we ignored all
+@@ -942,6 +946,48 @@ int ar_device(int devid);
+ DEV_NAME(_v->iv_ic->ic_dev))
+
+ void ath_radar_detected(struct ath_softc *sc, const char* message);
++static inline u_int getTimingOffset(struct ath_softc *sc)
++{
++ struct ieee80211com *ic = &sc->sc_ic;
++ u_int usec = 9;
++ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
++ usec = 20;
++ if (ic->ic_flags & IEEE80211_F_SHSLOT)
++ usec = 9;
++ } else if (IEEE80211_IS_CHAN_A(ic->ic_curchan))
++ usec = 9;
++
++ if (IEEE80211_IS_CHAN_TURBO(ic->ic_curchan))
++ usec = 6;
++
++ if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan))
++ usec = 13;
++ else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan))
++ usec = 21;
++ return usec;
++}
++
++static inline void ath_get_timings(struct ath_softc *sc, u_int *t_slot, u_int *t_sifs, u_int *t_difs)
++{
++ struct ieee80211_channel *c = sc->sc_ic.ic_curchan;
++
++ *t_slot = getTimingOffset(sc) + sc->sc_slottimeconf;
++
++ if (IEEE80211_IS_CHAN_HALF(c)) {
++ *t_sifs = 32;
++ *t_difs = 56;
++ } else if (IEEE80211_IS_CHAN_QUARTER(c)) {
++ *t_sifs = 64;
++ *t_difs = 112;
++ } else if (IEEE80211_IS_CHAN_TURBO(c)) {
++ *t_sifs = 8;
++ *t_difs = 28;
++ } else {
++ *t_sifs = 16;
++ *t_difs = 28;
++ }
++}
++
+
+ struct ath_hw_detect {
+ const char *vendor_name;
+--- a/tools/athctrl.c
++++ b/tools/athctrl.c
+@@ -118,7 +118,7 @@ CMD(athctrl)(int argc, char *argv[])
+ }
+
+ if (distance >= 0) {
+- int slottime = 9 + (distance / 300) + ((distance % 300) ? 1 : 0);
++ int slottime = (distance / 300) + ((distance % 300) ? 1 : 0);
+ int acktimeout = slottime * 2 + 3;
+ int ctstimeout = slottime * 2 + 3;
+
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -243,34 +243,17 @@ static const struct country_code_to_str
+ {CTRY_ZIMBABWE, "ZW"}
+ };
+
+-int
+-ieee80211_ifattach(struct ieee80211com *ic)
++void ieee80211_update_channels(struct ieee80211com *ic, int init)
+ {
+- struct net_device *dev = ic->ic_dev;
+ struct ieee80211_channel *c;
++ struct ieee80211vap *vap;
+ struct ifmediareq imr;
++ int ext = 0;
+ int i;
+
+- _MOD_INC_USE(THIS_MODULE, return -ENODEV);
+-
+- /*
+- * Pick an initial operating mode until we have a vap
+- * created to lock it down correctly. This is only
+- * drivers have something defined for configuring the
+- * hardware at startup.
+- */
+- ic->ic_opmode = IEEE80211_M_STA; /* everyone supports this */
+-
+- /*
+- * Fill in 802.11 available channel set, mark
+- * all available channels as active, and pick
+- * a default channel if not already specified.
+- */
+- KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
+- ("invalid number of channels specified: %u", ic->ic_nchans));
+ memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+- ic->ic_modecaps |= 1 << IEEE80211_MODE_AUTO;
+ ic->ic_max_txpower = IEEE80211_TXPOWER_MIN;
++ ic->ic_modecaps = 1 << IEEE80211_MODE_AUTO;
+
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+@@ -298,6 +281,8 @@ ieee80211_ifattach(struct ieee80211com *
+ ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A;
+ if (IEEE80211_IS_CHAN_108G(c))
+ ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G;
++ if (IEEE80211_IS_CHAN_HALF(c) || IEEE80211_IS_CHAN_QUARTER(c))
++ ext = 1;
+ }
+ /* Initialize candidate channels to all available */
+ memcpy(ic->ic_chan_active, ic->ic_chan_avail,
+@@ -311,11 +296,59 @@ ieee80211_ifattach(struct ieee80211com *
+ * When 11g is supported, force the rate set to
+ * include basic rates suitable for a mixed b/g bss.
+ */
+- if (ic->ic_modecaps & (1 << IEEE80211_MODE_11G))
++ if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11G)) && !ext)
+ ieee80211_set11gbasicrates(
+ &ic->ic_sup_rates[IEEE80211_MODE_11G],
+ IEEE80211_MODE_11G);
+
++ if (init)
++ return;
++
++ ifmedia_removeall(&ic->ic_media);
++ ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, NULL, NULL);
++ ieee80211com_media_status(ic->ic_dev, &imr);
++ ifmedia_set(&ic->ic_media, imr.ifm_active);
++
++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++ struct ieee80211vap *avp;
++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
++ (void) ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, NULL, NULL);
++ ieee80211_media_status(vap->iv_dev, &imr);
++ ifmedia_set(&vap->iv_media, imr.ifm_active);
++ }
++ (void) ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, NULL, NULL);
++ ieee80211_media_status(vap->iv_dev, &imr);
++ ifmedia_set(&vap->iv_media, imr.ifm_active);
++ }
++}
++EXPORT_SYMBOL(ieee80211_update_channels);
++
++int
++ieee80211_ifattach(struct ieee80211com *ic)
++{
++ struct net_device *dev = ic->ic_dev;
++ struct ieee80211_channel *c;
++ struct ifmediareq imr;
++
++ _MOD_INC_USE(THIS_MODULE, return -ENODEV);
++
++ /*
++ * Pick an initial operating mode until we have a vap
++ * created to lock it down correctly. This is only
++ * drivers have something defined for configuring the
++ * hardware at startup.
++ */
++ ic->ic_opmode = IEEE80211_M_STA; /* everyone supports this */
++
++ /*
++ * Fill in 802.11 available channel set, mark
++ * all available channels as active, and pick
++ * a default channel if not already specified.
++ */
++ KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
++ ("invalid number of channels specified: %u", ic->ic_nchans));
++ ieee80211_update_channels(ic, 1);
++
+ /* Setup initial channel settings */
+ ic->ic_bsschan = IEEE80211_CHAN_ANYC;
+ /* Arbitrarily pick the first channel */
+@@ -327,6 +360,7 @@ ieee80211_ifattach(struct ieee80211com *
+ /* Enable WME by default, if we're capable. */
+ if (ic->ic_caps & IEEE80211_C_WME)
+ ic->ic_flags |= IEEE80211_F_WME;
++
+ (void) ieee80211_setmode(ic, ic->ic_curmode);
+
+ /* Store default beacon interval, as nec. */
+@@ -763,7 +797,8 @@ ieee80211_media_setup(struct ieee80211co
+ struct ieee80211_rateset allrates;
+
+ /* Fill in media characteristics. */
+- ifmedia_init(media, 0, media_change, media_stat);
++ if (media_change || media_stat)
++ ifmedia_init(media, 0, media_change, media_stat);
+ maxrate = 0;
+ memset(&allrates, 0, sizeof(allrates));
+
+@@ -793,7 +828,7 @@ ieee80211_media_setup(struct ieee80211co
+ ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_WDS);
+ if (mode == IEEE80211_MODE_AUTO)
+ continue;
+- rs = &ic->ic_sup_rates[mode];
++ rs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)];
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rate = rs->rs_rates[i];
+@@ -1207,7 +1242,7 @@ ieee80211_announce(struct ieee80211com *
+ if ((ic->ic_modecaps & (1 << mode)) == 0)
+ continue;
+ if_printf(dev, "%s rates: ", ieee80211_phymode_name[mode]);
+- rs = &ic->ic_sup_rates[mode];
++ rs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)];
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rate = rs->rs_rates[i];
+ mword = ieee80211_rate2media(ic, rate, mode);
+@@ -1417,7 +1452,7 @@ ieee80211com_media_change(struct net_dev
+ * now so drivers have a consistent state.
+ */
+ KASSERT(vap->iv_bss != NULL, ("no bss node"));
+- vap->iv_bss->ni_rates = ic->ic_sup_rates[newphymode];
++ vap->iv_bss->ni_rates = ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, newphymode)];
+ }
+ error = -ENETRESET;
+ }
+@@ -1435,7 +1470,7 @@ findrate(struct ieee80211com *ic, enum i
+ {
+ #define IEEERATE(_ic,_m,_i) \
+ ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
+- int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
++ int i, nrates = ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)].rs_nrates;
+ for (i = 0; i < nrates; i++)
+ if (IEEERATE(ic, mode, i) == rate)
+ return i;
+@@ -1877,11 +1912,6 @@ ieee80211_build_countryie(struct ieee802
+ if (ieee80211_chan2mode(c) != curmode_noturbo)
+ continue;
+
+- /* Skip half/quarter rate channels */
+- if (IEEE80211_IS_CHAN_HALF(c) ||
+- IEEE80211_IS_CHAN_QUARTER(c))
+- continue;
+-
+ if (*cur_runlen == 0) {
+ (*cur_runlen)++;
+ *cur_pow = c->ic_maxregpower;
+@@ -1915,7 +1945,7 @@ void
+ ieee80211_build_sc_ie(struct ieee80211com *ic)
+ {
+ struct ieee80211_ie_sc *ie = &ic->ic_sc_ie;
+- int i, j;
++ int i, j, k;
+ struct ieee80211_channel *c;
+ u_int8_t prevchan;
+
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -336,8 +336,6 @@ struct ieee80211com {
+ u_int8_t ic_nopened; /* VAPs been opened */
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+ struct ieee80211_rateset ic_sup_xr_rates;
+- struct ieee80211_rateset ic_sup_half_rates;
+- struct ieee80211_rateset ic_sup_quarter_rates;
+ u_int16_t ic_modecaps; /* set of mode capabilities */
+ u_int16_t ic_curmode; /* current mode */
+ u_int16_t ic_lintval; /* beacon interval */
+@@ -714,6 +712,7 @@ MALLOC_DECLARE(M_80211_VAP);
+
+ int ieee80211_ifattach(struct ieee80211com *);
+ void ieee80211_ifdetach(struct ieee80211com *);
++void ieee80211_update_channels(struct ieee80211com *ic, int);
+ int ieee80211_vap_setup(struct ieee80211com *, struct net_device *,
+ const char *, int, int, struct ieee80211vap *);
+ int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t);
+@@ -793,6 +792,23 @@ ieee80211_anyhdrspace(struct ieee80211co
+ return size;
+ }
+
++static __inline int
++ieee80211_chan2ratemode(struct ieee80211_channel *c, int mode)
++{
++ if (mode == -1)
++ mode = ieee80211_chan2mode(c);
++
++ /*
++ * Use 11a rateset for half/quarter to restrict things
++ * to pure OFDM
++ */
++ if (IEEE80211_IS_CHAN_HALF(c) ||
++ IEEE80211_IS_CHAN_QUARTER(c))
++ return IEEE80211_MODE_11A;
++
++ return mode;
++}
++
+ /* Macros to print MAC address used in 802.11 headers */
+
+ #define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -287,7 +287,7 @@ ieee80211_node_set_chan(struct ieee80211
+ ni->ni_rates = ic->ic_sup_xr_rates;
+ else
+ #endif
+- ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(chan)];
++ ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2ratemode(chan, -1)];
+ }
+
+ static __inline void
+@@ -387,6 +387,8 @@ ieee80211_create_ibss(struct ieee80211va
+ ic->ic_bsschan = chan;
+ ieee80211_node_set_chan(ic, ni);
+ ic->ic_curmode = ieee80211_chan2mode(chan);
++ ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2ratemode(chan, -1)];
++
+ spin_lock_irqsave(&channel_lock, flags);
+ ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
+ spin_unlock_irqrestore(&channel_lock, flags);
+@@ -394,14 +396,8 @@ ieee80211_create_ibss(struct ieee80211va
+ /* Update country ie information */
+ ieee80211_build_countryie(ic);
+
+- if (IEEE80211_IS_CHAN_HALF(chan)) {
+- ni->ni_rates = ic->ic_sup_half_rates;
+- } else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
+- ni->ni_rates = ic->ic_sup_quarter_rates;
+- }
+-
+- if ((vap->iv_flags & IEEE80211_F_PUREG) &&
+- IEEE80211_IS_CHAN_ANYG(chan)) {
++ if ((ieee80211_chan2ratemode(chan, -1) != IEEE80211_MODE_11A) &&
++ IEEE80211_IS_CHAN_ANYG(chan) && (vap->iv_flags & IEEE80211_F_PUREG)) {
+ ieee80211_setpuregbasicrates(&ni->ni_rates);
+ }
+
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -490,12 +490,7 @@ check_rate(struct ieee80211vap *vap, con
+
+ okrate = badrate = fixedrate = 0;
+
+- if (IEEE80211_IS_CHAN_HALF(se->se_chan))
+- srs = &ic->ic_sup_half_rates;
+- else if (IEEE80211_IS_CHAN_QUARTER(se->se_chan))
+- srs = &ic->ic_sup_quarter_rates;
+- else
+- srs = &ic->ic_sup_rates[ieee80211_chan2mode(se->se_chan)];
++ srs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, -1)];
+ nrs = se->se_rates[1];
+ rs = se->se_rates + 2;
+ fixedrate = IEEE80211_FIXED_RATE_NONE;
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -1676,8 +1676,8 @@ ieee80211_send_probereq(struct ieee80211
+
+ frm = ieee80211_add_ssid(frm, ssid, ssidlen);
+ mode = ieee80211_chan2mode(ic->ic_curchan);
+- frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
+- frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
++ frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)]);
++ frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)]);
+
+ if (optie != NULL) {
+ memcpy(frm, optie, optielen);
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -404,7 +404,7 @@ ieee80211_fix_rate(struct ieee80211_node
+
+ error = 0;
+ okrate = badrate = fixedrate = 0;
+- srs = &ic->ic_sup_rates[ieee80211_chan2mode(ni->ni_chan)];
++ srs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, -1)];
+ nrs = &ni->ni_rates;
+ fixedrate = IEEE80211_FIXED_RATE_NONE;
+ for (i = 0; i < nrs->rs_nrates;) {
+@@ -1407,6 +1407,7 @@ ieee80211_new_state(struct ieee80211vap
+ IEEE80211_VAPS_UNLOCK_IRQ(ic);
+ return rc;
+ }
++EXPORT_SYMBOL(ieee80211_new_state);
+
+ static int
+ __ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -195,31 +195,7 @@ calc_usecs_unicast_packet(struct ath_sof
+ return 0;
+ }
+
+- /* XXX: Getting MAC/PHY level timings should be fixed for turbo
+- * rates, and there is probably a way to get this from the
+- * HAL... */
+- switch (rt->info[rix].phy) {
+- case IEEE80211_T_OFDM:
+-#if 0
+- t_slot = 9;
+- t_sifs = 16;
+- t_difs = 28;
+- /* fall through */
+-#endif
+- case IEEE80211_T_TURBO:
+- t_slot = 9;
+- t_sifs = 8;
+- t_difs = 28;
+- break;
+- case IEEE80211_T_DS:
+- /* Fall through to default */
+- default:
+- /* pg. 205 ieee.802.11.pdf */
+- t_slot = 20;
+- t_difs = 50;
+- t_sifs = 10;
+- }
+-
++ ath_get_timings(sc, &t_slot, &t_sifs, &t_difs);
+ if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ (rt->info[rix].phy == IEEE80211_T_OFDM)) {
+ if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -172,26 +172,7 @@ calc_usecs_unicast_packet(struct ath_sof
+ * rates, and there is probably a way to get this from the
+ * hal...
+ */
+- switch (rt->info[rix].phy) {
+- case IEEE80211_T_OFDM:
+- t_slot = 9;
+- t_sifs = 16;
+- t_difs = 28;
+- /* fall through */
+- case IEEE80211_T_TURBO:
+- t_slot = 9;
+- t_sifs = 8;
+- t_difs = 28;
+- break;
+- case IEEE80211_T_DS:
+- /* fall through to default */
+- default:
+- /* pg 205 ieee.802.11.pdf */
+- t_slot = 20;
+- t_difs = 50;
+- t_sifs = 10;
+- }
+-
++ ath_get_timings(sc, &t_slot, &t_sifs, &t_difs);
+ rts = cts = 0;
+
+ if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2142,7 +2142,7 @@ ieee80211_ioctl_setmode(struct net_devic
+
+ vap->iv_des_mode = mode;
+ if (IS_UP_AUTO(vap))
+- ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
++ ieee80211_init(vap->iv_dev, 0);
+
+ retv = 0;
+ }
+@@ -4090,46 +4090,60 @@ ieee80211_ioctl_getchanlist(struct net_d
+ return 0;
+ }
+
++static int alreadyListed(struct ieee80211req_chaninfo *chans, u_int16_t mhz)
++{
++ int i;
++ for (i = 0; i < chans->ic_nchans; i++) {
++ if (chans->ic_chans[i].ic_freq == mhz)
++ return 1;
++ }
++ return 0;
++}
++
+ static int
+ ieee80211_ioctl_getchaninfo(struct net_device *dev,
+- struct iw_request_info *info, void *w, char *extra)
++ struct iw_request_info *info, void *w, char *extra)
+ {
+ struct ieee80211vap *vap = dev->priv;
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ieee80211req_chaninfo chans;
++ struct ieee80211req_chaninfo *chans =
++ (struct ieee80211req_chaninfo *)extra;
++
+ u_int8_t reported[IEEE80211_CHAN_BYTES]; /* XXX stack usage? */
+ int i;
+
+- memset(&chans, 0, sizeof(chans));
+- memset(&reported, 0, sizeof(reported));
++ memset(chans, 0, sizeof(*chans));
++ memset(reported, 0, sizeof(reported));
+ for (i = 0; i < ic->ic_nchans; i++) {
+ const struct ieee80211_channel *c = &ic->ic_channels[i];
+ const struct ieee80211_channel *c1 = c;
+
+- if (isclr(reported, c->ic_ieee)) {
++ if (!alreadyListed(chans, c->ic_freq)) {
+ setbit(reported, c->ic_ieee);
+
+- /* pick turbo channel over non-turbo channel, and
+- * 11g channel over 11b channel */
+ if (IEEE80211_IS_CHAN_A(c))
+- c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_A);
++ c1 = findchannel(ic, c->ic_freq,
++ IEEE80211_MODE_TURBO_A);
+ if (IEEE80211_IS_CHAN_ANYG(c))
+- c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_G);
++ c1 = findchannel(ic, c->ic_freq,
++ IEEE80211_MODE_TURBO_G);
+ else if (IEEE80211_IS_CHAN_B(c)) {
+- c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_G);
++ c1 = findchannel(ic, c->ic_freq,
++ IEEE80211_MODE_TURBO_G);
+ if (!c1)
+- c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_11G);
++ c1 = findchannel(ic, c->ic_freq,
++ IEEE80211_MODE_11G);
+ }
+
+ if (c1)
+ c = c1;
+- /* Copy the entire structure, whereas it used to just copy a few fields */
+- memcpy(&chans.ic_chans[chans.ic_nchans], c, sizeof(struct ieee80211_channel));
+- if (++chans.ic_nchans >= IEEE80211_CHAN_MAX)
++ chans->ic_chans[chans->ic_nchans].ic_ieee = c->ic_ieee;
++ chans->ic_chans[chans->ic_nchans].ic_freq = c->ic_freq;
++ chans->ic_chans[chans->ic_nchans].ic_flags = c->ic_flags;
++ if (++chans->ic_nchans >= IEEE80211_CHAN_MAX)
+ break;
+ }
+ }
+- memcpy(extra, &chans, sizeof(struct ieee80211req_chaninfo));
+ return 0;
+ }
+
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -512,12 +512,13 @@ pick_channel(struct ieee80211_scan_state
+ int ss_last = ss->ss_last;
+ struct ieee80211_channel *best;
+ struct ap_state *as = ss->ss_priv;
+- struct channel chans[ss_last]; /* actually ss_last-1 is required */
++ struct channel *chans; /* actually ss_last-1 is required */
+ struct channel *c = NULL;
+ struct pc_params params = { vap, ss, flags };
+ int benefit = 0;
+ int sta_assoc = 0;
+
++ chans = (struct channel *)kmalloc(ss_last*sizeof(struct channel),GFP_ATOMIC);
+ for (i = 0; i < ss_last; i++) {
+ chans[i].chan = ss->ss_chans[i];
+ chans[i].orig = i;
+@@ -571,6 +572,7 @@ pick_channel(struct ieee80211_scan_state
+ "%s: best: channel %u rssi %d\n",
+ __func__, i, as->as_maxrssi[i]);
+ }
++ kfree(chans);
+ return best;
+ }
+
+@@ -609,6 +611,7 @@ ap_end(struct ieee80211_scan_state *ss,
+ res = 1; /* Do NOT restart scan */
+ } else {
+ struct ieee80211_scan_entry se;
++ int i;
+ /* XXX: notify all VAPs? */
+ /* if this is a dynamic turbo frequency , start with normal
+ * mode first */
+@@ -623,6 +626,11 @@ ap_end(struct ieee80211_scan_state *ss,
+ return 0;
+ }
+ }
++ for (i = (bestchan - &ic->ic_channels[0])/sizeof(*bestchan) + 1; i < ic->ic_nchans; i++) {
++ if ((ic->ic_channels[i].ic_freq == bestchan->ic_freq) &&
++ IEEE80211_IS_CHAN_ANYG(&ic->ic_channels[i]))
++ bestchan = &ic->ic_channels[i];
++ }
+ memset(&se, 0, sizeof(se));
+ se.se_chan = bestchan;
+
+--- a/tools/wlanconfig.c
++++ b/tools/wlanconfig.c
+@@ -737,7 +737,7 @@ list_channels(const char *ifname, int al
+ if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans)) < 0)
+ errx(1, "unable to get channel information");
+ if (!allchans) {
+- uint8_t active[32];
++ uint8_t active[IEEE80211_CHAN_BYTES];
+
+ if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANLIST, &active, sizeof(active)) < 0)
+ errx(1, "unable to get active channel list");
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -1044,6 +1044,7 @@ ieee80211_scan_assoc_fail(struct ieee802
+ ss->ss_ops->scan_assoc_fail(ss, mac, reason);
+ }
+ }
++EXPORT_SYMBOL(ieee80211_scan_flush);
+
+ /*
+ * Iterate over the contents of the scan cache.
+--- a/ath/if_ath_hal_wrappers.h
++++ b/ath/if_ath_hal_wrappers.h
+@@ -111,6 +111,11 @@ static inline HAL_BOOL ath_hal_getregdom
+ return (ath_hal_getcapability(ah, HAL_CAP_REG_DMN, 0, destination) == HAL_OK);
+ }
+
++static inline HAL_BOOL ath_hal_setregdomain(struct ath_hal *ah, u_int32_t v)
++{
++ return (ath_hal_setcapability(ah, HAL_CAP_REG_DMN, 0, v, NULL));
++}
++
+ static inline HAL_BOOL ath_hal_gettkipmic(struct ath_hal *ah)
+ {
+ return (ath_hal_getcapability(ah, HAL_CAP_TKIP_MIC, 1, NULL) == HAL_OK);
diff --git a/package/madwifi/patches/422-confchange_reset.patch b/package/madwifi/patches/422-confchange_reset.patch
new file mode 100644
index 000000000..33040ae0d
--- /dev/null
+++ b/package/madwifi/patches/422-confchange_reset.patch
@@ -0,0 +1,31 @@
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -70,7 +70,8 @@
+ (((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+ #define IS_UP_AUTO(_vap) \
+ (IS_UP((_vap)->iv_dev) && \
+- (_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO)
++ (((_vap)->iv_opmode == IEEE80211_M_HOSTAP) || \
++ (_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO))
+ #define RESCAN 1
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+@@ -283,7 +284,7 @@ ieee80211_ioctl_siwencode(struct net_dev
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
+ }
+ }
+- if ((error == 0) && IS_UP(vap->iv_dev)) {
++ if ((error == 0) && IS_UP_AUTO(vap) && wepchange) {
+ /*
+ * Device is up and running; we must kick it to
+ * effect the change. If we're enabling/disabling
+@@ -291,8 +292,7 @@ ieee80211_ioctl_siwencode(struct net_dev
+ * so the 802.11 state machine is reset. Otherwise
+ * the key state should have been updated above.
+ */
+- if (wepchange && IS_UP_AUTO(vap))
+- ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
++ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ }
+ #ifdef ATH_SUPERG_XR
+ /* set the same params on the xr vap device if exists */
diff --git a/package/madwifi/patches/423-phyerr_handling.patch b/package/madwifi/patches/423-phyerr_handling.patch
new file mode 100644
index 000000000..7f3cbafe2
--- /dev/null
+++ b/package/madwifi/patches/423-phyerr_handling.patch
@@ -0,0 +1,28 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -4391,13 +4391,12 @@ ath_key_update_end(struct ieee80211vap *
+ static u_int32_t
+ ath_calcrxfilter(struct ath_softc *sc)
+ {
+-#define RX_FILTER_PRESERVE (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR)
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct net_device *dev = ic->ic_dev;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rfilt;
+
+- rfilt = (ath_hal_getrxfilter(ah) & RX_FILTER_PRESERVE) |
++ rfilt = ath_hal_getrxfilter(ah) |
+ HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST |
+ HAL_RX_FILTER_MCAST;
+ if (ic->ic_opmode != IEEE80211_M_STA)
+@@ -4416,9 +4415,8 @@ ath_calcrxfilter(struct ath_softc *sc)
+ if (sc->sc_hasintmit && !sc->sc_needmib && ath_hal_getintmit(ah, NULL))
+ rfilt |= HAL_RX_FILTER_PHYERR;
+ if (sc->sc_curchan.privFlags & CHANNEL_DFS)
+- rfilt |= (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR);
++ rfilt |= HAL_RX_FILTER_PHYRADAR;
+ return rfilt;
+-#undef RX_FILTER_PRESERVE
+ }
+
+ /*
diff --git a/package/madwifi/patches/424-timing.patch b/package/madwifi/patches/424-timing.patch
new file mode 100644
index 000000000..8369db618
--- /dev/null
+++ b/package/madwifi/patches/424-timing.patch
@@ -0,0 +1,764 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -382,6 +382,7 @@ static u_int32_t ath_set_clamped_maxtxpo
+ static void ath_poll_disable(struct net_device *dev);
+ static void ath_poll_enable(struct net_device *dev);
+ static void ath_fetch_idle_time(struct ath_softc *sc);
++static void ath_set_timing(struct ath_softc *sc);
+
+ /* calibrate every 30 secs in steady state but check every second at first. */
+ static int ath_calinterval = ATH_SHORT_CALINTERVAL;
+@@ -1185,6 +1186,7 @@ ath_attach(u_int16_t devid, struct net_d
+ sc->sc_intmit = -1;
+ sc->sc_noise_immunity = -1;
+ sc->sc_ofdm_weak_det = -1;
++ sc->sc_coverage = 7; /* 2100 meters */
+
+ return 0;
+ bad3:
+@@ -2673,6 +2675,7 @@ ath_init(struct net_device *dev)
+ */
+ ath_chan_change(sc, ic->ic_curchan);
+ ath_set_ack_bitrate(sc, sc->sc_ackrate);
++ ath_set_timing(sc);
+ dev->flags |= IFF_RUNNING; /* we are ready to go */
+ ieee80211_start_running(ic); /* start all VAPs */
+ #ifdef ATH_TX99_DIAG
+@@ -4484,17 +4487,52 @@ ath_mode_init(struct net_device *dev)
+ * Set the slot time based on the current setting.
+ */
+ static void
+-ath_settiming(struct ath_softc *sc)
++ath_set_timing(struct ath_softc *sc)
+ {
++ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+- u_int offset = getTimingOffset(sc);
++ struct ath_timings *t = &sc->sc_timings;
++ u_int offset = 9;
++
++ t->sifs = 16;
++ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
++ offset = 20;
++ if (ic->ic_flags & IEEE80211_F_SHSLOT)
++ offset = 9;
++ } else if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) {
++ offset = 9;
++ }
++
++ if (IEEE80211_IS_CHAN_TURBO(ic->ic_curchan)) {
++ offset = 6;
++ t->sifs = 8;
++ } else if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan)) {
++ offset = 13;
++ t->sifs = 32;
++ } else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan)) {
++ offset = 21;
++ t->sifs = 64;
++ }
++
++ t->slot = offset + sc->sc_coverage;
++ t->ack = t->slot * 2 + 3;
++ t->cts = t->slot * 2 + 3;
+
+ if (sc->sc_slottimeconf > 0)
+- ath_hal_setslottime(ah, offset + sc->sc_slottimeconf);
++ t->slot = sc->sc_slottimeconf;
+ if (sc->sc_acktimeconf > 0)
+- ath_hal_setacktimeout(ah, 2 * offset + sc->sc_acktimeconf);
++ t->ack = sc->sc_acktimeconf;
+ if (sc->sc_ctstimeconf > 0)
+- ath_hal_setctstimeout(ah, 2 * offset + sc->sc_ctstimeconf);
++ t->cts = sc->sc_ctstimeconf;
++
++ t->difs = 2 * t->sifs + t->slot;
++ t->eifs = t->sifs + t->difs + 3;
++
++ ath_hal_setslottime(ah, t->slot);
++ ath_hal_setacktimeout(ah, t->ack);
++ ath_hal_setctstimeout(ah, t->cts);
++ ath_hal_seteifstime(ah, t->eifs);
++
+ sc->sc_updateslot = OK;
+ }
+
+@@ -4516,7 +4554,7 @@ ath_updateslot(struct net_device *dev)
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ sc->sc_updateslot = UPDATE;
+ else if (dev->flags & IFF_RUNNING)
+- ath_settiming(sc);
++ ath_set_timing(sc);
+ }
+
+ #ifdef ATH_SUPERG_DYNTURBO
+@@ -5360,7 +5398,7 @@ ath_beacon_send(struct ath_softc *sc, in
+ sc->sc_updateslot = COMMIT; /* commit next beacon */
+ sc->sc_slotupdate = slot;
+ } else if ((sc->sc_updateslot == COMMIT) && (sc->sc_slotupdate == slot))
+- ath_settiming(sc); /* commit change to hardware */
++ ath_set_timing(sc); /* commit change to hardware */
+
+ if (bfaddr != 0) {
+ /*
+@@ -9433,7 +9471,8 @@ ath_set_coverageclass(struct ieee80211co
+ {
+ struct ath_softc *sc = ic->ic_dev->priv;
+
+- ath_hal_setcoverageclass(sc->sc_ah, ic->ic_coverageclass, 0);
++ sc->sc_coverage = ic->ic_coverageclass * 3;
++ ath_set_timing(sc);
+
+ return;
+ }
+@@ -10956,6 +10995,7 @@ enum {
+ ATH_OFDM_WEAK_DET = 29,
+ ATH_CHANBW = 30,
+ ATH_OUTDOOR = 31,
++ ATH_DISTANCE = 32,
+ };
+
+ /*
+@@ -11168,21 +11208,31 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ sc->sc_slottimeconf = val;
+ else
+ sc->sc_slottimeconf = 0;
+- ath_settiming(sc);
++ ath_set_timing(sc);
+ break;
+ case ATH_ACKTIMEOUT:
+ if (val > 0)
+ sc->sc_acktimeconf = val;
+ else
+ sc->sc_acktimeconf = 0;
+- ath_settiming(sc);
++ ath_set_timing(sc);
+ break;
+ case ATH_CTSTIMEOUT:
+ if (val > 0)
+ sc->sc_ctstimeconf = val;
+ else
+ sc->sc_ctstimeconf = 0;
+- ath_settiming(sc);
++ ath_set_timing(sc);
++ break;
++ case ATH_DISTANCE:
++ if (val > 0) {
++ sc->sc_coverage = ((val - 1) / 300) + 1;
++ ic->ic_coverageclass = ((sc->sc_coverage - 1) / 3) + 1;
++ } else {
++ sc->sc_coverage = 0;
++ ic->ic_coverageclass = 0;
++ }
++ ath_set_timing(sc);
+ break;
+ case ATH_SOFTLED:
+ if (val != sc->sc_softled) {
+@@ -11338,6 +11388,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_CHANBW:
+ val = sc->sc_chanbw ?: 20;
+ break;
++ case ATH_DISTANCE:
++ val = sc->sc_coverage * 300;
++ break;
+ case ATH_SLOTTIME:
+ val = ath_hal_getslottime(ah);
+ break;
+@@ -11459,6 +11512,12 @@ static const ctl_table ath_sysctl_templa
+ .extra2 = (void *)ATH_CTSTIMEOUT,
+ },
+ { .ctl_name = CTL_AUTO,
++ .procname = "distance",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_DISTANCE,
++ },
++ { .ctl_name = CTL_AUTO,
+ .procname = "softled",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+--- a/ath/if_ath_hal.h
++++ b/ath/if_ath_hal.h
+@@ -284,6 +284,17 @@ static inline u_int ath_hal_getslottime(
+ return ret;
+ }
+
++static inline u_int ath_hal_geteifstime(struct ath_hal *ah)
++{
++ u_int ret;
++ ATH_HAL_LOCK_IRQ(ah->ah_sc);
++ ath_hal_set_function(__func__);
++ ret = ah->ah_getEifsTime(ah);
++ ath_hal_set_function(NULL);
++ ATH_HAL_UNLOCK_IRQ(ah->ah_sc);
++ return ret;
++}
++
+ static inline void ath_hal_beaconinit(struct ath_hal *ah, u_int32_t nexttbtt,
+ u_int32_t intval)
+ {
+@@ -839,6 +850,17 @@ static inline HAL_BOOL ath_hal_setslotti
+ ath_hal_set_function(NULL);
+ ATH_HAL_UNLOCK_IRQ(ah->ah_sc);
+ return ret;
++}
++
++static inline HAL_BOOL ath_hal_seteifstime(struct ath_hal *ah, u_int a1)
++{
++ HAL_BOOL ret;
++ ATH_HAL_LOCK_IRQ(ah->ah_sc);
++ ath_hal_set_function(__func__);
++ ret = ah->ah_setEifsTime(ah, a1);
++ ath_hal_set_function(NULL);
++ ATH_HAL_UNLOCK_IRQ(ah->ah_sc);
++ return ret;
+ }
+
+ static inline void ath_hal_setledstate(struct ath_hal *ah, HAL_LED_STATE a1)
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -613,6 +613,15 @@ struct ath_rp {
+ int rp_analyzed;
+ };
+
++struct ath_timings {
++ u_int slot;
++ u_int ack;
++ u_int cts;
++ u_int sifs;
++ u_int difs;
++ u_int eifs;
++};
++
+ struct ath_softc {
+ struct ieee80211com sc_ic; /* NB: must be first */
+ struct net_device *sc_dev;
+@@ -839,6 +848,8 @@ struct ath_softc {
+ * detected radars */
+ u_int32_t sc_nexttbtt;
+ u_int64_t sc_last_tsf;
++ u_int sc_coverage;
++ struct ath_timings sc_timings;
+ };
+
+ typedef void (*ath_callback) (struct ath_softc *);
+@@ -946,49 +957,76 @@ int ar_device(int devid);
+ DEV_NAME(_v->iv_ic->ic_dev))
+
+ void ath_radar_detected(struct ath_softc *sc, const char* message);
+-static inline u_int getTimingOffset(struct ath_softc *sc)
+-{
+- struct ieee80211com *ic = &sc->sc_ic;
+- u_int usec = 9;
+- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
+- usec = 20;
+- if (ic->ic_flags & IEEE80211_F_SHSLOT)
+- usec = 9;
+- } else if (IEEE80211_IS_CHAN_A(ic->ic_curchan))
+- usec = 9;
+-
+- if (IEEE80211_IS_CHAN_TURBO(ic->ic_curchan))
+- usec = 6;
+-
+- if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan))
+- usec = 13;
+- else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan))
+- usec = 21;
+- return usec;
+-}
+
+-static inline void ath_get_timings(struct ath_softc *sc, u_int *t_slot, u_int *t_sifs, u_int *t_difs)
+-{
+- struct ieee80211_channel *c = sc->sc_ic.ic_curchan;
++#ifndef MIN
++#define MIN(a,b) ((a) < (b) ? (a) : (b))
++#endif
++#ifndef MAX
++#define MAX(a,b) ((a) > (b) ? (a) : (b))
++#endif
+
+- *t_slot = getTimingOffset(sc) + sc->sc_slottimeconf;
+
+- if (IEEE80211_IS_CHAN_HALF(c)) {
+- *t_sifs = 32;
+- *t_difs = 56;
+- } else if (IEEE80211_IS_CHAN_QUARTER(c)) {
+- *t_sifs = 64;
+- *t_difs = 112;
+- } else if (IEEE80211_IS_CHAN_TURBO(c)) {
+- *t_sifs = 8;
+- *t_difs = 28;
+- } else {
+- *t_sifs = 16;
+- *t_difs = 28;
+- }
++/* Calculate the transmit duration of a frame. */
++static inline unsigned
++calc_usecs_unicast_packet(struct ath_softc *sc, int length,
++ int rix, int short_retries, int long_retries)
++{
++ const HAL_RATE_TABLE *rt = sc->sc_currates;
++ struct ieee80211com *ic = &sc->sc_ic;
++ struct ath_timings *t = &sc->sc_timings;
++ unsigned int x = 0, tt = 0;
++ unsigned int cix = rt->info[rix].controlRate;
++ int rts = 0, cts = 0;
++ int cw = ATH_DEFAULT_CWMIN;
++
++ KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
++
++ if (!rt->info[rix].rateKbps) {
++ printk(KERN_WARNING "rix %d (%d) bad ratekbps %d mode %u\n",
++ rix, rt->info[rix].dot11Rate,
++ rt->info[rix].rateKbps,
++ sc->sc_curmode);
++ return 0;
++ }
++
++ if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
++ (rt->info[rix].phy == IEEE80211_T_OFDM)) {
++
++ if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
++ rts = 1;
++ else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
++ cts = 1;
++
++ cix = rt->info[sc->sc_protrix].controlRate;
++ }
++
++ if ((rts || cts) && rt->info[cix].rateKbps) {
++ int ctsrate = rt->info[cix].rateCode;
++ int ctsduration = 0;
++
++ ctsrate |= rt->info[cix].shortPreamble;
++ if (rts) /* SIFS + CTS */
++ ctsduration += rt->info[cix].spAckDuration;
++
++ ctsduration += ath_hal_computetxtime(sc->sc_ah,
++ rt, length, rix, AH_TRUE);
++
++ if (cts) /* SIFS + ACK */
++ ctsduration += rt->info[cix].spAckDuration;
++
++ tt += (short_retries + 1) * ctsduration;
++ }
++ tt += t->difs;
++ tt += (long_retries + 1) * (t->sifs + rt->info[rix].spAckDuration);
++ tt += (long_retries + 1) * ath_hal_computetxtime(sc->sc_ah, rt, length,
++ rix, AH_TRUE);
++ for (x = 0; x <= short_retries + long_retries; x++) {
++ cw = MIN(ATH_DEFAULT_CWMAX, (cw + 1) * 2);
++ tt += (t->slot * cw / 2);
++ }
++ return tt;
+ }
+
+-
+ struct ath_hw_detect {
+ const char *vendor_name;
+ const char *card_name;
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -170,85 +170,6 @@ rate_to_ndx(struct minstrel_node *sn, in
+ return -1;
+ }
+
+-/* Calculate the transmit duration of a frame. */
+-static unsigned
+-calc_usecs_unicast_packet(struct ath_softc *sc, int length,
+- int rix, int short_retries, int long_retries)
+-{
+- const HAL_RATE_TABLE *rt = sc->sc_currates;
+- struct ieee80211com *ic = &sc->sc_ic;
+- unsigned t_slot = 20;
+- unsigned t_difs = 50;
+- unsigned t_sifs = 10;
+- unsigned int x = 0, tt = 0;
+- unsigned int cix = rt->info[rix].controlRate;
+- int rts = 0, cts = 0;
+- int cw = ATH_DEFAULT_CWMIN;
+-
+- KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+-
+- if (!rt->info[rix].rateKbps) {
+- printk(KERN_WARNING "rix %d (%d) bad ratekbps %d mode %u\n",
+- rix, rt->info[rix].dot11Rate,
+- rt->info[rix].rateKbps,
+- sc->sc_curmode);
+- return 0;
+- }
+-
+- ath_get_timings(sc, &t_slot, &t_sifs, &t_difs);
+- if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+- (rt->info[rix].phy == IEEE80211_T_OFDM)) {
+- if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+- rts = 1;
+- else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+- cts = 1;
+-
+- cix = rt->info[sc->sc_protrix].controlRate;
+- }
+-
+-#if 0
+- if (length > ic->ic_rtsthreshold)
+- rts = 1;
+-#endif
+-
+- if (rts || cts) {
+- int ctsrate = rt->info[cix].rateCode;
+- int ctsduration = 0;
+-
+- if (!rt->info[cix].rateKbps) {
+-#if 0
+- printk(KERN_WARNING "cix %d (%d) bad ratekbps %d mode %u\n",
+- cix, rt->info[cix].dot11Rate,
+- rt->info[cix].rateKbps,
+- sc->sc_curmode);
+-#endif
+- return 0;
+- }
+-
+-
+- ctsrate |= rt->info[cix].shortPreamble;
+- if (rts) /* SIFS + CTS */
+- ctsduration += rt->info[cix].spAckDuration;
+-
+- ctsduration += ath_hal_computetxtime(sc->sc_ah,
+- rt, length, rix, AH_TRUE);
+-
+- if (cts) /* SIFS + ACK */
+- ctsduration += rt->info[cix].spAckDuration;
+-
+- tt += (short_retries + 1) * ctsduration;
+- }
+- tt += t_difs;
+- tt += (long_retries + 1) * (t_sifs + rt->info[rix].spAckDuration);
+- tt += (long_retries + 1) * ath_hal_computetxtime(sc->sc_ah, rt, length,
+- rix, AH_TRUE);
+- for (x = 0; x <= short_retries + long_retries; x++) {
+- cw = MIN(ATH_DEFAULT_CWMAX, (cw + 1) * 2);
+- tt += (t_slot * cw / 2);
+- }
+- return tt;
+-}
+-
+ static void
+ ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
+ {
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -137,92 +137,6 @@ rate_to_ndx(struct sample_node *sn, int
+ return -1;
+ }
+
+-/*
+- * Calculate the transmit duration of a frame.
+- */
+-static unsigned
+-calc_usecs_unicast_packet(struct ath_softc *sc, int length,
+- int rix, int short_retries, int long_retries)
+-{
+- const HAL_RATE_TABLE *rt = sc->sc_currates;
+- int rts, cts;
+-
+- unsigned t_slot;
+- unsigned t_difs;
+- unsigned t_sifs;
+- struct ieee80211com *ic = &sc->sc_ic;
+- unsigned int tt = 0;
+- unsigned int x;
+- unsigned int cw = ATH_DEFAULT_CWMIN;
+- unsigned int cix = rt->info[rix].controlRate;
+- KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+-
+- if (!rt->info[rix].rateKbps) {
+- printk(KERN_WARNING "rix %u (%u) bad ratekbps %u mode %u\n",
+- rix, rt->info[rix].dot11Rate,
+- rt->info[rix].rateKbps,
+- sc->sc_curmode);
+-
+- return 0;
+- }
+-
+- cix = rt->info[rix].controlRate;
+- /*
+- * XXX getting mac/phy level timings should be fixed for turbo
+- * rates, and there is probably a way to get this from the
+- * hal...
+- */
+- ath_get_timings(sc, &t_slot, &t_sifs, &t_difs);
+- rts = cts = 0;
+-
+- if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+- rt->info[rix].phy == IEEE80211_T_OFDM) {
+- if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+- rts = 1;
+- else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+- cts = 1;
+-
+- cix = rt->info[sc->sc_protrix].controlRate;
+- }
+-
+- if (0 /*length > ic->ic_rtsthreshold */)
+- rts = 1;
+-
+- if (rts || cts) {
+- int ctsrate;
+- int ctsduration = 0;
+-
+- if (!rt->info[cix].rateKbps) {
+- printk(KERN_WARNING "cix %u (%u) bad ratekbps %u mode %u\n",
+- cix, rt->info[cix].dot11Rate,
+- rt->info[cix].rateKbps,
+- sc->sc_curmode);
+- return 0;
+- }
+-
+-
+- ctsrate = rt->info[cix].rateCode | rt->info[cix].shortPreamble;
+- if (rts) /* SIFS + CTS */
+- ctsduration += rt->info[cix].spAckDuration;
+-
+- ctsduration += ath_hal_computetxtime(sc->sc_ah,
+- rt, length, rix, AH_TRUE);
+-
+- if (cts) /* SIFS + ACK */
+- ctsduration += rt->info[cix].spAckDuration;
+-
+- tt += (short_retries + 1) * ctsduration;
+- }
+- tt += t_difs;
+- tt += (long_retries+1)*(t_sifs + rt->info[rix].spAckDuration);
+- tt += (long_retries+1)*ath_hal_computetxtime(sc->sc_ah, rt, length,
+- rix, AH_TRUE);
+- for (x = 0; x <= short_retries + long_retries; x++) {
+- cw = MIN(ATH_DEFAULT_CWMAX, (cw + 1) * 2);
+- tt += (t_slot * cw / 2);
+- }
+- return tt;
+-}
+
+ static void
+ ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2745,6 +2745,7 @@ ieee80211_ioctl_setparam(struct net_devi
+ case IEEE80211_PARAM_COVERAGE_CLASS:
+ if (value <= IEEE80211_COVERAGE_CLASS_MAX) {
+ ic->ic_coverageclass = value;
++ ic->ic_set_coverageclass(ic);
+ if (IS_UP_AUTO(vap))
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ retv = 0;
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -94,7 +94,7 @@
+
+ #define IEEE80211_BGSCAN_TRIGGER_INTVL 20 /* min trigger interval for thresh based bgscan (secs) */
+
+-#define IEEE80211_COVERAGE_CLASS_MAX 31 /* max coverage class */
++#define IEEE80211_COVERAGE_CLASS_MAX 255 /* max coverage class */
+ #define IEEE80211_REGCLASSIDS_MAX 10 /* max regclass id list */
+
+ #define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -50,7 +50,7 @@ all: compile
+
+ DEBUG = -DAR_DEBUG
+
+-ALLPROGS= athstats 80211stats athkey athchans athctrl \
++ALLPROGS= athstats 80211stats athkey athchans \
+ $(if $(DEBUG),athdebug 80211debug) wlanconfig ath_info
+
+ OBJS= $(patsubst %,%.o,$(ALLPROGS))
+--- a/tools/athctrl.c
++++ /dev/null
+@@ -1,133 +0,0 @@
+-/*-
+- * Copyright (c) 2002-2004 Gunter Burchardt, Local-Web AG
+- * 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.
+- *
+- * $Id: athctrl.c 2394 2007-05-30 01:41:18Z mtaylor $
+- */
+-
+-/*
+- * Simple Atheros-specific tool to inspect and set atheros specific values
+- * athctrl [-i interface] [-d distance]
+- * (default interface is wifi0).
+- */
+-#include <sys/types.h>
+-#include <sys/file.h>
+-
+-#include <getopt.h>
+-
+-#include <stdio.h>
+-#include <string.h>
+-#include <stdlib.h>
+-#include <err.h>
+-
+-#include <net/if.h>
+-#include "do_multi.h"
+-
+-static int
+-setsysctrl(const char *dev, const char *control , u_long value)
+-{
+- char buffer[256];
+- FILE * fd;
+-
+- snprintf(buffer, sizeof(buffer), "/proc/sys/dev/%s/%s", dev, control);
+- fd = fopen(buffer, "w");
+- if (fd != NULL) {
+- fprintf(fd, "%li", value);
+- fclose(fd);
+- } else
+- fprintf(stderr, "Could not open %s for writing!\n", buffer);
+-
+- return 0;
+-}
+-
+-static void usage(void)
+-{
+- fprintf(stderr,
+- "Atheros driver control\n"
+- "Copyright (c) 2002-2004 Gunter Burchardt, Local-Web AG\n"
+- "\n"
+- "usage: athctrl [-i interface] [-d distance]\n"
+- "\n"
+- "options:\n"
+- " -h show this usage\n"
+- " -i interface (default interface is wifi0)\n"
+- " -d specify the maximum distance of a sta or the distance\n"
+- " of the master\n");
+-
+- exit(1);
+-}
+-
+-int
+-CMD(athctrl)(int argc, char *argv[])
+-{
+- char device[IFNAMSIZ + 1];
+- int distance = -1;
+- int c;
+-
+- strncpy(device, "wifi0", sizeof (device));
+-
+- for (;;) {
+- c = getopt(argc, argv, "d:i:h");
+- if (c < 0)
+- break;
+- switch (c) {
+- case 'h':
+- usage();
+- break;
+- case 'd':
+- distance = atoi(optarg);
+- break;
+- case 'i':
+- strncpy(device, optarg, sizeof (device));
+- break;
+- default:
+- usage();
+- break;
+- }
+- }
+-
+- if (distance >= 0) {
+- int slottime = (distance / 300) + ((distance % 300) ? 1 : 0);
+- int acktimeout = slottime * 2 + 3;
+- int ctstimeout = slottime * 2 + 3;
+-
+- printf("Setting distance on interface %s to %i meters\n",
+- device, distance);
+- setsysctrl(device, "slottime", slottime);
+- setsysctrl(device, "acktimeout", acktimeout);
+- setsysctrl(device, "ctstimeout", ctstimeout);
+- } else
+- usage();
+- return 0;
+-}
+--- a/tools/do_multi.c
++++ b/tools/do_multi.c
+@@ -18,8 +18,6 @@ main(int argc, char *argv[])
+ ret = a80211stats_init(argc, argv);
+ if(strcmp(progname, "athchans") == 0)
+ ret = athchans_init(argc, argv);
+- if(strcmp(progname, "athctrl") == 0)
+- ret = athctrl_init(argc, argv);
+ #ifdef AR_DEBUG
+ if(strcmp(progname, "athdebug") == 0)
+ ret = athdebug_init(argc, argv);
+--- a/tools/do_multi.h
++++ b/tools/do_multi.h
+@@ -2,7 +2,6 @@
+ int a80211debug_init(int argc, char *argv[]);
+ int a80211stats_init(int argc, char *argv[]);
+ int athchans_init(int argc, char *argv[]);
+-int athctrl_init(int argc, char *argv[]);
+ int athdebug_init(int argc, char *argv[]);
+ int athkey_init(int argc, char *argv[]);
+ int athstats_init(int argc, char *argv[]);
+--- a/ath_rate/minstrel/minstrel.h
++++ b/ath_rate/minstrel/minstrel.h
+@@ -172,14 +172,6 @@ struct minstrel_node {
+
+ #define ATH_NODE_MINSTREL(an) ((struct minstrel_node *)&an[1])
+
+-
+-#ifndef MIN
+-#define MIN(a,b) ((a) < (b) ? (a) : (b))
+-#endif
+-#ifndef MAX
+-#define MAX(a,b) ((a) > (b) ? (a) : (b))
+-#endif
+-
+ /*
+ * Definitions for pulling the rate and trie counts from
+ * a 5212 h/w descriptor. These Don't belong here; the
+--- a/ath_rate/sample/sample.h
++++ b/ath_rate/sample/sample.h
+@@ -98,14 +98,6 @@ struct sample_node {
+ };
+ #define ATH_NODE_SAMPLE(an) ((struct sample_node *)&an[1])
+
+-
+-#ifndef MIN
+-#define MIN(a,b) ((a) < (b) ? (a) : (b))
+-#endif
+-#ifndef MAX
+-#define MAX(a,b) ((a) > (b) ? (a) : (b))
+-#endif
+-
+ /*
+ * Definitions for pulling the rate and trie counts from
+ * a 5212 h/w descriptor. These Don't belong here; the
diff --git a/package/madwifi/patches/425-rc_rexmit.patch b/package/madwifi/patches/425-rc_rexmit.patch
new file mode 100644
index 000000000..252767a8c
--- /dev/null
+++ b/package/madwifi/patches/425-rc_rexmit.patch
@@ -0,0 +1,506 @@
+--- a/net80211/ieee80211_rate.h
++++ b/net80211/ieee80211_rate.h
+@@ -81,6 +81,8 @@ struct ieee80211vap;
+
+ /* Multi-rare retry: 3 additional rate/retry pairs */
+ struct ieee80211_mrr {
++ int rate0;
++ int retries0;
+ int rate1;
+ int retries1;
+ int rate2;
+@@ -142,7 +144,7 @@ struct ieee80211_rate_ops {
+ * for packets that were successfully sent and for those that
+ * failed (consult the descriptor for details). */
+ void (*tx_complete)(struct ath_softc *sc, struct ath_node *an,
+- const struct ath_buf *bf);
++ const struct ath_buf *bf, const struct ieee80211_mrr *mrr);
+ };
+
+ struct ath_ratectrl {
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -8638,6 +8638,8 @@ ath_tx_processq(struct ath_softc *sc, st
+
+ ni = bf->bf_node;
+ if (ni != NULL) {
++ struct ieee80211_mrr mrr;
++
+ an = ATH_NODE(ni);
+ if (ts->ts_status == 0) {
+ u_int8_t txant = ts->ts_antenna;
+@@ -8690,15 +8692,43 @@ ath_tx_processq(struct ath_softc *sc, st
+ lr = ts->ts_longretry;
+ sc->sc_stats.ast_tx_shortretry += sr;
+ sc->sc_stats.ast_tx_longretry += lr;
++ memset(&mrr, 0, sizeof(mrr));
++
++ switch(ah->ah_macType) {
++ case 5210:
++ case 5211:
++ goto skip_mrr;
++
++ case 5212:
++ mrr.rate0 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate0)].ieeerate;
++ mrr.rate1 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate1)].ieeerate;
++ mrr.rate2 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate2)].ieeerate;
++ mrr.rate3 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate3)].ieeerate;
++ break;
++
++ case 5416:
++ mrr.rate0 = sc->sc_hwmap[MS(ds->ds_ctl3, AR5416_XmitRate0)].ieeerate;
++ mrr.rate1 = sc->sc_hwmap[MS(ds->ds_ctl3, AR5416_XmitRate1)].ieeerate;
++ mrr.rate2 = sc->sc_hwmap[MS(ds->ds_ctl3, AR5416_XmitRate2)].ieeerate;
++ mrr.rate3 = sc->sc_hwmap[MS(ds->ds_ctl3, AR5416_XmitRate3)].ieeerate;
++ break;
++ }
++
++ mrr.retries0 = MS(ds->ds_ctl2, AR_XmitDataTries0);
++ mrr.retries1 = MS(ds->ds_ctl2, AR_XmitDataTries1);
++ mrr.retries2 = MS(ds->ds_ctl2, AR_XmitDataTries2);
++ mrr.retries3 = MS(ds->ds_ctl2, AR_XmitDataTries3);
++
+ /*
+ * Hand the descriptor to the rate control algorithm
+ * if the frame wasn't dropped for filtering or sent
+ * w/o waiting for an ack. In those cases the rssi
+ * and retry counts will be meaningless.
+ */
++skip_mrr:
+ if ((ts->ts_status & HAL_TXERR_FILT) == 0 &&
+ (bf->bf_flags & HAL_TXDESC_NOACK) == 0)
+- sc->sc_rc->ops->tx_complete(sc, an, bf);
++ sc->sc_rc->ops->tx_complete(sc, an, bf, &mrr);
+ }
+
+ bus_unmap_single(sc->sc_bdev, bf->bf_skbaddr,
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -595,6 +595,46 @@ struct ath_vap {
+ (_tqs)->axq_link = NULL; \
+ } while (0)
+
++/*
++ * Definitions for pulling the rate and trie counts from
++ * a 5212 h/w descriptor. These Don't belong here; the
++ * driver should record this information so the rate control
++ * code doesn't go groveling around in the descriptor bits.
++ */
++#define ds_ctl2 ds_hw[0]
++#define ds_ctl3 ds_hw[1]
++
++/* TX ds_ctl3 */
++#define AR_XmitDataTries0 0x000f0000 /* series 0 max attempts */
++#define AR_XmitDataTries0_S 16
++#define AR_XmitDataTries1 0x00f00000 /* series 1 max attempts */
++#define AR_XmitDataTries1_S 20
++#define AR_XmitDataTries2 0x0f000000 /* series 2 max attempts */
++#define AR_XmitDataTries2_S 24
++#define AR_XmitDataTries3 0xf0000000 /* series 3 max attempts */
++#define AR_XmitDataTries3_S 28
++
++/* TX ds_ctl3 */
++#define AR_XmitRate0 0x0000001f /* series 0 tx rate */
++#define AR_XmitRate0_S 0
++#define AR_XmitRate1 0x000003e0 /* series 1 tx rate */
++#define AR_XmitRate1_S 5
++#define AR_XmitRate2 0x00007c00 /* series 2 tx rate */
++#define AR_XmitRate2_S 10
++#define AR_XmitRate3 0x000f8000 /* series 3 tx rate */
++#define AR_XmitRate3_S 15
++
++#define AR5416_XmitRate0 0x000000ff
++#define AR5416_XmitRate0_S 0
++#define AR5416_XmitRate1 0x0000ff00
++#define AR5416_XmitRate1_S 8
++#define AR5416_XmitRate2 0x00ff0000
++#define AR5416_XmitRate2_S 16
++#define AR5416_XmitRate3 0xff000000
++#define AR5416_XmitRate3_S 24
++
++#define MS(_v, _f) (((_v) & (_f)) >> _f##_S)
++
+ /*
+ * concat buffers from one queue to other
+ */
+--- a/ath_rate/amrr/amrr.c
++++ b/ath_rate/amrr/amrr.c
+@@ -123,7 +123,8 @@ ath_rate_get_mrr(struct ath_softc *sc, s
+
+ static void
+ ath_rate_tx_complete(struct ath_softc *sc,
+- struct ath_node *an, const struct ath_buf *bf)
++ struct ath_node *an, const struct ath_buf *bf,
++ const struct ieee80211_mrr *mrr)
+ {
+ struct amrr_node *amn = ATH_NODE_AMRR(an);
+ const struct ath_tx_status *ts = &bf->bf_dsstatus.ds_txstat;
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -333,7 +333,8 @@ ath_rate_get_mrr(struct ath_softc *sc, s
+
+ static void
+ ath_rate_tx_complete(struct ath_softc *sc,
+- struct ath_node *an, const struct ath_buf *bf)
++ struct ath_node *an, const struct ath_buf *bf,
++ const struct ieee80211_mrr *mrr)
+ {
+ struct minstrel_node *sn = ATH_NODE_MINSTREL(an);
+ struct ieee80211com *ic = &sc->sc_ic;
+@@ -341,12 +342,9 @@ ath_rate_tx_complete(struct ath_softc *s
+ const struct ath_desc *ds = &bf->bf_desc[0];
+ int final_rate = 0;
+ int tries = 0;
+- int mrr;
++ int use_mrr;
+ int final_ndx;
+- int rate0, tries0, ndx0;
+- int rate1, tries1, ndx1;
+- int rate2, tries2, ndx2;
+- int rate3, tries3, ndx3;
++ int ndx0, ndx1, ndx2, ndx3;
+
+ /* This is the index in the retry chain we finish at.
+ * With no retransmits, it is always 0.
+@@ -376,9 +374,9 @@ ath_rate_tx_complete(struct ath_softc *s
+ if (!ts->ts_status) /* Success when sending a packet*/
+ sn->rs_ratesuccess[final_ndx]++;
+
+- mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT) && ENABLE_MRR;
++ use_mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT) && ENABLE_MRR;
+
+- if (!mrr) {
++ if (!use_mrr) {
+ if ((0 <= final_ndx) && (final_ndx < sn->num_rates)) {
+ sn->rs_rateattempts[final_ndx] += tries; /* only one rate was used */
+ }
+@@ -388,47 +386,36 @@ ath_rate_tx_complete(struct ath_softc *s
+ /* Now, query the hal/hardware to find out the contents of the multirate retry chain.
+ * If we have it set to 6,3,2,2, this call will always return 6,3,2,2. For some packets, we can
+ * get a mrr of 0, -1, -1, -1, which indicates there is no chain installed for that packet */
+- rate0 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate0)].ieeerate;
+- tries0 = MS(ds->ds_ctl2, AR_XmitDataTries0);
+- ndx0 = rate_to_ndx(sn, rate0);
++ ndx0 = rate_to_ndx(sn, mrr->rate0);
++ ndx1 = rate_to_ndx(sn, mrr->rate1);
++ ndx2 = rate_to_ndx(sn, mrr->rate2);
++ ndx3 = rate_to_ndx(sn, mrr->rate3);
+
+- rate1 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate1)].ieeerate;
+- tries1 = MS(ds->ds_ctl2, AR_XmitDataTries1);
+- ndx1 = rate_to_ndx(sn, rate1);
+-
+- rate2 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate2)].ieeerate;
+- tries2 = MS(ds->ds_ctl2, AR_XmitDataTries2);
+- ndx2 = rate_to_ndx(sn, rate2);
+-
+- rate3 = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate3)].ieeerate;
+- tries3 = MS(ds->ds_ctl2, AR_XmitDataTries3);
+- ndx3 = rate_to_ndx(sn, rate3);
+-
+- sn->rs_rateattempts[ndx0] += MIN(tries, tries0);
+- if (tries <= tries0)
++ sn->rs_rateattempts[ndx0] += MIN(tries, mrr->retries0);
++ if (tries <= mrr->retries0)
+ return;
+
+- if (tries1 < 0)
++ if (mrr->retries1 < 0)
+ return;
+- tries = tries - tries0;
+- sn->rs_rateattempts[ndx1] += MIN(tries, tries1);
+- if (tries <= tries1)
++ tries = tries - mrr->retries0;
++ sn->rs_rateattempts[ndx1] += MIN(tries, mrr->retries1);
++ if (tries <= mrr->retries1)
+ return;
+
+ if (bf->rcflags)
+ sn->sample_count++;
+
+- if (tries2 < 0)
++ if (mrr->retries2 < 0)
+ return;
+- tries = tries - tries1;
+- sn->rs_rateattempts[ndx2] += MIN(tries, tries2);
+- if (tries <= tries2)
++ tries = tries - mrr->retries1;
++ sn->rs_rateattempts[ndx2] += MIN(tries, mrr->retries2);
++ if (tries <= mrr->retries2)
+ return;
+
+- if (tries3 < 0)
++ if (mrr->retries3 < 0)
+ return;
+- tries = tries - tries2;
+- sn->rs_rateattempts[ndx3] += MIN(tries, tries3);
++ tries = tries - mrr->retries2;
++ sn->rs_rateattempts[ndx3] += MIN(tries, mrr->retries3);
+ }
+
+ static void
+--- a/ath_rate/minstrel/minstrel.h
++++ b/ath_rate/minstrel/minstrel.h
+@@ -172,36 +172,6 @@ struct minstrel_node {
+
+ #define ATH_NODE_MINSTREL(an) ((struct minstrel_node *)&an[1])
+
+-/*
+- * Definitions for pulling the rate and trie counts from
+- * a 5212 h/w descriptor. These Don't belong here; the
+- * driver should record this information so the rate control
+- * code doesn't go groveling around in the descriptor bits.
+- */
+-#define ds_ctl2 ds_hw[0]
+-#define ds_ctl3 ds_hw[1]
+-
+-/* TX ds_ctl3 */
+-#define AR_XmitDataTries0 0x000f0000 /* series 0 max attempts */
+-#define AR_XmitDataTries0_S 16
+-#define AR_XmitDataTries1 0x00f00000 /* series 1 max attempts */
+-#define AR_XmitDataTries1_S 20
+-#define AR_XmitDataTries2 0x0f000000 /* series 2 max attempts */
+-#define AR_XmitDataTries2_S 24
+-#define AR_XmitDataTries3 0xf0000000 /* series 3 max attempts */
+-#define AR_XmitDataTries3_S 28
+-
+-/* TX ds_ctl3 */
+-#define AR_XmitRate0 0x0000001f /* series 0 tx rate */
+-#define AR_XmitRate0_S 0
+-#define AR_XmitRate1 0x000003e0 /* series 1 tx rate */
+-#define AR_XmitRate1_S 5
+-#define AR_XmitRate2 0x00007c00 /* series 2 tx rate */
+-#define AR_XmitRate2_S 10
+-#define AR_XmitRate3 0x000f8000 /* series 3 tx rate */
+-#define AR_XmitRate3_S 15
+-
+-#define MS(_v, _f) (((_v) & (_f)) >> _f##_S)
+ #endif /* _DEV_ATH_RATE_MINSTEL_H */
+
+ /* The comment below is magic for those who use emacs to edit this file. */
+--- a/ath_rate/onoe/onoe.c
++++ b/ath_rate/onoe/onoe.c
+@@ -137,7 +137,8 @@ ath_rate_get_mrr(struct ath_softc *sc, s
+
+ static void
+ ath_rate_tx_complete(struct ath_softc *sc,
+- struct ath_node *an, const struct ath_buf *bf)
++ struct ath_node *an, const struct ath_buf *bf,
++ const struct ieee80211_mrr *mrr)
+ {
+ struct onoe_node *on = ATH_NODE_ONOE(an);
+ const struct ath_tx_status *ts = &bf->bf_dsstatus.ds_txstat;
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -178,10 +178,6 @@ static __inline int best_rate_ndx(struct
+ !sn->stats[size_bin][x].packets_acked))
+ continue;
+
+- /* 9 megabits never works better than 12 */
+- if (sn->rates[x].rate == 18)
+- continue;
+-
+ /* don't use a bit-rate that has been failing */
+ if (sn->stats[size_bin][x].successive_failures > 3)
+ continue;
+@@ -234,10 +230,6 @@ pick_sample_ndx(struct sample_node *sn,
+ if (sn->rates[ndx].rate > 22 && ndx > current_ndx + 2)
+ continue;
+
+- /* 9 megabits never works better than 12 */
+- if (sn->rates[ndx].rate == 18)
+- continue;
+-
+ /* if we're using 11 megabits, only sample up to 12 megabits
+ */
+ if (sn->rates[current_ndx].rate == 22 && ndx > current_ndx + 1)
+@@ -531,7 +523,8 @@ update_stats(struct ath_softc *sc, struc
+
+ static void
+ ath_rate_tx_complete(struct ath_softc *sc,
+- struct ath_node *an, const struct ath_buf *bf)
++ struct ath_node *an, const struct ath_buf *bf,
++ const struct ieee80211_mrr *mrr)
+ {
+ struct sample_node *sn = ATH_NODE_SAMPLE(an);
+ struct ieee80211com *ic = &sc->sc_ic;
+@@ -541,7 +534,7 @@ ath_rate_tx_complete(struct ath_softc *s
+ unsigned int short_tries;
+ unsigned int long_tries;
+ unsigned int frame_size;
+- unsigned int mrr;
++ unsigned int use_mrr;
+
+ final_rate = sc->sc_hwmap[ts->ts_rate &~ HAL_TXSTAT_ALTRATE].ieeerate;
+ short_tries = ts->ts_shortretry + 1;
+@@ -557,7 +550,7 @@ ath_rate_tx_complete(struct ath_softc *s
+ return;
+ }
+
+- mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT) && ENABLE_MRR;
++ use_mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT) && ENABLE_MRR;
+
+
+ if (sc->sc_mrretry && ts->ts_status) {
+@@ -566,22 +559,15 @@ ath_rate_tx_complete(struct ath_softc *s
+ dev_info,
+ MAC_ADDR(an->an_node.ni_macaddr),
+ bin_to_size(size_to_bin(frame_size)),
+- sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate0)].ieeerate,
+- MS(ds->ds_ctl2, AR_XmitDataTries0),
+- sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate1)].ieeerate,
+- MS(ds->ds_ctl2, AR_XmitDataTries1),
+- sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate2)].ieeerate,
+- MS(ds->ds_ctl2, AR_XmitDataTries2),
+- sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate3)].ieeerate,
+- MS(ds->ds_ctl2, AR_XmitDataTries3),
++ mrr->rate0,
++ mrr->rate1,
++ mrr->rate2,
++ mrr->rate3,
+ ts->ts_status ? "FAIL" : "OK",
+ short_tries, long_tries);
+ }
+
+- mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT) && ENABLE_MRR;
+-
+-
+- if (!mrr || !(ts->ts_rate & HAL_TXSTAT_ALTRATE)) {
++ if (!use_mrr || !(ts->ts_rate & HAL_TXSTAT_ALTRATE)) {
+ /* only one rate was used */
+ int ndx = rate_to_ndx(sn, final_rate);
+ if ((ndx >= 0) && (ndx < sn->num_rates)) {
+@@ -593,7 +579,6 @@ ath_rate_tx_complete(struct ath_softc *s
+ short_tries, long_tries, ts->ts_status);
+ }
+ } else {
+- unsigned int rate[4], tries[4];
+ int ndx[4];
+ int finalTSIdx = ts->ts_finaltsi;
+
+@@ -601,21 +586,10 @@ ath_rate_tx_complete(struct ath_softc *s
+ * Process intermediate rates that failed.
+ */
+
+- rate[0] = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate0)].ieeerate;
+- tries[0] = MS(ds->ds_ctl2, AR_XmitDataTries0);
+- ndx[0] = rate_to_ndx(sn, rate[0]);
+-
+- rate[1] = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate1)].ieeerate;
+- tries[1] = MS(ds->ds_ctl2, AR_XmitDataTries1);
+- ndx[1] = rate_to_ndx(sn, rate[1]);
+-
+- rate[2] = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate2)].ieeerate;
+- tries[2] = MS(ds->ds_ctl2, AR_XmitDataTries2);
+- ndx[2] = rate_to_ndx(sn, rate[2]);
+-
+- rate[3] = sc->sc_hwmap[MS(ds->ds_ctl3, AR_XmitRate3)].ieeerate;
+- tries[3] = MS(ds->ds_ctl2, AR_XmitDataTries3);
+- ndx[3] = rate_to_ndx(sn, rate[3]);
++ ndx[0] = rate_to_ndx(sn, mrr->rate0);
++ ndx[1] = rate_to_ndx(sn, mrr->rate1);
++ ndx[2] = rate_to_ndx(sn, mrr->rate2);
++ ndx[3] = rate_to_ndx(sn, mrr->rate3);
+
+ #if 0
+ DPRINTF(sc, ATH_DEBUG_RATE, "%s: " MAC_FMT " size %u finaltsidx %u tries %u status %u rate/try %u/%u %u/%u %u/%u %u/%u\n",
+@@ -636,43 +610,43 @@ ath_rate_tx_complete(struct ath_softc *s
+ * sample higher rates 1 try at a time doing so
+ * may unfairly penalize them.
+ */
+- if (tries[0] && ndx[0] >= 0) {
++ if (mrr->retries0 && ndx[0] >= 0) {
+ update_stats(sc, an, frame_size,
+- ndx[0], tries[0],
+- ndx[1], tries[1],
+- ndx[2], tries[2],
+- ndx[3], tries[3],
++ ndx[0], mrr->retries0,
++ ndx[1], mrr->retries1,
++ ndx[2], mrr->retries2,
++ ndx[3], mrr->retries3,
+ short_tries, long_tries,
+- long_tries > tries[0]);
+- long_tries -= tries[0];
++ long_tries > mrr->retries0);
++ long_tries -= mrr->retries0;
+
+ }
+
+- if (tries[1] && ndx[1] >= 0 && finalTSIdx > 0) {
++ if (mrr->retries1 && ndx[1] >= 0 && finalTSIdx > 0) {
+ update_stats(sc, an, frame_size,
+- ndx[1], tries[1],
+- ndx[2], tries[2],
+- ndx[3], tries[3],
++ ndx[1], mrr->retries1,
++ ndx[2], mrr->retries2,
++ ndx[3], mrr->retries3,
+ 0, 0,
+ short_tries, long_tries,
+ ts->ts_status);
+- long_tries -= tries[1];
++ long_tries -= mrr->retries1;
+ }
+
+- if (tries[2] && ndx[2] >= 0 && finalTSIdx > 1) {
++ if (mrr->retries2 && ndx[2] >= 0 && finalTSIdx > 1) {
+ update_stats(sc, an, frame_size,
+- ndx[2], tries[2],
+- ndx[3], tries[3],
++ ndx[2], mrr->retries2,
++ ndx[3], mrr->retries3,
+ 0, 0,
+ 0, 0,
+ short_tries, long_tries,
+ ts->ts_status);
+- long_tries -= tries[2];
++ long_tries -= mrr->retries2;
+ }
+
+- if (tries[3] && ndx[3] >= 0 && finalTSIdx > 2) {
++ if (mrr->retries3 && ndx[3] >= 0 && finalTSIdx > 2) {
+ update_stats(sc, an, frame_size,
+- ndx[3], tries[3],
++ ndx[3], mrr->retries3,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+--- a/ath_rate/sample/sample.h
++++ b/ath_rate/sample/sample.h
+@@ -98,35 +98,4 @@ struct sample_node {
+ };
+ #define ATH_NODE_SAMPLE(an) ((struct sample_node *)&an[1])
+
+-/*
+- * Definitions for pulling the rate and trie counts from
+- * a 5212 h/w descriptor. These Don't belong here; the
+- * driver should record this information so the rate control
+- * code doesn't go groveling around in the descriptor bits.
+- */
+-#define ds_ctl2 ds_hw[0]
+-#define ds_ctl3 ds_hw[1]
+-
+-/* TX ds_ctl3 */
+-#define AR_XmitDataTries0 0x000f0000 /* series 0 max attempts */
+-#define AR_XmitDataTries0_S 16
+-#define AR_XmitDataTries1 0x00f00000 /* series 1 max attempts */
+-#define AR_XmitDataTries1_S 20
+-#define AR_XmitDataTries2 0x0f000000 /* series 2 max attempts */
+-#define AR_XmitDataTries2_S 24
+-#define AR_XmitDataTries3 0xf0000000 /* series 3 max attempts */
+-#define AR_XmitDataTries3_S 28
+-
+-/* TX ds_ctl3 */
+-#define AR_XmitRate0 0x0000001f /* series 0 tx rate */
+-#define AR_XmitRate0_S 0
+-#define AR_XmitRate1 0x000003e0 /* series 1 tx rate */
+-#define AR_XmitRate1_S 5
+-#define AR_XmitRate2 0x00007c00 /* series 2 tx rate */
+-#define AR_XmitRate2_S 10
+-#define AR_XmitRate3 0x000f8000 /* series 3 tx rate */
+-#define AR_XmitRate3_S 15
+-
+-#define MS(_v, _f) (((_v) & (_f)) >> _f##_S)
+-
+ #endif /* _DEV_ATH_RATE_SAMPLE_H */
diff --git a/package/madwifi/patches/426-header_len.patch b/package/madwifi/patches/426-header_len.patch
new file mode 100644
index 000000000..acfbc18c6
--- /dev/null
+++ b/package/madwifi/patches/426-header_len.patch
@@ -0,0 +1,12 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -888,9 +888,6 @@ ath_attach(u_int16_t devid, struct net_d
+ IEEE80211_ADDR_LEN +
+ IEEE80211_WEP_IVLEN +
+ IEEE80211_WEP_KIDLEN;
+-#ifdef ATH_SUPERG_FF
+- dev->hard_header_len += ATH_FF_MAX_HDR;
+-#endif
+ #endif
+ dev->type = ARPHRD_IEEE80211;
+
diff --git a/package/madwifi/patches/427-ignore_eeprom_ff.patch b/package/madwifi/patches/427-ignore_eeprom_ff.patch
new file mode 100644
index 000000000..eead70c99
--- /dev/null
+++ b/package/madwifi/patches/427-ignore_eeprom_ff.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -915,7 +915,7 @@ ath_attach(u_int16_t devid, struct net_d
+ ic->ic_ath_cap = 0;
+ sc->sc_fftxqmin = ATH_FF_TXQMIN;
+ #ifdef ATH_SUPERG_FF
+- ic->ic_ath_cap |= (ath_hal_fastframesupported(ah) ?
++ ic->ic_ath_cap |= (ah->ah_macType >= 5212 ?
+ IEEE80211_ATHC_FF : 0);
+ #endif
+ ic->ic_ath_cap |= (ath_hal_burstsupported(ah) ?
diff --git a/package/madwifi/patches/430-use_netdev_priv.patch b/package/madwifi/patches/430-use_netdev_priv.patch
new file mode 100644
index 000000000..3f65424a5
--- /dev/null
+++ b/package/madwifi/patches/430-use_netdev_priv.patch
@@ -0,0 +1,1936 @@
+--- a/ath/ath_wprobe.c
++++ b/ath/ath_wprobe.c
+@@ -119,7 +119,7 @@ ath_wprobe_sync(struct wprobe_iface *dev
+ struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif);
+ struct ieee80211vap *vap = &avp->av_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ struct ath_hal *ah = sc->sc_ah;
+ u32 cc, busy, rx, tx;
+ s16 noise;
+@@ -192,7 +192,7 @@ ath_lookup_rateval(struct ieee80211_node
+ {
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ const HAL_RATE_TABLE *rt = sc->sc_currates;
+
+ if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap)))
+--- a/ath/if_ath_ahb.c
++++ b/ath/if_ath_ahb.c
+@@ -203,7 +203,7 @@ static int ahb_wmac_probe(struct platfor
+ if (!dev)
+ return -ENOMEM;
+
+- sc = dev->priv;
++ sc = netdev_priv(dev);
+ sc->aps_sc.sc_dev = dev;
+
+ dev->irq = platform_get_irq(pdev, 0);
+@@ -300,7 +300,7 @@ init_ath_wmac(u_int16_t devid, u_int16_t
+ printk(KERN_ERR "%s: no memory for device state\n", dev_info);
+ goto bad2;
+ }
+- sc = dev->priv;
++ sc = netdev_priv(dev);
+ sc->aps_sc.sc_dev = dev;
+
+ /*
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -569,7 +569,7 @@ static inline int rate_factor(int mode)
+ int
+ ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap;
+ struct ath_hal *ah;
+@@ -1206,7 +1206,7 @@ bad:
+ int
+ ath_detach(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+
+ HAL_INT tmp;
+@@ -1266,7 +1266,7 @@ static struct ieee80211vap *
+ ath_vap_create(struct ieee80211com *ic, const char *name,
+ int opmode, int flags, struct net_device *mdev, struct ieee80211vap *master)
+ {
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct net_device *dev;
+ struct ath_vap *avp;
+@@ -1344,7 +1344,7 @@ ath_vap_create(struct ieee80211com *ic,
+ return NULL;
+ }
+
+- avp = dev->priv;
++ avp = netdev_priv(dev);
+ ieee80211_vap_setup(ic, dev, name, opmode, flags, master);
+ /* override with driver methods */
+ vap = &avp->av_vap;
+@@ -1571,7 +1571,7 @@ static void
+ ath_vap_delete(struct ieee80211vap *vap)
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_vap *avp = ATH_VAP(vap);
+ int decrease = 1;
+@@ -1673,7 +1673,7 @@ void
+ ath_suspend(struct net_device *dev)
+ {
+ #ifdef AR_DEBUG
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ #endif
+
+ DPRINTF(sc, ATH_DEBUG_ANY, "flags=%x\n", dev->flags);
+@@ -1684,7 +1684,7 @@ void
+ ath_resume(struct net_device *dev)
+ {
+ #ifdef AR_DEBUG
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ #endif
+
+ DPRINTF(sc, ATH_DEBUG_ANY, "flags=%x\n", dev->flags);
+@@ -2248,7 +2248,7 @@ ath_intr(int irq, void *dev_id, struct p
+ #endif
+ {
+ struct net_device *dev = dev_id;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ u_int64_t hw_tsf = 0;
+ HAL_INT status;
+@@ -2469,7 +2469,7 @@ static void
+ ath_fatal_tasklet(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ EPRINTF(sc, "Hardware error; resetting.\n");
+ ath_reset(dev);
+@@ -2479,7 +2479,7 @@ static void
+ ath_rxorn_tasklet(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ EPRINTF(sc, "Receive FIFO overrun; resetting.\n");
+ ath_reset(dev);
+@@ -2489,7 +2489,7 @@ static void
+ ath_bmiss_tasklet(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ if (time_before(jiffies, sc->sc_ic.ic_bmiss_guard)) {
+ /* Beacon miss interrupt occured too short after last beacon
+@@ -2568,7 +2568,7 @@ done:
+ static int
+ ath_init(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+ HAL_STATUS status;
+@@ -2693,7 +2693,7 @@ done:
+ static int
+ ath_stop_locked(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+
+@@ -2778,7 +2778,7 @@ static void ath_set_beacon_cal(struct at
+ static int
+ ath_stop(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ int error;
+
+ ATH_LOCK(sc);
+@@ -2998,7 +2998,7 @@ ath_fetch_idle_time(struct ath_softc *sc
+ static int
+ ath_reset(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211_channel *c;
+@@ -3164,7 +3164,7 @@ dot11_to_ratecode(struct ath_softc *sc,
+ static int
+ ath_tx_startraw(struct net_device *dev, struct ath_buf *bf, struct sk_buff *skb)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211_phy_params *ph = (struct ieee80211_phy_params *)
+ (SKB_CB(skb) + 1); /* NB: SKB_CB casts to CB struct*. */
+@@ -3477,7 +3477,7 @@ _take_txbuf(struct ath_softc *sc, int fo
+ static int
+ ath_hardstart(struct sk_buff *skb, struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211_node *ni = NULL;
+ struct ath_buf *bf = NULL;
+ ath_bufhead bf_head;
+@@ -3792,7 +3792,7 @@ static int
+ ath_mgtstart(struct ieee80211com *ic, struct sk_buff *skb)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_buf *bf = NULL;
+ int error;
+
+@@ -4151,7 +4151,7 @@ static ieee80211_keyix_t
+ ath_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k)
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ /*
+ * Group key allocation must be handled specially for
+@@ -4216,7 +4216,7 @@ ath_key_delete(struct ieee80211vap *vap,
+ struct ieee80211_node *ninfo)
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211_node *ni = NULL;
+ const struct ieee80211_cipher *cip = k->wk_cipher;
+@@ -4292,14 +4292,14 @@ ath_key_set(struct ieee80211vap *vap, co
+ const u_int8_t mac[IEEE80211_ADDR_LEN])
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ return ath_keyset(sc, k, mac, vap->iv_bss);
+ }
+
+ static void ath_poll_disable(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ /*
+ * XXX Using in_softirq is not right since we might
+@@ -4317,7 +4317,7 @@ static void ath_poll_disable(struct net_
+
+ static void ath_poll_enable(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ /* NB: see above */
+ if (!in_softirq()) {
+@@ -4343,7 +4343,7 @@ ath_key_update_begin(struct ieee80211vap
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
+ #ifdef AR_DEBUG
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ #endif
+
+ DPRINTF(sc, ATH_DEBUG_KEYCACHE, "Begin\n");
+@@ -4361,7 +4361,7 @@ ath_key_update_end(struct ieee80211vap *
+ {
+ struct net_device *dev = vap->iv_ic->ic_dev;
+ #ifdef AR_DEBUG
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ #endif
+
+ DPRINTF(sc, ATH_DEBUG_KEYCACHE, "End\n");
+@@ -4454,7 +4454,7 @@ ath_merge_mcast(struct ath_softc *sc, u_
+ static void
+ ath_mode_init(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rfilt, mfilt[2];
+
+@@ -4540,7 +4540,7 @@ ath_set_timing(struct ath_softc *sc)
+ static void
+ ath_updateslot(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ /*
+@@ -4570,7 +4570,7 @@ ath_beacon_dturbo_config(struct ieee8021
+ (vap->iv_bss && (vap->iv_bss->ni_ath_flags & (IEEE80211_ATHC_TURBOP)) == \
+ (IEEE80211_ATHC_TURBOP))
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP && IS_CAPABLE(vap)) {
+
+@@ -4618,7 +4618,7 @@ static void
+ ath_beacon_dturbo_update(struct ieee80211vap *vap, int *needmark, u_int8_t dtim)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ u_int32_t bss_traffic;
+
+ if (sc->sc_ignore_ar) {
+@@ -4759,7 +4759,7 @@ static void
+ ath_turbo_switch_mode(unsigned long data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ unsigned int newflags;
+
+@@ -5438,7 +5438,7 @@ static void
+ ath_bstuck_tasklet(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ /*
+ * XXX:if the bmisscount is cleared while the
+ * tasklet execution is pending, the following
+@@ -5891,7 +5891,7 @@ ath_node_alloc_debug(struct ieee80211vap
+ ath_node_alloc(struct ieee80211vap *vap)
+ #endif
+ {
+- struct ath_softc *sc = vap->iv_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
+ const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space;
+ struct ath_node *an = kmalloc(space, GFP_ATOMIC);
+ if (an != NULL) {
+@@ -5927,7 +5927,7 @@ ath_node_cleanup(struct ieee80211_node *
+ #endif
+ {
+ struct ieee80211com *ic = ni->ni_ic;
+- struct ath_softc *sc = ni->ni_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ni->ni_ic->ic_dev);
+ struct ath_node *an = ATH_NODE(ni);
+ struct ath_buf *bf;
+
+@@ -5985,7 +5985,7 @@ ath_node_free_debug(struct ieee80211_nod
+ ath_node_free(struct ieee80211_node *ni)
+ #endif
+ {
+- struct ath_softc *sc = ni->ni_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ni->ni_ic->ic_dev);
+
+ #ifdef IEEE80211_DEBUG_REFCNT
+ sc->sc_node_free_debug(ni, func, line);
+@@ -6033,7 +6033,7 @@ ath_node_move_data(const struct ieee8021
+ #ifdef NOT_YET
+ struct ath_txq *txq = NULL;
+ struct ieee80211com *ic = ni->ni_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ struct ath_buf *bf, *prev, *bf_tmp, *bf_tmp1;
+ struct ath_hal *ah = sc->sc_ah;
+ struct sk_buff *skb = NULL;
+@@ -6553,7 +6553,7 @@ static void
+ ath_capture(struct net_device *dev, const struct ath_buf *bf,
+ struct sk_buff *skb, u_int64_t tsf, unsigned int tx)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct sk_buff *tskb = NULL;
+
+@@ -6613,7 +6613,7 @@ static void
+ ath_recv_mgmt(struct ieee80211vap * vap, struct ieee80211_node *ni_or_null,
+ struct sk_buff *skb, int subtype, int rssi, u_int64_t rtsf)
+ {
+- struct ath_softc *sc = vap->iv_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
+ #ifdef AR_DEBUG
+ struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
+ #endif
+@@ -6780,7 +6780,7 @@ ath_rx_poll(struct net_device *dev, int
+ struct net_device *dev = sc->sc_dev;
+ int rx_limit = budget;
+ #else
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ int rx_limit = min(dev->quota, *budget);
+ #endif
+ struct ath_buf *bf;
+@@ -7305,7 +7305,7 @@ static void ath_grppoll_start(struct iee
+ struct sk_buff *skb = NULL;
+ struct ath_buf *bf, *head = NULL;
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ struct ath_hal *ah = sc->sc_ah;
+ u_int8_t rate;
+ unsigned int ctsrate = 0, ctsduration = 0;
+@@ -7523,7 +7523,7 @@ static void ath_grppoll_start(struct iee
+ static void ath_grppoll_stop(struct ieee80211vap *vap)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_txq *txq = &sc->sc_grpplq;
+ struct ath_buf *bf;
+@@ -7735,7 +7735,7 @@ ath_txq_update(struct ath_softc *sc, str
+ static int
+ ath_wme_update(struct ieee80211com *ic)
+ {
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+
+ if (sc->sc_uapsdq)
+ ath_txq_update(sc, sc->sc_uapsdq, WME_AC_VO);
+@@ -7754,7 +7754,7 @@ ath_uapsd_flush(struct ieee80211_node *n
+ {
+ struct ath_node *an = ATH_NODE(ni);
+ struct ath_buf *bf;
+- struct ath_softc *sc = ni->ni_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ni->ni_ic->ic_dev);
+ struct ath_txq *txq;
+
+ ATH_NODE_UAPSD_LOCK_IRQ(an);
+@@ -7945,7 +7945,7 @@ ath_tx_start(struct net_device *dev, str
+ struct ath_buf *bf, struct sk_buff *skb, int nextfraglen)
+ {
+ #define MIN(a,b) ((a) < (b) ? (a) : (b))
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_hal *ah = sc->sc_ah;
+@@ -8854,7 +8854,7 @@ static void
+ ath_tx_tasklet_q0(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ unsigned long flags;
+
+ process_tx_again:
+@@ -8885,7 +8885,7 @@ static void
+ ath_tx_tasklet_q0123(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ unsigned long flags;
+
+ process_tx_again:
+@@ -8930,7 +8930,7 @@ static void
+ ath_tx_tasklet(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ unsigned int i;
+ unsigned long flags;
+
+@@ -8958,7 +8958,7 @@ process_tx_again:
+ static void
+ ath_tx_timeout(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ if (ath_chan_unavail(sc))
+ return;
+@@ -9366,7 +9366,7 @@ static void
+ ath_calibrate(unsigned long arg)
+ {
+ struct net_device *dev = (struct net_device *)arg;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211com *ic = &sc->sc_ic;
+ /* u_int32_t nchans; */
+@@ -9441,7 +9441,7 @@ static void
+ ath_scan_start(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rfilt;
+
+@@ -9461,7 +9461,7 @@ static void
+ ath_scan_end(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rfilt;
+
+@@ -9479,7 +9479,7 @@ static void
+ ath_set_channel(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ (void) ath_chan_set(sc, ic->ic_curchan);
+ ic->ic_channoise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
+@@ -9496,7 +9496,7 @@ ath_set_channel(struct ieee80211com *ic)
+ static void
+ ath_set_coverageclass(struct ieee80211com *ic)
+ {
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+
+ sc->sc_coverage = ic->ic_coverageclass * 3;
+ ath_set_timing(sc);
+@@ -9507,7 +9507,7 @@ ath_set_coverageclass(struct ieee80211co
+ static u_int
+ ath_mhz2ieee(struct ieee80211com *ic, u_int freq, u_int flags)
+ {
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+
+ return (ath_hal_mhz2ieee(sc->sc_ah, freq, flags));
+ }
+@@ -9522,7 +9522,7 @@ ath_newstate(struct ieee80211vap *vap, e
+ struct ath_vap *avp = ATH_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211_node *ni, *wds_ni;
+ unsigned int i;
+@@ -9962,7 +9962,7 @@ ath_setup_comp(struct ieee80211_node *ni
+ {
+ #define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
+ struct ieee80211vap *vap = ni->ni_vap;
+- struct ath_softc *sc = vap->iv_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
+ struct ath_node *an = ATH_NODE(ni);
+ ieee80211_keyix_t keyix;
+
+@@ -10016,7 +10016,7 @@ static void
+ ath_setup_stationkey(struct ieee80211_node *ni)
+ {
+ struct ieee80211vap *vap = ni->ni_vap;
+- struct ath_softc *sc = vap->iv_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
+ ieee80211_keyix_t keyix;
+
+ keyix = ath_key_alloc(vap, &ni->ni_ucastkey);
+@@ -10177,7 +10177,7 @@ ath_newassoc(struct ieee80211_node *ni,
+ {
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+
+ sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
+ ath_wprobe_node_join(ni->ni_vap, ni);
+@@ -10208,7 +10208,7 @@ ath_newassoc(struct ieee80211_node *ni,
+ static int
+ ath_getchannels(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+ HAL_CHANNEL *chans;
+@@ -10485,7 +10485,7 @@ ath_update_txpow(struct ath_softc *sc)
+ static int
+ ath_xr_rate_setup(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211com *ic = &sc->sc_ic;
+ const HAL_RATE_TABLE *rt;
+@@ -10516,7 +10516,7 @@ ath_xr_rate_setup(struct net_device *dev
+ static int
+ ath_rate_setup(struct net_device *dev, u_int mode)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211com *ic = &sc->sc_ic;
+ const HAL_RATE_TABLE *rt;
+@@ -10763,7 +10763,7 @@ ath_printtxbuf(const struct ath_buf *bf,
+ {
+ const struct ath_tx_status *ts = &bf->bf_dsstatus.ds_txstat;
+ const struct ath_desc *ds = bf->bf_desc;
+- struct ath_softc *sc = bf->bf_node->ni_ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(bf->bf_node->ni_ic->ic_dev);
+ u_int8_t status = done ? ts->ts_status : 0;
+
+ DPRINTF(sc, ATH_DEBUG_ANY,
+@@ -10790,7 +10790,7 @@ ath_printtxbuf(const struct ath_buf *bf,
+ static struct net_device_stats *
+ ath_getstats(struct net_device *dev)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct net_device_stats *stats = &sc->sc_devstats;
+
+ /* update according to private statistics */
+@@ -10813,7 +10813,7 @@ ath_getstats(struct net_device *dev)
+ static int
+ ath_set_mac_address(struct net_device *dev, void *addr)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+ struct sockaddr *mac = addr;
+@@ -10842,7 +10842,7 @@ ath_set_mac_address(struct net_device *d
+ static int
+ ath_change_mtu(struct net_device *dev, int mtu)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ int error = 0;
+
+ if (!(ATH_MIN_MTU < mtu && mtu <= ATH_MAX_MTU)) {
+@@ -10929,7 +10929,7 @@ bad:
+ static int
+ ath_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ int error;
+
+@@ -11810,7 +11810,7 @@ static void
+ ath_announce(struct net_device *dev)
+ {
+ #define HAL_MODE_DUALBAND (HAL_MODE_11A|HAL_MODE_11B)
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ u_int modes, cc;
+ static const int MLEN = 1024;
+@@ -11997,7 +11997,7 @@ static void
+ txcont_configure_radio(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+@@ -12271,7 +12271,7 @@ static void
+ txcont_queue_packet(struct ieee80211com *ic, struct ath_txq* txq)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_buf *bf = NULL;
+ struct sk_buff *skb = NULL;
+@@ -12404,7 +12404,7 @@ static void
+ txcont_on(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ if (IFF_RUNNING != (ic->ic_dev->flags & IFF_RUNNING)) {
+ EPRINTF(sc, "Cannot enable txcont when"
+@@ -12425,7 +12425,7 @@ static void
+ txcont_off(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ if (TAILQ_FIRST(&ic->ic_vaps)->iv_opmode != IEEE80211_M_WDS)
+ sc->sc_beacons = 1;
+@@ -12439,7 +12439,7 @@ static int
+ ath_get_dfs_testmode(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ return sc->sc_dfs_testmode;
+ }
+
+@@ -12466,7 +12466,7 @@ static void
+ ath_set_dfs_testmode(struct ieee80211com *ic, int value)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ sc->sc_dfs_testmode = !!value;
+ }
+
+@@ -12476,7 +12476,7 @@ static int
+ ath_get_txcont(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ return sc->sc_txcont;
+ }
+
+@@ -12494,7 +12494,7 @@ static void
+ ath_set_txcont_power(struct ieee80211com *ic, unsigned int txpower)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ int new_txcont_power = txpower > IEEE80211_TXPOWER_MAX ?
+ IEEE80211_TXPOWER_MAX : txpower;
+ if (sc->sc_txcont_power != new_txcont_power) {
+@@ -12512,7 +12512,7 @@ static int
+ ath_get_txcont_power(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ /* VERY conservative default */
+ return sc->sc_txcont_power ? sc->sc_txcont_power : 0;
+ }
+@@ -12522,7 +12522,7 @@ ath_get_txcont_power(struct ieee80211com
+ ath_set_txcont_rate(struct ieee80211com *ic, unsigned int new_rate)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ if (sc->sc_txcont_rate != new_rate) {
+ /* NOTE: This value is sanity checked and dropped down to
+ * closest rate in txcont_on. */
+@@ -12539,7 +12539,7 @@ ath_set_txcont_rate(struct ieee80211com
+ ath_get_txcont_rate(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ return sc->sc_txcont_rate ? sc->sc_txcont_rate : 0;
+ }
+
+@@ -12549,7 +12549,7 @@ static void
+ ath_set_dfs_cac_time(struct ieee80211com *ic, unsigned int time_s)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ sc->sc_dfs_cac_period = time_s;
+ }
+
+@@ -12559,7 +12559,7 @@ static unsigned int
+ ath_get_dfs_cac_time(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ return sc->sc_dfs_cac_period;
+ }
+
+@@ -12579,7 +12579,7 @@ static void
+ ath_set_dfs_excl_period(struct ieee80211com *ic, unsigned int time_s)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ sc->sc_dfs_excl_period = time_s;
+ }
+
+@@ -12588,7 +12588,7 @@ static unsigned int
+ ath_get_dfs_excl_period(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ return sc->sc_dfs_excl_period;
+ }
+
+@@ -12600,7 +12600,7 @@ static unsigned int
+ ath_test_radar(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ if ((ic->ic_flags & IEEE80211_F_DOTH) && (sc->sc_curchan.privFlags & CHANNEL_DFS))
+ ath_radar_detected(sc, "ath_test_radar from user space");
+ else
+@@ -12616,7 +12616,7 @@ static unsigned int
+ ath_dump_hal_map(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ ath_hal_dump_map(sc->sc_ah);
+ return 0;
+ }
+@@ -12724,7 +12724,7 @@ ath_rcv_dev_event(struct notifier_block
+ void *ptr)
+ {
+ struct net_device *dev = (struct net_device *)ptr;
+- struct ath_softc *sc = (struct ath_softc *)dev->priv;
++ struct ath_softc *sc = (struct ath_softc *)netdev_priv(dev);
+
+ if (!dev || !sc || dev->open != &ath_init)
+ return 0;
+@@ -13459,7 +13459,7 @@ static unsigned int
+ ath_read_register(struct ieee80211com *ic, unsigned int address,
+ unsigned int* value)
+ {
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ if (address >= MAX_REGISTER_ADDRESS) {
+ IPRINTF(sc, "Illegal Atheros register access "
+ "attempted: 0x%04x >= 0x%04x\n",
+@@ -13489,7 +13489,7 @@ static unsigned int
+ ath_write_register(struct ieee80211com *ic, unsigned int address,
+ unsigned int value)
+ {
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ if (address >= MAX_REGISTER_ADDRESS) {
+ IPRINTF(sc, "Illegal Atheros register access "
+ "attempted: 0x%04x >= 0x%04x\n",
+@@ -13517,7 +13517,7 @@ static void
+ ath_registers_dump(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ ath_ar5212_registers_dump(sc);
+ }
+ #endif /* #ifdef ATH_REVERSE_ENGINEERING */
+@@ -13529,7 +13529,7 @@ static void
+ ath_registers_mark(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ ath_ar5212_registers_mark(sc);
+ }
+ #endif /* #ifdef ATH_REVERSE_ENGINEERING */
+@@ -13541,7 +13541,7 @@ static void
+ ath_registers_dump_delta(struct ieee80211com *ic)
+ {
+ struct net_device *dev = ic->ic_dev;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ ath_ar5212_registers_dump_delta(sc);
+ }
+ #endif /* #ifdef ATH_REVERSE_ENGINEERING */
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -226,7 +226,7 @@ ath_pci_probe(struct pci_dev *pdev, cons
+ printk(KERN_ERR "%s: no memory for device state\n", dev_info);
+ goto bad2;
+ }
+- sc = dev->priv;
++ sc = netdev_priv(dev);
+ sc->aps_sc.sc_dev = dev;
+ sc->aps_sc.sc_iobase = mem;
+
+@@ -309,7 +309,7 @@ static void
+ ath_pci_remove(struct pci_dev *pdev)
+ {
+ struct net_device *dev = pci_get_drvdata(pdev);
+- struct ath_pci_softc *sc = dev->priv;
++ struct ath_pci_softc *sc = netdev_priv(dev);
+
+ ath_detach(dev);
+ if (dev->irq)
+@@ -327,7 +327,7 @@ ath_pci_suspend(struct pci_dev *pdev, pm
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ ath_suspend(dev);
+- PCI_SAVE_STATE(pdev, ((struct ath_pci_softc *)dev->priv)->aps_pmstate);
++ PCI_SAVE_STATE(pdev, ((struct ath_pci_softc *)netdev_priv(dev))->aps_pmstate);
+ pci_disable_device(pdev);
+ return pci_set_power_state(pdev, PCI_D3hot);
+ }
+@@ -344,7 +344,7 @@ ath_pci_resume(struct pci_dev *pdev)
+ return err;
+
+ /* XXX - Should this return nonzero on fail? */
+- PCI_RESTORE_STATE(pdev, ((struct ath_pci_softc *)dev->priv)->aps_pmstate);
++ PCI_RESTORE_STATE(pdev, ((struct ath_pci_softc *)netdev_priv(dev))->aps_pmstate);
+
+ err = pci_enable_device(pdev);
+ if (err)
+--- a/ath/if_ath_radar.c
++++ b/ath/if_ath_radar.c
+@@ -1533,7 +1533,7 @@ static void ath_rp_clear(struct ath_soft
+ static void ath_rp_tasklet(TQUEUE_ARG data)
+ {
+ struct net_device *dev = (struct net_device *) data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+
+ if (sc->sc_rp_analyse != NULL)
+ sc->sc_rp_analyse(sc);
+--- a/ath_rate/amrr/amrr.c
++++ b/ath_rate/amrr/amrr.c
+@@ -298,7 +298,7 @@ ath_rate_ctl_start(struct ath_softc *sc,
+ static void
+ ath_rate_cb(void *arg, struct ieee80211_node *ni)
+ {
+- ath_rate_update(ni->ni_ic->ic_dev->priv, ni, (long) arg);
++ ath_rate_update(netdev_priv(ni->ni_ic->ic_dev), ni, (long) arg);
+ }
+
+ /*
+@@ -308,7 +308,7 @@ static void
+ ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ struct amrr_softc *asc = (struct amrr_softc *) sc->sc_rc;
+ struct ieee80211_node *ni;
+
+@@ -420,7 +420,7 @@ static void
+ ath_ratectl(unsigned long data)
+ {
+ struct net_device *dev = (struct net_device *)data;
+- struct ath_softc *sc = dev->priv;
++ struct ath_softc *sc = netdev_priv(dev);
+ struct amrr_softc *asc = (struct amrr_softc *)sc->sc_rc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ int interval;
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -622,7 +622,7 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ static void
+ ath_rate_cb(void *arg, struct ieee80211_node *ni)
+ {
+- ath_rate_ctl_reset(ni->ni_ic->ic_dev->priv, ni);
++ ath_rate_ctl_reset(netdev_priv(ni->ni_ic->ic_dev), ni);
+ }
+
+ /* Reset the rate control state for each 802.11 state transition. */
+@@ -636,7 +636,7 @@ ath_rate_newstate(struct ieee80211vap *v
+ /* Sync rates for associated stations and neighbors. */
+ ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, NULL);
+ }
+- ath_rate_newassoc(ic->ic_dev->priv, ATH_NODE(vap->iv_bss), 1);
++ ath_rate_newassoc(netdev_priv(ic->ic_dev), ATH_NODE(vap->iv_bss), 1);
+ }
+ }
+
+@@ -822,7 +822,7 @@ ath_proc_read_nodes(struct ieee80211vap
+ unsigned int x = 0;
+ unsigned int this_tp, this_prob, this_eprob;
+ #ifdef AR_DEBUG
+- struct ath_softc *sc = vap->iv_ic->ic_dev->priv;;
++ struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
+ #endif
+
+ IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+--- a/ath_rate/onoe/onoe.c
++++ b/ath_rate/onoe/onoe.c
+@@ -281,7 +281,7 @@ ath_rate_ctl_start(struct ath_softc *sc,
+ static void
+ ath_rate_cb(void *arg, struct ieee80211_node *ni)
+ {
+- ath_rate_update(ni->ni_ic->ic_dev->priv, ni, (long) arg);
++ ath_rate_update(netdev_priv(ni->ni_ic->ic_dev), ni, (long) arg);
+ }
+
+ /*
+@@ -291,7 +291,7 @@ static void
+ ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
+- struct ath_softc *sc = ic->ic_dev->priv;
++ struct ath_softc *sc = netdev_priv(ic->ic_dev);
+ struct ieee80211_node *ni;
+
+ if (state == IEEE80211_S_INIT)
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -803,7 +803,7 @@ ath_rate_ctl_reset(struct ath_softc *sc,
+ static void
+ ath_rate_cb(void *arg, struct ieee80211_node *ni)
+ {
+- ath_rate_ctl_reset(ni->ni_ic->ic_dev->priv, ni);
++ ath_rate_ctl_reset(netdev_priv(ni->ni_ic->ic_dev), ni);
+ }
+
+ /*
+@@ -821,7 +821,7 @@ ath_rate_newstate(struct ieee80211vap *v
+ */
+ ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, NULL);
+ }
+- ath_rate_newassoc(ic->ic_dev->priv, ATH_NODE(vap->iv_bss), 1);
++ ath_rate_newassoc(netdev_priv(ic->ic_dev), ATH_NODE(vap->iv_bss), 1);
+ }
+ }
+
+--- a/include/compat.h
++++ b/include/compat.h
+@@ -162,6 +162,10 @@ static inline int timeval_compare(struct
+ #define IRQF_SHARED SA_SHIRQ
+ #endif
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27)
++#define netdev_priv(_netdev) ((_netdev)->priv)
++#endif
++
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+ #define skb_end_pointer(_skb) ((_skb)->end)
+ #define skb_tail_pointer(_skb) ((_skb)->tail)
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -458,7 +458,7 @@ ieee80211_vap_setup(struct ieee80211com
+ #define IEEE80211_C_OPMODE \
+ (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
+ IEEE80211_C_MONITOR)
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct net_device *parent = ic->ic_dev;
+ int err;
+
+@@ -1355,7 +1355,7 @@ media_status(enum ieee80211_opmode opmod
+ static void
+ ieee80211com_media_status(struct net_device *dev, struct ifmediareq *imr)
+ {
+- struct ieee80211com *ic = dev->priv; /* XXX */
++ struct ieee80211com *ic = netdev_priv(dev); /* XXX */
+
+ imr->ifm_status = IFM_AVALID;
+ if (!TAILQ_EMPTY(&ic->ic_vaps))
+@@ -1407,7 +1407,7 @@ media2mode(const struct ifmedia_entry *i
+ static int
+ ieee80211com_media_change(struct net_device *dev)
+ {
+- struct ieee80211com *ic = dev->priv; /* XXX */
++ struct ieee80211com *ic = netdev_priv(dev); /* XXX */
+ struct ieee80211vap *vap;
+ struct ifmedia_entry *ime = ic->ic_media.ifm_cur;
+ enum ieee80211_phymode newphymode;
+@@ -1511,7 +1511,7 @@ checkrate(struct ieee80211com *ic, enum
+ int
+ ieee80211_media_change(struct net_device *dev)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifmedia_entry *ime = vap->iv_media.ifm_cur;
+ enum ieee80211_phymode newmode;
+@@ -1545,7 +1545,7 @@ EXPORT_SYMBOL(ieee80211_media_change);
+ void
+ ieee80211_media_status(struct net_device *dev, struct ifmediareq *imr)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_phymode mode;
+ struct ieee80211_rateset *rs;
+@@ -1751,7 +1751,7 @@ EXPORT_SYMBOL(ieee80211_media2rate);
+ static struct net_device_stats *
+ ieee80211_getstats(struct net_device *dev)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct net_device_stats *stats = &vap->iv_devstats;
+
+ /* XXX: Total guess as to what to count where */
+@@ -1790,7 +1790,7 @@ ieee80211_change_mtu(struct net_device *
+ static void
+ ieee80211_set_multicast_list(struct net_device *dev)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *parent = ic->ic_dev;
+
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -183,7 +183,7 @@ EXPORT_SYMBOL(ieee80211_getmgtframe);
+ static void
+ ieee80211_vlan_register(struct net_device *dev, struct vlan_group *grp)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ vap->iv_vlgrp = grp;
+ }
+@@ -194,7 +194,7 @@ ieee80211_vlan_register(struct net_devic
+ static void
+ ieee80211_vlan_add_vid(struct net_device *dev, unsigned short vid)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (vap->iv_vlgrp != NULL)
+ vap->iv_bss->ni_vlan = vid;
+@@ -206,7 +206,7 @@ ieee80211_vlan_add_vid(struct net_device
+ static void
+ ieee80211_vlan_kill_vid(struct net_device *dev, unsigned short vid)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (vap->iv_vlgrp != NULL)
+ vlan_group_set_device(vap->iv_vlgrp, vid, NULL);
+@@ -989,8 +989,8 @@ ieee80211_rcv_dev_event(struct notifier_
+
+ switch (event) {
+ case NETDEV_CHANGENAME:
+- ieee80211_virtfs_vdetach(dev->priv);
+- ieee80211_virtfs_latevattach(dev->priv);
++ ieee80211_virtfs_vdetach(netdev_priv(dev));
++ ieee80211_virtfs_latevattach(netdev_priv(dev));
+ return NOTIFY_DONE;
+ default:
+ break;
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -201,7 +201,7 @@ ieee80211_classify(struct ieee80211_node
+ int
+ ieee80211_hardstart(struct sk_buff *skb, struct net_device *dev)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *parent = ic->ic_dev;
+ struct ieee80211_node *ni = NULL;
+@@ -317,7 +317,7 @@ bad:
+ */
+
+ void ieee80211_parent_queue_xmit(struct sk_buff *skb) {
+- struct ieee80211vap *vap = skb->dev->priv;
++ struct ieee80211vap *vap = netdev_priv(skb->dev);
+
+ vap->iv_devstats.tx_packets++;
+ vap->iv_devstats.tx_bytes += skb->len;
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -970,7 +970,7 @@ ieee80211_init(struct net_device *dev, i
+ {
+ #define IS_RUNNING(_dev) \
+ ((_dev->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *parent = ic->ic_dev;
+
+@@ -1087,7 +1087,7 @@ ieee80211_init(struct net_device *dev, i
+ int
+ ieee80211_open(struct net_device *dev)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ return ieee80211_init(dev, 0);
+ }
+@@ -1131,7 +1131,7 @@ EXPORT_SYMBOL(ieee80211_start_running);
+ int
+ ieee80211_stop(struct net_device *dev)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *parent = ic->ic_dev;
+ struct ieee80211_node *tni, *ni;
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -87,7 +87,7 @@ pre_announced_chanswitch(struct net_devi
+ static int
+ preempt_scan(struct net_device *dev, int max_grace, int max_wait)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ int total_delay = 0;
+ int canceled = 0, ready = 0;
+@@ -122,7 +122,7 @@ preempt_scan(struct net_device *dev, int
+ static struct iw_statistics *
+ ieee80211_iw_getstats(struct net_device *dev)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct iw_statistics *is = &vap->iv_iwstats;
+ struct ieee80211com *ic = vap->iv_ic;
+
+@@ -146,7 +146,7 @@ static int
+ ieee80211_ioctl_giwname(struct net_device *dev, struct iw_request_info *info,
+ char *name, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211_channel *c = vap->iv_ic->ic_curchan;
+
+ if (IEEE80211_IS_CHAN_108G(c))
+@@ -198,7 +198,7 @@ static int
+ ieee80211_ioctl_siwencode(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *erq, char *keybuf)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ int error;
+ int wepchange = 0;
+ ieee80211_keyix_t kix;
+@@ -306,7 +306,7 @@ static int
+ ieee80211_ioctl_giwencode(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *erq, char *key)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211_key *k;
+ int error;
+ ieee80211_keyix_t kix;
+@@ -351,7 +351,7 @@ ieee80211_ioctl_siwrate(struct net_devic
+ IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
+ };
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifreq ifr;
+ int rate, retv;
+@@ -386,7 +386,7 @@ static int
+ ieee80211_ioctl_giwrate(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ifmediareq imr;
+ int rate;
+
+@@ -424,7 +424,7 @@ static int
+ ieee80211_ioctl_siwrts(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ u16 val;
+
+@@ -447,7 +447,7 @@ static int
+ ieee80211_ioctl_giwrts(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ rts->value = vap->iv_rtsthreshold;
+ rts->disabled = (rts->value == IEEE80211_RTS_MAX);
+@@ -460,7 +460,7 @@ static int
+ ieee80211_ioctl_siwfrag(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ u16 val;
+
+@@ -483,7 +483,7 @@ static int
+ ieee80211_ioctl_giwfrag(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ rts->value = vap->iv_fragthreshold;
+ rts->disabled = (rts->value == 2346);
+@@ -496,7 +496,7 @@ static int
+ ieee80211_ioctl_siwap(struct net_device *dev, struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ /* NB: should not be set when in AP mode */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+@@ -532,7 +532,7 @@ static int
+ ieee80211_ioctl_giwap(struct net_device *dev, struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (vap->iv_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(&ap_addr->sa_data, vap->iv_des_bssid);
+@@ -553,7 +553,7 @@ static int
+ ieee80211_ioctl_siwnickn(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *nickname)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (data->length > IEEE80211_NWID_LEN)
+ return -E2BIG;
+@@ -569,7 +569,7 @@ static int
+ ieee80211_ioctl_giwnickn(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *nickname)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (data->length > vap->iv_nicknamelen + 1)
+ data->length = vap->iv_nicknamelen + 1;
+@@ -678,7 +678,7 @@ static int
+ ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *c, *c2;
+ int i;
+@@ -767,7 +767,7 @@ static int
+ ieee80211_ioctl_giwfreq(struct net_device *dev, struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if (vap->iv_state == IEEE80211_S_RUN &&
+@@ -808,7 +808,7 @@ static int
+ ieee80211_ioctl_siwessid(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ return -EOPNOTSUPP;
+@@ -853,7 +853,7 @@ static int
+ ieee80211_ioctl_giwessid(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *essid)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ return -EOPNOTSUPP;
+@@ -899,7 +899,7 @@ static int
+ ieee80211_ioctl_giwrange(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct iw_range *range = (struct iw_range *) extra;
+@@ -1047,7 +1047,7 @@ ieee80211_ioctl_setspy(struct net_device
+ struct iw_point *data, char *extra)
+ {
+ /* save the list of node addresses */
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct sockaddr address[IW_MAX_SPY];
+ unsigned int number = data->length;
+ int i;
+@@ -1085,7 +1085,7 @@ ieee80211_ioctl_getspy(struct net_device
+ * locate nodes by mac (ieee80211_find_node()),
+ * copy out rssi, set updated flag appropriately
+ */
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta;
+ struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+@@ -1133,7 +1133,7 @@ static int
+ ieee80211_ioctl_setthrspy(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct iw_thrspy threshold;
+
+ if (data->length != 1)
+@@ -1170,7 +1170,7 @@ static int
+ ieee80211_ioctl_getthrspy(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iw_thrspy *threshold;
+
+@@ -1191,7 +1191,7 @@ static int
+ ieee80211_ioctl_siwmode(struct net_device *dev, struct iw_request_info *info,
+ __u32 *mode, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ifmediareq imr;
+ int valid = 0;
+
+@@ -1216,7 +1216,7 @@ static int
+ ieee80211_ioctl_giwmode(struct net_device *dev, struct iw_request_info *info,
+ __u32 *mode, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ifmediareq imr;
+
+ memset(&imr, 0, sizeof(imr));
+@@ -1239,7 +1239,7 @@ static int
+ ieee80211_ioctl_siwpower(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *wrq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+
+ /* XXX: These values, flags, and caps do not seem to be used elsewhere
+@@ -1278,7 +1278,7 @@ static int
+ ieee80211_ioctl_giwpower(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+
+ rrq->disabled = (ic->ic_flags & IEEE80211_F_PMGTON) == 0;
+@@ -1302,7 +1302,7 @@ static int
+ ieee80211_ioctl_siwretry(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if (rrq->disabled) {
+@@ -1334,7 +1334,7 @@ static int
+ ieee80211_ioctl_giwretry(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ rrq->disabled = (vap->iv_flags & IEEE80211_F_SWRETRY) == 0;
+ if (!rrq->disabled) {
+@@ -1365,7 +1365,7 @@ static int
+ ieee80211_ioctl_siwtxpow(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ int fixed, disabled;
+
+@@ -1402,7 +1402,7 @@ ieee80211_get_txcont(struct net_device *
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ params[0] = ic->ic_get_txcont(ic);
+ return 0;
+@@ -1413,7 +1413,7 @@ ieee80211_get_dfs_cac_time(struct net_de
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ params[0] = ic->ic_get_dfs_cac_time(ic);
+ return 0;
+@@ -1424,7 +1424,7 @@ ieee80211_get_dfs_excl_period(struct net
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ params[0] = ic->ic_get_dfs_excl_period(ic);
+ return 0;
+@@ -1434,7 +1434,7 @@ ieee80211_set_dfs_cac_time(struct net_de
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ ic->ic_set_dfs_cac_time(ic, params[1]);
+ return 0;
+@@ -1444,7 +1444,7 @@ ieee80211_set_dfs_excl_period (struct n
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ ic->ic_set_dfs_excl_period(ic, params[1]);
+ return 0;
+@@ -1455,7 +1455,7 @@ ieee80211_get_dfs_testmode(struct net_de
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ params[0] = ic->ic_get_dfs_testmode(ic);
+ return 0;
+@@ -1466,7 +1466,7 @@ ieee80211_get_txcont_rate(struct net_dev
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ params[0] = ic->ic_get_txcont_rate(ic);
+ return 0;
+@@ -1477,7 +1477,7 @@ ieee80211_set_txcont(struct net_device *
+ void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ ic->ic_set_txcont(ic, params[1]);
+ return 0;
+@@ -1488,7 +1488,7 @@ ieee80211_set_dfs_testmode(struct net_de
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ ic->ic_set_dfs_testmode(ic, params[1]);
+ return 0;
+@@ -1499,7 +1499,7 @@ ieee80211_set_txcont_rate(struct net_dev
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ ic->ic_set_txcont_rate(ic, params[1]);
+ return 0;
+@@ -1510,7 +1510,7 @@ ieee80211_set_txcont_power(struct net_de
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ ic->ic_set_txcont_power(ic, params[1]);
+ return 0;
+@@ -1521,7 +1521,7 @@ ieee80211_get_txcont_power(struct net_de
+ struct iw_request_info *info, void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ params[0] = ic->ic_get_txcont_power(ic);
+ return 0;
+@@ -1533,7 +1533,7 @@ ieee80211_ioctl_hal_map(struct net_devic
+ void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ params[0] = ic->ic_dump_hal_map(ic);
+ return 0;
+@@ -1545,7 +1545,7 @@ ieee80211_ioctl_radar(struct net_device
+ void *w, char *extra)
+ {
+ int *params = (int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ if (!(ic->ic_flags & IEEE80211_F_DOTH))
+ return 0;
+@@ -1557,7 +1557,7 @@ static int
+ ieee80211_ioctl_giwtxpow(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ unsigned int power = ic->ic_txpowlimit;
+ struct ieee80211_channel *c;
+@@ -1581,7 +1581,7 @@ static int
+ ieee80211_dump_registers(struct net_device *dev, struct iw_request_info *info, void *w, char *extra)
+ {
+ unsigned int *params = (unsigned int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ switch (params[1]) {
+ case 2:
+@@ -1604,7 +1604,7 @@ static int
+ ieee80211_ioctl_writereg(struct net_device *dev, struct iw_request_info *info, void *w, char *extra)
+ {
+ unsigned int *params = (unsigned int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ return ic->ic_write_register(ic, params[0], params[1]);
+ }
+@@ -1615,7 +1615,7 @@ static int
+ ieee80211_ioctl_readreg(struct net_device *dev, struct iw_request_info *info, void *w, char *extra)
+ {
+ unsigned int *params = (unsigned int*) extra;
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ return ic->ic_read_register(ic, params[0], &params[0]);
+ }
+@@ -1651,7 +1651,7 @@ static int
+ ieee80211_ioctl_iwaplist(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct waplistreq req; /* XXX off stack */
+
+@@ -1673,7 +1673,7 @@ static int
+ ieee80211_ioctl_siwscan(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ /*
+ * XXX don't permit a scan to be started unless we
+@@ -1997,7 +1997,7 @@ static int
+ ieee80211_ioctl_giwscan(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iwscanreq req;
+ int res = 0;
+@@ -2098,7 +2098,7 @@ static int
+ ieee80211_ioctl_setmode(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *wri, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifreq ifr;
+ char s[6]; /* big enough for ``11adt'' */
+@@ -2222,10 +2222,10 @@ ieee80211_setathcap(struct ieee80211vap
+ static int
+ ieee80211_set_turbo(struct net_device *dev, int flag)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifreq ifr;
+- struct ieee80211vap *tmpvap = dev->priv;
++ struct ieee80211vap *tmpvap = netdev_priv(dev);
+ int nvap = 0;
+
+ TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next)
+@@ -2246,7 +2246,7 @@ static int
+ ieee80211_ioctl_setparam(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_rsnparms *rsn = &vap->iv_bss->ni_rsn;
+ unsigned int *i = (unsigned int *) extra;
+@@ -2926,7 +2926,7 @@ static int
+ ieee80211_ioctl_getmode(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *wri, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifmediareq imr;
+
+@@ -2964,7 +2964,7 @@ static int
+ ieee80211_ioctl_getparam(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_rsnparms *rsn = &vap->iv_bss->ni_rsn;
+ unsigned int *param = (unsigned int *) extra;
+@@ -3309,7 +3309,7 @@ static int
+ ieee80211_ioctl_setoptie(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *wri, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ void *ie;
+
+ /*
+@@ -3343,7 +3343,7 @@ static int
+ ieee80211_ioctl_getoptie(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *wri, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (vap->iv_opt_ie == NULL) {
+ wri->length = 0;
+@@ -3407,7 +3407,7 @@ ieee80211_ioctl_setappiebuf(struct net_d
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211req_getset_appiebuf *iebuf =
+ (struct ieee80211req_getset_appiebuf *)extra;
+ enum ieee80211_opmode chk_opmode;
+@@ -3449,7 +3449,7 @@ static int
+ ieee80211_ioctl_getappiebuf(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211req_getset_appiebuf *iebuf =
+ (struct ieee80211req_getset_appiebuf *)extra;
+ int max_iebuf_len;
+@@ -3490,7 +3490,7 @@ static int
+ ieee80211_ioctl_setfilter(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211req_set_filter *app_filter = (struct ieee80211req_set_filter *)extra;
+
+ if ((extra == NULL) || (app_filter->app_filterype & ~IEEE80211_FILTER_TYPE_ALL))
+@@ -3505,7 +3505,7 @@ static int
+ ieee80211_ioctl_setkey(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211req_key *ik = (struct ieee80211req_key *)extra;
+ struct ieee80211_node *ni;
+@@ -3588,7 +3588,7 @@ ieee80211_ioctl_setkey(struct net_device
+ static int
+ ieee80211_ioctl_getkey(struct net_device *dev, struct iwreq *iwr)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ struct ieee80211req_key ik;
+@@ -3649,7 +3649,7 @@ static int
+ ieee80211_ioctl_delkey(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211req_del_key *dk = (struct ieee80211req_del_key *)extra;
+ ieee80211_keyix_t kix;
+@@ -3723,7 +3723,7 @@ static int
+ ieee80211_ioctl_setmlme(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211req_mlme *mlme = (struct ieee80211req_mlme *)extra;
+ struct ieee80211_node *ni;
+@@ -3826,7 +3826,7 @@ static int
+ ieee80211_ioctl_wdsaddmac(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct sockaddr *sa = (struct sockaddr *)extra;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211vap *avp;
+@@ -3855,7 +3855,7 @@ static int
+ ieee80211_ioctl_wdssetmac(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct sockaddr *sa = (struct sockaddr *)extra;
+
+ if (vap->iv_opmode != IEEE80211_M_WDS)
+@@ -3922,7 +3922,7 @@ ieee80211_ioctl_setscanlist(struct net_d
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ char *s, *next;
+ int val = 1;
+@@ -3997,7 +3997,7 @@ static int
+ ieee80211_ioctl_addmac(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct sockaddr *sa = (struct sockaddr *)extra;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
+
+@@ -4015,7 +4015,7 @@ static int
+ ieee80211_ioctl_delmac(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct sockaddr *sa = (struct sockaddr *)extra;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
+
+@@ -4033,7 +4033,7 @@ static int
+ ieee80211_ioctl_setchanlist(struct net_device *dev,
+ struct iw_request_info *info, void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211req_chanlist *list =
+ (struct ieee80211req_chanlist *)extra;
+@@ -4084,7 +4084,7 @@ static int
+ ieee80211_ioctl_getchanlist(struct net_device *dev,
+ struct iw_request_info *info, void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+
+ memcpy(extra, ic->ic_chan_active, sizeof(ic->ic_chan_active));
+@@ -4105,7 +4105,7 @@ static int
+ ieee80211_ioctl_getchaninfo(struct net_device *dev,
+ struct iw_request_info *info, void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211req_chaninfo *chans =
+ (struct ieee80211req_chaninfo *)extra;
+@@ -4152,7 +4152,7 @@ static int
+ ieee80211_ioctl_setwmmparams(struct net_device *dev,
+ struct iw_request_info *info, void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ unsigned int *param = (unsigned int *) extra;
+ unsigned int ac = (param[1] < WME_NUM_AC) ? param[1] : WME_AC_BE;
+ unsigned int bss = param[2];
+@@ -4240,7 +4240,7 @@ static int
+ ieee80211_ioctl_getwmmparams(struct net_device *dev,
+ struct iw_request_info *info, void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ unsigned int *param = (unsigned int *) extra;
+ unsigned int ac = (param[1] < WME_NUM_AC) ? param[1] : WME_AC_BE;
+ struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
+@@ -4275,7 +4275,7 @@ ieee80211_ioctl_getwmmparams(struct net_
+ static int
+ ieee80211_ioctl_getwpaie(struct net_device *dev, struct iwreq *iwr)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ struct ieee80211req_wpaie wpaie;
+@@ -4309,7 +4309,7 @@ ieee80211_ioctl_getwpaie(struct net_devi
+ static int
+ ieee80211_ioctl_getstastats(struct net_device *dev, struct iwreq *iwr)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ u_int8_t macaddr[IEEE80211_ADDR_LEN];
+@@ -4428,7 +4428,7 @@ get_scan_result(void *arg, const struct
+ static int
+ ieee80211_ioctl_getscanresults(struct net_device *dev, struct iwreq *iwr)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct scanreq req;
+ int error;
+@@ -4591,7 +4591,7 @@ get_sta_info(void *arg, struct ieee80211
+ static int
+ ieee80211_ioctl_getstainfo(struct net_device *dev, struct iwreq *iwr)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct stainforeq req;
+ int error;
+@@ -4625,7 +4625,7 @@ ieee80211_ioctl_getstainfo(struct net_de
+
+ static void
+ pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt) {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211vap *avp;
+
+@@ -4643,7 +4643,7 @@ static int
+ ieee80211_ioctl_chanswitch(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ unsigned int *param = (unsigned int *) extra;
+
+@@ -4688,7 +4688,7 @@ static int
+ ieee80211_ioctl_giwgenie(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *out, char *buf)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+
+ if (out->length < vap->iv_opt_ie_len)
+ return -E2BIG;
+@@ -5221,7 +5221,7 @@ static int
+ ieee80211_ioctl_giwencodeext(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *erq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct iw_encode_ext *ext;
+ struct ieee80211_key *wk;
+ ieee80211_keyix_t kix;
+@@ -5281,7 +5281,7 @@ static int
+ ieee80211_ioctl_siwencodeext(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *erq, char *extra)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ struct ieee80211req_key kr;
+ ieee80211_keyix_t kix;
+@@ -5957,7 +5957,7 @@ static struct iw_handler_def ieee80211_i
+ static int
+ ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+- struct ieee80211vap *vap = dev->priv;
++ struct ieee80211vap *vap = netdev_priv(dev);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+
diff --git a/package/madwifi/patches/431-compile_fixes.patch b/package/madwifi/patches/431-compile_fixes.patch
new file mode 100644
index 000000000..b11d406a0
--- /dev/null
+++ b/package/madwifi/patches/431-compile_fixes.patch
@@ -0,0 +1,35 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -2388,7 +2388,9 @@ ath_intr(int irq, void *dev_id, struct p
+ if (status & (HAL_INT_RX | HAL_INT_RXPHY)) {
+ ath_uapsd_processtriggers(sc, hw_tsf);
+ sc->sc_isr &= ~HAL_INT_RX;
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
++ if (napi_schedule_prep(&sc->sc_napi))
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (netif_rx_schedule_prep(dev, &sc->sc_napi))
+ #else
+ if (netif_rx_schedule_prep(dev))
+@@ -2396,7 +2398,9 @@ ath_intr(int irq, void *dev_id, struct p
+ {
+ sc->sc_imask &= ~HAL_INT_RX;
+ ath_hal_intrset(ah, sc->sc_imask);
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
++ __napi_schedule(&sc->sc_napi);
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ __netif_rx_schedule(dev, &sc->sc_napi);
+ #else
+ __netif_rx_schedule(dev);
+@@ -7135,7 +7139,9 @@ rx_next:
+ local_irq_restore(flags);
+ }
+
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
++ napi_complete(napi);
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ netif_rx_complete(dev, napi);
+ #else
+ netif_rx_complete(dev);
diff --git a/package/madwifi/patches/432-netdev_ops.patch b/package/madwifi/patches/432-netdev_ops.patch
new file mode 100644
index 000000000..af829b164
--- /dev/null
+++ b/package/madwifi/patches/432-netdev_ops.patch
@@ -0,0 +1,184 @@
+Convert to net_device_ops for Linux 2.6.29+
+http://madwifi-project.org/changeset/4005
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -566,6 +566,20 @@ static inline int rate_factor(int mode)
+
+ /* Initialize ath_softc structure */
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
++static const struct net_device_ops ath_netdev_ops = {
++ .ndo_open = ath_init,
++ .ndo_stop = ath_stop,
++ .ndo_start_xmit = ath_hardstart,
++ .ndo_tx_timeout = ath_tx_timeout,
++ .ndo_set_multicast_list = ath_mode_init,
++ .ndo_do_ioctl = ath_ioctl,
++ .ndo_get_stats = ath_getstats,
++ .ndo_set_mac_address = ath_set_mac_address,
++ .ndo_change_mtu = ath_change_mtu,
++};
++#endif
++
+ int
+ ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
+ {
+@@ -865,16 +879,20 @@ ath_attach(u_int16_t devid, struct net_d
+ }
+
+ /* NB: ether_setup is done by bus-specific code */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+ dev->open = ath_init;
+ dev->stop = ath_stop;
+ dev->hard_start_xmit = ath_hardstart;
+ dev->tx_timeout = ath_tx_timeout;
+- dev->watchdog_timeo = 5 * HZ;
+ dev->set_multicast_list = ath_mode_init;
+ dev->do_ioctl = ath_ioctl;
+ dev->get_stats = ath_getstats;
+ dev->set_mac_address = ath_set_mac_address;
+ dev->change_mtu = ath_change_mtu;
++#else
++ dev->netdev_ops = &ath_netdev_ops;
++#endif
++ dev->watchdog_timeo = 5 * HZ;
+ dev->tx_queue_len = ATH_TXBUF - ATH_TXBUF_MGT_RESERVED;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ netif_napi_add(dev, &sc->sc_napi, ath_rx_poll, 64);
+@@ -1257,7 +1275,6 @@ ath_detach(struct net_device *dev)
+ ath_dynamic_sysctl_unregister(sc);
+ ATH_LOCK_DESTROY(sc);
+ ATH_HAL_LOCK_DESTROY(sc);
+- dev->stop = NULL; /* prevent calling ath_stop again */
+ unregister_netdev(dev);
+ return 0;
+ }
+@@ -12732,8 +12749,13 @@ ath_rcv_dev_event(struct notifier_block
+ struct net_device *dev = (struct net_device *)ptr;
+ struct ath_softc *sc = (struct ath_softc *)netdev_priv(dev);
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+ if (!dev || !sc || dev->open != &ath_init)
+ return 0;
++#else
++ if (!dev || !sc || dev->netdev_ops->ndo_open != &ath_init)
++ return 0;
++#endif
+
+ switch (event) {
+ case NETDEV_CHANGENAME:
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -451,6 +451,18 @@ ieee80211_ifdetach(struct ieee80211com *
+ }
+ EXPORT_SYMBOL(ieee80211_ifdetach);
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
++static const struct net_device_ops ieee80211_netdev_ops = {
++ .ndo_get_stats = ieee80211_getstats,
++ .ndo_open = ieee80211_open,
++ .ndo_stop = ieee80211_stop,
++ .ndo_start_xmit = ieee80211_hardstart,
++ .ndo_set_multicast_list = ieee80211_set_multicast_list,
++ .ndo_change_mtu = ieee80211_change_mtu,
++ .ndo_do_ioctl = ieee80211_ioctl,
++};
++#endif
++
+ int
+ ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,
+ const char *name, int opmode, int flags, struct ieee80211vap *master)
+@@ -471,16 +483,21 @@ ieee80211_vap_setup(struct ieee80211com
+ } else
+ strncpy(dev->name, name, sizeof(dev->name));
+ }
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+
+ dev->get_stats = ieee80211_getstats;
+ dev->open = ieee80211_open;
+ dev->stop = ieee80211_stop;
+ dev->hard_start_xmit = ieee80211_hardstart;
+ dev->set_multicast_list = ieee80211_set_multicast_list;
++ dev->do_ioctl = ieee80211_ioctl;
+ #if 0
+ dev->set_mac_address = ieee80211_set_mac_address;
+ #endif
+ dev->change_mtu = ieee80211_change_mtu;
++#else
++ dev->netdev_ops = &ieee80211_netdev_ops;
++#endif
+ dev->tx_queue_len = 0; /* NB: bypass queuing */
+ dev->hard_header_len = parent->hard_header_len;
+ /*
+@@ -1824,7 +1841,11 @@ ieee80211_set_multicast_list(struct net_
+ IEEE80211_UNLOCK_IRQ(ic);
+
+ /* XXX: Merge multicast list into parent device */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+ parent->set_multicast_list(ic->ic_dev);
++#else
++ parent->netdev_ops->ndo_set_multicast_list(ic->ic_dev);
++#endif
+ }
+
+ void
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -984,8 +984,14 @@ ieee80211_rcv_dev_event(struct notifier_
+ void *ptr)
+ {
+ struct net_device *dev = (struct net_device *) ptr;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+ if (!dev || dev->open != &ieee80211_open)
+ return 0;
++#else
++ if (!dev || dev->netdev_ops->ndo_open != &ieee80211_open)
++ return 0;
++#endif
+
+ switch (event) {
+ case NETDEV_CHANGENAME:
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -739,6 +739,7 @@ void ieee80211_build_sc_ie(struct ieee80
+ void ieee80211_dfs_action(struct ieee80211com *);
+ void ieee80211_expire_channel_excl_restrictions(struct ieee80211com *);
+ void ieee80211_setpuregbasicrates(struct ieee80211_rateset *rs);
++int ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+ /*
+ * Iterate through ic_channels to enumerate all distinct ic_ieee channel numbers.
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -5954,7 +5954,7 @@ static struct iw_handler_def ieee80211_i
+ /*
+ * Handle private ioctl requests.
+ */
+-static int
++int
+ ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+ struct ieee80211vap *vap = netdev_priv(dev);
+@@ -6044,7 +6044,6 @@ ieee80211_ioctl_vattach(struct ieee80211
+ {
+ struct net_device *dev = vap->iv_dev;
+
+- dev->do_ioctl = ieee80211_ioctl;
+ #if IW_HANDLER_VERSION < 7
+ dev->get_wireless_stats = ieee80211_iw_getstats;
+ #endif
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1188,7 +1188,11 @@ ieee80211_deliver_data(struct ieee80211_
+ skb1->protocol = __constant_htons(ETH_P_802_2);
+ /* XXX insert vlan tag before queue it? */
+ ni_tmp = SKB_CB(skb1)->ni; /* remember node so we can free it */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+ ret = dev->hard_start_xmit(skb1, dev);
++#else
++ ret = dev->netdev_ops->ndo_start_xmit(skb1, dev);
++#endif
+
+ if (ret == NETDEV_TX_BUSY)
+ ieee80211_dev_kfree_skb(&skb1);
diff --git a/package/madwifi/patches/433-backport_remove_irq_none.patch b/package/madwifi/patches/433-backport_remove_irq_none.patch
new file mode 100644
index 000000000..5166d9e10
--- /dev/null
+++ b/package/madwifi/patches/433-backport_remove_irq_none.patch
@@ -0,0 +1,21 @@
+Fix Linux 2.6.30 compatibility
+
+Linux 2.6.30 doesn't define IRQ_NONE as a macro. Assume irqreturn_t,
+IRQ_NONE and IRQ_HANDLED to be present on Linux 2.6.29 and newer.
+http://madwifi-project.org/changeset/3986
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -83,11 +83,13 @@ typedef void *TQUEUE_ARG;
+ /*
+ * Guess how the interrupt handler should work.
+ */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+ #if !defined(IRQ_NONE)
+ typedef void irqreturn_t;
+ #define IRQ_NONE
+ #define IRQ_HANDLED
+ #endif /* !defined(IRQ_NONE) */
++#endif /* Linux < 2.6.29 */
+
+ #ifndef SET_MODULE_OWNER
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
diff --git a/package/madwifi/patches/434-name-alloc-fix.patch b/package/madwifi/patches/434-name-alloc-fix.patch
new file mode 100644
index 000000000..ab277630d
--- /dev/null
+++ b/package/madwifi/patches/434-name-alloc-fix.patch
@@ -0,0 +1,28 @@
+--- a/ath/if_ath_ahb.c
++++ b/ath/if_ath_ahb.c
+@@ -311,6 +311,11 @@ init_ath_wmac(u_int16_t devid, u_int16_t
+ SET_MODULE_OWNER(dev);
+ sclist[wlanNum] = sc;
+
++ if (dev_alloc_name(dev, dev->name) < 0) {
++ printk(KERN_ERR "%s: cannot allocate name\n", dev_info);
++ goto bad3;
++ }
++
+ switch (wlanNum) {
+ case AR531X_WLAN0_NUM:
+ if (((devid & AR5315_REV_MAJ_M) == AR5315_REV_MAJ) ||
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -236,6 +236,11 @@ ath_pci_probe(struct pci_dev *pdev, cons
+ */
+ sc->aps_sc.sc_invalid = 1;
+
++ if (dev_alloc_name(dev, dev->name) < 0) {
++ printk(KERN_ERR "%s: cannot allocate name\n", dev_info);
++ goto bad3;
++ }
++
+ dev->irq = pdev->irq;
+
+ SET_MODULE_OWNER(dev);
diff --git a/package/madwifi/patches/435-ibss_neighbor_fix.patch b/package/madwifi/patches/435-ibss_neighbor_fix.patch
new file mode 100644
index 000000000..d66af9b24
--- /dev/null
+++ b/package/madwifi/patches/435-ibss_neighbor_fix.patch
@@ -0,0 +1,11 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -313,7 +313,7 @@ ieee80211_input(struct ieee80211vap * va
+ if (type == IEEE80211_FC0_TYPE_DATA && ni == vap->iv_bss &&
+ !IEEE80211_ADDR_EQ(vap->iv_bss->ni_macaddr, wh->i_addr2)) {
+ /* Try to find sender in local node table. */
+- ni = ieee80211_find_node(vap->iv_bss->ni_table, wh->i_addr2);
++ ni = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
+ if (ni == NULL) {
+ /*
+ * Fake up a node for this newly discovered
diff --git a/package/madwifi/patches/436-injection_checks.patch b/package/madwifi/patches/436-injection_checks.patch
new file mode 100644
index 000000000..274873111
--- /dev/null
+++ b/package/madwifi/patches/436-injection_checks.patch
@@ -0,0 +1,26 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -3199,7 +3199,13 @@ ath_tx_startraw(struct net_device *dev,
+ struct ieee80211_frame *wh;
+
+ wh = (struct ieee80211_frame *)skb->data;
++
+ try0 = ph->try0;
++ if (!try0)
++ try0 = 1;
++ else if (try0 > 11)
++ try0 = 11;
++
+ rt = sc->sc_currates;
+ txrate = dot11_to_ratecode(sc, rt, ph->rate0);
+ power = ph->power > 63 ? 63 : ph->power;
+@@ -3224,7 +3230,8 @@ ath_tx_startraw(struct net_device *dev,
+ rt = sc->sc_currates;
+ KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+- if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
++ if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) ||
++ IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */
+ sc->sc_stats.ast_tx_noack++;
+ try0 = 1;
diff --git a/package/madwifi/patches/437-sysctl_cleanup.patch b/package/madwifi/patches/437-sysctl_cleanup.patch
new file mode 100644
index 000000000..e35ed0293
--- /dev/null
+++ b/package/madwifi/patches/437-sysctl_cleanup.patch
@@ -0,0 +1,73 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -11021,38 +11021,38 @@ enum {
+ * mirrored in /proc/sys.
+ */
+ enum {
+- ATH_SLOTTIME = 1,
+- ATH_ACKTIMEOUT = 2,
+- ATH_CTSTIMEOUT = 3,
+- ATH_SOFTLED = 4,
+- ATH_LEDPIN = 5,
+- ATH_COUNTRYCODE = 6,
+- ATH_REGDOMAIN = 7,
+- ATH_DEBUG = 8,
+- ATH_TXANTENNA = 9,
+- ATH_RXANTENNA = 10,
+- ATH_DIVERSITY = 11,
+- ATH_TXINTRPERIOD = 12,
+- ATH_FFTXQMIN = 13,
+- ATH_XR_POLL_PERIOD = 14,
+- ATH_XR_POLL_COUNT = 15,
+- ATH_ACKRATE = 16,
+- ATH_RP = 17,
+- ATH_RP_PRINT = 18,
+- ATH_RP_PRINT_ALL = 19,
+- ATH_RP_PRINT_MEM = 20,
+- ATH_RP_PRINT_MEM_ALL = 21,
+- ATH_RP_FLUSH = 22,
+- ATH_PANIC = 23,
+- ATH_RP_IGNORED = 24,
+- ATH_RADAR_IGNORED = 25,
+- ATH_MAXVAPS = 26,
+- ATH_INTMIT = 27,
+- ATH_NOISE_IMMUNITY = 28,
+- ATH_OFDM_WEAK_DET = 29,
+- ATH_CHANBW = 30,
+- ATH_OUTDOOR = 31,
+- ATH_DISTANCE = 32,
++ ATH_SLOTTIME,
++ ATH_ACKTIMEOUT,
++ ATH_CTSTIMEOUT,
++ ATH_SOFTLED,
++ ATH_LEDPIN,
++ ATH_COUNTRYCODE,
++ ATH_REGDOMAIN,
++ ATH_DEBUG,
++ ATH_TXANTENNA,
++ ATH_RXANTENNA,
++ ATH_DIVERSITY,
++ ATH_TXINTRPERIOD,
++ ATH_FFTXQMIN,
++ ATH_XR_POLL_PERIOD,
++ ATH_XR_POLL_COUNT,
++ ATH_ACKRATE,
++ ATH_RP,
++ ATH_RP_PRINT,
++ ATH_RP_PRINT_ALL,
++ ATH_RP_PRINT_MEM,
++ ATH_RP_PRINT_MEM_ALL,
++ ATH_RP_FLUSH,
++ ATH_PANIC,
++ ATH_RP_IGNORED,
++ ATH_RADAR_IGNORED,
++ ATH_MAXVAPS,
++ ATH_INTMIT,
++ ATH_NOISE_IMMUNITY,
++ ATH_OFDM_WEAK_DET,
++ ATH_CHANBW,
++ ATH_OUTDOOR,
++ ATH_DISTANCE,
+ };
+
+ /*
diff --git a/package/madwifi/patches/438-poweroffset_sysctl.patch b/package/madwifi/patches/438-poweroffset_sysctl.patch
new file mode 100644
index 000000000..e82046ec6
--- /dev/null
+++ b/package/madwifi/patches/438-poweroffset_sysctl.patch
@@ -0,0 +1,59 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -10476,11 +10476,11 @@ set_node_txpower(void *arg, struct ieee8
+ * XXX: this function needs some locking to avoid being called
+ * twice/interrupted. Returns the value actually stored. */
+ static u_int32_t
+-ath_set_clamped_maxtxpower(struct ath_softc *sc,
+- u_int32_t new_clamped_maxtxpower)
++ath_set_clamped_maxtxpower(struct ath_softc *sc, u_int32_t new_txpwr)
+ {
+- new_clamped_maxtxpower -= sc->sc_poweroffset;
+- (void)ath_hal_settxpowlimit(sc->sc_ah, new_clamped_maxtxpower);
++ new_txpwr = ((new_txpwr < sc->sc_poweroffset) ? 0 :
++ new_txpwr - sc->sc_poweroffset);
++ (void)ath_hal_settxpowlimit(sc->sc_ah, new_txpwr);
+ return ath_get_clamped_maxtxpower(sc);
+ }
+
+@@ -11031,6 +11031,7 @@ enum {
+ ATH_DEBUG,
+ ATH_TXANTENNA,
+ ATH_RXANTENNA,
++ ATH_POWEROFFSET,
+ ATH_DIVERSITY,
+ ATH_TXINTRPERIOD,
+ ATH_FFTXQMIN,
+@@ -11311,6 +11312,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ ath_debug_global = (val & ATH_DEBUG_GLOBAL);
+ #endif
+ break;
++ case ATH_POWEROFFSET:
++ sc->sc_poweroffset = val;
++ break;
+ case ATH_TXANTENNA:
+ /*
+ * antenna can be:
+@@ -11478,6 +11482,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_DEBUG:
+ val = sc->sc_debug | ath_debug_global;
+ break;
++ case ATH_POWEROFFSET:
++ val = sc->sc_poweroffset;
++ break;
+ case ATH_TXANTENNA:
+ val = sc->sc_txantenna;
+ break;
+@@ -11619,6 +11626,12 @@ static const ctl_table ath_sysctl_templa
+ },
+ #endif
+ { .ctl_name = CTL_AUTO,
++ .procname = "poweroffset",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_POWEROFFSET,
++ },
++ { .ctl_name = CTL_AUTO,
+ .procname = "txantenna",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
diff --git a/package/madwifi/patches/439-wlanconfig_stack_usage.patch b/package/madwifi/patches/439-wlanconfig_stack_usage.patch
new file mode 100644
index 000000000..f346143c3
--- /dev/null
+++ b/package/madwifi/patches/439-wlanconfig_stack_usage.patch
@@ -0,0 +1,20 @@
+--- a/tools/wlanconfig.c
++++ b/tools/wlanconfig.c
+@@ -560,7 +560,7 @@ ieee80211_ntoa(const uint8_t mac[IEEE802
+ static void
+ list_stations(const char *ifname)
+ {
+- uint8_t buf[24*1024];
++ static uint8_t buf[24*1024];
+ struct iwreq iwr;
+ uint8_t *cp;
+ int s, len;
+@@ -653,7 +653,7 @@ list_stations(const char *ifname)
+ static void
+ list_scan(const char *ifname)
+ {
+- uint8_t buf[24 * 1024];
++ static uint8_t buf[24 * 1024];
+ char ssid[14];
+ uint8_t *cp;
+ int len;
diff --git a/package/madwifi/patches/440-wme_cleanup.patch b/package/madwifi/patches/440-wme_cleanup.patch
new file mode 100644
index 000000000..bb81ac7f8
--- /dev/null
+++ b/package/madwifi/patches/440-wme_cleanup.patch
@@ -0,0 +1,136 @@
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -73,6 +73,29 @@ doprint(struct ieee80211vap *vap, int su
+ }
+ #endif
+
++static const int ieee802_1d_to_ac[8] = {
++ WME_AC_BE, WME_AC_BK, WME_AC_BK, WME_AC_BE,
++ WME_AC_VI, WME_AC_VI, WME_AC_VO, WME_AC_VO
++};
++
++/* Given a data frame determine the 802.1p/1d tag to use. */
++static unsigned int ieee80211_classify_ip(struct sk_buff *skb)
++{
++ const struct ether_header *eh = (struct ether_header *) skb->data;
++ const struct iphdr *ip = (struct iphdr *)
++ (skb->data + sizeof (struct ether_header));
++ unsigned int dscp;
++
++ switch (skb->protocol) {
++ case __constant_htons(ETH_P_IP):
++ dscp = ip->tos & 0xfc;
++ break;
++ default:
++ return 0;
++ }
++
++ return dscp >> 5;
++}
+
+ /*
+ * Determine the priority based on VLAN and/or IP TOS. Priority is
+@@ -83,90 +106,24 @@ static int
+ ieee80211_classify(struct ieee80211_node *ni, struct sk_buff *skb)
+ {
+ struct ieee80211vap *vap = ni->ni_vap;
+- struct ether_header *eh = (struct ether_header *) skb->data;
+- int v_wme_ac = 0, d_wme_ac = 0;
+
+- /* default priority */
+- skb->priority = WME_AC_BE;
+-
+- if (!(ni->ni_flags & IEEE80211_NODE_QOS))
+- return 0;
+-
+- /*
+- * If node has a vlan tag then all traffic
+- * to it must have a matching vlan id.
++ /* skb->priority values from 256->263 are magic values to
++ * directly indicate a specific 802.1d priority. This is used
++ * to allow 802.1d priority to be passed directly in from VLAN
++ * tags, etc.
+ */
+- if (ni->ni_vlan != 0 && vlan_tx_tag_present(skb)) {
+- u_int32_t tag=0;
+- int v_pri;
+-
+- if (vap->iv_vlgrp == NULL) {
+- IEEE80211_NODE_STAT(ni, tx_novlantag);
+- ni->ni_stats.ns_tx_novlantag++;
+- return 1;
+- }
+- if (((tag = vlan_tx_tag_get(skb)) & VLAN_VID_MASK) !=
+- (ni->ni_vlan & VLAN_VID_MASK)) {
+- IEEE80211_NODE_STAT(ni, tx_vlanmismatch);
+- ni->ni_stats.ns_tx_vlanmismatch++;
+- return 1;
+- }
+- if (ni->ni_flags & IEEE80211_NODE_QOS) {
+- v_pri = (tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;
+- switch (v_pri) {
+- case 1:
+- case 2: /* Background (BK) */
+- v_wme_ac = WME_AC_BK;
+- break;
+- case 0:
+- case 3: /* Best Effort (BE) */
+- v_wme_ac = WME_AC_BE;
+- break;
+- case 4:
+- case 5: /* Video (VI) */
+- v_wme_ac = WME_AC_VI;
+- break;
+- case 6:
+- case 7: /* Voice (VO) */
+- v_wme_ac = WME_AC_VO;
+- break;
+- }
+- }
++ if (skb->priority >= 256 && skb->priority <= 263) {
++ skb->priority = ieee802_1d_to_ac[skb->priority - 256];
++ return 0;
+ }
+
+- if (eh->ether_type == __constant_htons(ETHERTYPE_IP)) {
+- const struct iphdr *ip = (struct iphdr *)
+- (skb->data + sizeof (struct ether_header));
+- /*
+- * IP frame, map the TOS field.
+- *
+- * XXX: fill out these mappings???
+- */
+- switch (ip->tos) {
+- case 0x08: /* Background */
+- case 0x20:
+- d_wme_ac = WME_AC_BK;
+- break;
+- case 0x28: /* Video */
+- case 0xa0:
+- d_wme_ac = WME_AC_VI;
+- break;
+- case 0x30: /* Voice */
+- case 0xe0:
+- case 0x88: /* XXX UPSD */
+- case 0xb8:
+- d_wme_ac = WME_AC_VO;
+- break;
+- default: /* All others */
+- d_wme_ac = WME_AC_BE;
+- break;
+- }
+- } else {
+- d_wme_ac = WME_AC_BE;
++ if (!(ni->ni_flags & IEEE80211_NODE_QOS)) {
++ /* default priority */
++ skb->priority = WME_AC_BE;
++ return 0;
+ }
+- skb->priority = d_wme_ac;
+- if (v_wme_ac > d_wme_ac)
+- skb->priority = v_wme_ac;
++
++ skb->priority = ieee802_1d_to_ac[ieee80211_classify_ip(skb)];
+
+ /* Applying ACM policy */
+ if (vap->iv_opmode == IEEE80211_M_STA) {
diff --git a/package/madwifi/patches/441-fix_ibss_node_handling.patch b/package/madwifi/patches/441-fix_ibss_node_handling.patch
new file mode 100644
index 000000000..20d59de26
--- /dev/null
+++ b/package/madwifi/patches/441-fix_ibss_node_handling.patch
@@ -0,0 +1,91 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6641,10 +6641,8 @@ static void
+ ath_recv_mgmt(struct ieee80211vap * vap, struct ieee80211_node *ni_or_null,
+ struct sk_buff *skb, int subtype, int rssi, u_int64_t rtsf)
+ {
++ const struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
+ struct ath_softc *sc = netdev_priv(vap->iv_ic->ic_dev);
+-#ifdef AR_DEBUG
+- struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
+-#endif
+ struct ieee80211_node * ni = ni_or_null;
+ u_int64_t hw_tsf, beacon_tsf;
+ u_int32_t hw_tu, beacon_tu, intval;
+@@ -6686,7 +6684,7 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ }
+ if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
+ (sc->sc_opmode == HAL_M_HOSTAP) &&
+- IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) {
++ IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid)) {
+ /* In this mode, we drive the HAL in HOSTAP mode. Hence
+ * we do the IBSS merging in software. Also do not merge
+ * if the difference it too small. Otherwise we are playing
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -311,7 +311,8 @@ ieee80211_input(struct ieee80211vap * va
+ }
+ /* Do not try to find a node reference if the packet really did come from the BSS */
+ if (type == IEEE80211_FC0_TYPE_DATA && ni == vap->iv_bss &&
+- !IEEE80211_ADDR_EQ(vap->iv_bss->ni_macaddr, wh->i_addr2)) {
++ !IEEE80211_ADDR_EQ(vap->iv_bss->ni_macaddr, wh->i_addr2) &&
++ IEEE80211_ADDR_EQ(vap->iv_bssid, wh->i_addr3)) {
+ /* Try to find sender in local node table. */
+ ni = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
+ if (ni == NULL) {
+@@ -513,6 +514,10 @@ ieee80211_input(struct ieee80211vap * va
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
++ /* ignore foreign data frames */
++ if (ni == vap->iv_bss)
++ goto out;
++
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "data", "invalid dir 0x%x", dir);
+@@ -3558,6 +3563,11 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ } else if ((vap->iv_opmode == IEEE80211_M_WDS) && vap->iv_wdsnode) {
+ found = 1;
+ ni = ni_or_null = vap->iv_wdsnode;
++ } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
++ ni_or_null = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
++ if (ni_or_null)
++ ni = ni_or_null;
++ found = 1;
+ }
+ IEEE80211_UNLOCK_IRQ(vap->iv_ic);
+
+@@ -3686,19 +3696,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
+ return;
+ }
+- if (ni == vap->iv_bss) {
+- if (vap->iv_opmode == IEEE80211_M_IBSS) {
+- /*
+- * XXX Cannot tell if the sender is operating
+- * in ibss mode. But we need a new node to
+- * send the response so blindly add them to the
+- * neighbor table.
+- */
+- ni = ieee80211_fakeup_adhoc_node(vap,
+- wh->i_addr2);
+- } else {
+- ni = ieee80211_dup_bss(vap, wh->i_addr2, 1);
+- }
++ if (ni == vap->iv_bss && vap->iv_opmode != IEEE80211_M_IBSS) {
++ ni = ieee80211_dup_bss(vap, wh->i_addr2, 1);
+ if (ni == NULL)
+ return;
+ allocbs = 1;
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -601,6 +601,8 @@ ieee80211_ibss_merge(struct ieee80211_no
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
+ ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long",
+ ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "");
++ if (!IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bssid))
++ ieee80211_node_table_reset(&vap->iv_ic->ic_sta, vap);
+ return ieee80211_sta_join1(ieee80211_ref_node(ni));
+ }
+ EXPORT_SYMBOL(ieee80211_ibss_merge);
diff --git a/package/madwifi/patches/442-ibss_rx_filter.patch b/package/madwifi/patches/442-ibss_rx_filter.patch
new file mode 100644
index 000000000..eaf807118
--- /dev/null
+++ b/package/madwifi/patches/442-ibss_rx_filter.patch
@@ -0,0 +1,20 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -294,6 +294,17 @@ ieee80211_input(struct ieee80211vap * va
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
++ if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid) ||
++ (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
++ !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
++ (subtype != IEEE80211_FC0_SUBTYPE_BEACON))) {
++ if (!(vap->iv_dev->flags & IFF_PROMISC)) {
++ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
++ bssid, NULL, "%s", "not to bss");
++ vap->iv_stats.is_rx_wrongbss++;
++ goto out;
++ }
++ }
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr1;
+ else if (type == IEEE80211_FC0_TYPE_CTL)
diff --git a/package/madwifi/patches/443-tx_drop_counter.patch b/package/madwifi/patches/443-tx_drop_counter.patch
new file mode 100644
index 000000000..30630ae43
--- /dev/null
+++ b/package/madwifi/patches/443-tx_drop_counter.patch
@@ -0,0 +1,25 @@
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -1592,10 +1592,6 @@ found:
+ ieee80211_ref_node(ni);
+ #endif
+ }
+- } else {
+- IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, mac,
+- "no node, discard frame (%s)", __func__);
+- vap->iv_stats.is_tx_nonode++;
+ }
+ }
+ return ni;
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -208,6 +208,9 @@ ieee80211_hardstart(struct sk_buff *skb,
+ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
++ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, mac,
++ "no node, discard frame (%s)", __func__);
++ vap->iv_stats.is_tx_nonode++;
+ goto bad;
+ }
+
diff --git a/package/madwifi/patches/444-beacon_update_war.patch b/package/madwifi/patches/444-beacon_update_war.patch
new file mode 100644
index 000000000..6a3178a6d
--- /dev/null
+++ b/package/madwifi/patches/444-beacon_update_war.patch
@@ -0,0 +1,17 @@
+--- a/net80211/ieee80211_beacon.c
++++ b/net80211/ieee80211_beacon.c
+@@ -476,6 +476,14 @@ ieee80211_beacon_update(struct ieee80211
+ tie->tim_bitctl |= BITCTL_BUFD_MCAST;
+ else
+ tie->tim_bitctl &= ~BITCTL_BUFD_MCAST;
++
++ /* WAR: on some platforms, a race condition between beacon
++ * contents update and beacon transmission leads to beacon
++ * data not being updated in time. For most fields this is
++ * not critical, but for powersave it is. Work around this
++ * by always remapping the beacon when the TIM IE changes.
++ */
++ len_changed = 1;
+ }
+
+ /* Whenever we want to switch to a new channel, we need to follow the
diff --git a/package/madwifi/patches/445-fix_ps_sta_count.patch b/package/madwifi/patches/445-fix_ps_sta_count.patch
new file mode 100644
index 000000000..27514650a
--- /dev/null
+++ b/package/madwifi/patches/445-fix_ps_sta_count.patch
@@ -0,0 +1,18 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -1957,6 +1957,7 @@ ath_uapsd_processtriggers(struct ath_sof
+ if (qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) {
+ ni->ni_flags |=
+ IEEE80211_NODE_UAPSD_TRIG;
++ ni->ni_vap->iv_ps_sta++;
+ ic->ic_uapsdmaxtriggers++;
+ WME_UAPSD_NODE_TRIGSEQINIT(ni);
+ DPRINTF(sc, ATH_DEBUG_UAPSD,
+@@ -1967,6 +1968,7 @@ ath_uapsd_processtriggers(struct ath_sof
+ } else {
+ ni->ni_flags &=
+ ~IEEE80211_NODE_UAPSD_TRIG;
++ ni->ni_vap->iv_ps_sta--;
+ ic->ic_uapsdmaxtriggers--;
+ DPRINTF(sc, ATH_DEBUG_UAPSD,
+ "Node (" MAC_FMT ") no longer U-APSD"
diff --git a/package/madwifi/patches/446-single_module.patch b/package/madwifi/patches/446-single_module.patch
new file mode 100644
index 000000000..b2898ca7e
--- /dev/null
+++ b/package/madwifi/patches/446-single_module.patch
@@ -0,0 +1,778 @@
+--- a/ath/Makefile
++++ b/ath/Makefile
+@@ -41,7 +41,6 @@
+ #
+
+ obj := $(firstword $(obj) $(SUBDIRS) .)
+-TOP = $(obj)/..
+
+ ifeq ($(strip $(BUS)),AHB)
+ BUSNAME=ahb
+@@ -57,7 +56,24 @@ COPTS += -DDFS_DOMAIN_ETSI -DDFS_DOMAIN_
+ include $(TOP)/Makefile.inc
+
+ obj-m += ath_$(BUSNAME).o
+-ath_$(BUSNAME)-objs := if_ath.o if_ath_radar.o if_ath_$(BUSNAME).o
++ath_objs := if_ath.o if_ath_radar.o if_ath_$(BUSNAME).o
++ath_$(BUSNAME)-objs := $(ath_objs)
++
++ifneq ($(SINGLE_MODULE),)
++include $(TOP)/net80211/Makefile
++include $(TOP)/ath_rate/sample/Makefile
++include $(TOP)/ath_rate/minstrel/Makefile
++RC_DECLARE=$(foreach R,$(ATH_RATE),extern void ath_rate_$(R)_init(void);extern void ath_rate_$(R)_exit(void);)
++RC_INIT=$(foreach R,$(ATH_RATE),ath_rate_$(R)_init();)
++RC_EXIT=$(foreach R,$(ATH_RATE),ath_rate_$(R)_exit();)
++
++ath_$(BUSNAME)-objs += $(patsubst %,../net80211/%,$(wlan-objs) $(foreach var,wep tkip ccmp acl xauth scan_sta scan_ap,$(wlan_$(var)-objs))) $(foreach RC,$(ATH_RATE),$(patsubst %,../ath_rate/$(RC)/%,$(ath_rate_$(RC)-objs)))
++ifdef LINUX24
++ ath_$(BUSNAME)-linkobjs := $(ath_objs) $(wlan-objs) $(foreach var,wep tkip ccmp acl xauth scan_sta scan_ap,$(wlan_$(var)-objs)) $(foreach RC,$(ATH_RATE),$(ath_rate_$(RC)-objs))
++endif
++
++EXTRA_CFLAGS += -DSINGLE_MODULE -DRC_INIT="$(RC_INIT)" -DRC_EXIT="$(RC_EXIT)" -DRC_DECLARE="$(RC_DECLARE)"
++endif
+
+ INCS += -I$(TOP) -I$(ATH_HAL) -I$(HAL) -I$(WLAN)
+
+@@ -72,13 +88,8 @@ install:
+ test -d $(DESTDIR)/$(KMODPATH) || mkdir -p $(DESTDIR)/$(KMODPATH)
+ install -m 0644 ath_$(BUSNAME).$(KMODSUF) $(DESTDIR)/$(KMODPATH)
+
+-clean:
+- rm -f *~ *.o *.ko *.mod.c .*.cmd
+- rm -f .depend .version .*.o.flags .*.o.d
+- rm -rf .tmp_versions
+-
+ ath_$(BUSNAME).o: $(ath_$(BUSNAME)-objs)
+- $(LD) $(LDOPTS) -o ath_$(BUSNAME).$(KMODSUF) -r $(ath_$(BUSNAME)-objs)
++ $(LD) $(LDOPTS) -o ath_$(BUSNAME).$(KMODSUF) -r $(if $(ath_$(BUSNAME)-linkobjs),$(ath_$(BUSNAME)-linkobjs),$(ath_$(BUSNAME)-objs))
+
+ if_ath_hal.h: $(HAL)/ah.h
+ $(TOP)/scripts/if_ath_hal_generator.pl $< $@
+--- a/net80211/ieee80211_acl.c
++++ b/net80211/ieee80211_acl.c
+@@ -281,16 +281,6 @@ acl_getpolicy(struct ieee80211vap *vap)
+ return as->as_policy;
+ }
+
+-/*
+- * Module glue.
+- */
+-
+-MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+-MODULE_DESCRIPTION("802.11 wireless support: MAC-based ACL policy");
+-#ifdef MODULE_LICENSE
+-MODULE_LICENSE("Dual BSD/GPL");
+-#endif
+-
+ static const struct ieee80211_aclator mac = {
+ .iac_name = "mac",
+ .iac_attach = acl_attach,
+@@ -303,6 +293,18 @@ static const struct ieee80211_aclator ma
+ .iac_getpolicy = acl_getpolicy,
+ };
+
++#include "module.h"
++/*
++ * Module glue.
++ */
++
++MODULE_AUTHOR("Errno Consulting, Sam Leffler");
++MODULE_DESCRIPTION("802.11 wireless support: MAC-based ACL policy");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("Dual BSD/GPL");
++#endif
++
++
+ static int __init
+ init_ieee80211_acl(void)
+ {
+--- a/net80211/ieee80211_crypto_ccmp.c
++++ b/net80211/ieee80211_crypto_ccmp.c
+@@ -686,6 +686,8 @@ ccmp_decrypt(struct ieee80211_key *key,
+ }
+ #undef CCMP_DECRYPT
+
++#include "module.h"
++
+ /*
+ * Module glue.
+ */
+--- a/net80211/ieee80211_crypto_tkip.c
++++ b/net80211/ieee80211_crypto_tkip.c
+@@ -1046,6 +1046,8 @@ tkip_decrypt(struct tkip_ctx *ctx, struc
+ return 1;
+ }
+
++#include "module.h"
++
+ /*
+ * Module glue.
+ */
+--- a/net80211/ieee80211_crypto_wep.c
++++ b/net80211/ieee80211_crypto_wep.c
+@@ -497,6 +497,8 @@ wep_decrypt(struct ieee80211_key *key, s
+ * Module glue.
+ */
+
++#include "module.h"
++
+ MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+ MODULE_DESCRIPTION("802.11 wireless support: WEP cipher");
+ #ifdef MODULE_LICENSE
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -1015,6 +1015,10 @@ static struct notifier_block ieee80211_e
+ static char *version = RELEASE_VERSION;
+ static char *dev_info = "wlan";
+
++extern void ieee80211_auth_setup(void);
++
++#include "module.h"
++
+ MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+ MODULE_DESCRIPTION("802.11 wireless LAN protocol support");
+ #ifdef MODULE_VERSION
+@@ -1024,8 +1028,6 @@ MODULE_VERSION(RELEASE_VERSION);
+ MODULE_LICENSE("Dual BSD/GPL");
+ #endif
+
+-extern void ieee80211_auth_setup(void);
+-
+ static int __init
+ init_wlan(void)
+ {
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -763,15 +763,6 @@ action_tasklet(IEEE80211_TQUEUE_ARG data
+ (*ss->ss_ops->scan_default)(vap, &as->as_selbss);
+ }
+
+-/*
+- * Module glue.
+- */
+-MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+-MODULE_DESCRIPTION("802.11 wireless support: default ap scanner");
+-#ifdef MODULE_LICENSE
+-MODULE_LICENSE("Dual BSD/GPL");
+-#endif
+-
+ static const struct ieee80211_scanner ap_default = {
+ .scan_name = "default",
+ .scan_attach = ap_attach,
+@@ -789,6 +780,16 @@ static const struct ieee80211_scanner ap
+ .scan_default = ap_default_action,
+ };
+
++#include "module.h"
++
++/*
++ * Module glue.
++ */
++MODULE_AUTHOR("Errno Consulting, Sam Leffler");
++MODULE_DESCRIPTION("802.11 wireless support: default ap scanner");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("Dual BSD/GPL");
++#endif
+
+ static int __init
+ init_scanner_ap(void)
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -1208,6 +1208,8 @@ action_tasklet(IEEE80211_TQUEUE_ARG data
+ ieee80211_start_scan(vap, ss->ss_flags, ss->ss_duration, ss->ss_nssid, ss->ss_ssid);
+ }
+
++#include "module.h"
++
+ /*
+ * Module glue.
+ */
+@@ -1217,6 +1219,7 @@ MODULE_DESCRIPTION("802.11 wireless supp
+ MODULE_LICENSE("Dual BSD/GPL");
+ #endif
+
++
+ static int __init
+ init_scanner_sta(void)
+ {
+--- a/net80211/ieee80211_xauth.c
++++ b/net80211/ieee80211_xauth.c
+@@ -65,15 +65,6 @@
+ #include <net80211/ieee80211_var.h>
+
+ /*
+- * Module glue.
+- */
+-MODULE_AUTHOR("Errno Consulting, Sam Leffler");
+-MODULE_DESCRIPTION("802.11 wireless support: external (user mode) authenticator");
+-#ifdef MODULE_LICENSE
+-MODULE_LICENSE("Dual BSD/GPL");
+-#endif
+-
+-/*
+ * One module handles everything for now. May want
+ * to split things up for embedded applications.
+ */
+@@ -85,6 +76,18 @@ static const struct ieee80211_authentica
+ .ia_node_leave = NULL,
+ };
+
++#include "module.h"
++
++/*
++ * Module glue.
++ */
++MODULE_AUTHOR("Errno Consulting, Sam Leffler");
++MODULE_DESCRIPTION("802.11 wireless support: external (user mode) authenticator");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("Dual BSD/GPL");
++#endif
++
++
+ static int __init
+ init_ieee80211_xauth(void)
+ {
+--- a/net80211/Makefile
++++ b/net80211/Makefile
+@@ -40,7 +40,11 @@
+ # Makefile for the 802.11 WLAN modules.
+ #
+ obj := $(firstword $(obj) $(SUBDIRS) .)
+-TOP = $(obj)/..
++
++include $(TOP)/Makefile.inc
++
++ifeq ($(SINGLE_MODULE),)
++
+ #
+ # There is one authenticator mechanism: an in-kernel implementation
+ # (wlan_xauth).
+@@ -59,29 +63,8 @@ MOD_INSTALL := wlan.o wlan_wep.o wlan_tk
+
+ obj-m += $(MOD_INSTALL)
+
+-wlan-objs := if_media.o \
+- ieee80211_skb.o \
+- ieee80211.o ieee80211_beacon.o ieee80211_crypto.o \
+- ieee80211_crypto_none.o ieee80211_input.o ieee80211_node.o \
+- ieee80211_output.o ieee80211_power.o ieee80211_proto.o \
+- ieee80211_scan.o ieee80211_wireless.o ieee80211_linux.o \
+- ieee80211_monitor.o ieee80211_rate.o
+-wlan_wep-objs := ieee80211_crypto_wep.o
+-wlan_tkip-objs := ieee80211_crypto_tkip.o
+-wlan_ccmp-objs := ieee80211_crypto_ccmp.o
+-wlan_acl-objs := ieee80211_acl.o
+-wlan_xauth-objs := ieee80211_xauth.o
+-wlan_scan_sta-objs :=ieee80211_scan_sta.o
+-wlan_scan_ap-objs := ieee80211_scan_ap.o
+-
+-include $(TOP)/Makefile.inc
+-
+ INCS += -I$(TOP) -I$(ATH_HAL) -I$(HAL)
+
+-EXTRA_CFLAGS+=$(INCS) $(COPTS) -DOPT_AH_H=\"public/$(TARGET).opt_ah.h\"
+-
+--include $(TOPDIR)/Rules.make
+-
+ all:
+ $(MAKE) -C $(KERNELPATH) SUBDIRS=$(shell pwd) modules
+
+@@ -108,8 +91,28 @@ install:
+ f=`basename $$i .o`; \
+ install -m 0644 $$f.$(KMODSUF) $(DESTDIR)/$(KMODPATH); \
+ done
++else
++all:
++endif
++
++wlan-objs := if_media.o \
++ ieee80211_skb.o \
++ ieee80211.o ieee80211_beacon.o ieee80211_crypto.o \
++ ieee80211_crypto_none.o ieee80211_input.o ieee80211_node.o \
++ ieee80211_output.o ieee80211_power.o ieee80211_proto.o \
++ ieee80211_scan.o ieee80211_wireless.o ieee80211_linux.o \
++ ieee80211_monitor.o ieee80211_rate.o
++wlan_wep-objs := ieee80211_crypto_wep.o
++wlan_tkip-objs := ieee80211_crypto_tkip.o
++wlan_ccmp-objs := ieee80211_crypto_ccmp.o
++wlan_acl-objs := ieee80211_acl.o
++wlan_xauth-objs := ieee80211_xauth.o
++wlan_scan_sta-objs :=ieee80211_scan_sta.o
++wlan_scan_ap-objs := ieee80211_scan_ap.o
++
++
++EXTRA_CFLAGS+=$(INCS) $(COPTS) -DOPT_AH_H=\"public/$(TARGET).opt_ah.h\"
++
++-include $(TOPDIR)/Rules.make
++
+
+-clean:
+- -rm -f *~ *.o *.ko *.mod.c
+- -rm -f .depend .version .*.o.flags .*.o.d .*.o.cmd .*.ko.cmd
+- -rm -rf .tmp_versions
+--- /dev/null
++++ b/net80211/module.h
+@@ -0,0 +1,19 @@
++#ifdef SINGLE_MODULE
++
++#undef static
++#define static
++#undef module_init
++#undef module_exit
++#define module_init(...)
++#define module_exit(...)
++
++#undef MODULE_AUTHOR
++#undef MODULE_LICENSE
++#undef MODULE_VERSION
++#undef MODULE_DESCRIPTION
++#define MODULE_AUTHOR(...)
++#define MODULE_LICENSE(...)
++#define MODULE_VERSION(...)
++#define MODULE_DESCRIPTION(...)
++
++#endif
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -13780,3 +13780,5 @@ cleanup_ath_buf(struct ath_softc *sc, st
+ return bf;
+ }
+
++
++
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -42,6 +42,7 @@
+ #include <linux/config.h>
+ #endif
+ #include <linux/version.h>
++#include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/skbuff.h>
+ #include <linux/netdevice.h>
+@@ -2015,3 +2016,65 @@ ieee80211_build_sc_ie(struct ieee80211co
+ int ath_debug_global = 0;
+ EXPORT_SYMBOL(ath_debug_global);
+
++#ifdef SINGLE_MODULE
++typedef void (*initfunc)(void);
++
++extern void init_ieee80211_acl(void);
++extern void init_crypto_ccmp(void);
++extern void init_crypto_tkip(void);
++extern void init_crypto_wep(void);
++extern void init_wlan(void);
++extern void init_scanner_ap(void);
++extern void init_scanner_sta(void);
++extern void init_ieee80211_xauth(void);
++
++extern void exit_ieee80211_acl(void);
++extern void exit_crypto_ccmp(void);
++extern void exit_crypto_tkip(void);
++extern void exit_crypto_wep(void);
++extern void exit_wlan(void);
++extern void exit_scanner_ap(void);
++extern void exit_scanner_sta(void);
++extern void exit_ieee80211_xauth(void);
++
++static __initdata initfunc net80211_init[] = {
++ init_wlan,
++ init_ieee80211_acl,
++ init_crypto_ccmp,
++ init_crypto_tkip,
++ init_crypto_wep,
++ init_ieee80211_xauth,
++ init_scanner_ap,
++ init_scanner_sta,
++};
++
++static __exitdata initfunc net80211_exit[] = {
++ exit_crypto_ccmp,
++ exit_crypto_tkip,
++ exit_crypto_wep,
++ exit_scanner_ap,
++ exit_scanner_sta,
++ exit_ieee80211_xauth,
++ exit_ieee80211_acl,
++ exit_wlan,
++};
++
++void net80211_init_module(void)
++{
++ int i;
++ for (i = 0; i < sizeof(net80211_init)/sizeof(net80211_init[0]); i++) {
++ if (net80211_init[i])
++ net80211_init[i]();
++ }
++}
++
++void net80211_exit_module(void)
++{
++ int i;
++ for (i = 0; i < sizeof(net80211_exit)/sizeof(net80211_exit[0]); i++) {
++ if (net80211_exit[i])
++ net80211_exit[i]();
++ }
++}
++
++#endif
+--- a/ath/if_ath_ahb.c
++++ b/ath/if_ath_ahb.c
+@@ -447,10 +447,18 @@ MODULE_SUPPORTED_DEVICE("Atheros WLAN ca
+ MODULE_LICENSE("Dual BSD/GPL");
+ #endif
+
++#ifdef SINGLE_MODULE
++RC_DECLARE
++#endif
++
+ static int __init
+ init_ath_ahb(void)
+ {
+ printk(KERN_INFO "%s: %s\n", dev_info, version);
++#ifdef SINGLE_MODULE
++ net80211_init_module();
++ RC_INIT
++#endif
+ platform_driver_register(&ahb_wmac_driver);
+ ath_sysctl_register();
+
+@@ -463,6 +471,10 @@ exit_ath_ahb(void)
+ {
+ ath_sysctl_unregister();
+ platform_driver_unregister(&ahb_wmac_driver);
++#ifdef SINGLE_MODULE
++ RC_EXIT
++ net80211_exit_module();
++#endif
+ printk(KERN_INFO "%s: driver unloaded\n", dev_info);
+ }
+ module_exit(exit_ath_ahb);
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -415,11 +415,19 @@ MODULE_SUPPORTED_DEVICE("Atheros WLAN ca
+ MODULE_LICENSE("Dual BSD/GPL");
+ #endif
+
++#ifdef SINGLE_MODULE
++RC_DECLARE
++#endif
++
+ static int __init
+ init_ath_pci(void)
+ {
+ printk(KERN_INFO "%s: %s\n", dev_info, version);
+
++#ifdef SINGLE_MODULE
++ net80211_init_module();
++ RC_INIT
++#endif
+ if (pci_register_driver(&ath_pci_driver) < 0) {
+ printk(KERN_ERR "%s: No devices found, driver not installed.\n", dev_info);
+ return (-ENODEV);
+@@ -434,6 +442,10 @@ exit_ath_pci(void)
+ {
+ ath_sysctl_unregister();
+ pci_unregister_driver(&ath_pci_driver);
++#ifdef SINGLE_MODULE
++ RC_EXIT
++ net80211_exit_module();
++#endif
+
+ printk(KERN_INFO "%s: driver unloaded\n", dev_info);
+ }
+--- a/ath_rate/minstrel/Makefile
++++ b/ath_rate/minstrel/Makefile
+@@ -39,9 +39,7 @@
+ # Makefile for the Atheros Rate Control Support.
+ #
+ obj := $(firstword $(obj) $(SUBDIRS) .)
+-TOP = $(obj)/../..
+
+-obj-m += ath_rate_minstrel.o
+ ath_rate_minstrel-objs := minstrel.o
+
+ include $(TOP)/Makefile.inc
+@@ -50,6 +48,10 @@ INCS += -I$(TOP) -I$(ATH) -I$(ATH_HAL) -
+
+ EXTRA_CFLAGS+= $(INCS) $(COPTS) -DOPT_AH_H=\"public/$(TARGET).opt_ah.h\"
+
++ifeq ($(SINGLE_MODULE),)
++
++obj-m += ath_rate_minstrel.o
++
+ -include $(TOPDIR)/Rules.make
+
+ all:
+@@ -59,10 +61,9 @@ install:
+ test -d $(DESTDIR)/$(KMODPATH) || mkdir -p $(DESTDIR)/$(KMODPATH)
+ install -m 0644 ath_rate_minstrel.$(KMODSUF) $(DESTDIR)/$(KMODPATH)
+
+-clean:
+- -rm -f *~ *.o *.ko *.mod.c
+- -rm -f .depend .version .*.o.flags .*.o.d .*.o.cmd .*.ko.cmd
+- -rm -rf .tmp_versions
+-
+ ath_rate_minstrel.o: $(ath_rate_minstrel-objs)
+ $(LD) $(LDOPTS) -o ath_rate_minstrel.$(KMODSUF) -r $(ath_rate_minstrel-objs)
++else
++all:
++install:
++endif
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -945,6 +945,8 @@ static struct ieee80211_rate_ops ath_rat
+ .dynamic_proc_register = ath_rate_dynamic_proc_register,
+ };
+
++#include <net80211/module.h>
++
+ MODULE_AUTHOR("John Bicket/Derek Smithies");
+ MODULE_DESCRIPTION("Minstrel Rate bit-rate selection algorithm for Atheros devices");
+ #ifdef MODULE_VERSION
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -47,6 +47,7 @@
+ #include <net80211/ieee80211_power.h>
+ #include <net80211/ieee80211_proto.h>
+ #include <net80211/ieee80211_scan.h>
++#include "symbol.h"
+
+ /* NB:
+ * - Atheros chips use 6 bits when power is specified in whole dBm units, with
+@@ -740,6 +741,8 @@ void ieee80211_dfs_action(struct ieee802
+ void ieee80211_expire_channel_excl_restrictions(struct ieee80211com *);
+ void ieee80211_setpuregbasicrates(struct ieee80211_rateset *rs);
+ int ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
++void net80211_init_module(void);
++void net80211_exit_module(void);
+
+ /*
+ * Iterate through ic_channels to enumerate all distinct ic_ieee channel numbers.
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -521,7 +521,10 @@ extern struct sk_buff * ieee80211_getmgt
+ #define IF_DRAIN(_q) skb_queue_drain(_q)
+ extern void skb_queue_drain(struct sk_buff_head *q);
+
+-#ifndef __MOD_INC_USE_COUNT
++#ifdef SINGLE_MODULE
++#define _MOD_DEC_USE(_m) do {} while(0)
++#define _MOD_INC_USE(_m, _err) do {} while(0)
++#elif !defined(__MOD_INC_USE_COUNT)
+ #define _MOD_INC_USE(_m, _err) \
+ if (!try_module_get(_m)) { \
+ printk(KERN_WARNING "%s: try_module_get failed\n", \
+--- a/ath_rate/sample/Makefile
++++ b/ath_rate/sample/Makefile
+@@ -39,9 +39,7 @@
+ # Makefile for the Atheros Rate Control Support.
+ #
+ obj := $(firstword $(obj) $(SUBDIRS) .)
+-TOP = $(obj)/../..
+
+-obj-m += ath_rate_sample.o
+ ath_rate_sample-objs := sample.o
+
+ include $(TOP)/Makefile.inc
+@@ -50,6 +48,9 @@ INCS += -I$(TOP) -I$(ATH) -I$(ATH_HAL) -
+
+ EXTRA_CFLAGS+= $(INCS) $(COPTS) -DOPT_AH_H=\"public/$(TARGET).opt_ah.h\"
+
++ifeq ($(SINGLE_MODULE),)
++obj-m += ath_rate_sample.o
++
+ -include $(TOPDIR)/Rules.make
+
+ all:
+@@ -59,10 +60,9 @@ install:
+ test -d $(DESTDIR)/$(KMODPATH) || mkdir -p $(DESTDIR)/$(KMODPATH)
+ install -m 0644 ath_rate_sample.$(KMODSUF) $(DESTDIR)/$(KMODPATH)
+
+-clean:
+- -rm -f *~ *.o *.ko *.mod.c
+- -rm -f .depend .version .*.o.flags .*.o.d .*.o.cmd .*.ko.cmd
+- -rm -rf .tmp_versions
+-
+ ath_rate_sample.o: $(ath_rate_sample-objs)
+ $(LD) $(LDOPTS) -o ath_rate_sample.$(KMODSUF) -r $(ath_rate_sample-objs)
++else
++all:
++install:
++endif
+--- a/Makefile
++++ b/Makefile
+@@ -41,7 +41,7 @@
+ #
+
+ obj := $(firstword $(obj) $(SUBDIRS) .)
+-TOP = $(obj)
++export TOP:=$(if $(wildcard $(firstword $(SUBDIRS))/Makefile.inc),$(firstword $(SUBDIRS)),$(CURDIR))
+
+ ifneq (svnversion.h,$(MAKECMDGOALS))
+ include $(TOP)/Makefile.inc
+@@ -54,7 +54,7 @@ all: modules tools
+ modules: configcheck svnversion.h
+ ifdef LINUX24
+ for i in $(obj-y); do \
+- $(MAKE) -C $$i || exit 1; \
++ $(MAKE) -C $$i TOP="$(TOP)" || exit 1; \
+ done
+ else
+ $(MAKE) -C $(KERNELPATH) SUBDIRS=$(shell pwd) modules
+@@ -89,7 +89,7 @@ install-modules: modules
+ sh scripts/find-madwifi-modules.sh -r $(KERNELRELEASE) $(DESTDIR)
+
+ for i in $(obj-y); do \
+- $(MAKE) -C $$i install || exit 1; \
++ $(MAKE) -C $$i install TOP="$(TOP)" || exit 1; \
+ done
+ ifeq ($(DESTDIR),)
+ (export KMODPATH=$(KMODPATH); /sbin/depmod -ae $(KERNELRELEASE))
+@@ -114,12 +114,21 @@ reinstall-tools: uninstall-tools install
+ reinstall-modules: uninstall-modules install-modules
+
+ clean:
+- for i in $(obj-y); do \
+- $(MAKE) -C $$i clean; \
+- done
+- -$(MAKE) -C $(TOOLS) clean
+- rm -rf .tmp_versions
++ -find $(obj-y) -name '*~' \
++ -or -name '*.o' \
++ -or -name '*.o.d' \
++ -or -name '*.o.cmd' \
++ -or -name '*.o.flags' \
++ -or -name '*.ko' \
++ -or -name '*.ko.cmd' \
++ -or -name '*.mod.c' \
++ -or -name '.depend' \
++ -or -name '.version' \
++ -or -name '.symvers' | \
++ xargs -r rm -f
+ rm -f *.symvers svnversion.h
++ rm -rf .tmp_versions
++ make -C tools clean
+
+ info:
+ @echo "The following settings will be used for compilation:"
+@@ -135,18 +144,6 @@ info:
+ @echo "KMODPATH : $(KMODPATH)"
+ @echo "KMODSUF : $(KMODSUF)"
+
+-sanitycheck:
+- @echo -n "Checking requirements... "
+-
+- @# check if specified rate control is available
+- @if [ ! -d $(ATH_RATE) ]; then \
+- echo "FAILED"; \
+- echo "Selected rate control $(ATH_RATE) not available."; \
+- exit 1; \
+- fi
+-
+- @echo "ok."
+-
+ .PHONY: release
+ release:
+ sh scripts/make-release.bash
+@@ -155,7 +152,7 @@ release:
+ unload:
+ bash scripts/madwifi-unload
+
+-configcheck: sanitycheck
++configcheck:
+ @echo -n "Checking kernel configuration... "
+
+ @# check version of kernel
+--- a/Makefile.inc
++++ b/Makefile.inc
+@@ -68,6 +68,9 @@ endif
+ export KERNELPATH
+ endif
+
++# build net80211 and ath_ahb/ath_pci into a single module
++export SINGLE_MODULE=1
++
+ # KERNELRELEASE is the target kernel's version. It's always taken from
+ # the kernel build tree. Kernel Makefile doesn't always know the exact
+ # kernel version (especially for vendor stock kernels), so we get it
+@@ -100,6 +103,7 @@ export ARCH
+ include $(TOP)/ath_hal/ah_target.inc
+ export TARGET
+ COPTS += -DTARGET='"$(TARGET)"'
++COPTS += -DCONFIG_ATHEROS_RATE_DEFAULT='"$(firstword $(ATH_RATE))"'
+
+ # KMODPATH nominates the directory where the modules will be
+ # installed to
+@@ -141,7 +145,7 @@ ATH= $(TOP)/ath
+ #
+ # Path to the rate control algorithms.
+ #
+-ATH_RATE= $(TOP)/ath_rate
++ATH_RATE= minstrel
+ #
+ # Path to the userspace utilities.
+ #
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -991,6 +991,8 @@ static struct ieee80211_rate_ops ath_rat
+ .dynamic_proc_register = ath_rate_dynamic_proc_register,
+ };
+
++#include <net80211/module.h>
++
+ MODULE_AUTHOR("John Bicket");
+ MODULE_DESCRIPTION("SampleRate bit-rate selection algorithm for Atheros devices");
+ #ifdef MODULE_VERSION
+@@ -1000,18 +1002,17 @@ MODULE_VERSION(RELEASE_VERSION);
+ MODULE_LICENSE("Dual BSD/GPL");
+ #endif
+
+-static int __init
+-init_ath_rate_sample(void)
++static int __init ath_rate_sample_init(void)
+ {
+ printk(KERN_INFO "%s: %s\n", dev_info, version);
+ return ieee80211_rate_register(&ath_rate_ops);
+ }
+-module_init(init_ath_rate_sample);
++module_init(ath_rate_sample_init);
+
+ static void __exit
+-exit_ath_rate_sample(void)
++ath_rate_sample_exit(void)
+ {
+ ieee80211_rate_unregister(&ath_rate_ops);
+ printk(KERN_INFO "%s: unloaded\n", dev_info);
+ }
+-module_exit(exit_ath_rate_sample);
++module_exit(ath_rate_sample_exit);
+--- a/net80211/if_media.h
++++ b/net80211/if_media.h
+@@ -42,6 +42,7 @@
+ #define _NET_IF_MEDIA_H_
+
+ #include <net80211/ieee80211_linux.h>
++#include "symbol.h"
+
+ /*
+ * Prototypes and definitions for BSD/OS-compatible network interface
+--- /dev/null
++++ b/net80211/symbol.h
+@@ -0,0 +1,4 @@
++#ifdef SINGLE_MODULE
++#undef EXPORT_SYMBOL
++#define EXPORT_SYMBOL(...)
++#endif
+--- a/ath_rate/Makefile
++++ b/ath_rate/Makefile
+@@ -1,7 +1,7 @@
+ obj := $(firstword $(obj) $(SUBDIRS) .)
+ TOP = $(obj)/..
+
+-obj-y := amrr/ onoe/ sample/ minstrel/
++obj-y := sample/ minstrel/
+
+ include $(TOP)/Makefile.inc
+
diff --git a/package/madwifi/patches/447-sta_reconnect.patch b/package/madwifi/patches/447-sta_reconnect.patch
new file mode 100644
index 000000000..960d1b8ed
--- /dev/null
+++ b/package/madwifi/patches/447-sta_reconnect.patch
@@ -0,0 +1,25 @@
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -647,8 +647,11 @@ ieee80211_sta_join1(struct ieee80211_nod
+ (vap->iv_state == IEEE80211_S_RUN) && bssid_equal(obss, selbs)); */
+ vap->iv_bss = selbs;
+ IEEE80211_ADDR_COPY(vap->iv_bssid, selbs->ni_bssid);
+- if (obss != NULL)
++ if (obss != NULL) {
++ if (obss->ni_table)
++ ieee80211_node_leave(obss);
+ ieee80211_unref_node(&obss);
++ }
+ ic->ic_bsschan = selbs->ni_chan;
+ ic->ic_curchan = ic->ic_bsschan;
+ ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -1602,7 +1602,6 @@ __ieee80211_newstate(struct ieee80211vap
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ break;
+ case IEEE80211_S_RUN:
+- ieee80211_node_leave(ni);
+ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
+ /* NB: caller specifies ASSOC/REASSOC by arg */
+ IEEE80211_SEND_MGMT(ni, arg ?
diff --git a/package/madwifi/patches/448-beacon_handling_fixes.patch b/package/madwifi/patches/448-beacon_handling_fixes.patch
new file mode 100644
index 000000000..9c0f912e4
--- /dev/null
+++ b/package/madwifi/patches/448-beacon_handling_fixes.patch
@@ -0,0 +1,407 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -160,7 +160,7 @@ static int ath_check_beacon_done(struct
+ static void ath_beacon_send(struct ath_softc *, int *, uint64_t hw_tsf);
+ static void ath_beacon_return(struct ath_softc *, struct ath_buf *);
+ static void ath_beacon_free(struct ath_softc *);
+-static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *);
++static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *, int);
+ static void ath_hw_beacon_stop(struct ath_softc *sc);
+ static int ath_desc_alloc(struct ath_softc *);
+ static void ath_desc_free(struct ath_softc *);
+@@ -387,13 +387,11 @@ static void ath_set_timing(struct ath_so
+ /* calibrate every 30 secs in steady state but check every second at first. */
+ static int ath_calinterval = ATH_SHORT_CALINTERVAL;
+ static int ath_xchanmode = AH_TRUE; /* enable extended channels */
+-static int ath_maxvaps = ATH_MAXVAPS_DEFAULT; /* set default maximum vaps */
+ static int bstuck_thresh = BSTUCK_THRESH; /* Stuck beacon count required for reset */
+ static char *autocreate = NULL;
+ static char *ratectl = DEF_RATE_CTL;
+ static int rfkill = 0;
+ static int tpc = 1;
+-static int maxvaps = -1;
+ static int xchanmode = -1;
+ #include "ath_wprobe.c"
+ static int beacon_cal = 1;
+@@ -432,7 +430,6 @@ static struct notifier_block ath_event_b
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+ MODULE_PARM(beacon_cal, "i");
+-MODULE_PARM(maxvaps, "i");
+ MODULE_PARM(xchanmode, "i");
+ MODULE_PARM(rfkill, "i");
+ #ifdef ATH_CAP_TPC
+@@ -444,7 +441,6 @@ MODULE_PARM(ratectl, "s");
+ #else
+ #include <linux/moduleparam.h>
+ module_param(beacon_cal, int, 0600);
+-module_param(maxvaps, int, 0600);
+ module_param(xchanmode, int, 0600);
+ module_param(rfkill, int, 0600);
+ #ifdef ATH_CAP_TPC
+@@ -454,7 +450,6 @@ module_param(bstuck_thresh, int, 0600);
+ module_param(autocreate, charp, 0600);
+ module_param(ratectl, charp, 0600);
+ #endif
+-MODULE_PARM_DESC(maxvaps, "Maximum VAPs");
+ MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode");
+ MODULE_PARM_DESC(rfkill, "Enable/disable RFKILL capability");
+ #ifdef ATH_CAP_TPC
+@@ -512,7 +507,7 @@ MODULE_PARM_DESC(ieee80211_debug, "Load-
+ * and use the higher bits as the index of the VAP.
+ */
+ #define ATH_SET_VAP_BSSID_MASK(bssid_mask) \
+- ((bssid_mask)[0] &= ~(((ath_maxvaps-1) << 2) | 0x02))
++ ((bssid_mask)[0] &= ~(((ATH_MAXVAPS_BCN-1) << 2) | 0x02))
+ #define ATH_GET_VAP_ID(bssid) ((bssid)[0] >> 2)
+ #define ATH_SET_VAP_BSSID(bssid, id) \
+ do { \
+@@ -604,8 +599,8 @@ ath_attach(u_int16_t devid, struct net_d
+
+ /* Allocate space for dynamically determined maximum VAP count */
+ sc->sc_bslot =
+- kmalloc(ath_maxvaps * sizeof(struct ieee80211vap*), GFP_KERNEL);
+- memset(sc->sc_bslot, 0, ath_maxvaps * sizeof(struct ieee80211vap*));
++ kmalloc(ATH_MAXVAPS_BCN * sizeof(struct ieee80211vap*), GFP_KERNEL);
++ memset(sc->sc_bslot, 0, ATH_MAXVAPS_BCN * sizeof(struct ieee80211vap*));
+
+ /*
+ * Cache line size is used to size and align various
+@@ -694,13 +689,6 @@ ath_attach(u_int16_t devid, struct net_d
+ for (i = 0; i < sc->sc_keymax; i++)
+ ath_hal_keyreset(ah, i);
+
+- if (maxvaps != -1) {
+- ath_maxvaps = maxvaps;
+- if (ath_maxvaps < ATH_MAXVAPS_MIN)
+- ath_maxvaps = ATH_MAXVAPS_MIN;
+- else if (ath_maxvaps > ATH_MAXVAPS_MAX)
+- ath_maxvaps = ATH_MAXVAPS_MAX;
+- }
+ if (xchanmode != -1)
+ ath_xchanmode = xchanmode;
+ error = ath_getchannels(dev);
+@@ -1349,12 +1337,6 @@ ath_vap_create(struct ieee80211com *ic,
+ return NULL;
+ }
+
+- if (sc->sc_nvaps >= ath_maxvaps) {
+- EPRINTF(sc, "Too many virtual APs (%d already exist).\n",
+- sc->sc_nvaps);
+- return NULL;
+- }
+-
+ dev = alloc_etherdev(sizeof(struct ath_vap) + sc->sc_rc->arc_vap_space);
+ if (dev == NULL) {
+ /* XXX msg */
+@@ -1424,7 +1406,7 @@ ath_vap_create(struct ieee80211com *ic,
+ TAILQ_FOREACH(v, &ic->ic_vaps, iv_next)
+ id_mask |= (1 << ATH_GET_VAP_ID(v->iv_myaddr));
+
+- for (id = 0; id < ath_maxvaps; id++) {
++ for (id = 0; id < ATH_MAXVAPS_BCN; id++) {
+ /* get the first available slot */
+ if ((id_mask & (1 << id)) == 0) {
+ ATH_SET_VAP_BSSID(vap->iv_myaddr, id);
+@@ -1451,11 +1433,11 @@ ath_vap_create(struct ieee80211com *ic,
+ /* Assign the VAP to a beacon xmit slot. As
+ * above, this cannot fail to find one. */
+ avp->av_bslot = 0;
+- for (slot = 0; slot < ath_maxvaps; slot++)
++ for (slot = 0; slot < ATH_MAXVAPS_BCN; slot++)
+ if (sc->sc_bslot[slot] == NULL) {
+ /* XXX: Hack, space out slots to better
+ * deal with misses. */
+- if (slot + 1 < ath_maxvaps &&
++ if (slot + 1 < ATH_MAXVAPS_BCN &&
+ sc->sc_bslot[slot+1] == NULL) {
+ avp->av_bslot = slot + 1;
+ break;
+@@ -1463,8 +1445,11 @@ ath_vap_create(struct ieee80211com *ic,
+ avp->av_bslot = slot;
+ /* NB: keep looking for a double slot */
+ }
+- KASSERT(sc->sc_bslot[avp->av_bslot] == NULL,
+- ("beacon slot %u not empty?", avp->av_bslot));
++ if (sc->sc_bslot[avp->av_bslot]) {
++ free_netdev(dev);
++ return NULL;
++ }
++
+ sc->sc_bslot[avp->av_bslot] = vap;
+ sc->sc_nbcnvaps++;
+
+@@ -1475,15 +1460,7 @@ ath_vap_create(struct ieee80211com *ic,
+ * of staggered beacons.
+ */
+ /* XXX check for beacon interval too small */
+- if (ath_maxvaps > 4) {
+- DPRINTF(sc, ATH_DEBUG_BEACON,
+- "Staggered beacons are not "
+- "possible with maxvaps set "
+- "to %d.\n", ath_maxvaps);
+- sc->sc_stagbeacons = 0;
+- } else {
+- sc->sc_stagbeacons = 1;
+- }
++ sc->sc_stagbeacons = 1;
+ }
+ DPRINTF(sc, ATH_DEBUG_BEACON, "sc->sc_stagbeacons %sabled\n",
+ (sc->sc_stagbeacons ? "en" : "dis"));
+@@ -1553,7 +1530,7 @@ ath_vap_create(struct ieee80211com *ic,
+ if (ath_startrecv(sc) != 0) /* restart recv */
+ EPRINTF(sc, "Unable to start receive logic.\n");
+ if (sc->sc_beacons)
+- ath_beacon_config(sc, NULL); /* restart beacons */
++ ath_beacon_config(sc, NULL, 0); /* restart beacons */
+ ath_hal_intrset(ah, sc->sc_imask);
+ }
+
+@@ -1681,7 +1658,7 @@ ath_vap_delete(struct ieee80211vap *vap)
+ if (ath_startrecv(sc) != 0) /* restart recv. */
+ EPRINTF(sc, "Unable to start receive logic.\n");
+ if (sc->sc_beacons)
+- ath_beacon_config(sc, NULL); /* restart beacons */
++ ath_beacon_config(sc, NULL, 0); /* restart beacons */
+ ath_hal_intrset(ah, sc->sc_imask);
+ }
+ }
+@@ -3066,7 +3043,7 @@ ath_reset(struct net_device *dev)
+ */
+ ath_chan_change(sc, c);
+ if (sc->sc_beacons)
+- ath_beacon_config(sc, NULL); /* restart beacons */
++ ath_beacon_config(sc, NULL, 1); /* restart beacons */
+ ath_hal_intrset(ah, sc->sc_imask);
+ ath_set_ack_bitrate(sc, sc->sc_ackrate);
+ netif_wake_queue(dev); /* restart xmit */
+@@ -4763,7 +4740,7 @@ ath_check_beacon_done(struct ath_softc *
+ /*
+ * check if the last beacon went out with the mode change flag set.
+ */
+- for (slot = 0; slot < ath_maxvaps; slot++) {
++ for (slot = 0; slot < ATH_MAXVAPS_BCN; slot++) {
+ if (sc->sc_bslot[slot]) {
+ vap = sc->sc_bslot[slot];
+ break;
+@@ -4968,7 +4945,7 @@ ath_beacon_alloc_internal(struct ath_sof
+ * has a timestamp in one beacon interval while the
+ * others get a timestamp aligned to the next interval.
+ */
+- tuadjust = (ni->ni_intval * (ath_maxvaps - avp->av_bslot)) / ath_maxvaps;
++ tuadjust = (ni->ni_intval * (ATH_MAXVAPS_BCN - avp->av_bslot)) / ATH_MAXVAPS_BCN;
+ tsfadjust = cpu_to_le64(tuadjust << 10); /* TU->TSF */
+
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+@@ -5361,8 +5338,8 @@ ath_beacon_send(struct ath_softc *sc, in
+ u_int32_t tsftu;
+
+ tsftu = hw_tsf >> 10; /* NB: 64 -> 32: See note far above. */
+- slot = ((tsftu % ic->ic_lintval) * ath_maxvaps) / ic->ic_lintval;
+- vap = sc->sc_bslot[(slot + 1) % ath_maxvaps];
++ slot = ((tsftu % ic->ic_lintval) * ATH_MAXVAPS_BCN) / ic->ic_lintval;
++ vap = sc->sc_bslot[(slot + 1) % ATH_MAXVAPS_BCN];
+ DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+ "Slot %d [tsf %llu tsftu %llu intval %u] vap %p\n",
+ slot, (unsigned long long)hw_tsf,
+@@ -5377,7 +5354,7 @@ ath_beacon_send(struct ath_softc *sc, in
+ u_int32_t *bflink = NULL;
+
+ /* XXX: rotate/randomize order? */
+- for (slot = 0; slot < ath_maxvaps; slot++) {
++ for (slot = 0; slot < ATH_MAXVAPS_BCN; slot++) {
+ if ((vap = sc->sc_bslot[slot]) != NULL) {
+ if ((bf = ath_beacon_generate(
+ sc, vap,
+@@ -5418,7 +5395,7 @@ ath_beacon_send(struct ath_softc *sc, in
+ * again. If we miss a beacon for that slot then we'll be
+ * slow to transition but we'll be sure at least one beacon
+ * interval has passed. When bursting slot is always left
+- * set to ath_maxvaps so this check is a no-op.
++ * set to ATH_MAXVAPS_BCN so this check is a no-op.
+ */
+ /* XXX locking */
+ if (sc->sc_updateslot == UPDATE) {
+@@ -5526,7 +5503,7 @@ ath_beacon_free(struct ath_softc *sc)
+ * (2^(32 + 10 - 1) - 1)us is a really long time.
+ */
+ static void
+-ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
++ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap, int reset)
+ {
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_hal *ah = sc->sc_ah;
+@@ -5553,7 +5530,7 @@ ath_beacon_config(struct ath_softc *sc,
+ /* We should reset hw TSF only once, so we increment
+ * ni_tstamp.tsf to avoid resetting the hw TSF multiple
+ * times */
+- if (tsf == 0) {
++ if (tsf == 0 || reset) {
+ reset_tsf = 1;
+ ni->ni_tstamp.tsf = cpu_to_le64(1);
+ }
+@@ -5567,7 +5544,7 @@ ath_beacon_config(struct ath_softc *sc,
+ /* NB: the beacon interval is kept internally in TUs */
+ intval = ic->ic_lintval & HAL_BEACON_PERIOD;
+ if (sc->sc_stagbeacons)
+- intval /= ath_maxvaps; /* for staggered beacons */
++ intval /= ATH_MAXVAPS_BCN; /* for staggered beacons */
+ if ((sc->sc_nostabeacons) &&
+ (vap->iv_opmode == IEEE80211_M_HOSTAP))
+ reset_tsf = 1;
+@@ -5583,31 +5560,24 @@ ath_beacon_config(struct ath_softc *sc,
+ * time */
+ nexttbtt = intval;
+ } else if (intval) { /* NB: can be 0 for monitor mode */
+- if (tsf == 1) {
+- /* We have not received any beacons or probe
+- * responses. Since a beacon should be sent
+- * every 'intval' ms, we compute the next
+- * beacon timestamp using the hardware TSF. We
+- * ensure that it is at least FUDGE TUs ahead
+- * of the current TSF. Otherwise, we use the
+- * next beacon timestamp again */
+- nexttbtt = roundup(hw_tsftu + FUDGE, intval);
+- }
+- else if (ic->ic_opmode == IEEE80211_M_IBSS) {
+- if (tsf > hw_tsf) {
+- /* We received a beacon, but the HW TSF has
+- * not been updated (otherwise hw_tsf > tsf)
+- * We cannot use the hardware TSF, so we
+- * wait to synchronize beacons again. */
+- sc->sc_syncbeacon = 1;
+- goto ath_beacon_config_debug;
+- } else {
+- /* Normal case: we received a beacon to which
+- * we have synchronized. Make sure that nexttbtt
+- * is at least FUDGE TU ahead of hw_tsf */
+- nexttbtt = tsftu + roundup(hw_tsftu + FUDGE -
+- tsftu, intval);
+- }
++ if ((tsf > hw_tsf) && (ic->ic_opmode == IEEE80211_M_IBSS)) {
++ /* We received a beacon, but the HW TSF has
++ * not been updated (otherwise hw_tsf > tsf)
++ * We cannot use the hardware TSF, so we
++ * wait to synchronize beacons again. */
++ sc->sc_syncbeacon = 1;
++ goto ath_beacon_config_debug;
++ } else if ((tsftu + FUDGE) > hw_tsftu) {
++ if (tsftu > hw_tsftu + 2 * intval)
++ nexttbtt = roundup(hw_tsftu + FUDGE, intval);
++ else
++ nexttbtt = tsftu;
++ } else {
++ /* Normal case: we received a beacon to which
++ * we have synchronized. Make sure that nexttbtt
++ * is at least FUDGE TU ahead of hw_tsf */
++ nexttbtt = tsftu + roundup(hw_tsftu + FUDGE -
++ tsftu, intval);
+ }
+ }
+
+@@ -5730,9 +5700,6 @@ ath_beacon_config(struct ath_softc *sc,
+ ath_beacon_dturbo_config(vap, intval &
+ ~(HAL_BEACON_RESET_TSF | HAL_BEACON_ENA));
+ #endif
+- if ((nexttbtt & HAL_BEACON_PERIOD) - (ath_hal_gettsf32(ah) >> 10)
+- <= ath_hal_sw_beacon_response_time)
+- nexttbtt += intval;
+ sc->sc_nexttbtt = nexttbtt;
+
+ /* stop beacons before reconfiguring the timers to avoid race
+@@ -5889,7 +5856,7 @@ ath_desc_alloc(struct ath_softc *sc)
+
+ /* XXX allocate beacon state together with VAP */
+ error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
+- "beacon", ath_maxvaps, 1);
++ "beacon", ATH_MAXVAPS_BCN, 1);
+ if (error != 0) {
+ ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+ BUS_DMA_TODEVICE);
+@@ -6680,7 +6647,7 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ /* Resync beacon timers using the tsf of the
+ * beacon frame we just received. */
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_APPIE_UPDATE;
+- ath_beacon_config(sc, vap);
++ ath_beacon_config(sc, vap, 0);
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "Updated beacon timers\n");
+ }
+@@ -9359,7 +9326,7 @@ ath_chan_set(struct ath_softc *sc, struc
+ * HW seems to turn off beacons during turbo mode switch.
+ */
+ if (sc->sc_beacons && !sc->sc_dfs_cac)
+- ath_beacon_config(sc, NULL);
++ ath_beacon_config(sc, NULL, 0);
+ /*
+ * Re-enable interrupts.
+ */
+@@ -9813,7 +9780,7 @@ ath_newstate(struct ieee80211vap *vap, e
+ ATH_DEBUG_BEACON_PROC,
+ "Beacons reconfigured by %p[%s]!\n",
+ vap, vap->iv_nickname);
+- ath_beacon_config(sc, vap);
++ ath_beacon_config(sc, vap, 1);
+ sc->sc_beacons = 1;
+ }
+ } else {
+@@ -9948,9 +9915,6 @@ ath_dfs_cac_completed(unsigned long data
+ }
+ netif_wake_queue(dev);
+ ath_reset(dev);
+- if (sc->sc_beacons) {
+- ath_beacon_config(sc, NULL);
+- }
+ dev->watchdog_timeo = 5 * HZ; /* restore normal timeout */
+ } else {
+ do_gettimeofday(&tv);
+@@ -11473,9 +11437,6 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_OUTDOOR:
+ val = ic->ic_country_outdoor;
+ break;
+- case ATH_MAXVAPS:
+- val = ath_maxvaps;
+- break;
+ case ATH_REGDOMAIN:
+ ath_hal_getregdomain(ah, &val);
+ break;
+@@ -11606,12 +11567,6 @@ static const ctl_table ath_sysctl_templa
+ .extra2 = (void *)ATH_OUTDOOR,
+ },
+ { .ctl_name = CTL_AUTO,
+- .procname = "maxvaps",
+- .mode = 0444,
+- .proc_handler = ath_sysctl_halparam,
+- .extra2 = (void *)ATH_MAXVAPS,
+- },
+- { .ctl_name = CTL_AUTO,
+ .procname = "regdomain",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+@@ -11928,13 +11883,6 @@ static ctl_table ath_static_sysctls[] =
+ },
+ #endif
+ { .ctl_name = CTL_AUTO,
+- .procname = "maxvaps",
+- .mode = 0444,
+- .data = &ath_maxvaps,
+- .maxlen = sizeof(ath_maxvaps),
+- .proc_handler = proc_dointvec
+- },
+- { .ctl_name = CTL_AUTO,
+ .procname = "xchanmode",
+ .mode = 0444,
+ .data = &ath_xchanmode,
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -211,9 +211,7 @@ static inline struct net_device *_alloc_
+ #define ATH_RXBUF 40 /* number of RX buffers */
+ #define ATH_TXBUF 200 /* number of TX buffers */
+
+-#define ATH_MAXVAPS_MIN 2 /* minimum number of beacon buffers */
+-#define ATH_MAXVAPS_MAX 64 /* maximum number of beacon buffers */
+-#define ATH_MAXVAPS_DEFAULT 4 /* default number of beacon buffers */
++#define ATH_MAXVAPS_BCN 4 /* maximum number of beacon buffers */
+
+ /* free buffer threshold to restart net dev */
+ #define ATH_TXBUF_FREE_THRESHOLD (ATH_TXBUF / 20)
diff --git a/package/madwifi/patches/449-fix_txbuf_leak.patch b/package/madwifi/patches/449-fix_txbuf_leak.patch
new file mode 100644
index 000000000..31f2fef3a
--- /dev/null
+++ b/package/madwifi/patches/449-fix_txbuf_leak.patch
@@ -0,0 +1,10 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -3697,6 +3697,7 @@ ff_bypass:
+ */
+ skb = ieee80211_encap(ni, skb, &framecnt);
+ if (skb == NULL) {
++ STAILQ_INSERT_TAIL(&bf_head, bf, bf_list);
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "Dropping; encapsulation failure\n");
+ sc->sc_stats.ast_tx_encap++;
diff --git a/package/madwifi/patches/450-calibration.patch b/package/madwifi/patches/450-calibration.patch
new file mode 100644
index 000000000..87397903d
--- /dev/null
+++ b/package/madwifi/patches/450-calibration.patch
@@ -0,0 +1,177 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -394,7 +394,6 @@ static int rfkill = 0;
+ static int tpc = 1;
+ static int xchanmode = -1;
+ #include "ath_wprobe.c"
+-static int beacon_cal = 1;
+
+ static const struct ath_hw_detect generic_hw_info = {
+ .vendor_name = "Unknown",
+@@ -429,7 +428,6 @@ static struct notifier_block ath_event_b
+ };
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+-MODULE_PARM(beacon_cal, "i");
+ MODULE_PARM(xchanmode, "i");
+ MODULE_PARM(rfkill, "i");
+ #ifdef ATH_CAP_TPC
+@@ -440,7 +438,6 @@ MODULE_PARM(autocreate, "s");
+ MODULE_PARM(ratectl, "s");
+ #else
+ #include <linux/moduleparam.h>
+-module_param(beacon_cal, int, 0600);
+ module_param(xchanmode, int, 0600);
+ module_param(rfkill, int, 0600);
+ #ifdef ATH_CAP_TPC
+@@ -825,6 +822,7 @@ ath_attach(u_int16_t devid, struct net_d
+ error = EIO;
+ goto bad2;
+ }
++ sc->sc_cal_interval = ath_calinterval;
+ init_timer(&sc->sc_cal_ch);
+ sc->sc_cal_ch.function = ath_calibrate;
+ sc->sc_cal_ch.data = (unsigned long) dev;
+@@ -2737,8 +2735,7 @@ ath_stop_locked(struct net_device *dev)
+ }
+ if (!sc->sc_invalid) {
+ del_timer_sync(&sc->sc_dfs_cac_timer);
+- if (!sc->sc_beacon_cal)
+- del_timer_sync(&sc->sc_cal_ch);
++ del_timer_sync(&sc->sc_cal_ch);
+ }
+ ath_draintxq(sc);
+ if (!sc->sc_invalid) {
+@@ -2763,10 +2760,9 @@ static void ath_set_beacon_cal(struct at
+ if (val) {
+ del_timer_sync(&sc->sc_cal_ch);
+ } else {
+- sc->sc_cal_ch.expires = jiffies + (ath_calinterval * HZ);
+- add_timer(&sc->sc_cal_ch);
++ mod_timer(&sc->sc_cal_ch, jiffies + (sc->sc_cal_interval * HZ));
+ }
+- sc->sc_beacon_cal = !!val && beacon_cal;
++ sc->sc_beacon_cal = !!val;
+ }
+
+ /*
+@@ -3008,7 +3004,7 @@ ath_reset(struct net_device *dev)
+ * XXX: starting the calibration too early seems to lead to
+ * problems with the beacons.
+ */
+- sc->sc_lastcal = jiffies;
++ sc->sc_nextcal = jiffies + msecs_to_jiffies(sc->sc_cal_interval * 1000);
+
+ /*
+ * Convert to a HAL channel description with the flags
+@@ -5430,10 +5426,9 @@ ath_beacon_send(struct ath_softc *sc, in
+ "Invoking ath_hal_txstart with sc_bhalq: %d\n",
+ sc->sc_bhalq);
+ ath_hal_txstart(ah, sc->sc_bhalq);
+- if (sc->sc_beacon_cal && (jiffies > sc->sc_lastcal + (ath_calinterval * HZ))) {
+- sc->sc_cal_ch.expires = jiffies + msecs_to_jiffies(10);
+- add_timer(&sc->sc_cal_ch);
+- }
++ if (sc->sc_beacon_cal && ((sc->sc_bmisscount == 3) ||
++ (jiffies > sc->sc_nextcal)))
++ mod_timer(&sc->sc_cal_ch, jiffies + 1);
+
+ sc->sc_stats.ast_be_xmit++; /* XXX per-VAP? */
+ }
+@@ -9104,6 +9099,7 @@ ath_startrecv(struct ath_softc *sc)
+ dev->mtu, sc->sc_cachelsz, sc->sc_rxbufsize);
+
+ sc->sc_rxlink = NULL;
++ ath_set_beacon_cal(sc, IEEE80211_IS_MODE_BEACON(sc->sc_ic.ic_opmode));
+ STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+ int error = ath_rxbuf_init(sc, bf);
+ ATH_RXBUF_RESET(bf);
+@@ -9320,7 +9316,7 @@ ath_chan_set(struct ath_softc *sc, struc
+ jiffies + (sc->sc_dfs_cac_period * HZ));
+
+ /* This is a good time to start a calibration */
+- ath_set_beacon_cal(sc, 1);
++ mod_timer(&sc->sc_cal_ch, jiffies + 1);
+ }
+ /*
+ * re configure beacons when it is a turbo mode switch.
+@@ -9414,25 +9410,23 @@ ath_calibrate(unsigned long arg)
+ if (isIQdone == AH_TRUE) {
+ /* Unless user has overridden calibration interval,
+ * upgrade to less frequent calibration */
+- if (ath_calinterval == ATH_SHORT_CALINTERVAL)
+- ath_calinterval = ATH_LONG_CALINTERVAL;
++ if (sc->sc_cal_interval == ATH_SHORT_CALINTERVAL)
++ sc->sc_cal_interval = ATH_LONG_CALINTERVAL;
+ }
+ else {
+ /* Unless user has overridden calibration interval,
+ * downgrade to more frequent calibration */
+- if (ath_calinterval == ATH_LONG_CALINTERVAL)
+- ath_calinterval = ATH_SHORT_CALINTERVAL;
++ if (sc->sc_cal_interval == ATH_LONG_CALINTERVAL)
++ sc->sc_cal_interval = ATH_SHORT_CALINTERVAL;
+ }
+
+ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "Channel %u/%x -- IQ %s.\n",
+ sc->sc_curchan.channel, sc->sc_curchan.channelFlags,
+ isIQdone ? "done" : "not done");
+
+- sc->sc_lastcal = jiffies;
+- if (!sc->sc_beacon_cal) {
+- sc->sc_cal_ch.expires = jiffies + (ath_calinterval * HZ);
+- add_timer(&sc->sc_cal_ch);
+- }
++ sc->sc_nextcal = jiffies + msecs_to_jiffies(sc->sc_cal_interval * 1000);
++ if (!sc->sc_beacon_cal)
++ mod_timer(&sc->sc_cal_ch, sc->sc_nextcal);
+ }
+
+ static void
+@@ -9540,9 +9534,6 @@ ath_newstate(struct ieee80211vap *vap, e
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+- if (!sc->sc_beacon_cal)
+- del_timer(&sc->sc_cal_ch); /* periodic calibration timer */
+-
+ ath_hal_setledstate(ah, leds[nstate]); /* set LED */
+ netif_stop_queue(dev); /* before we do anything else */
+
+@@ -9764,10 +9755,7 @@ ath_newstate(struct ieee80211vap *vap, e
+ IEEE80211_IS_MODE_DFS_MASTER(vap->iv_opmode)) {
+ DPRINTF(sc, ATH_DEBUG_STATE | ATH_DEBUG_DOTH,
+ "VAP -> DFSWAIT_PENDING \n");
+- /* start calibration timer with a really small value
+- * 1/10 sec */
+- if (!sc->sc_beacon_cal)
+- mod_timer(&sc->sc_cal_ch, jiffies + (HZ/10));
++ mod_timer(&sc->sc_cal_ch, jiffies + 1);
+ /* wake the receiver */
+ netif_wake_queue(dev);
+ /* don't do the other usual stuff... */
+@@ -9809,12 +9797,6 @@ done:
+ /* Invoke the parent method to complete the work. */
+ error = avp->av_newstate(vap, nstate, arg);
+
+- /* Finally, start any timers. */
+- if (nstate == IEEE80211_S_RUN && !sc->sc_beacon_cal) {
+- /* start periodic recalibration timer */
+- mod_timer(&sc->sc_cal_ch, jiffies + (ath_calinterval * HZ));
+- }
+-
+ #ifdef ATH_SUPERG_XR
+ if (vap->iv_flags & IEEE80211_F_XR &&
+ nstate == IEEE80211_S_RUN)
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -832,7 +832,8 @@ struct ath_softc {
+
+ struct ieee80211_channel *sc_last_chan;
+ int sc_beacon_cal; /* use beacon timer for calibration */
+- u_int64_t sc_lastcal; /* last time the calibration was performed */
++ u_int64_t sc_nextcal; /* last time the calibration was performed */
++ int sc_cal_interval; /* current calibration interval */
+ struct timer_list sc_cal_ch; /* calibration timer */
+ HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */
+
diff --git a/package/madwifi/patches/451-ibss_race_fix.patch b/package/madwifi/patches/451-ibss_race_fix.patch
new file mode 100644
index 000000000..27e1b47da
--- /dev/null
+++ b/package/madwifi/patches/451-ibss_race_fix.patch
@@ -0,0 +1,342 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -294,10 +294,10 @@ ieee80211_input(struct ieee80211vap * va
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+- if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid) ||
++ if ((!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid) ||
+ (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
+- !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+- (subtype != IEEE80211_FC0_SUBTYPE_BEACON))) {
++ !IEEE80211_IS_MULTICAST(wh->i_addr1))) &&
++ (type == IEEE80211_FC0_TYPE_DATA)) {
+ if (!(vap->iv_dev->flags & IFF_PROMISC)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+@@ -322,22 +322,15 @@ ieee80211_input(struct ieee80211vap * va
+ }
+ /* Do not try to find a node reference if the packet really did come from the BSS */
+ if (type == IEEE80211_FC0_TYPE_DATA && ni == vap->iv_bss &&
+- !IEEE80211_ADDR_EQ(vap->iv_bss->ni_macaddr, wh->i_addr2) &&
+ IEEE80211_ADDR_EQ(vap->iv_bssid, wh->i_addr3)) {
+ /* Try to find sender in local node table. */
+- ni = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
++ if (!ni_or_null) {
++ ieee80211_unref_node(&ni);
++ ni = ieee80211_find_txnode(vap, wh->i_addr2);
++ }
+ if (ni == NULL) {
+- /*
+- * Fake up a node for this newly discovered
+- * member of the IBSS. This should probably
+- * done after an ACL check.
+- */
+- ni = ieee80211_fakeup_adhoc_node(vap,
+- wh->i_addr2);
+- if (ni == NULL) {
+- /* NB: stat kept for alloc failure */
+- goto err;
+- }
++ /* NB: stat kept for alloc failure */
++ goto discard;
+ }
+ }
+ iwspy_event(vap, ni, rssi);
+@@ -3553,8 +3546,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ (((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
+ (vap->iv_opmode == IEEE80211_M_WDS)) &&
+ (scan.capinfo & IEEE80211_CAPINFO_ESS))) {
++ struct ieee80211_node *tni = NULL;
+ struct ieee80211vap *avp = NULL;
+- int do_unref = 0;
+ int found = 0;
+
+ IEEE80211_LOCK_IRQ(vap->iv_ic);
+@@ -3570,14 +3563,12 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ }
+ }
+ if (found)
+- ni = ni_or_null = avp->iv_wdsnode;
++ tni = ieee80211_ref_node(avp->iv_wdsnode);
+ } else if ((vap->iv_opmode == IEEE80211_M_WDS) && vap->iv_wdsnode) {
+ found = 1;
+- ni = ni_or_null = vap->iv_wdsnode;
+- } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
+- ni_or_null = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
+- if (ni_or_null)
+- ni = ni_or_null;
++ tni = ieee80211_ref_node(vap->iv_wdsnode);
++ } else if ((vap->iv_opmode == IEEE80211_M_IBSS) && (vap->iv_state == IEEE80211_S_RUN)) {
++ tni = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
+ found = 1;
+ }
+ IEEE80211_UNLOCK_IRQ(vap->iv_ic);
+@@ -3585,20 +3576,21 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ if (!found)
+ break;
+
+- if (ni_or_null == NULL) {
++ memcpy(&SKB_CB(skb)->beacon_tsf, scan.tstamp, sizeof(u_int64_t));
++
++ if (tni == NULL) {
+ if (avp) {
+ IEEE80211_LOCK_IRQ(ic);
+- ni = ieee80211_add_neighbor(avp, wh, &scan);
++ tni = ieee80211_add_neighbor(avp, wh, &scan);
+ /* force assoc */
+- ni->ni_associd |= 0xc000;
+- avp->iv_wdsnode = ieee80211_ref_node(ni);
++ tni->ni_associd |= 0xc000;
++ avp->iv_wdsnode = ieee80211_ref_node(tni);
+ IEEE80211_UNLOCK_IRQ(ic);
+ } else if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
+ IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid)) {
+ /* Create a new entry in the neighbor table. */
+- ni = ieee80211_add_neighbor(vap, wh, &scan);
++ tni = ieee80211_add_neighbor(vap, wh, &scan);
+ }
+- do_unref = 1;
+ } else {
+ /*
+ * Copy data from beacon to neighbor table.
+@@ -3606,39 +3598,38 @@ ieee80211_recv_mgmt(struct ieee80211vap
+ * ieee80211_add_neighbor(), so we just copy
+ * everything over to be safe.
+ */
+- ni->ni_esslen = scan.ssid[1];
+- memcpy(ni->ni_essid, scan.ssid + 2, scan.ssid[1]);
+- IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+- memcpy(ni->ni_tstamp.data, scan.tstamp,
+- sizeof(ni->ni_tstamp));
+- ni->ni_inact = ni->ni_inact_reload;
+- ni->ni_intval =
++ tni->ni_esslen = scan.ssid[1];
++ memcpy(tni->ni_essid, scan.ssid + 2, scan.ssid[1]);
++ IEEE80211_ADDR_COPY(tni->ni_bssid, wh->i_addr3);
++ memcpy(tni->ni_tstamp.data, scan.tstamp,
++ sizeof(tni->ni_tstamp));
++ tni->ni_inact = tni->ni_inact_reload;
++ tni->ni_intval =
+ IEEE80211_BINTVAL_SANITISE(scan.bintval);
+- ni->ni_capinfo = scan.capinfo;
+- ni->ni_chan = ic->ic_curchan;
+- ni->ni_fhdwell = scan.fhdwell;
+- ni->ni_fhindex = scan.fhindex;
+- ni->ni_erp = scan.erp;
+- ni->ni_timoff = scan.timoff;
++ tni->ni_capinfo = scan.capinfo;
++ tni->ni_chan = ic->ic_curchan;
++ tni->ni_fhdwell = scan.fhdwell;
++ tni->ni_fhindex = scan.fhindex;
++ tni->ni_erp = scan.erp;
++ tni->ni_timoff = scan.timoff;
+ if (scan.wme != NULL)
+- ieee80211_saveie(&ni->ni_wme_ie, scan.wme);
++ ieee80211_saveie(&tni->ni_wme_ie, scan.wme);
+ if (scan.wpa != NULL)
+- ieee80211_saveie(&ni->ni_wpa_ie, scan.wpa);
++ ieee80211_saveie(&tni->ni_wpa_ie, scan.wpa);
+ if (scan.rsn != NULL)
+- ieee80211_saveie(&ni->ni_rsn_ie, scan.rsn);
++ ieee80211_saveie(&tni->ni_rsn_ie, scan.rsn);
+ if (scan.ath != NULL)
+- ieee80211_saveath(ni, scan.ath);
++ ieee80211_saveath(tni, scan.ath);
+
+ /* NB: must be after ni_chan is setup */
+- ieee80211_setup_rates(ni, scan.rates,
++ ieee80211_setup_rates(tni, scan.rates,
+ scan.xrates, IEEE80211_F_DOSORT);
+ }
+- if (ni != NULL) {
+- ni->ni_rssi = rssi;
+- ni->ni_rtsf = rtsf;
+- ni->ni_last_rx = jiffies;
+- if (do_unref)
+- ieee80211_unref_node(&ni);
++ if (tni != NULL) {
++ tni->ni_rssi = rssi;
++ tni->ni_rtsf = rtsf;
++ tni->ni_last_rx = jiffies;
++ ieee80211_unref_node(&tni);
+ }
+ }
+ break;
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -53,6 +53,7 @@
+
+ #include <net80211/ieee80211_var.h>
+ #include <net80211/if_athproto.h>
++#include <net80211/ieee80211_node.h>
+
+ /*
+ * Association IDs are managed with a bit vector.
+@@ -317,16 +318,11 @@ ieee80211_create_ibss(struct ieee80211va
+ /* Check to see if we already have a node for this mac
+ * NB: we gain a node reference here
+ */
+- ni = ieee80211_find_txnode(vap, vap->iv_myaddr);
++ ieee80211_node_table_reset(&ic->ic_sta, vap);
++ ni = ieee80211_alloc_node_table(vap, vap->iv_myaddr);
+ if (ni == NULL) {
+- ni = ieee80211_alloc_node_table(vap, vap->iv_myaddr);
+- IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
+- "%s: ni:%p allocated for " MAC_FMT "\n",
+- __func__, ni, MAC_ADDR(vap->iv_myaddr));
+- if (ni == NULL) {
+- /* XXX recovery? */
+- return;
+- }
++ /* XXX recovery? */
++ return;
+ }
+
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
+@@ -647,7 +643,7 @@ ieee80211_sta_join1(struct ieee80211_nod
+ (vap->iv_state == IEEE80211_S_RUN) && bssid_equal(obss, selbs)); */
+ vap->iv_bss = selbs;
+ IEEE80211_ADDR_COPY(vap->iv_bssid, selbs->ni_bssid);
+- if (obss != NULL) {
++ if ((obss != NULL) && (obss != selbs)) {
+ if (obss->ni_table)
+ ieee80211_node_leave(obss);
+ ieee80211_unref_node(&obss);
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6625,14 +6625,6 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+
+ sc->sc_recv_mgmt(vap, ni_or_null, skb, subtype, rssi, rtsf);
+
+- /* Lookup the new node if any (this grabs a reference to it) */
+- ni = ieee80211_find_rxnode(vap->iv_ic, vap,
+- (const struct ieee80211_frame_min *)skb->data);
+- if (ni == NULL) {
+- DPRINTF(sc, ATH_DEBUG_BEACON, "Dropping; node unknown.\n");
+- return;
+- }
+-
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ /* update RSSI statistics for use by the HAL */
+@@ -6654,11 +6646,9 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ * we do the IBSS merging in software. Also do not merge
+ * if the difference it too small. Otherwise we are playing
+ * tsf-pingpong with other vendors drivers */
+- beacon_tsf = le64_to_cpu(ni->ni_tstamp.tsf);
+- if (beacon_tsf > rtsf + 0xffff) {
++ beacon_tsf = le64_to_cpu(SKB_CB(skb)->beacon_tsf);
++ if (beacon_tsf > rtsf + 0xffff)
+ ath_hal_settsf64(sc->sc_ah, beacon_tsf - rtsf);
+- ieee80211_ibss_merge(ni);
+- }
+ break;
+ }
+ /* NB: Fall Through */
+@@ -6680,13 +6670,21 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ hw_tsf = ath_hal_gettsf64(sc->sc_ah);
+ hw_tu = hw_tsf >> 10;
+
+- beacon_tsf = le64_to_cpu(ni->ni_tstamp.tsf);
++ beacon_tsf = le64_to_cpu(SKB_CB(skb)->beacon_tsf);
+ beacon_tu = beacon_tsf >> 10;
+
++ if (!beacon_tsf)
++ break;
++
++ if (IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid))
++ break;
++
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+- "Beacon transmitted at %10llx, "
++ "Beacon transmitted from "MAC_FMT" ("MAC_FMT") at %10llx, "
+ "received at %10llx(%lld), hw TSF "
+ "%10llx(%lld)\n",
++ MAC_ADDR(wh->i_addr3),
++ MAC_ADDR(vap->iv_bssid),
+ beacon_tsf,
+ rtsf, rtsf - beacon_tsf,
+ hw_tsf, hw_tsf - beacon_tsf);
+@@ -6699,39 +6697,13 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ do_merge = 1;
+ }
+
+- /* Check sc_nexttbtt */
+- if (sc->sc_nexttbtt < hw_tu) {
+- DPRINTF(sc, ATH_DEBUG_BEACON,
+- "sc_nexttbtt (%8x TU) is in the past "
+- "(tsf %8x TU), updating timers\n",
+- sc->sc_nexttbtt, hw_tu);
+- do_merge = 1;
+- }
+-
+- intval = ni->ni_intval & HAL_BEACON_PERIOD;
+-#if 0
+- /* This code is disabled since it would produce
+- * unwanted merge. For instance, in a two nodes network
+- * A & B, A can merge to B and at the same time, B will
+- * merge to A, still having a split */
+- if (intval != 0) {
+- if ((sc->sc_nexttbtt % intval) !=
+- (beacon_tu % intval)) {
+- DPRINTF(sc, ATH_DEBUG_BEACON,
+- "ibss merge: "
+- "sc_nexttbtt %10x TU "
+- "(%3d) beacon %10x TU "
+- "(%3d)\n",
+- sc->sc_nexttbtt,
+- sc->sc_nexttbtt % intval,
+- beacon_tu,
+- beacon_tu % intval);
+- do_merge = 1;
+- }
+- }
+-#endif
+- if (do_merge)
++ if (do_merge) {
++ /* Lookup the new node if any (this grabs a reference to it) */
++ ni = ieee80211_find_txnode(vap, wh->i_addr2);
++ memcpy(ni->ni_bssid, wh->i_addr3, IEEE80211_ADDR_LEN);
+ ieee80211_ibss_merge(ni);
++ ieee80211_unref_node(&ni);
++ }
+
+ if ((sc->sc_opmode == HAL_M_IBSS) &&
+ ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval))
+@@ -6739,8 +6711,6 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+ }
+ break;
+ }
+-
+- ieee80211_unref_node(&ni);
+ }
+
+ static void
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -411,7 +411,7 @@ typedef spinlock_t acl_lock_t;
+ * 8 bytes so we reserve/avoid it.
+ */
+ struct ieee80211_cb {
+- u_int8_t vlan[8]; /* reserve for vlan tag info */
++ u_int64_t beacon_tsf;
+ struct ieee80211_node *ni;
+ u_int32_t flags;
+ #define M_LINK0 0x01 /* frame needs WEP encryption */
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -1125,11 +1125,8 @@ adhoc_default_action(struct ieee80211vap
+ u_int8_t zeroMacAddr[IEEE80211_ADDR_LEN];
+
+ memset(&zeroMacAddr, 0, IEEE80211_ADDR_LEN);
+- if (IEEE80211_ADDR_EQ(se->se_bssid, &zeroMacAddr[0])) {
+- ieee80211_create_ibss(vap, se->se_chan);
+- return 1;
+- } else
+- return ieee80211_sta_join(vap, se);
++ ieee80211_create_ibss(vap, se->se_chan);
++ return 1;
+ }
+
+ static const struct ieee80211_scanner adhoc_default = {
diff --git a/package/madwifi/patches/452-minstrel_no_timer.patch b/package/madwifi/patches/452-minstrel_no_timer.patch
new file mode 100644
index 000000000..f0f5c26f9
--- /dev/null
+++ b/package/madwifi/patches/452-minstrel_no_timer.patch
@@ -0,0 +1,134 @@
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -119,6 +119,7 @@
+ #include "minstrel.h"
+
+ #define ONE_SECOND (1000 * 1000) /* 1 second, or 1000 milliseconds; eternity, in other words */
++#define TIMER_INTERVAL 100 /* msecs */
+
+ #include "release.h"
+
+@@ -128,9 +129,6 @@ static char *dev_info = "ath_rate_minstr
+ #define STALE_FAILURE_TIMEOUT_MS 10000
+ #define ENABLE_MRR 1
+
+-static int ath_timer_interval = (1000 / 10); /* every 1/10 second, timer runs */
+-static void ath_timer_function(unsigned long data);
+-
+ /* 10% of the time, send a packet at something other than the optimal rate, which fills
+ * the statistics tables nicely. This percentage is applied to the first packet of the
+ * multi rate retry chain. */
+@@ -142,7 +140,7 @@ static void ath_rate_ctl_reset(struct at
+ /* Calculate the throughput and probability of success for each node
+ * we are talking on, based on the statistics collected during the
+ * last timer period. */
+-static void ath_rate_statistics(void *arg, struct ieee80211_node *ni);
++static void ath_rate_statistics(struct ieee80211_node *ni);
+
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+@@ -204,6 +202,11 @@ ath_rate_findrate(struct ath_softc *sc,
+ unsigned int ndx, offset;
+ int mrr;
+
++
++ if (abs(jiffies - sn->last_update) > msecs_to_jiffies(TIMER_INTERVAL)) {
++ ath_rate_statistics(&an->an_node);
++ sn->last_update = jiffies;
++ }
+ if (sn->num_rates <= 0) {
+ printk(KERN_WARNING "%s: no rates for " MAC_FMT "?\n",
+ dev_info,
+@@ -640,54 +643,11 @@ ath_rate_newstate(struct ieee80211vap *v
+ }
+ }
+
+-static void
+-ath_timer_function(unsigned long data)
+-{
+- struct minstrel_softc *ssc = (struct minstrel_softc *) data;
+- struct ath_softc *sc = ssc->sc;
+- struct ieee80211com *ic;
+- struct net_device *dev = ssc->sc_dev;
+- struct timer_list *timer;
+- unsigned int interval = ath_timer_interval;
+-
+- if (dev == NULL)
+- DPRINTF(sc, ATH_DEBUG_RATE, "%s: 'dev' is null in this timer \n", __func__);
+-
+- if (sc == NULL)
+- DPRINTF(sc, ATH_DEBUG_RATE, "%s: 'sc' is null in this timer\n", __func__);
+-
+- ic = &sc->sc_ic;
+-
+- if (ssc->close_timer_now)
+- return;
+-
+- if (dev->flags & IFF_RUNNING) {
+- sc->sc_stats.ast_rate_calls++;
+-
+- if (ic->ic_opmode == IEEE80211_M_STA) {
+- struct ieee80211vap *tmpvap;
+- TAILQ_FOREACH(tmpvap, &ic->ic_vaps, iv_next) {
+- ath_rate_statistics(sc, tmpvap->iv_bss);/* NB: no reference */
+- }
+- } else
+- ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_statistics, sc);
+- }
+-
+- if (ic->ic_opmode == IEEE80211_M_STA)
+- interval = ath_timer_interval >> 1;
+-
+- timer = &(ssc->timer);
+- if (timer == NULL)
+- DPRINTF(sc, ATH_DEBUG_RATE, "%s: timer is null - leave it\n", __func__);
+-
+- timer->expires = jiffies + ((HZ * interval) / 1000);
+- add_timer(timer);
+-}
+
+ static void
+-ath_rate_statistics(void *arg, struct ieee80211_node *ni)
++ath_rate_statistics(struct ieee80211_node *ni)
+ {
+- struct ath_node *an = (struct ath_node *) ni;
++ struct ath_node *an = ATH_NODE(ni);
+ struct ieee80211_rateset *rs = &ni->ni_rates;
+ struct minstrel_node *rn = ATH_NODE_MINSTREL(an);
+ unsigned int i;
+@@ -786,15 +746,8 @@ ath_rate_attach(struct ath_softc *sc)
+ osc->arc.arc_space = sizeof(struct minstrel_node);
+ osc->arc.arc_vap_space = 0;
+
+- osc->close_timer_now = 0;
+- init_timer(&osc->timer);
+ osc->sc = sc;
+ osc->sc_dev = sc->sc_dev;
+- osc->timer.function = ath_timer_function;
+- osc->timer.data = (unsigned long)osc;
+-
+- osc->timer.expires = jiffies + HZ;
+- add_timer(&osc->timer);
+
+ return &osc->arc;
+ }
+@@ -803,8 +756,6 @@ static void
+ ath_rate_detach(struct ath_ratectrl *arc)
+ {
+ struct minstrel_softc *osc = (struct minstrel_softc *) arc;
+- osc->close_timer_now = 1;
+- del_timer(&osc->timer);
+ kfree(osc);
+ _MOD_DEC_USE(THIS_MODULE);
+ }
+--- a/ath_rate/minstrel/minstrel.h
++++ b/ath_rate/minstrel/minstrel.h
+@@ -167,6 +167,8 @@ struct minstrel_node {
+ packet, or a packet at an optimal rate.*/
+ int random_n;
+ int a, b; /**Coefficients of the random thing */
++
++ unsigned long last_update;
+ };
+
+
diff --git a/package/madwifi/patches/453-procps.patch b/package/madwifi/patches/453-procps.patch
new file mode 100644
index 000000000..5a5633b1c
--- /dev/null
+++ b/package/madwifi/patches/453-procps.patch
@@ -0,0 +1,55 @@
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -640,12 +640,24 @@ static __inline unsigned long msecs_to_j
+ void __user *buffer, size_t *lenp)
+ #define IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+ proc_dointvec(ctl, write, filp, buffer, lenp)
+-#else
++#define IEEE80211_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dostring(ctl, write, filp, buffer, lenp)
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+ #define IEEE80211_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
+ f(ctl_table *ctl, int write, struct file *filp, \
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+ #define IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+ proc_dointvec(ctl, write, filp, buffer, lenp, ppos)
++#define IEEE80211_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dostring(ctl, write, filp, buffer, lenp, ppos)
++#else /* Linux 2.6.32+ */
++#define IEEE80211_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
++ f(ctl_table *ctl, int write, \
++ void __user *buffer, size_t *lenp, loff_t *ppos)
++#define IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dointvec(ctl, write, buffer, lenp, ppos)
++#define IEEE80211_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dostring(ctl, write, buffer, lenp, ppos)
+ #endif
+
+ void ieee80211_virtfs_latevattach(struct ieee80211vap *);
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -173,14 +173,22 @@ static inline struct net_device *_alloc_
+ proc_dointvec(ctl, write, filp, buffer, lenp)
+ #define ATH_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
+ proc_dostring(ctl, write, filp, buffer, lenp)
+-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) */
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+ #define ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
+ f(ctl_table *ctl, int write, struct file *filp, \
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+ #define ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
+ proc_dointvec(ctl, write, filp, buffer, lenp, ppos)
++#define ATH_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dostring(ctl, write, filp, buffer, lenp, ppos)
++#else /* Linux 2.6.32+ */
++#define ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
++ f(ctl_table *ctl, int write, \
++ void __user *buffer, size_t *lenp, loff_t *ppos)
++#define ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
++ proc_dointvec(ctl, write, buffer, lenp, ppos)
+ #define ATH_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
+- proc_dostring(ctl, write, filp, buffer, lenp, ppos)
++ proc_dostring(ctl, write, buffer, lenp, ppos)
+ #endif
+
+ #define ATH_TIMEOUT 1000
diff --git a/package/madwifi/patches/454-cca.patch b/package/madwifi/patches/454-cca.patch
new file mode 100644
index 000000000..53792cc8f
--- /dev/null
+++ b/package/madwifi/patches/454-cca.patch
@@ -0,0 +1,186 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -383,6 +383,8 @@ static void ath_poll_disable(struct net_
+ static void ath_poll_enable(struct net_device *dev);
+ static void ath_fetch_idle_time(struct ath_softc *sc);
+ static void ath_set_timing(struct ath_softc *sc);
++static void ath_update_cca_thresh(struct ath_softc *sc);
++static int ath_hw_read_nf(struct ath_softc *sc);
+
+ /* calibrate every 30 secs in steady state but check every second at first. */
+ static int ath_calinterval = ATH_SHORT_CALINTERVAL;
+@@ -2623,6 +2625,10 @@ ath_init(struct net_device *dev)
+ goto done;
+ }
+
++ ath_hal_process_noisefloor(ah);
++ ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
++ ath_update_cca_thresh(sc);
++
+ if (sc->sc_softled)
+ ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+
+@@ -3024,6 +3030,10 @@ ath_reset(struct net_device *dev)
+ EPRINTF(sc, "Unable to reset hardware: '%s' (HAL status %u)\n",
+ ath_get_hal_status_desc(status), status);
+
++ ath_hal_process_noisefloor(ah);
++ ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
++ ath_update_cca_thresh(sc);
++
+ ath_setintmit(sc);
+ ath_update_txpow(sc); /* update tx power state */
+ ath_radar_update(sc);
+@@ -9374,9 +9384,11 @@ ath_calibrate(unsigned long arg)
+ sc->sc_curchan.channel);
+ sc->sc_stats.ast_per_calfail++;
+ }
+- ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
+
+ ath_hal_process_noisefloor(ah);
++ ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
++ ath_update_cca_thresh(sc);
++
+ if (isIQdone == AH_TRUE) {
+ /* Unless user has overridden calibration interval,
+ * upgrade to less frequent calibration */
+@@ -9711,8 +9723,6 @@ ath_newstate(struct ieee80211vap *vap, e
+ break;
+ }
+
+- ath_hal_process_noisefloor(ah);
+- ic->ic_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
+ /*
+ * Reset rssi stats; maybe not the best place...
+ */
+@@ -10968,6 +10978,7 @@ enum {
+ ATH_INTMIT,
+ ATH_NOISE_IMMUNITY,
+ ATH_OFDM_WEAK_DET,
++ ATH_CCA_THRESH,
+ ATH_CHANBW,
+ ATH_OUTDOOR,
+ ATH_DISTANCE,
+@@ -11110,6 +11121,66 @@ ath_sysctl_get_intmit(struct ath_softc *
+ return 0;
+ }
+
++#define AR_PHY_CCA 0x9864
++#define AR_PHY_MINCCA_PWR 0x0FF80000
++#define AR_PHY_MINCCA_PWR_S 19
++#define AR_PHY_CCA_THRESH62 0x0007F000
++#define AR_PHY_CCA_THRESH62_S 12
++
++static int
++ath_nf_from_cca(u32 phy_cca)
++{
++ int nf = (phy_cca >> 19) & 0x1ff;
++ nf = -((nf ^ 0x1ff) + 1);
++ return nf;
++}
++
++static int
++ath_hw_read_nf(struct ath_softc *sc)
++{
++ return ath_nf_from_cca(OS_REG_READ(sc->sc_ah, AR_PHY_CCA));
++}
++
++static void
++ath_update_cca_thresh(struct ath_softc *sc)
++{
++ struct ath_hal *ah = sc->sc_ah;
++ int newthr = 0;
++ u32 phy_cca;
++ int nf;
++
++ phy_cca = OS_REG_READ(ah, AR_PHY_CCA);
++ if (sc->sc_cca_thresh < 0) {
++ newthr = sc->sc_cca_thresh - ath_nf_from_cca(phy_cca);
++
++ /* 0xf is a typical eeprom value for thresh62,
++ * use it as minimum for signal based thresholds
++ * to prevent complete connection drops */
++ if (newthr < 0xf)
++ newthr = 0xf;
++ } else {
++ newthr = sc->sc_cca_thresh;
++ }
++
++ if ((newthr < 4) || (newthr >= 127))
++ return;
++
++ phy_cca &= ~AR_PHY_CCA_THRESH62;
++ phy_cca |= newthr << AR_PHY_CCA_THRESH62_S;
++ OS_REG_WRITE(ah, AR_PHY_CCA, phy_cca);
++}
++
++static int
++ath_get_cca_thresh(struct ath_softc *sc)
++{
++ struct ath_hal *ah = sc->sc_ah;
++ u32 phy_cca;
++
++ phy_cca = OS_REG_READ(ah, AR_PHY_CCA);
++ return ath_nf_from_cca(phy_cca) +
++ ((phy_cca & AR_PHY_CCA_THRESH62) >> AR_PHY_CCA_THRESH62_S);
++}
++
+ static int
+ ATH_SYSCTL_DECL(ath_sysctl_hwinfo, ctl, write, filp, buffer, lenp, ppos)
+ {
+@@ -11356,6 +11427,10 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_OFDM_WEAK_DET:
+ ret = ath_sysctl_set_intmit(sc, (long)ctl->extra2, val);
+ break;
++ case ATH_CCA_THRESH:
++ sc->sc_cca_thresh = val;
++ ath_update_cca_thresh(sc);
++ break;
+ default:
+ ret = -EINVAL;
+ break;
+@@ -11436,6 +11511,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_OFDM_WEAK_DET:
+ ret = ath_sysctl_get_intmit(sc, (long)ctl->extra2, &val);
+ break;
++ case ATH_CCA_THRESH:
++ val = ath_get_cca_thresh(sc);
++ break;
+ default:
+ ret = -EINVAL;
+ break;
+@@ -11667,6 +11745,12 @@ static const ctl_table ath_sysctl_templa
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_OFDM_WEAK_DET,
+ },
++ { .ctl_name = CTL_AUTO,
++ .procname = "cca_thresh",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_CCA_THRESH,
++ },
+ { 0 }
+ };
+
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -844,6 +844,7 @@ struct ath_softc {
+ int sc_cal_interval; /* current calibration interval */
+ struct timer_list sc_cal_ch; /* calibration timer */
+ HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */
++ int sc_cca_thresh; /* configured CCA threshold */
+
+ struct ctl_table_header *sc_sysctl_header;
+ struct ctl_table *sc_sysctls;
+--- a/ath/ath_wprobe.c
++++ b/ath/ath_wprobe.c
+@@ -133,8 +133,7 @@ ath_wprobe_sync(struct wprobe_iface *dev
+ rx = READ_CLR(ah, AR5K_RXFC);
+ tx = READ_CLR(ah, AR5K_TXFC);
+ OS_REG_WRITE(ah, AR5K_MIBC, 0);
+- noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
+- ic->ic_channoise = noise;
++ noise = ath_hw_read_nf(sc);
+
+ WPROBE_FILL_BEGIN(val, ath_wprobe_globals);
+ if (cc & 0xf0000000) {
diff --git a/package/madwifi/patches/455-beacon_watchdog.patch b/package/madwifi/patches/455-beacon_watchdog.patch
new file mode 100644
index 000000000..d0b4fa63e
--- /dev/null
+++ b/package/madwifi/patches/455-beacon_watchdog.patch
@@ -0,0 +1,95 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -379,6 +379,7 @@ static u_int32_t ath_get_clamped_maxtxpo
+ static u_int32_t ath_set_clamped_maxtxpower(struct ath_softc *sc,
+ u_int32_t new_clamped_maxtxpower);
+
++static void ath_bcn_timer(unsigned long arg);
+ static void ath_poll_disable(struct net_device *dev);
+ static void ath_poll_enable(struct net_device *dev);
+ static void ath_fetch_idle_time(struct ath_softc *sc);
+@@ -829,6 +830,10 @@ ath_attach(u_int16_t devid, struct net_d
+ sc->sc_cal_ch.function = ath_calibrate;
+ sc->sc_cal_ch.data = (unsigned long) dev;
+
++ init_timer(&sc->sc_bcntimer);
++ sc->sc_bcntimer.function = ath_bcn_timer;
++ sc->sc_bcntimer.data = (unsigned long) dev;
++
+ /* initialize DFS related variables */
+ sc->sc_dfswait = 0;
+ sc->sc_dfs_cac = 0;
+@@ -2704,6 +2709,7 @@ ath_stop_locked(struct net_device *dev)
+ DPRINTF(sc, ATH_DEBUG_RESET, "invalid=%u flags=0x%x\n",
+ sc->sc_invalid, dev->flags);
+
++ del_timer_sync(&sc->sc_bcntimer);
+ if (dev->flags & IFF_RUNNING) {
+ /*
+ * Shutdown the hardware and driver:
+@@ -3006,6 +3012,7 @@ ath_reset(struct net_device *dev)
+ struct ieee80211_channel *c;
+ HAL_STATUS status;
+
++ del_timer_sync(&sc->sc_bcntimer);
+ /*
+ * XXX: starting the calibration too early seems to lead to
+ * problems with the beacons.
+@@ -5305,6 +5312,7 @@ ath_beacon_send(struct ath_softc *sc, in
+ if (ath_chan_unavail_dbgmsg(sc))
+ return;
+
++ mod_timer(&sc->sc_bcntimer, jiffies + sc->sc_bcntimer_reload);
+ /*
+ * Check if the previous beacon has gone out. If
+ * not don't try to post another, skip this period
+@@ -5487,6 +5495,18 @@ ath_beacon_free(struct ath_softc *sc)
+ cleanup_ath_buf(sc, bf, BUS_DMA_TODEVICE);
+ }
+
++static void ath_bcn_timer(unsigned long arg)
++{
++ struct net_device *dev = (struct net_device *)arg;
++ struct ath_softc *sc = netdev_priv(dev);
++ struct ath_hal *ah = sc->sc_ah;
++
++ if (!sc->sc_beacons)
++ return;
++
++ ath_reset(dev);
++}
++
+ /*
+ * Configure the beacon and sleep timers.
+ *
+@@ -5523,6 +5543,7 @@ ath_beacon_config(struct ath_softc *sc,
+ if (vap == NULL)
+ vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */
+
++ del_timer_sync(&sc->sc_bcntimer);
+ ni = vap->iv_bss;
+
+ /* TSF calculation is timing critical - we don't want to be interrupted here */
+@@ -5699,6 +5720,9 @@ ath_beacon_config(struct ath_softc *sc,
+ sc->sc_imask |= HAL_INT_SWBA;
+ ath_set_beacon_cal(sc, 1);
+ ath_beaconq_config(sc);
++
++ sc->sc_bcntimer_reload = msecs_to_jiffies(10 * (intval & HAL_BEACON_PERIOD));
++ mod_timer(&sc->sc_bcntimer, jiffies + sc->sc_bcntimer_reload);
+ } else
+ ath_set_beacon_cal(sc, 0);
+
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -789,6 +789,10 @@ struct ath_softc {
+ u_int16_t sc_ledoff; /* off time for current blink */
+ struct timer_list sc_ledtimer; /* led off timer */
+
++ /* beacon watchdog timer */
++ u_int32_t sc_bcntimer_reload;
++ struct timer_list sc_bcntimer;
++
+ struct ATH_TQ_STRUCT sc_fataltq; /* fatal error intr tasklet */
+
+ int sc_rxbufsize; /* rx size based on mtu */
diff --git a/package/madwifi/patches/456-rfsilent.patch b/package/madwifi/patches/456-rfsilent.patch
new file mode 100644
index 000000000..21031e7ca
--- /dev/null
+++ b/package/madwifi/patches/456-rfsilent.patch
@@ -0,0 +1,85 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -2996,6 +2996,19 @@ ath_fetch_idle_time(struct ath_softc *sc
+ #undef AR5K_RXCLEAR
+ #undef AR5K_CYCLES
+
++static void
++ath_set_silent(struct ath_softc *sc)
++{
++ struct ath_hal *ah = sc->sc_ah;
++
++ if (!sc->sc_silent)
++ return;
++
++ del_timer_sync(&sc->sc_bcntimer);
++ ath_hal_intrset(ah, 0);
++ OS_REG_WRITE(ah, 0x8048, 0x60); /* set tx loopback and rx disable */
++}
++
+ /*
+ * Reset the hardware w/o losing operational state. This is
+ * basically a more efficient way of doing ath_stop, ath_init,
+@@ -3073,6 +3086,7 @@ ath_reset(struct net_device *dev)
+ ath_grppoll_start(vap, sc->sc_xrpollcount);
+ }
+ #endif
++ ath_set_silent(sc);
+ return 0;
+ }
+
+@@ -10972,6 +10986,7 @@ enum {
+ * mirrored in /proc/sys.
+ */
+ enum {
++ ATH_SILENT,
+ ATH_SLOTTIME,
+ ATH_ACKTIMEOUT,
+ ATH_CTSTIMEOUT,
+@@ -11294,6 +11309,13 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ sc->sc_ctstimeconf = 0;
+ ath_set_timing(sc);
+ break;
++ case ATH_SILENT:
++ sc->sc_silent = !!val;
++ if (val)
++ ath_set_silent(sc);
++ else
++ ath_reset(sc->sc_dev);
++ break;
+ case ATH_DISTANCE:
+ if (val > 0) {
+ sc->sc_coverage = ((val - 1) / 300) + 1;
+@@ -11477,6 +11499,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ case ATH_CTSTIMEOUT:
+ val = ath_hal_getctstimeout(ah);
+ break;
++ case ATH_SILENT:
++ val = sc->sc_silent;
++ break;
+ case ATH_SOFTLED:
+ val = sc->sc_softled;
+ break;
+@@ -11598,6 +11623,12 @@ static const ctl_table ath_sysctl_templa
+ .extra2 = (void *)ATH_DISTANCE,
+ },
+ { .ctl_name = CTL_AUTO,
++ .procname = "silent",
++ .mode = 0644,
++ .proc_handler = ath_sysctl_halparam,
++ .extra2 = (void *)ATH_SILENT,
++ },
++ { .ctl_name = CTL_AUTO,
+ .procname = "softled",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -737,6 +737,7 @@ struct ath_softc {
+ * 'channel availability check' indefinately,
+ * reporting radar and interference detections.
+ */
++ unsigned int sc_silent:1; /* Turn RF silent */
+ unsigned int sc_txcont_power; /* Continuous transmit power in 0.5dBm units */
+ unsigned int sc_txcont_rate; /* Continuous transmit rate in Mbps */
+
diff --git a/package/madwifi/patches/457-idletime.patch b/package/madwifi/patches/457-idletime.patch
new file mode 100644
index 000000000..429da8522
--- /dev/null
+++ b/package/madwifi/patches/457-idletime.patch
@@ -0,0 +1,151 @@
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -355,6 +355,8 @@ ieee80211_ifattach(struct ieee80211com *
+ /* Arbitrarily pick the first channel */
+ ic->ic_curchan = &ic->ic_channels[0];
+
++ ic->ic_inact_tick = IEEE80211_INACT_WAIT;
++
+ /* Enable marking of dfs by default */
+ ic->ic_flags_ext |= IEEE80211_FEXT_MARKDFS;
+
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -350,6 +350,7 @@ struct ieee80211com {
+ u_int8_t ic_protmode_rssi; /* rssi threshold for protection mode */
+ u_int64_t ic_protmode_lasttrig; /* last trigger for protection mode */
+ u_int16_t ic_protmode_timeout; /* protection mode timeout */
++ u_int16_t ic_inact_tick; /* inact timer tick interval (seconds) */
+
+ /* Channel state:
+ *
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -591,6 +591,7 @@ enum {
+ IEEE80211_PARAM_INACT_AUTH = 24, /* station auth inact timeout */
+ IEEE80211_PARAM_INACT_INIT = 25, /* station init inact timeout */
+ IEEE80211_PARAM_ABOLT = 26, /* Atheros Adv. Capabilities */
++ IEEE80211_PARAM_INACT_TICK = 27, /* station inactivity timer tick (seconds) */
+ IEEE80211_PARAM_DTIM_PERIOD = 28, /* DTIM period (beacons) */
+ IEEE80211_PARAM_BEACON_INTERVAL = 29, /* beacon interval (ms) */
+ IEEE80211_PARAM_DOTH = 30, /* 11.h is on/off */
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2504,13 +2504,18 @@ ieee80211_ioctl_setparam(struct net_devi
+ vap->iv_flags &= ~IEEE80211_F_NOBRIDGE;
+ break;
+ case IEEE80211_PARAM_INACT:
+- vap->iv_inact_run = value / IEEE80211_INACT_WAIT;
++ vap->iv_inact_run = value / ic->ic_inact_tick;
++ break;
++ case IEEE80211_PARAM_INACT_TICK:
++ if (value <= 0)
++ return -EINVAL;
++ ic->ic_inact_tick = value;
+ break;
+ case IEEE80211_PARAM_INACT_AUTH:
+- vap->iv_inact_auth = value / IEEE80211_INACT_WAIT;
++ vap->iv_inact_auth = value / ic->ic_inact_tick;
+ break;
+ case IEEE80211_PARAM_INACT_INIT:
+- vap->iv_inact_init = value / IEEE80211_INACT_WAIT;
++ vap->iv_inact_init = value / ic->ic_inact_tick;
+ break;
+ case IEEE80211_PARAM_ABOLT:
+ caps = 0;
+@@ -3050,13 +3055,16 @@ ieee80211_ioctl_getparam(struct net_devi
+ param[0] = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0;
+ break;
+ case IEEE80211_PARAM_INACT:
+- param[0] = vap->iv_inact_run * IEEE80211_INACT_WAIT;
++ param[0] = vap->iv_inact_run * ic->ic_inact_tick;
+ break;
+ case IEEE80211_PARAM_INACT_AUTH:
+- param[0] = vap->iv_inact_auth * IEEE80211_INACT_WAIT;
++ param[0] = vap->iv_inact_auth * ic->ic_inact_tick;
+ break;
+ case IEEE80211_PARAM_INACT_INIT:
+- param[0] = vap->iv_inact_init * IEEE80211_INACT_WAIT;
++ param[0] = vap->iv_inact_init * ic->ic_inact_tick;
++ break;
++ case IEEE80211_PARAM_INACT_TICK:
++ param[0] = ic->ic_inact_tick;
+ break;
+ case IEEE80211_PARAM_ABOLT:
+ /*
+@@ -4557,14 +4565,7 @@ get_sta_info(void *arg, struct ieee80211
+ si->isi_opmode = IEEE80211_STA_OPMODE_XR;
+ else
+ si->isi_opmode = IEEE80211_STA_OPMODE_NORMAL;
+- /* NB: leave all cases in case we relax ni_associd == 0 check */
+- if (ieee80211_node_is_authorized(ni))
+- si->isi_inact = vap->iv_inact_run;
+- else if (ni->ni_associd != 0)
+- si->isi_inact = vap->iv_inact_auth;
+- else
+- si->isi_inact = vap->iv_inact_init;
+- si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
++ si->isi_inact = (ni->ni_inact_reload - ni->ni_inact) * ic->ic_inact_tick;
+
+ cp = (u_int8_t *)(si+1);
+ if (ni->ni_rsn_ie != NULL) {
+@@ -5597,6 +5598,10 @@ static const struct iw_priv_args ieee802
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact_init" },
+ { IEEE80211_PARAM_INACT_INIT,
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact_init" },
++ { IEEE80211_PARAM_INACT_TICK,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact_tick" },
++ { IEEE80211_PARAM_INACT_TICK,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact_tick" },
+ { IEEE80211_PARAM_ABOLT,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "abolt" },
+ { IEEE80211_PARAM_ABOLT,
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -137,7 +137,7 @@ ieee80211_node_attach(struct ieee80211co
+ init_timer(&ic->ic_inact);
+ ic->ic_inact.function = ieee80211_node_timeout;
+ ic->ic_inact.data = (unsigned long) ic;
+- ic->ic_inact.expires = jiffies + IEEE80211_INACT_WAIT * HZ;
++ ic->ic_inact.expires = jiffies + ic->ic_inact_tick * HZ;
+ add_timer(&ic->ic_inact);
+
+ #ifdef IEEE80211_DEBUG_REFCNT
+@@ -261,7 +261,7 @@ void
+ ieee80211_node_authorize(struct ieee80211_node *ni)
+ {
+ ni->ni_flags |= IEEE80211_NODE_AUTH;
+- ni->ni_inact_reload = ni->ni_vap->iv_inact_run;
++ ni->ni_inact = ni->ni_inact_reload = ni->ni_vap->iv_inact_run;
+ }
+ EXPORT_SYMBOL(ieee80211_node_authorize);
+
+@@ -1819,7 +1819,7 @@ ieee80211_node_timeout(unsigned long arg
+ }
+ }
+
+- ic->ic_inact.expires = jiffies + IEEE80211_INACT_WAIT * HZ;
++ ic->ic_inact.expires = jiffies + ic->ic_inact_tick * HZ;
+ add_timer(&ic->ic_inact);
+ }
+
+--- a/net80211/ieee80211_power.c
++++ b/net80211/ieee80211_power.c
+@@ -148,7 +148,7 @@ ieee80211_node_saveq_age(struct ieee8021
+
+ IEEE80211_NODE_SAVEQ_LOCK_IRQ(ni);
+ while ((skb = skb_peek(&ni->ni_savedq)) != NULL &&
+- M_AGE_GET(skb) < IEEE80211_INACT_WAIT) {
++ M_AGE_GET(skb) < ni->ni_vap->iv_ic->ic_inact_tick) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "discard frame, age %u", M_AGE_GET(skb));
+
+@@ -159,7 +159,7 @@ ieee80211_node_saveq_age(struct ieee8021
+ discard++;
+ }
+ if (skb != NULL)
+- M_AGE_SUB(skb, IEEE80211_INACT_WAIT);
++ M_AGE_SUB(skb, ni->ni_vap->iv_ic->ic_inact_tick);
+ IEEE80211_NODE_SAVEQ_UNLOCK_IRQ(ni);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
diff --git a/package/madwifi/patches/458-ibss_wpa_none.patch b/package/madwifi/patches/458-ibss_wpa_none.patch
new file mode 100644
index 000000000..df77510b2
--- /dev/null
+++ b/package/madwifi/patches/458-ibss_wpa_none.patch
@@ -0,0 +1,13 @@
+--- a/net80211/ieee80211_crypto_ccmp.c
++++ b/net80211/ieee80211_crypto_ccmp.c
+@@ -273,7 +273,9 @@ ccmp_decap(struct ieee80211_key *k, stru
+ tid = ((struct ieee80211_qosframe *)wh)->i_qos[0] & IEEE80211_QOS_TID;
+ /* NB: assume IEEE80211_WEP_MINLEN covers the extended IV */
+ pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+- if (pn && pn <= k->wk_keyrsc[tid]) {
++ if ((vap->iv_opmode != IEEE80211_M_IBSS) &&
++ (vap->iv_opmode != IEEE80211_M_AHDEMO) &&
++ (pn && pn <= k->wk_keyrsc[tid])) {
+ /*
+ * Replay violation.
+ */
diff --git a/package/madwifi/patches/459-2.6.33_compile.patch b/package/madwifi/patches/459-2.6.33_compile.patch
new file mode 100644
index 000000000..4e08a011a
--- /dev/null
+++ b/package/madwifi/patches/459-2.6.33_compile.patch
@@ -0,0 +1,524 @@
+--- a/kernelversion.c
++++ b/kernelversion.c
+@@ -10,7 +10,11 @@
+
+ /* Linux 2.6.18+ uses <linux/utsrelease.h> */
+ #ifndef UTS_RELEASE
+-#include <linux/utsrelease.h>
++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)
++ #include <generated/utsrelease.h>
++ #else
++ #include <linux/utsrelease.h>
++ #endif
+ #endif
+
+ char *uts_release = UTS_RELEASE;
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -11580,227 +11580,231 @@ static int mincalibrate = 1; /* once a
+ static int maxint = 0x7fffffff; /* 32-bit big */
+
+ static const ctl_table ath_sysctl_template[] = {
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "dev_vendor",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_hwinfo,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ .strategy = &sysctl_string,
++#endif
+ .data = "N/A",
+ .maxlen = 1,
+ .extra2 = (void *)ATH_CARD_VENDOR,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "dev_name",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_hwinfo,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ .strategy = &sysctl_string,
++#endif
+ .data = "N/A",
+ .maxlen = 1,
+ .extra2 = (void *)ATH_CARD_NAME,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "slottime",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_SLOTTIME,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "acktimeout",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_ACKTIMEOUT,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "ctstimeout",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_CTSTIMEOUT,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "distance",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_DISTANCE,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "silent",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_SILENT,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "softled",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_SOFTLED,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "ledpin",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_LEDPIN,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "countrycode",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_COUNTRYCODE,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "outdoor",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_OUTDOOR,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "regdomain",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_REGDOMAIN,
+ },
+ #ifdef AR_DEBUG
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "debug",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_DEBUG,
+ },
+ #endif
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "poweroffset",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_POWEROFFSET,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "txantenna",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_TXANTENNA,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "rxantenna",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RXANTENNA,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "diversity",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_DIVERSITY,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "txintrperiod",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_TXINTRPERIOD,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "fftxqmin",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_FFTXQMIN,
+ },
+ #ifdef ATH_SUPERG_XR
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "xrpollperiod",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_XR_POLL_PERIOD,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "xrpollcount",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_XR_POLL_COUNT,
+ },
+ #endif
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "ackrate",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_ACKRATE,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "channelbw",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_CHANBW,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "rp",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RP,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "radar_print",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RP_PRINT,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "radar_print_all",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RP_PRINT_ALL,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "radar_dump",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RP_PRINT_MEM,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "radar_dump_all",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RP_PRINT_MEM_ALL,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "rp_flush",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RP_FLUSH,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "panic",
+ .mode = 0200,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_PANIC,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "rp_ignored",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RP_IGNORED,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "radar_ignored",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_RADAR_IGNORED,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "intmit",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_INTMIT,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "noise_immunity",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_NOISE_IMMUNITY,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "ofdm_weak_det",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+ .extra2 = (void *)ATH_OFDM_WEAK_DET,
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "cca_thresh",
+ .mode = 0644,
+ .proc_handler = ath_sysctl_halparam,
+@@ -11838,12 +11842,16 @@ ath_dynamic_sysctl_register(struct ath_s
+
+ /* setup the table */
+ memset(sc->sc_sysctls, 0, space);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ sc->sc_sysctls[0].ctl_name = CTL_DEV;
++#endif
+ sc->sc_sysctls[0].procname = "dev";
+ sc->sc_sysctls[0].mode = 0555;
+ sc->sc_sysctls[0].child = &sc->sc_sysctls[2];
+ /* [1] is NULL terminator */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ sc->sc_sysctls[2].ctl_name = CTL_AUTO;
++#endif
+ sc->sc_sysctls[2].procname = dev_name;
+ sc->sc_sysctls[2].mode = 0555;
+ sc->sc_sysctls[2].child = &sc->sc_sysctls[4];
+@@ -11966,7 +11974,7 @@ ath_announce(struct net_device *dev)
+ */
+ static ctl_table ath_static_sysctls[] = {
+ #ifdef AR_DEBUG
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "debug",
+ .mode = 0644,
+ .data = &ath_debug,
+@@ -11974,14 +11982,14 @@ static ctl_table ath_static_sysctls[] =
+ .proc_handler = proc_dointvec
+ },
+ #endif
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "xchanmode",
+ .mode = 0444,
+ .data = &ath_xchanmode,
+ .maxlen = sizeof(ath_xchanmode),
+ .proc_handler = proc_dointvec
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "calibrate",
+ .mode = 0644,
+ .data = &ath_calinterval,
+@@ -11993,14 +12001,14 @@ static ctl_table ath_static_sysctls[] =
+ { 0 }
+ };
+ static ctl_table ath_ath_table[] = {
+- { .ctl_name = DEV_ATH,
++ { CTLNAME(DEV_ATH)
+ .procname = "ath",
+ .mode = 0555,
+ .child = ath_static_sysctls
+ }, { 0 }
+ };
+ static ctl_table ath_root_table[] = {
+- { .ctl_name = CTL_DEV,
++ { CTLNAME(CTL_DEV)
+ .procname = "dev",
+ .mode = 0555,
+ .child = ath_ath_table
+--- a/ath/if_ath_ahb.h
++++ b/ath/if_ath_ahb.h
+@@ -112,7 +112,11 @@
+ do { (void) (start); (void) (size); } while (0)
+ #endif
+
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
++#define bus_dma_sync_single dma_sync_single_for_cpu
++#else
+ #define bus_dma_sync_single dma_sync_single
++#endif
+ #define bus_map_single dma_map_single
+ #define bus_unmap_single dma_unmap_single
+ #define bus_alloc_consistent(_hwdev, _sz, _hdma) \
+--- a/ath_hal/ah_os.c
++++ b/ath_hal/ah_os.c
+@@ -518,7 +518,7 @@ EXPORT_SYMBOL(ath_hal_memcmp);
+
+ static ctl_table ath_hal_sysctls[] = {
+ #ifdef AH_DEBUG
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "debug",
+ .mode = 0644,
+ .data = &ath_hal_debug,
+@@ -526,21 +526,21 @@ static ctl_table ath_hal_sysctls[] = {
+ .proc_handler = proc_dointvec
+ },
+ #endif
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "dma_beacon_response_time",
+ .data = &ath_hal_dma_beacon_response_time,
+ .maxlen = sizeof(ath_hal_dma_beacon_response_time),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "sw_beacon_response_time",
+ .mode = 0644,
+ .data = &ath_hal_sw_beacon_response_time,
+ .maxlen = sizeof(ath_hal_sw_beacon_response_time),
+ .proc_handler = proc_dointvec
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "swba_backoff",
+ .mode = 0644,
+ .data = &ath_hal_additional_swba_backoff,
+@@ -548,19 +548,19 @@ static ctl_table ath_hal_sysctls[] = {
+ .proc_handler = proc_dointvec
+ },
+ #ifdef AH_DEBUG_ALQ
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "alq",
+ .mode = 0644,
+ .proc_handler = sysctl_hw_ath_hal_log
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "alq_size",
+ .mode = 0644,
+ .data = &ath_hal_alq_qsize,
+ .maxlen = sizeof(ath_hal_alq_qsize),
+ .proc_handler = proc_dointvec
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "alq_lost",
+ .mode = 0644,
+ .data = &ath_hal_alq_lost,
+@@ -571,21 +571,21 @@ static ctl_table ath_hal_sysctls[] = {
+ { 0 }
+ };
+ static ctl_table ath_hal_table[] = {
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "hal",
+ .mode = 0555,
+ .child = ath_hal_sysctls
+ }, { 0 }
+ };
+ static ctl_table ath_ath_table[] = {
+- { .ctl_name = DEV_ATH,
++ { CTLNAME(DEV_ATH)
+ .procname = "ath",
+ .mode = 0555,
+ .child = ath_hal_table
+ }, { 0 }
+ };
+ static ctl_table ath_root_table[] = {
+- { .ctl_name = CTL_DEV,
++ { CTLNAME(CTL_DEV)
+ .procname = "dev",
+ .mode = 0555,
+ .child = ath_ath_table
+--- a/include/compat.h
++++ b/include/compat.h
+@@ -193,6 +193,12 @@ static inline int timeval_compare(struct
+ #define __skb_queue_after(_list, _old, _new) __skb_append(_old, _new, _list)
+ #endif
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
++#define CTLNAME(x) .ctl_name = x,
++#else
++#define CTLNAME(x)
++#endif
++
+ #endif /* __KERNEL__ */
+
+ #endif /* _ATH_COMPAT_H_ */
+--- a/net80211/ieee80211_linux.c
++++ b/net80211/ieee80211_linux.c
+@@ -699,39 +699,39 @@ IEEE80211_SYSCTL_DECL(ieee80211_sysctl_m
+
+ static const ctl_table ieee80211_sysctl_template[] = {
+ #ifdef IEEE80211_DEBUG
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "debug",
+ .mode = 0644,
+ .proc_handler = ieee80211_sysctl_debug
+ },
+ #endif
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "dev_type",
+ .mode = 0644,
+ .proc_handler = ieee80211_sysctl_dev_type
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "monitor_nods_only",
+ .mode = 0644,
+ .proc_handler = ieee80211_sysctl_monitor_nods_only
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "monitor_txf_len",
+ .mode = 0644,
+ .proc_handler = ieee80211_sysctl_monitor_txf_len
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "monitor_phy_errors",
+ .mode = 0644,
+ .proc_handler = ieee80211_sysctl_monitor_phy_errors
+ },
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "monitor_crc_errors",
+ .mode = 0644,
+ .proc_handler = ieee80211_sysctl_monitor_crc_errors
+ },
+ /* NB: must be last entry before NULL */
+- { .ctl_name = CTL_AUTO,
++ { CTLNAME(CTL_AUTO)
+ .procname = "%parent",
+ .maxlen = IFNAMSIZ,
+ .mode = 0444,
+@@ -786,12 +786,16 @@ ieee80211_virtfs_latevattach(struct ieee
+
+ /* setup the table */
+ memset(vap->iv_sysctls, 0, space);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ vap->iv_sysctls[0].ctl_name = CTL_NET;
++#endif
+ vap->iv_sysctls[0].procname = "net";
+ vap->iv_sysctls[0].mode = 0555;
+ vap->iv_sysctls[0].child = &vap->iv_sysctls[2];
+ /* [1] is NULL terminator */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ vap->iv_sysctls[2].ctl_name = CTL_AUTO;
++#endif
+ vap->iv_sysctls[2].procname = devname; /* XXX bad idea? */
+ vap->iv_sysctls[2].mode = 0555;
+ vap->iv_sysctls[2].child = &vap->iv_sysctls[4];
diff --git a/package/madwifi/patches/460-pci_softled_disable.patch b/package/madwifi/patches/460-pci_softled_disable.patch
new file mode 100644
index 000000000..328e8c350
--- /dev/null
+++ b/package/madwifi/patches/460-pci_softled_disable.patch
@@ -0,0 +1,18 @@
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -264,6 +264,7 @@ ath_pci_probe(struct pci_dev *pdev, cons
+ }
+ }
+
++#if 0
+ /*
+ * Auto-enable soft led processing for IBM cards and for
+ * 5211 minipci cards. Users can also manually enable/disable
+@@ -279,6 +280,7 @@ ath_pci_probe(struct pci_dev *pdev, cons
+ sc->aps_sc.sc_softled = 1;
+ sc->aps_sc.sc_ledpin = 1;
+ }
++#endif
+
+ if ((i = ath_attach(vdevice, dev, NULL)) != 0) {
+ printk(KERN_ERR "%s: ath_attach failed: %d\n", dev_info, i);
diff --git a/package/madwifi/patches/461-rx_stats_count_fix.patch b/package/madwifi/patches/461-rx_stats_count_fix.patch
new file mode 100644
index 000000000..4b5ce28cc
--- /dev/null
+++ b/package/madwifi/patches/461-rx_stats_count_fix.patch
@@ -0,0 +1,23 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -202,7 +202,6 @@ ieee80211_input(struct ieee80211vap * va
+ struct ieee80211com *ic;
+ struct net_device *dev;
+ struct ieee80211_node *ni_wds = NULL;
+- struct net_device_stats *stats;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+@@ -685,12 +684,6 @@ ieee80211_input(struct ieee80211vap * va
+ if (! accept_data_frame(vap, ni, key, skb, eh))
+ goto out;
+
+- if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
+- stats = &ni->ni_subif->iv_devstats;
+- else
+- stats = &vap->iv_devstats;
+- stats->rx_packets++;
+- stats->rx_bytes += skb->len;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
+ ic->ic_lastdata = jiffies;
diff --git a/package/madwifi/patches/462-fix_ap_scan.patch b/package/madwifi/patches/462-fix_ap_scan.patch
new file mode 100644
index 000000000..8798f534e
--- /dev/null
+++ b/package/madwifi/patches/462-fix_ap_scan.patch
@@ -0,0 +1,26 @@
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -595,6 +595,14 @@ ap_end(struct ieee80211_scan_state *ss,
+
+ ic = vap->iv_ic;
+
++ /* if we're already running, switch back to the home channel */
++ if ((vap->iv_state == IEEE80211_S_RUN) &&
++ (ic->ic_bsschan != IEEE80211_CHAN_ANYC)) {
++ ic->ic_curchan = ic->ic_bsschan;
++ ic->ic_set_channel(ic);
++ goto out;
++ }
++
+ /* record stats for the channel that was scanned last */
+ ic->ic_set_channel(ic);
+ spin_lock_irqsave(&channel_lock, sflags);
+@@ -648,6 +656,8 @@ ap_end(struct ieee80211_scan_state *ss,
+ IEEE80211_SCHEDULE_TQUEUE(&as->as_actiontq);
+ res = 1;
+ }
++
++out:
+ SCAN_AP_UNLOCK_IRQ(as);
+ return res;
+ }
diff --git a/package/madwifi/patches/463-fix_txrate_display.patch b/package/madwifi/patches/463-fix_txrate_display.patch
new file mode 100644
index 000000000..0889f78fe
--- /dev/null
+++ b/package/madwifi/patches/463-fix_txrate_display.patch
@@ -0,0 +1,10 @@
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -728,6 +728,7 @@ ath_rate_statistics(struct ieee80211_nod
+ rn->max_tp_rate2 = index_max_tp2;
+ rn->max_prob_rate = index_max_prob;
+ rn->current_rate = index_max_tp;
++ ni->ni_txrate = index_max_tp;
+ }
+
+ static struct ath_ratectrl *
diff --git a/package/madwifi/patches/464-0dbm_txpower_fix.patch b/package/madwifi/patches/464-0dbm_txpower_fix.patch
new file mode 100644
index 000000000..fbd1d7673
--- /dev/null
+++ b/package/madwifi/patches/464-0dbm_txpower_fix.patch
@@ -0,0 +1,30 @@
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -1370,15 +1370,9 @@ ieee80211_ioctl_siwtxpow(struct net_devi
+ int fixed, disabled;
+
+ fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED);
+- disabled = (fixed && ic->ic_txpowlimit == 0);
+- if (rrq->disabled) {
+- if (!disabled) {
+- ic->ic_flags |= IEEE80211_F_TXPOW_FIXED;
+- ic->ic_txpowlimit = 0;
+- goto done;
+- }
+- return 0;
+- }
++
++ if (rrq->disabled)
++ return -EINVAL;
+
+ if (rrq->fixed) {
+ if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+@@ -1571,7 +1565,7 @@ ieee80211_ioctl_giwtxpow(struct net_devi
+ rrq->fixed = 0;
+ }
+ rrq->value = txp / 2;
+- rrq->disabled = (rrq->fixed && rrq->value == 0);
++ rrq->disabled = 0;
+ rrq->flags = IW_TXPOW_DBM;
+ return 0;
+ }
diff --git a/package/madwifi/patches/465-mc_list-2.6.35.patch b/package/madwifi/patches/465-mc_list-2.6.35.patch
new file mode 100644
index 000000000..4931cfb28
--- /dev/null
+++ b/package/madwifi/patches/465-mc_list-2.6.35.patch
@@ -0,0 +1,40 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -4466,7 +4466,11 @@ ath_merge_mcast(struct ath_softc *sc, u_
+ {
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap;
++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
++ struct netdev_hw_addr *ha;
++ #else
+ struct dev_mc_list *mc;
++ #endif
+ u_int32_t val;
+ u_int8_t pos;
+
+@@ -4474,6 +4478,17 @@ ath_merge_mcast(struct ath_softc *sc, u_
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct net_device *dev = vap->iv_dev;
++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
++ netdev_for_each_mc_addr(ha, dev) {
++ /* calculate XOR of eight 6-bit values */
++ val = LE_READ_4(ha->addr + 0);
++ pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
++ val = LE_READ_4(ha->addr + 3);
++ pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
++ pos &= 0x3f;
++ mfilt[pos / 32] |= (1 << (pos % 32));
++ }
++ #else
+ for (mc = dev->mc_list; mc; mc = mc->next) {
+ /* calculate XOR of eight 6-bit values */
+ val = LE_READ_4(mc->dmi_addr + 0);
+@@ -4483,6 +4498,7 @@ ath_merge_mcast(struct ath_softc *sc, u_
+ pos &= 0x3f;
+ mfilt[pos / 32] |= (1 << (pos % 32));
+ }
++ #endif
+ }
+ }
+
diff --git a/package/madwifi/patches/466-2.6.38_compile.patch b/package/madwifi/patches/466-2.6.38_compile.patch
new file mode 100644
index 000000000..10a6f0deb
--- /dev/null
+++ b/package/madwifi/patches/466-2.6.38_compile.patch
@@ -0,0 +1,14 @@
+--- a/include/compat.h
++++ b/include/compat.h
+@@ -131,6 +131,11 @@
+ #ifdef __KERNEL__
+
+ #include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
++#define AUTOCONF_INCLUDED 1
++#endif
++
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+ #define ATH_REGISTER_SYSCTL_TABLE(t) register_sysctl_table(t, 1)
+ #else
diff --git a/package/madwifi/patches/470-mac_addresss_from_ath5k_platform_data.patch b/package/madwifi/patches/470-mac_addresss_from_ath5k_platform_data.patch
new file mode 100644
index 000000000..0c35050c5
--- /dev/null
+++ b/package/madwifi/patches/470-mac_addresss_from_ath5k_platform_data.patch
@@ -0,0 +1,36 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -63,6 +63,8 @@
+ #include <linux/rtnetlink.h>
+ #include <linux/time.h>
+ #include <linux/pci.h>
++#include <linux/device.h>
++#include <linux/ath5k_platform.h>
+ #include <asm/uaccess.h>
+
+ #include "if_ethersubr.h" /* for ETHER_IS_MULTICAST */
+@@ -587,6 +589,10 @@ ath_attach(u_int16_t devid, struct net_d
+ unsigned int i;
+ int autocreatemode = -1;
+ u_int8_t csz;
++#ifdef ATH_PCI
++ struct ath5k_platform_data *pdata;
++ struct pci_dev *pdev;
++#endif
+
+ sc->devid = devid;
+ #ifdef AR_DEBUG
+@@ -648,6 +654,13 @@ ath_attach(u_int16_t devid, struct net_d
+ }
+ sc->sc_ah = ah;
+
++#ifdef ATH_PCI
++ /* set MAC from ath_platform_data */
++ pdev = (struct pci_dev *)sc->sc_bdev;
++ pdata = pdev->dev.platform_data;
++ if (pdata && pdata->macaddr)
++ ath_hal_setmac(ah, pdata->macaddr);
++#endif
+ /* WAR for AR7100 PCI bug */
+ #if defined(CONFIG_ATHEROS_AR71XX) || defined(CONFIG_ATH79)
+ if ((ar_device(sc->devid) >= 5210) && (ar_device(sc->devid) < 5416)) {
diff --git a/package/madwifi/patches/471-netdev_ops_mac_mtu.patch b/package/madwifi/patches/471-netdev_ops_mac_mtu.patch
new file mode 100644
index 000000000..c7e1f404a
--- /dev/null
+++ b/package/madwifi/patches/471-netdev_ops_mac_mtu.patch
@@ -0,0 +1,30 @@
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -47,6 +47,7 @@
+ #include <linux/skbuff.h>
+ #include <linux/netdevice.h>
+ #include <linux/rtnetlink.h> /* XXX for rtnl_lock */
++#include <linux/etherdevice.h>
+
+ #include "if_media.h"
+
+@@ -463,6 +464,8 @@ static const struct net_device_ops ieee8
+ .ndo_set_multicast_list = ieee80211_set_multicast_list,
+ .ndo_change_mtu = ieee80211_change_mtu,
+ .ndo_do_ioctl = ieee80211_ioctl,
++ .ndo_validate_addr = eth_validate_addr,
++ .ndo_set_mac_address = eth_mac_addr,
+ };
+ #endif
+
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -574,6 +574,8 @@ static const struct net_device_ops ath_n
+ .ndo_get_stats = ath_getstats,
+ .ndo_set_mac_address = ath_set_mac_address,
+ .ndo_change_mtu = ath_change_mtu,
++ .ndo_validate_addr = eth_validate_addr,
++ .ndo_set_mac_address = eth_mac_addr,
+ };
+ #endif
+
diff --git a/package/madwifi/patches/472-remove_11n_devids.patch b/package/madwifi/patches/472-remove_11n_devids.patch
new file mode 100644
index 000000000..cf80f06bf
--- /dev/null
+++ b/package/madwifi/patches/472-remove_11n_devids.patch
@@ -0,0 +1,20 @@
+--- a/ath/if_ath_pci.c
++++ b/ath/if_ath_pci.c
+@@ -111,8 +111,6 @@ static struct pci_device_id ath_pci_id_t
+ { 0x168c, 0x001b, PCI_ANY_ID, PCI_ANY_ID },
+ { 0x168c, 0x001c, PCI_ANY_ID, PCI_ANY_ID }, /* PCI Express 5424 */
+ { 0x168c, 0x001d, PCI_ANY_ID, PCI_ANY_ID }, /* PCI Express ??? */
+- { 0x168c, 0x0023, PCI_ANY_ID, PCI_ANY_ID },
+- { 0x168c, 0x0024, PCI_ANY_ID, PCI_ANY_ID },
+ { 0x168c, 0x9013, PCI_ANY_ID, PCI_ANY_ID }, /* sonicwall */
+ { 0x168c, 0xff1a, PCI_ANY_ID, PCI_ANY_ID },
+ { 0 }
+@@ -146,8 +144,6 @@ static const struct ath_hw_detect cards[
+ { ubnt, "SR4C", 0x168c, 0x0013, 0x7777, 0x1004, 6 },
+ { ubnt, "SR5", 0x168c, 0x0013, 0x168c, 0x2042, 7 },
+ { ubnt, "SR9", 0x168c, 0x0013, 0x7777, 0x2009, 12 },
+- { ubnt, "SR71A", 0x168c, 0x0027, 0x168c, 0x2082, 10 },
+- { ubnt, "SR71", 0x168c, 0x0027, 0x0777, 0x4082, 10 },
+ };
+
+ static int
diff --git a/package/madwifi/patches/473-mutex_fix.patch b/package/madwifi/patches/473-mutex_fix.patch
new file mode 100644
index 000000000..22cea46a2
--- /dev/null
+++ b/package/madwifi/patches/473-mutex_fix.patch
@@ -0,0 +1,11 @@
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -974,7 +974,7 @@ typedef void (*ath_callback) (struct ath
+ #endif
+
+ /* Protects the device from concurrent accesses */
+-#define ATH_LOCK_INIT(_sc) init_MUTEX(&(_sc)->sc_lock)
++#define ATH_LOCK_INIT(_sc) sema_init(&(_sc)->sc_lock, 1)
+ #define ATH_LOCK_DESTROY(_sc)
+ #define ATH_LOCK(_sc) down(&(_sc)->sc_lock)
+ #define ATH_UNLOCK(_sc) up(&(_sc)->sc_lock)
diff --git a/package/madwifi/patches/474_fix_ssid_scan_length.patch b/package/madwifi/patches/474_fix_ssid_scan_length.patch
new file mode 100644
index 000000000..2e85e18c6
--- /dev/null
+++ b/package/madwifi/patches/474_fix_ssid_scan_length.patch
@@ -0,0 +1,29 @@
+--- a/tools/wlanconfig.c
++++ b/tools/wlanconfig.c
+@@ -654,7 +654,7 @@ static void
+ list_scan(const char *ifname)
+ {
+ static uint8_t buf[24 * 1024];
+- char ssid[14];
++ char ssid[30];
+ uint8_t *cp;
+ int len;
+
+@@ -665,7 +665,7 @@ list_scan(const char *ifname)
+ if (len < sizeof(struct ieee80211req_scan_result))
+ return;
+
+- printf("%-14.14s %-17.17s %4s %4s %-5s %3s %4s\n",
++ printf("%-30.30s %-17.17s %4s %4s %-5s %3s %4s\n",
+ "SSID",
+ "BSSID",
+ "CHAN",
+@@ -680,7 +680,7 @@ list_scan(const char *ifname)
+
+ sr = (struct ieee80211req_scan_result *) cp;
+ vp = (u_int8_t *)(sr+1);
+- printf("%-14.*s %s %3d %3dM %2d:%-2d %3d %-4.4s",
++ printf("%-30.*s %s %3d %3dM %2d:%-2d %3d %-4.4s",
+ copy_essid(ssid, sizeof(ssid), vp, sr->isr_ssid_len),
+ ssid,
+ ieee80211_ntoa(sr->isr_bssid),
diff --git a/package/madwifi/patches/475-2.6.39-compile.patch b/package/madwifi/patches/475-2.6.39-compile.patch
new file mode 100644
index 000000000..5b8a77718
--- /dev/null
+++ b/package/madwifi/patches/475-2.6.39-compile.patch
@@ -0,0 +1,11 @@
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -97,7 +97,7 @@ struct scan_state {
+ static void scan_restart_pwrsav(unsigned long);
+ static void scan_next(unsigned long);
+
+-spinlock_t channel_lock = SPIN_LOCK_UNLOCKED;
++DEFINE_SPINLOCK(channel_lock);
+ static LIST_HEAD(channels_inuse);
+
+ struct channel_inuse {
diff --git a/package/madwifi/patches/476-3.0_detection_fix.patch b/package/madwifi/patches/476-3.0_detection_fix.patch
new file mode 100644
index 000000000..2f8bfb4a5
--- /dev/null
+++ b/package/madwifi/patches/476-3.0_detection_fix.patch
@@ -0,0 +1,25 @@
+--- a/Makefile
++++ b/Makefile
+@@ -155,13 +155,15 @@ unload:
+ configcheck:
+ @echo -n "Checking kernel configuration... "
+
+- @# check version of kernel
+- @echo $(KERNELRELEASE) | grep -q -i '^[2-9]\.[4-9]\.' || { \
+- echo "FAILED"; \
+- echo "Only kernel versions 2.4.x and above are supported."; \
+- echo "You have $(KERNELRELEASE)."; \
+- exit 1; \
+- }
++ @# check kernel version
++ @case $(KERNELRELEASE) in \
++ 2.[456].*) ;; \
++ [3-9].*) ;; \
++ *) echo "FAILED"; \
++ echo "Only kernel versions 2.4.x and above are supported."; \
++ echo "You have $(KERNELRELEASE)."; \
++ exit 1 ;; \
++ esac
+
+ @# check kernel configuration
+ @if [ -z "$(CONFIG_SYSCTL)" ]; then \
diff --git a/package/madwifi/patches/477-3.2_fixes.patch b/package/madwifi/patches/477-3.2_fixes.patch
new file mode 100644
index 000000000..89cdfc649
--- /dev/null
+++ b/package/madwifi/patches/477-3.2_fixes.patch
@@ -0,0 +1,45 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -569,7 +569,11 @@ static const struct net_device_ops ath_n
+ .ndo_stop = ath_stop,
+ .ndo_start_xmit = ath_hardstart,
+ .ndo_tx_timeout = ath_tx_timeout,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
++ .ndo_set_rx_mode = ath_mode_init,
++#else
+ .ndo_set_multicast_list = ath_mode_init,
++#endif
+ .ndo_do_ioctl = ath_ioctl,
+ .ndo_get_stats = ath_getstats,
+ .ndo_set_mac_address = ath_set_mac_address,
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -461,7 +461,11 @@ static const struct net_device_ops ieee8
+ .ndo_open = ieee80211_open,
+ .ndo_stop = ieee80211_stop,
+ .ndo_start_xmit = ieee80211_hardstart,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
++ .ndo_set_rx_mode = ieee80211_set_multicast_list,
++#else
+ .ndo_set_multicast_list = ieee80211_set_multicast_list,
++#endif
+ .ndo_change_mtu = ieee80211_change_mtu,
+ .ndo_do_ioctl = ieee80211_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+@@ -1847,10 +1851,14 @@ ieee80211_set_multicast_list(struct net_
+ IEEE80211_UNLOCK_IRQ(ic);
+
+ /* XXX: Merge multicast list into parent device */
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+- parent->set_multicast_list(ic->ic_dev);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
++ parent->netdev_ops->ndo_set_rx_mode(ic->ic_dev);
+ #else
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+ parent->netdev_ops->ndo_set_multicast_list(ic->ic_dev);
++#else
++ parent->set_multicast_list(ic->ic_dev);
++#endif
+ #endif
+ }
+
diff --git a/package/madwifi/patches/478-remove_vlan_code.patch b/package/madwifi/patches/478-remove_vlan_code.patch
new file mode 100644
index 000000000..9ceda4aa0
--- /dev/null
+++ b/package/madwifi/patches/478-remove_vlan_code.patch
@@ -0,0 +1,21 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1223,6 +1223,7 @@ ieee80211_deliver_data(struct ieee80211_
+ #endif
+ vap->iv_devstats.rx_packets++;
+ vap->iv_devstats.rx_bytes += skb->len;
++#if IEEE80211_VLAN_TAG_USED
+ if (ni->ni_vlan != 0 && vap->iv_vlgrp != NULL) {
+ /* attach vlan tag */
+ struct ieee80211_node *ni_tmp = SKB_CB(skb)->ni;
+@@ -1236,7 +1237,9 @@ ieee80211_deliver_data(struct ieee80211_
+ vap->iv_devstats.rx_dropped++;
+ }
+ skb = NULL; /* SKB is no longer ours */
+- } else {
++ } else
++#endif
++ {
+ struct ieee80211_node *ni_tmp = SKB_CB(skb)->ni;
+ if (netif_receive_skb(skb) == NET_RX_DROP) {
+ /* If netif_receive_skb dropped the packet because