bool "Use the OpenWrt stable version of madwifi"
config MADWIFI_UPSTREAM
+ depends BROKEN
depends !LINUX_2_6_26
depends !TARGET_atheros
bool "Use the upstream release version 0.9.4"
ifneq ($(CONFIG_MADWIFI_UPSTREAM),)
PKG_VERSION:=0.9.4
- PKG_RELEASE:=2
+ PKG_RELEASE:=2.1
PKG_SOURCE:=madwifi-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=http://downloads.sourceforge.net/madwifi/
# PKG_BRANCH:=madwifi-dfs
PKG_REV:=3314
PKG_VERSION:=r$(PKG_REV)
- PKG_RELEASE:=2
+ PKG_RELEASE:=2.1
PKG_SOURCE_PROTO:=svn
PKG_SOURCE_VERSION:=$(PKG_REV)
*) echo "$device: Invalid mode combination in config"; return 1;;
esac
- config_set "$device" vifs "${ap:+$ap }${adhoc:+$adhoc }${ahdemo:+$ahdemo }${sta:+$sta }${wds:+$wds }${monitor:+$monitor}"
+ config_set "$device" vifs "${sta:+$sta }${ap:+$ap }${adhoc:+$adhoc }${ahdemo:+$ahdemo }${wds:+$wds }${monitor:+$monitor}"
}
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 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
config_get channel "$device" channel
config_get vifs "$device" vifs
config_get txpower "$device" txpower
*fh) hwmode=fh;;
*) hwmode=auto;;
esac
- iwpriv "$ifname" mode "$hwmode"
iwpriv "$ifname" pureg "$pureg"
[ "$first" = 1 ] && {
+ iwpriv "$ifname" mode "$hwmode"
iwconfig "$ifname" channel "$channel" >/dev/null 2>/dev/null
}
esac
case "$mode" in
- adhoc|ahdemo)
+ sta|adhoc|ahdemo)
config_get addr "$vif" bssid
[ -z "$addr" ] || {
iwconfig "$ifname" ap "$addr"
config_get distance "$device" distance
[ -n "$distance" ] && athctrl -i "$device" -d "$distance" >&-
- config_get txpwr "$vif" txpower
- [ -n "$txpwr" ] && iwconfig "$ifname" txpower "${txpwr%%.*}"
-
config_get rate "$vif" rate
[ -n "$rate" ] && iwconfig "$ifname" rate "${rate%%.*}"
config_get_bool turbo "$vif" turbo
[ -n "$turbo" ] && iwpriv "$ifname" turbo "$turbo"
+ config_get_bool beacon_power "$vif" beacon_power
+ [ -n "$beacon_power" ] && iwpriv "$ifname" beacon_pwr "$beacon_power"
+
config_get_bool doth "$vif" doth 0
[ -n "$doth" ] && iwpriv "$ifname" doth "$doth"
esac
ifconfig "$ifname" up
+
local net_cfg bridge
net_cfg="$(find_net_config "$vif")"
[ -z "$net_cfg" ] || {
* Convert to a HAL channel description with the flags
* constrained to reflect the current operating mode.
*/
-@@ -5154,6 +5178,8 @@ ath_beacon_send(struct ath_softc *sc, in
+@@ -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)))
-+ ath_calibrate((unsigned long) sc->sc_dev);
++ 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 +5429,7 @@ ath_beacon_config(struct ath_softc *sc,
+@@ -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);
} else {
ath_hal_intrset(ah, 0);
if (reset_tsf)
-@@ -5414,8 +5441,11 @@ ath_beacon_config(struct ath_softc *sc,
+@@ -5414,8 +5443,11 @@ ath_beacon_config(struct ath_softc *sc,
*/
intval |= HAL_BEACON_ENA;
sc->sc_imask |= HAL_INT_SWBA;
#ifdef ATH_SUPERG_DYNTURBO
ath_beacon_dturbo_config(vap, intval &
~(HAL_BEACON_RESET_TSF | HAL_BEACON_ENA));
-@@ -8879,6 +8909,9 @@ ath_chan_set(struct ath_softc *sc, struc
+@@ -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));
}
/*
* re configure beacons when it is a turbo mode switch.
-@@ -8988,8 +9021,11 @@ ath_calibrate(unsigned long arg)
+@@ -8988,8 +9023,11 @@ ath_calibrate(unsigned long arg)
sc->sc_curchan.channel, sc->sc_curchan.channelFlags,
isIQdone ? "done" : "not done");
}
static void
-@@ -9096,7 +9132,8 @@ ath_newstate(struct ieee80211vap *vap, e
+@@ -9096,7 +9134,8 @@ ath_newstate(struct ieee80211vap *vap, e
ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
ath_hal_setledstate(ah, leds[nstate]); /* set LED */
netif_stop_queue(dev); /* before we do anything else */
-@@ -9321,7 +9358,8 @@ ath_newstate(struct ieee80211vap *vap, e
+@@ -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 */
/* wake the receiver */
netif_wake_queue(dev);
/* don't do the other usual stuff... */
-@@ -9364,7 +9402,7 @@ done:
+@@ -9364,7 +9404,7 @@ done:
error = avp->av_newstate(vap, nstate, arg);
/* Finally, start any timers. */
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -8920,7 +8920,7 @@ ath_chan_set(struct ath_softc *sc, struc
+@@ -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.
*/
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -8251,6 +8251,17 @@ ath_tx_processq(struct ath_softc *sc, st
+@@ -8253,6 +8253,17 @@ ath_tx_processq(struct ath_softc *sc, st
goto bf_fail;
}
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -8101,6 +8101,7 @@ ath_tx_start(struct net_device *dev, str
+@@ -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);
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -10274,11 +10274,11 @@ ath_setcurmode(struct ath_softc *sc, enu
+@@ -10276,11 +10276,11 @@ ath_setcurmode(struct ath_softc *sc, enu
sc->sc_currates = rt;
sc->sc_curmode = mode;
/*
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 */
-@@ -10789,8 +10792,13 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+@@ -10791,8 +10794,13 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
break;
#endif
case ATH_ACKRATE:
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -8862,8 +8862,7 @@ ath_chan_set(struct ath_softc *sc, struc
+@@ -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 (sc->sc_curchan.privFlags & CHANNEL_DFS)
rfilt |= (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR);
return rfilt;
-@@ -6524,9 +6563,6 @@ process_rx_again:
+@@ -6526,9 +6565,6 @@ process_rx_again:
rs->rs_rssi = 0;
len = rs->rs_datalen;
if (rs->rs_more) {
/*
-@@ -8874,9 +8910,7 @@ ath_chan_set(struct ath_softc *sc, struc
+@@ -8876,9 +8912,7 @@ ath_chan_set(struct ath_softc *sc, struc
if (sc->sc_softled)
ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
sc->sc_curchan = hchan;
ath_update_txpow(sc); /* update tx power state */
ath_radar_update(sc);
-@@ -10653,9 +10687,54 @@ enum {
+@@ -10655,9 +10689,54 @@ enum {
ATH_RP_IGNORED = 24,
ATH_RADAR_IGNORED = 25,
ATH_MAXVAPS = 26,
ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos)
{
struct ath_softc *sc = ctl->extra1;
-@@ -10841,6 +10920,11 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+@@ -10843,6 +10922,11 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
case ATH_RADAR_IGNORED:
sc->sc_radar_ignored = val;
break;
default:
ret = -EINVAL;
break;
-@@ -10907,6 +10991,11 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+@@ -10909,6 +10993,11 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
case ATH_RADAR_IGNORED:
val = sc->sc_radar_ignored;
break;
default:
ret = -EINVAL;
break;
-@@ -11084,6 +11173,24 @@ static const ctl_table ath_sysctl_templa
+@@ -11086,6 +11175,24 @@ static const ctl_table ath_sysctl_templa
.proc_handler = ath_sysctl_halparam,
.extra2 = (void *)ATH_RADAR_IGNORED,
},
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -8332,6 +8332,14 @@ ath_tx_processq(struct ath_softc *sc, st
+@@ -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++;
ATH_SCHEDULE_TQUEUE(&sc->sc_bstucktq, needmark);
return;
}
-@@ -5228,7 +5232,7 @@ ath_bstuck_tasklet(TQUEUE_ARG data)
+@@ -5230,7 +5234,7 @@ ath_bstuck_tasklet(TQUEUE_ARG data)
* check will be true, in which case return
* without resetting the driver.
*/
if (ic->ic_dev->flags & IFF_RUNNING) {
/* needs to disable hardware too */
-@@ -1271,8 +1269,11 @@ ath_vap_create(struct ieee80211com *ic,
+@@ -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;
/* 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 +1305,7 @@ ath_vap_create(struct ieee80211com *ic,
+@@ -1304,7 +1306,7 @@ ath_vap_create(struct ieee80211com *ic,
}
avp = dev->priv;
/* override with driver methods */
vap = &avp->av_vap;
avp->av_newstate = vap->iv_newstate;
-@@ -4209,8 +4210,7 @@ ath_calcrxfilter(struct ath_softc *sc)
+@@ -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 ||
rfilt |= HAL_RX_FILTER_BEACON;
if (sc->sc_nmonvaps > 0)
rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON |
-@@ -9030,8 +9030,6 @@ ath_calibrate(unsigned long arg)
+@@ -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 &&
}
break;
case IEEE80211_M_IBSS:
-@@ -540,16 +548,32 @@ ieee80211_input(struct ieee80211vap * va
+@@ -540,16 +548,28 @@ ieee80211_input(struct ieee80211vap * va
vap->iv_stats.is_rx_notassoc++;
goto err;
}
-+
-+ /* subif isn't fully set up yet, drop the frame */
-+ if (ni->ni_subif == ni->ni_vap)
-+ goto err;
+
/*
* If we're a 4 address packet, make sure we have an entry in
if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
-@@ -557,7 +581,6 @@ ieee80211_input(struct ieee80211vap * va
+@@ -557,7 +577,6 @@ ieee80211_input(struct ieee80211vap * va
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)) {
-@@ -608,6 +631,11 @@ ieee80211_input(struct ieee80211vap * va
+@@ -608,6 +627,11 @@ ieee80211_input(struct ieee80211vap * va
goto out;
}
/*
* Handle privacy requirements. Note that we
* must not be preempted from here until after
-@@ -680,8 +708,12 @@ ieee80211_input(struct ieee80211vap * va
+@@ -680,8 +704,12 @@ ieee80211_input(struct ieee80211vap * va
if (! accept_data_frame(vap, ni, key, skb, eh))
goto out;
IEEE80211_NODE_STAT(ni, rx_data);
IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
ic->ic_lastdata = jiffies;
-@@ -1114,6 +1146,13 @@ ieee80211_deliver_data(struct ieee80211_
+@@ -1114,6 +1142,17 @@ 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))) {
-+ vap = ni->ni_subif;
-+ dev = vap->iv_dev;
++ if (ni->ni_vap == ni->ni_subif) {
++ ieee80211_dev_kfree_skb(&skb);
++ } else {
++ vap = ni->ni_subif;
++ dev = vap->iv_dev;
++ }
+ }
+
/* perform as a bridge within the vap */
+ (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) {
-+ int found = 0;
-+
+ 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)
+ break;
+ }
+ }
-+ if (!found)
-+ break;
-+
-+ ni = ni_or_null = avp->iv_wdsnode;
++ if (found)
++ ni = ni_or_null = avp->iv_wdsnode;
+ } else if (vap->iv_opmode == IEEE80211_M_WDS) {
++ 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. */
}
/* This is overridden by ath_node_alloc in ath/if_ath.c, and so
-@@ -1134,6 +1145,62 @@ ieee80211_alloc_node(struct ieee80211vap
+@@ -1134,6 +1145,65 @@ ieee80211_alloc_node(struct ieee80211vap
return ni;
}
+ 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);
/* Add wds address to the node table */
int
#ifdef IEEE80211_DEBUG_REFCNT
-@@ -1553,22 +1620,39 @@ ieee80211_find_rxnode(struct ieee80211co
+@@ -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;
#endif
IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
-@@ -1596,9 +1680,19 @@ ieee80211_find_txnode_debug(struct ieee8
+@@ -1596,9 +1683,19 @@ ieee80211_find_txnode_debug(struct ieee8
ieee80211_find_txnode(struct ieee80211vap *vap, const u_int8_t *mac)
#endif
{
/*
* The destination address should be in the node table
* unless we are operating in station mode or this is a
-@@ -1669,6 +1763,11 @@ ieee80211_free_node(struct ieee80211_nod
+@@ -1669,6 +1766,11 @@ ieee80211_free_node(struct ieee80211_nod
{
struct ieee80211vap *vap = ni->ni_vap;
atomic_dec(&ni->ni_ic->ic_node_counter);
node_print_message(IEEE80211_MSG_NODE|IEEE80211_MSG_NODE_REF,
1 /* show counter */,
-@@ -1781,22 +1880,6 @@ restart:
+@@ -1781,22 +1883,6 @@ restart:
jiffies > ni->ni_rxfragstamp + HZ) {
ieee80211_dev_kfree_skb(&ni->ni_rxfrag);
}
ni->ni_inact--;
if (ni->ni_associd != 0 || isadhoc) {
struct ieee80211vap *vap = ni->ni_vap;
-@@ -2263,6 +2346,36 @@ ieee80211_node_leave_11g(struct ieee8021
+@@ -2263,6 +2349,35 @@ ieee80211_node_leave_11g(struct ieee8021
}
}
+ 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 addif is waiting for the timer to fire, cancel! */
-+ if (vap == ni->ni_vap) {
-+ ni->ni_subif = NULL;
-+ goto done;
-+ }
-+
+ if (!vap)
+ goto done;
+
/*
* Handle bookkeeping for a station/neighbor leaving
* the bss when operating in ap or adhoc modes.
-@@ -2279,6 +2392,12 @@ ieee80211_node_leave(struct ieee80211_no
+@@ -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));
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -5483,6 +5483,9 @@ ath_beacon_config(struct ath_softc *sc,
+@@ -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
static int ath_desc_alloc(struct ath_softc *);
static void ath_desc_free(struct ath_softc *);
static void ath_desc_swap(struct ath_desc *);
-@@ -2792,6 +2793,72 @@ ath_set_ack_bitrate(struct ath_softc *sc
+@@ -2793,6 +2794,72 @@ ath_set_ack_bitrate(struct ath_softc *sc
return 1;
}
/*
* Reset the hardware w/o losing operational state. This is
* basically a more efficient way of doing ath_stop, ath_init,
-@@ -5291,6 +5358,7 @@ ath_beacon_config(struct ath_softc *sc,
+@@ -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;
int reset_tsf = 0;
if (vap == NULL)
-@@ -5298,6 +5366,9 @@ ath_beacon_config(struct ath_softc *sc,
+@@ -5301,6 +5369,9 @@ ath_beacon_config(struct ath_softc *sc,
ni = vap->iv_bss;
hw_tsf = ath_hal_gettsf64(ah);
tsf = le64_to_cpu(ni->ni_tstamp.tsf);
hw_tsftu = hw_tsf >> 10;
-@@ -5487,15 +5558,27 @@ ath_beacon_config(struct ath_softc *sc,
+@@ -5490,15 +5561,27 @@ ath_beacon_config(struct ath_softc *sc,
<= ath_hal_sw_beacon_response_time)
nexttbtt += intval;
sc->sc_nexttbtt = nexttbtt;
/* We print all debug messages here, in order to preserve the
* time critical aspect of this function */
DPRINTF(sc, ATH_DEBUG_BEACON,
-@@ -6398,6 +6481,11 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+@@ -6401,6 +6484,11 @@ ath_recv_mgmt(struct ieee80211vap * vap,
DPRINTF(sc, ATH_DEBUG_BEACON,
"Updated beacon timers\n");
}
break;
case IEEE80211_M_AHDEMO:
case IEEE80211_M_MONITOR:
-@@ -1454,7 +1457,7 @@ ath_vap_create(struct ieee80211com *ic,
+@@ -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)
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -1451,6 +1451,23 @@ ath_vap_create(struct ieee80211com *ic,
+@@ -1452,6 +1452,23 @@ ath_vap_create(struct ieee80211com *ic,
sc->sc_nstavaps++;
else if (opmode == IEEE80211_M_MONITOR)
sc->sc_nmonvaps++;
/*
* Adhoc demo mode is a pseudo mode; to the HAL it's
* just IBSS mode and the driver doesn't use management
-@@ -4278,7 +4295,8 @@ ath_calcrxfilter(struct ath_softc *sc)
+@@ -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_nostabeacons) || sc->sc_scanning ||
(ic->ic_opmode == IEEE80211_M_HOSTAP))
rfilt |= HAL_RX_FILTER_BEACON;
-@@ -6432,6 +6450,33 @@ ath_capture(struct net_device *dev, cons
+@@ -6435,6 +6453,33 @@ ath_capture(struct net_device *dev, cons
}
/*
* 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.
-@@ -6484,10 +6529,19 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+@@ -6487,10 +6532,19 @@ ath_recv_mgmt(struct ieee80211vap * vap,
DPRINTF(sc, ATH_DEBUG_BEACON,
"Updated beacon timers\n");
}
}
/* NB: Fall Through */
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
-@@ -6560,6 +6614,10 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+@@ -6563,6 +6617,10 @@ ath_recv_mgmt(struct ieee80211vap * vap,
#endif
if (do_merge)
ieee80211_ibss_merge(ni);
/* Allocate space for dynamically determined maximum VAP count */
sc->sc_bslot =
kmalloc(ath_maxvaps * sizeof(struct ieee80211vap*), GFP_KERNEL);
-@@ -1507,6 +1519,28 @@ ath_vap_create(struct ieee80211com *ic,
+@@ -1508,6 +1520,28 @@ ath_vap_create(struct ieee80211com *ic,
return vap;
}
static void
ath_vap_delete(struct ieee80211vap *vap)
{
-@@ -10818,6 +10852,12 @@ ath_ioctl(struct net_device *dev, struct
+@@ -10821,6 +10855,12 @@ ath_ioctl(struct net_device *dev, struct
* is to add module parameters.
*/
/*
* Dynamic (i.e. per-device) sysctls. These are automatically
* mirrored in /proc/sys.
-@@ -10897,6 +10937,38 @@ ath_sysctl_get_intmit(struct ath_softc *
+@@ -10900,6 +10940,38 @@ ath_sysctl_get_intmit(struct ath_softc *
}
static int
ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos)
{
struct ath_softc *sc = ctl->extra1;
-@@ -11176,6 +11248,24 @@ static int maxint = 0x7fffffff; /* 32-b
+@@ -11179,6 +11251,24 @@ static int maxint = 0x7fffffff; /* 32-b
static const ctl_table ath_sysctl_template[] = {
{ .ctl_name = CTL_AUTO,
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -6665,6 +6665,7 @@ ath_setdefantenna(struct ath_softc *sc,
+@@ -6668,6 +6668,7 @@ ath_setdefantenna(struct ath_softc *sc,
struct ath_hal *ah = sc->sc_ah;
/* XXX block beacon interrupts */
/* calibrate every 30 secs in steady state but check every second at first. */
static int ath_calinterval = ATH_SHORT_CALINTERVAL;
-@@ -2579,6 +2580,7 @@ ath_init(struct net_device *dev)
+@@ -2580,6 +2581,7 @@ ath_init(struct net_device *dev)
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
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)) {
-@@ -2913,6 +2915,40 @@ ath_hw_check_atim(struct ath_softc *sc,
+@@ -2914,6 +2916,40 @@ ath_hw_check_atim(struct ath_softc *sc,
}
/*
* Reset the hardware w/o losing operational state. This is
* basically a more efficient way of doing ath_stop, ath_init,
-@@ -2939,6 +2975,7 @@ ath_reset(struct net_device *dev)
+@@ -2940,6 +2976,7 @@ ath_reset(struct net_device *dev)
* Convert to a HAL channel description with the flags
* constrained to reflect the current operating mode.
*/
c = ic->ic_curchan;
sc->sc_curchan.channel = c->ic_freq;
sc->sc_curchan.channelFlags = ath_chan2flags(c);
-@@ -9019,6 +9056,7 @@ ath_chan_set(struct ath_softc *sc, struc
+@@ -9022,6 +9059,7 @@ ath_chan_set(struct ath_softc *sc, struc
u_int8_t channel_change_required = 0;
struct timeval tv;
--- /dev/null
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1374,7 +1374,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;
+@@ -1762,6 +1762,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)) { \
+@@ -1776,6 +1778,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)) { \
--- /dev/null
+--- 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 *);
--- /dev/null
+--- 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,10 @@ 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);
+ }
++
++out:
+ if (skb != NULL) /* no vaps, reclaim skb */
+ ieee80211_dev_kfree_skb(&skb);
+ return type;
+@@ -1146,11 +1155,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) {
+
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6580,9 +6580,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");
+@@ -6737,7 +6736,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;
+@@ -6892,12 +6893,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);
+@@ -6959,11 +6963,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);
+@@ -6972,24 +6989,35 @@ 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:
++ 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 */
--- a/net80211/ieee80211_input.c
+++ b/net80211/ieee80211_input.c
-@@ -3604,6 +3604,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
+@@ -3611,6 +3611,8 @@ ieee80211_recv_mgmt(struct ieee80211vap
vap->iv_stats.is_rx_mgtdiscard++;
return;
}
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -13508,7 +13508,7 @@ cleanup_ath_buf(struct ath_softc *sc, st
+@@ -13511,7 +13511,7 @@ cleanup_ath_buf(struct ath_softc *sc, st
bus_unmap_single(
sc->sc_bdev,
bf->bf_skbaddrff[i],
--- /dev/null
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6725,10 +6725,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;
+@@ -6771,13 +6771,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) {
+@@ -7061,8 +7063,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
+@@ -1203,6 +1203,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 */
+++ /dev/null
---- a/ath_hal/ah_os.h
-+++ b/ath_hal/ah_os.h
-@@ -194,10 +194,6 @@ extern u_int32_t __ahdecl ath_hal_getupt
- */
- #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) ? \
-@@ -219,6 +215,21 @@ extern u_int32_t __ahdecl ath_hal_getupt
- readl((_ah)->ah_sh + (_reg)) : \
- cpu_to_le32(readl((_ah)->ah_sh + (_reg))))
- #endif /* KERNEL_VERSION(2,6,12) */
-+#else /* AH_BYTE_ORDER != AH_BIG_ENDIAN */
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
-+#define _OS_REG_WRITE(_ah, _reg, _val) do { \
-+ iowrite32((_val), (_ah)->ah_sh + (_reg)); \
-+ } while (0)
-+#define _OS_REG_READ(_ah, _reg) \
-+ ioread32((_ah)->ah_sh + (_reg))
-+#else
-+#define _OS_REG_WRITE(_ah, _reg, _val) do { \
-+ writel((_val), (_ah)->ah_sh + (_reg)); \
-+ } while (0)
-+#define _OS_REG_READ(_ah, _reg) \
-+ readl((_ah)->ah_sh + (_reg))
-+#endif /* KERNEL_VERSION(2,6,12) */
-+#endif /* AH_BYTE_ORDER != AH_BIG_ENDIAN */
-
- /*
- * The functions in this section are not intended to be invoked by MadWifi
--- /dev/null
+--- 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 */
++#ifdef CONFIG_ATHEROS_AR71XX
++ 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
+@@ -7555,7 +7563,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
#define IEEE80211_QOS_TXOP 0x00ff
--- a/net80211/ieee80211_input.c
+++ b/net80211/ieee80211_input.c
-@@ -429,7 +429,7 @@ ieee80211_input(struct ieee80211vap * va
+@@ -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) &&
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -6510,7 +6510,7 @@ ath_capture(struct net_device *dev, cons
+@@ -6521,7 +6521,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. */
if (tskb == NULL) {
DPRINTF(sc, ATH_DEBUG_ANY,
"Dropping; ath_skb_removepad failed!\n");
-@@ -6518,6 +6518,8 @@ ath_capture(struct net_device *dev, cons
+@@ -6529,6 +6529,8 @@ ath_capture(struct net_device *dev, cons
}
ieee80211_input_monitor(ic, tskb, bf, tx, tsf, sc);
Please let us know if you think your name should be mentioned here!
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
-@@ -3129,7 +3129,7 @@ ath_tx_startraw(struct net_device *dev,
+@@ -3138,7 +3138,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 *)
--- /dev/null
+--- 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";
+ static const struct ath_hw_detect cards[] = {
+ {
+@@ -266,6 +238,114 @@ static const struct ath_hw_detect cards[
+ },
+ };
+
++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)
+ {
+@@ -318,7 +398,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;
+ }
+@@ -328,21 +408,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;
+
+@@ -357,6 +428,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;
+@@ -377,11 +471,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)
+ {
--- /dev/null
+--- 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 min(bias, (u32) 100);
++}
++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)
+ {
+@@ -1155,7 +1272,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__,
+@@ -1166,6 +1283,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
+@@ -2772,6 +2772,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",
+@@ -2794,6 +2795,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
+@@ -1225,6 +1225,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 */
+@@ -1243,6 +1244,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
+@@ -213,9 +213,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 */
+ as->as_vap_desired_mode = vap->iv_des_mode;
+ as->as_required_mode = 0;
+@@ -429,8 +435,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));
+ }
+
+
+@@ -616,6 +624,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);
+@@ -624,8 +633,11 @@ ap_end(struct ieee80211_scan_state *ss,
+ ("wrong opmode %u", vap->iv_opmode));
+
+ ic = vap->iv_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! "
+@@ -644,6 +656,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;
+ }
+@@ -656,6 +669,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. */
--- /dev/null
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -3675,6 +3675,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)
--- /dev/null
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -2307,6 +2307,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
+@@ -2319,10 +2330,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;
--- /dev/null
+--- 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,9 @@ 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_cur_txpower; /* current tx power */
+ 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
+@@ -920,17 +920,23 @@ 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 = ic->ic_max_txpower;
++ if (ic->ic_bsschan && (ic->ic_bsschan != IEEE80211_CHAN_ANYC))
++ power = min(power, (u_int16_t) ic->ic_bsschan->ic_maxpower);
++
+ /* 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 +1385,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 +1400,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 +1574,22 @@ 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 = ic->ic_max_txpower;
++
++ if (ic->ic_bsschan && (ic->ic_bsschan != IEEE80211_CHAN_ANYC)) {
++ txp = min(txp, (u16) ic->ic_bsschan->ic_maxpower);
++ } else if (ic->ic_cur_txpower > 0) {
++ txp = min(txp, ic->ic_cur_txpower);
++ }
++ 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);
+@@ -3159,7 +3158,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;
+
+@@ -8381,7 +8380,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 */
+@@ -10364,59 +10363,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_cur_txpower = ath_get_clamped_maxtxpower(sc);
+ }
+
+ #ifdef ATH_SUPERG_XR
--- /dev/null
+--- 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)
+@@ -4625,7 +4597,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;
--- /dev/null
+--- 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;
+@@ -4923,6 +4923,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;
+@@ -4990,7 +4991,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
+@@ -2862,6 +2862,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);
+@@ -3227,6 +3230,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;
+ }
+@@ -5801,6 +5807,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
+ /*
--- /dev/null
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -1129,7 +1129,11 @@ ieee80211_scan_add_channels(struct ieee8
+ continue;
+ if (c->ic_scanflags & IEEE80211_NOSCAN_SET)
+ continue;
+- if (modeflags &&
++ if (ss->ss_vap->iv_opmode == IEEE80211_M_HOSTAP) {
++ if ((c->ic_flags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) !=
++ (modeflags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)))
++ continue;
++ } else if (modeflags &&
+ ((c->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
+ (modeflags & IEEE80211_CHAN_ALLTURBO)))
+ continue;
--- /dev/null
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -13477,7 +13477,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,
+@@ -13485,8 +13485,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
--- /dev/null
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -5344,27 +5344,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.
+++ /dev/null
---- a/ath_hal/ah_os.h
-+++ b/ath_hal/ah_os.h
-@@ -156,80 +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))
--#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)
--#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) */
--#else /* AH_BYTE_ORDER != AH_BIG_ENDIAN */
--#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
--#define _OS_REG_WRITE(_ah, _reg, _val) do { \
-- iowrite32((_val), (_ah)->ah_sh + (_reg)); \
-- } while (0)
--#define _OS_REG_READ(_ah, _reg) \
-- ioread32((_ah)->ah_sh + (_reg))
--#else
- #define _OS_REG_WRITE(_ah, _reg, _val) do { \
-- writel((_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) \
-- readl((_ah)->ah_sh + (_reg))
--#endif /* KERNEL_VERSION(2,6,12) */
--#endif /* AH_BYTE_ORDER != AH_BIG_ENDIAN */
-+ 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.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 */
-+#ifdef CONFIG_ATHEROS_AR71XX
-+ 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
-@@ -7524,7 +7532,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.
- */