From: Felix Fietkau Date: Wed, 6 May 2009 11:52:24 +0000 (+0000) Subject: madwifi: make country code and outdoor flag changeable without reloading the driver X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;hb=64efa4169bbce3ffdedafb3456d25952ab285c00 madwifi: make country code and outdoor flag changeable without reloading the driver SVN-Revision: 15633 --- diff --git a/package/madwifi/files/lib/wifi/madwifi.sh b/package/madwifi/files/lib/wifi/madwifi.sh index 8c4a75426e..2fe866410e 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 0000000000..781194eed7 --- /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 + 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.