summaryrefslogtreecommitdiffstats
path: root/package/madwifi
diff options
context:
space:
mode:
authornbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2009-05-06 11:52:24 +0000
committernbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2009-05-06 11:52:24 +0000
commit0ee2c9dc7b5f895c88d5d6bb8b91b95028409b7c (patch)
treee427cc76cbfcfd02c20665632b3770f5ba8a3d96 /package/madwifi
parent6f2c5c53e8bd71563c3968bf0c81f4d8a388e687 (diff)
madwifi: make country code and outdoor flag changeable without reloading the driver
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@15633 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/madwifi')
-rwxr-xr-xpackage/madwifi/files/lib/wifi/madwifi.sh17
-rw-r--r--package/madwifi/patches/421-channel_handling.patch1354
2 files changed, 1364 insertions, 7 deletions
diff --git a/package/madwifi/files/lib/wifi/madwifi.sh b/package/madwifi/files/lib/wifi/madwifi.sh
index 8c4a75426..2fe866410 100755
--- a/package/madwifi/files/lib/wifi/madwifi.sh
+++ b/package/madwifi/files/lib/wifi/madwifi.sh
@@ -67,15 +67,18 @@ disable_atheros() (
enable_atheros() {
local device="$1"
- # Can only set the country code to one setting for the entire system. The last country code is the one that will be applied.
+
+ config_get regdomain "$device" regdomain
+ [ -n "$regdomain" ] && echo "$regdomain" > /proc/sys/dev/$device/regdomain
+
config_get country "$device" country
[ -z "$country" ] && country="0"
- local cc="0"
- [ -e /proc/sys/dev/$device/countrycode ] && cc="$(cat /proc/sys/dev/$device/countrycode)"
- if [ ! "$cc" = "$country" ] ; then
- rmmod ath_pci
- insmod ath_pci countrycode=$country
- fi
+ echo "$country" > /proc/sys/dev/$device/countrycode
+
+ config_get outdoor "$device" outdoor
+ [ -z "$outdoor" ] && outdoor="0"
+ echo "$outdoor" > /proc/sys/dev/$device/outdoor
+
config_get channel "$device" channel
config_get vifs "$device" vifs
config_get txpower "$device" txpower
diff --git a/package/madwifi/patches/421-channel_handling.patch b/package/madwifi/patches/421-channel_handling.patch
new file mode 100644
index 000000000..781194eed
--- /dev/null
+++ b/package/madwifi/patches/421-channel_handling.patch
@@ -0,0 +1,1354 @@
+--- 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);
+@@ -909,10 +929,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
+@@ -4466,17 +4482,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;
+ }
+
+@@ -4498,7 +4514,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
+@@ -5342,7 +5358,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) {
+ /*
+@@ -7796,12 +7812,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;
+ }
+@@ -8094,7 +8112,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;
+@@ -9061,7 +9079,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);
+@@ -10110,8 +10128,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;
+@@ -10125,17 +10142,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.
+ */
+@@ -10379,7 +10410,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)
+@@ -10392,57 +10423,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)
+ {
+@@ -10451,7 +10441,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:
+@@ -10469,6 +10459,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;
+@@ -10483,10 +10479,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;
+ }
+
+@@ -10515,13 +10517,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];
+@@ -10531,7 +10538,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;
+@@ -10932,9 +10939,115 @@ 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;
++ void *ptr;
++ u32 *reg;
++
++ if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
++ freq = ic->ic_curchan->ic_freq;
++
++ switch(ctl) {
++ case ATH_REGDOMAIN:
++ ptr = ah;
++ ptr += sizeof(struct ath_hal);
++ ptr += 116;
++ reg = ptr;
++ *reg = val;
++ return 0;
++ 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)
+ {
+@@ -11013,6 +11126,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];
+@@ -11036,25 +11150,31 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ lenp, ppos);
+ if (ret == 0) {
+ switch ((long)ctl->extra2) {
++ case ATH_REGDOMAIN:
++ 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) {
+@@ -11206,7 +11326,12 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+ }
+ }
+ } else {
++ void *ptr;
++ u32 *reg;
+ switch ((long)ctl->extra2) {
++ case ATH_CHANBW:
++ val = sc->sc_chanbw ?: 20;
++ break;
+ case ATH_SLOTTIME:
+ val = ath_hal_getslottime(ah);
+ break;
+@@ -11225,11 +11350,18 @@ 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;
+ case ATH_REGDOMAIN:
+- ath_hal_getregdomain(ah, &val);
++ ptr = ah;
++ ptr += sizeof(struct ath_hal);
++ ptr += 116;
++ reg = ptr;
++ val = *reg;
+ break;
+ case ATH_DEBUG:
+ val = sc->sc_debug | ath_debug_global;
+@@ -11338,11 +11470,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,
+@@ -11350,7 +11488,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,
+ },
+@@ -11413,6 +11551,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,
+@@ -11653,13 +11797,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,
+@@ -11667,13 +11804,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
+@@ -689,16 +689,17 @@ 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_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,58 @@ 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;
++
++ 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 +359,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 +796,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 +827,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 +1241,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 +1451,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 +1469,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;
+@@ -1881,11 +1915,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;
+@@ -1919,7 +1948,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 */
+@@ -715,6 +713,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);
+@@ -794,6 +793,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;) {
+@@ -1401,6 +1401,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
+@@ -2133,7 +2133,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;
+ }
+@@ -4081,46 +4081,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
+@@ -518,12 +518,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;
+@@ -612,6 +613,7 @@ pick_channel(struct ieee80211_scan_state
+ "%s: best: channel %u rssi %d\n",
+ __func__, i, as->as_maxrssi[i]);
+ }
++ kfree(chans);
+ return best;
+ }
+
+@@ -647,6 +649,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 */
+@@ -661,6 +664,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.