summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath9k/beacon.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-07-24 10:01:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-24 10:01:50 -0700
commit3c4cfadef6a1665d9cd02a543782d03d3e6740c6 (patch)
tree3df72faaacd494d5ac8c9668df4f529b1b5e4457 /drivers/net/wireless/ath/ath9k/beacon.c
parente017507f37d5cb8b541df165a824958bc333bec3 (diff)
parent320f5ea0cedc08ef65d67e056bcb9d181386ef2c (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking changes from David S Miller: 1) Remove the ipv4 routing cache. Now lookups go directly into the FIB trie and use prebuilt routes cached there. No more garbage collection, no more rDOS attacks on the routing cache. Instead we now get predictable and consistent performance, no matter what the pattern of traffic we service. This has been almost 2 years in the making. Special thanks to Julian Anastasov, Eric Dumazet, Steffen Klassert, and others who have helped along the way. I'm sure that with a change of this magnitude there will be some kind of fallout, but such things ought the be simple to fix at this point. Luckily I'm not European so I'll be around all of August to fix things :-) The major stages of this work here are each fronted by a forced merge commit whose commit message contains a top-level description of the motivations and implementation issues. 2) Pre-demux of established ipv4 TCP sockets, saves a route demux on input. 3) TCP SYN/ACK performance tweaks from Eric Dumazet. 4) Add namespace support for netfilter L4 conntrack helpers, from Gao Feng. 5) Add config mechanism for Energy Efficient Ethernet to ethtool, from Yuval Mintz. 6) Remove quadratic behavior from /proc/net/unix, from Eric Dumazet. 7) Support for connection tracker helpers in userspace, from Pablo Neira Ayuso. 8) Allow userspace driven TX load balancing functions in TEAM driver, from Jiri Pirko. 9) Kill off NLMSG_PUT and RTA_PUT macros, more gross stuff with embedded gotos. 10) TCP Small Queues, essentially minimize the amount of TCP data queued up in the packet scheduler layer. Whereas the existing BQL (Byte Queue Limits) limits the pkt_sched --> netdevice queuing levels, this controls the TCP --> pkt_sched queueing levels. From Eric Dumazet. 11) Reduce the number of get_page/put_page ops done on SKB fragments, from Alexander Duyck. 12) Implement protection against blind resets in TCP (RFC 5961), from Eric Dumazet. 13) Support the client side of TCP Fast Open, basically the ability to send data in the SYN exchange, from Yuchung Cheng. Basically, the sender queues up data with a sendmsg() call using MSG_FASTOPEN, then they do the connect() which emits the queued up fastopen data. 14) Avoid all the problems we get into in TCP when timers or PMTU events hit a locked socket. The TCP Small Queues changes added a tcp_release_cb() that allows us to queue work up to the release_sock() caller, and that's what we use here too. From Eric Dumazet. 15) Zero copy on TX support for TUN driver, from Michael S. Tsirkin. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1870 commits) genetlink: define lockdep_genl_is_held() when CONFIG_LOCKDEP r8169: revert "add byte queue limit support". ipv4: Change rt->rt_iif encoding. net: Make skb->skb_iif always track skb->dev ipv4: Prepare for change of rt->rt_iif encoding. ipv4: Remove all RTCF_DIRECTSRC handliing. ipv4: Really ignore ICMP address requests/replies. decnet: Don't set RTCF_DIRECTSRC. net/ipv4/ip_vti.c: Fix __rcu warnings detected by sparse. ipv4: Remove redundant assignment rds: set correct msg_namelen openvswitch: potential NULL deref in sample() tcp: dont drop MTU reduction indications bnx2x: Add new 57840 device IDs tcp: avoid oops in tcp_metrics and reset tcpm_stamp niu: Change niu_rbr_fill() to use unlikely() to check niu_rbr_add_page() return value niu: Fix to check for dma mapping errors. net: Fix references to out-of-scope variables in put_cmsg_compat() net: ethernet: davinci_emac: add pm_runtime support net: ethernet: davinci_emac: Remove unnecessary #include ...
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/beacon.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c528
1 files changed, 205 insertions, 323 deletions
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 11bc55e3d697..76f07d8c272d 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -30,7 +30,7 @@ static void ath9k_reset_beacon_status(struct ath_softc *sc)
* the operating mode of the station (AP or AdHoc). Parameters are AIFS
* settings and channel width min/max
*/
-int ath_beaconq_config(struct ath_softc *sc)
+static void ath9k_beaconq_config(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -38,6 +38,7 @@ int ath_beaconq_config(struct ath_softc *sc)
struct ath_txq *txq;
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
+
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
/* Always burst out beacon and CAB traffic. */
qi.tqi_aifs = 1;
@@ -48,17 +49,17 @@ int ath_beaconq_config(struct ath_softc *sc)
txq = sc->tx.txq_map[WME_AC_BE];
ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
qi.tqi_aifs = qi_be.tqi_aifs;
- qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
+ if (ah->slottime == ATH9K_SLOT_TIME_20)
+ qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
+ else
+ qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
qi.tqi_cwmax = qi_be.tqi_cwmax;
}
if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
- ath_err(common,
- "Unable to update h/w beacon queue parameters\n");
- return 0;
+ ath_err(common, "Unable to update h/w beacon queue parameters\n");
} else {
ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
- return 1;
}
}
@@ -67,7 +68,7 @@ int ath_beaconq_config(struct ath_softc *sc)
* up rate codes, and channel flags. Beacons are always sent out at the
* lowest rate, and are not retried.
*/
-static void ath_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
+static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
struct ath_buf *bf, int rateidx)
{
struct sk_buff *skb = bf->bf_mpdu;
@@ -78,8 +79,6 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
u8 chainmask = ah->txchainmask;
u8 rate = 0;
- ath9k_reset_beacon_status(sc);
-
sband = &sc->sbands[common->hw->conf.channel->band];
rate = sband->bitrates[rateidx].hw_value;
if (vif->bss_conf.use_short_preamble)
@@ -108,7 +107,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
}
-static void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
+static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -125,28 +124,22 @@ static void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
}
}
-static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_buf *bf;
- struct ath_vif *avp;
+ struct ath_vif *avp = (void *)vif->drv_priv;
struct sk_buff *skb;
- struct ath_txq *cabq;
+ struct ath_txq *cabq = sc->beacon.cabq;
struct ieee80211_tx_info *info;
+ struct ieee80211_mgmt *mgmt_hdr;
int cabq_depth;
- ath9k_reset_beacon_status(sc);
-
- avp = (void *)vif->drv_priv;
- cabq = sc->beacon.cabq;
-
- if ((avp->av_bcbuf == NULL) || !avp->is_bslot_active)
+ if (avp->av_bcbuf == NULL)
return NULL;
- /* Release the old beacon first */
-
bf = avp->av_bcbuf;
skb = bf->bf_mpdu;
if (skb) {
@@ -156,14 +149,14 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
bf->bf_buf_addr = 0;
}
- /* Get a new beacon from mac80211 */
-
skb = ieee80211_beacon_get(hw, vif);
- bf->bf_mpdu = skb;
if (skb == NULL)
return NULL;
- ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
- avp->tsf_adjust;
+
+ bf->bf_mpdu = skb;
+
+ mgmt_hdr = (struct ieee80211_mgmt *)skb->data;
+ mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust;
info = IEEE80211_SKB_CB(skb);
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
@@ -209,61 +202,52 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
}
}
- ath_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
+ ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
while (skb) {
- ath_tx_cabq(hw, skb);
+ ath9k_tx_cabq(hw, skb);
skb = ieee80211_get_buffered_bc(hw, vif);
}
return bf;
}
-int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif)
+void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath_vif *avp;
- struct ath_buf *bf;
- struct sk_buff *skb;
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
- __le64 tstamp;
-
- avp = (void *)vif->drv_priv;
-
- /* Allocate a beacon descriptor if we haven't done so. */
- if (!avp->av_bcbuf) {
- /* Allocate beacon state for hostap/ibss. We know
- * a buffer is available. */
- avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf,
- struct ath_buf, list);
- list_del(&avp->av_bcbuf->list);
-
- if (ath9k_uses_beacons(vif->type)) {
- int slot;
- /*
- * Assign the vif to a beacon xmit slot. As
- * above, this cannot fail to find one.
- */
- avp->av_bslot = 0;
- for (slot = 0; slot < ATH_BCBUF; slot++)
- if (sc->beacon.bslot[slot] == NULL) {
- avp->av_bslot = slot;
- avp->is_bslot_active = false;
-
- /* NB: keep looking for a double slot */
- if (slot == 0 || !sc->beacon.bslot[slot-1])
- break;
- }
- BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL);
- sc->beacon.bslot[avp->av_bslot] = vif;
- sc->nbcnvifs++;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ int slot;
+
+ avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list);
+ list_del(&avp->av_bcbuf->list);
+
+ for (slot = 0; slot < ATH_BCBUF; slot++) {
+ if (sc->beacon.bslot[slot] == NULL) {
+ avp->av_bslot = slot;
+ break;
}
}
- /* release the previous beacon frame, if it already exists. */
- bf = avp->av_bcbuf;
- if (bf->bf_mpdu != NULL) {
- skb = bf->bf_mpdu;
+ sc->beacon.bslot[avp->av_bslot] = vif;
+ sc->nbcnvifs++;
+
+ ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
+ avp->av_bslot);
+}
+
+void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ struct ath_buf *bf = avp->av_bcbuf;
+
+ ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
+ avp->av_bslot);
+
+ tasklet_disable(&sc->bcon_tasklet);
+
+ if (bf && bf->bf_mpdu) {
+ struct sk_buff *skb = bf->bf_mpdu;
dma_unmap_single(sc->dev, bf->bf_buf_addr,
skb->len, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
@@ -271,99 +255,74 @@ int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_vif *vif)
bf->bf_buf_addr = 0;
}
- /* NB: the beacon data buffer must be 32-bit aligned. */
- skb = ieee80211_beacon_get(sc->hw, vif);
- if (skb == NULL)
- return -ENOMEM;
+ avp->av_bcbuf = NULL;
+ sc->beacon.bslot[avp->av_bslot] = NULL;
+ sc->nbcnvifs--;
+ list_add_tail(&bf->list, &sc->beacon.bbuf);
- tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
- sc->beacon.bc_tstamp = (u32) le64_to_cpu(tstamp);
- /* Calculate a TSF adjustment factor required for staggered beacons. */
- if (avp->av_bslot > 0) {
- u64 tsfadjust;
- int intval;
-
- intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
+ tasklet_enable(&sc->bcon_tasklet);
+}
- /*
- * Calculate the TSF offset for this beacon slot, i.e., the
- * number of usecs that need to be added to the timestamp field
- * in Beacon and Probe Response frames. Beacon slot 0 is
- * processed at the correct offset, so it does not require TSF
- * adjustment. Other slots are adjusted to get the timestamp
- * close to the TBTT for the BSS.
- */
- tsfadjust = TU_TO_USEC(intval * avp->av_bslot) / ATH_BCBUF;
- avp->tsf_adjust = cpu_to_le64(tsfadjust);
+static int ath9k_beacon_choose_slot(struct ath_softc *sc)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ u16 intval;
+ u32 tsftu;
+ u64 tsf;
+ int slot;
- ath_dbg(common, BEACON,
- "stagger beacons, bslot %d intval %u tsfadjust %llu\n",
- avp->av_bslot, intval, (unsigned long long)tsfadjust);
+ if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) {
+ ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
+ ath9k_hw_gettsf64(sc->sc_ah));
+ return 0;
+ }
- ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
- avp->tsf_adjust;
- } else
- avp->tsf_adjust = cpu_to_le64(0);
+ intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
+ tsf = ath9k_hw_gettsf64(sc->sc_ah);
+ tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time);
+ tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF);
+ slot = (tsftu % (intval * ATH_BCBUF)) / intval;
- bf->bf_mpdu = skb;
- bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
- skb->len, DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
- dev_kfree_skb_any(skb);
- bf->bf_mpdu = NULL;
- bf->bf_buf_addr = 0;
- ath_err(common, "dma_mapping_error on beacon alloc\n");
- return -ENOMEM;
- }
- avp->is_bslot_active = true;
+ ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n",
+ slot, tsf, tsftu / ATH_BCBUF);
- return 0;
+ return slot;
}
-void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp)
+void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
{
- if (avp->av_bcbuf != NULL) {
- struct ath_buf *bf;
-
- avp->is_bslot_active = false;
- if (avp->av_bslot != -1) {
- sc->beacon.bslot[avp->av_bslot] = NULL;
- sc->nbcnvifs--;
- avp->av_bslot = -1;
- }
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ u64 tsfadjust;
- bf = avp->av_bcbuf;
- if (bf->bf_mpdu != NULL) {
- struct sk_buff *skb = bf->bf_mpdu;
- dma_unmap_single(sc->dev, bf->bf_buf_addr,
- skb->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(skb);
- bf->bf_mpdu = NULL;
- bf->bf_buf_addr = 0;
- }
- list_add_tail(&bf->list, &sc->beacon.bbuf);
+ if (avp->av_bslot == 0)
+ return;
- avp->av_bcbuf = NULL;
- }
+ tsfadjust = cur_conf->beacon_interval * avp->av_bslot / ATH_BCBUF;
+ avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
+
+ ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n",
+ (unsigned long long)tsfadjust, avp->av_bslot);
}
-void ath_beacon_tasklet(unsigned long data)
+void ath9k_beacon_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf = NULL;
struct ieee80211_vif *vif;
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
int slot;
- u32 bfaddr, bc = 0;
- if (work_pending(&sc->hw_reset_work)) {
+ if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
ath_dbg(common, RESET,
"reset work is pending, skip beaconing now\n");
return;
}
+
/*
* Check if the previous beacon has gone out. If
* not don't try to post another, skip this period
@@ -387,55 +346,25 @@ void ath_beacon_tasklet(unsigned long data)
} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
ath_dbg(common, BSTUCK, "beacon is officially stuck\n");
sc->beacon.bmisscnt = 0;
- sc->sc_flags |= SC_OP_TSF_RESET;
- ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK);
}
return;
}
- /*
- * Generate beacon frames. we are sending frames
- * staggered so calculate the slot for this frame based
- * on the tsf to safeguard against missing an swba.
- */
-
-
- if (ah->opmode == NL80211_IFTYPE_AP) {
- u16 intval;
- u32 tsftu;
- u64 tsf;
-
- intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
- tsf = ath9k_hw_gettsf64(ah);
- tsf += TU_TO_USEC(ah->config.sw_beacon_response_time);
- tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF);
- slot = (tsftu % (intval * ATH_BCBUF)) / intval;
- vif = sc->beacon.bslot[slot];
-
- ath_dbg(common, BEACON,
- "slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
- slot, tsf, tsftu / ATH_BCBUF, intval, vif);
- } else {
- slot = 0;
- vif = sc->beacon.bslot[slot];
- }
+ slot = ath9k_beacon_choose_slot(sc);
+ vif = sc->beacon.bslot[slot];
+ if (!vif || !vif->bss_conf.enable_beacon)
+ return;
- bfaddr = 0;
- if (vif) {
- bf = ath_beacon_generate(sc->hw, vif);
- if (bf != NULL) {
- bfaddr = bf->bf_daddr;
- bc = 1;
- }
+ bf = ath9k_beacon_generate(sc->hw, vif);
+ WARN_ON(!bf);
- if (sc->beacon.bmisscnt != 0) {
- ath_dbg(common, BSTUCK,
- "resume beacon xmit after %u misses\n",
- sc->beacon.bmisscnt);
- sc->beacon.bmisscnt = 0;
- }
+ if (sc->beacon.bmisscnt != 0) {
+ ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n",
+ sc->beacon.bmisscnt);
+ sc->beacon.bmisscnt = 0;
}
/*
@@ -455,39 +384,40 @@ void ath_beacon_tasklet(unsigned long data)
* set to ATH_BCBUF so this check is a noop.
*/
if (sc->beacon.updateslot == UPDATE) {
- sc->beacon.updateslot = COMMIT; /* commit next beacon */
+ sc->beacon.updateslot = COMMIT;
sc->beacon.slotupdate = slot;
- } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) {
+ } else if (sc->beacon.updateslot == COMMIT &&
+ sc->beacon.slotupdate == slot) {
ah->slottime = sc->beacon.slottime;
ath9k_hw_init_global_settings(ah);
sc->beacon.updateslot = OK;
}
- if (bfaddr != 0) {
+
+ if (bf) {
+ ath9k_reset_beacon_status(sc);
+
+ ath_dbg(common, BEACON,
+ "Transmitting beacon for slot: %d\n", slot);
+
/* NB: cabq traffic should already be queued and primed */
- ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr);
+ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
if (!edma)
ath9k_hw_txstart(ah, sc->beacon.beaconq);
-
- sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */
}
}
-static void ath9k_beacon_init(struct ath_softc *sc,
- u32 next_beacon,
- u32 beacon_period)
+static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, u32 intval)
{
- if (sc->sc_flags & SC_OP_TSF_RESET) {
- ath9k_ps_wakeup(sc);
- ath9k_hw_reset_tsf(sc->sc_ah);
- }
-
- ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period);
+ struct ath_hw *ah = sc->sc_ah;
- if (sc->sc_flags & SC_OP_TSF_RESET) {
- ath9k_ps_restore(sc);
- sc->sc_flags &= ~SC_OP_TSF_RESET;
- }
+ ath9k_hw_disable_interrupts(ah);
+ ath9k_hw_reset_tsf(ah);
+ ath9k_beaconq_config(sc);
+ ath9k_hw_beaconinit(ah, nexttbtt, intval);
+ sc->beacon.bmisscnt = 0;
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
}
/*
@@ -495,32 +425,27 @@ static void ath9k_beacon_init(struct ath_softc *sc,
* burst together. For the former arrange for the SWBA to be delivered for each
* slot. Slots that are not occupied will generate nothing.
*/
-static void ath_beacon_config_ap(struct ath_softc *sc,
- struct ath_beacon_config *conf)
+static void ath9k_beacon_config_ap(struct ath_softc *sc,
+ struct ath_beacon_config *conf)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
u32 nexttbtt, intval;
/* NB: the beacon interval is kept internally in TU's */
intval = TU_TO_USEC(conf->beacon_interval);
- intval /= ATH_BCBUF; /* for staggered beacons */
+ intval /= ATH_BCBUF;
nexttbtt = intval;
- /*
- * In AP mode we enable the beacon timers and SWBA interrupts to
- * prepare beacon frames.
- */
- ah->imask |= ATH9K_INT_SWBA;
- ath_beaconq_config(sc);
+ if (conf->enable_beacon)
+ ah->imask |= ATH9K_INT_SWBA;
+ else
+ ah->imask &= ~ATH9K_INT_SWBA;
- /* Set the computed AP beacon timers */
+ ath_dbg(common, BEACON, "AP nexttbtt: %u intval: %u conf_intval: %u\n",
+ nexttbtt, intval, conf->beacon_interval);
- ath9k_hw_disable_interrupts(ah);
- sc->sc_flags |= SC_OP_TSF_RESET;
ath9k_beacon_init(sc, nexttbtt, intval);
- sc->beacon.bmisscnt = 0;
- ath9k_hw_set_interrupts(ah);
- ath9k_hw_enable_interrupts(ah);
}
/*
@@ -531,8 +456,8 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
* we'll receive a BMISS interrupt when we stop seeing beacons from the AP
* we've associated with.
*/
-static void ath_beacon_config_sta(struct ath_softc *sc,
- struct ath_beacon_config *conf)
+static void ath9k_beacon_config_sta(struct ath_softc *sc,
+ struct ath_beacon_config *conf)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -544,7 +469,7 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
int num_beacons, offset, dtim_dec_count, cfp_dec_count;
/* No need to configure beacon if we are not associated */
- if (!common->curaid) {
+ if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
ath_dbg(common, BEACON,
"STA is not yet associated..skipping beacon config\n");
return;
@@ -651,97 +576,65 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
ath9k_hw_enable_interrupts(ah);
}
-static void ath_beacon_config_adhoc(struct ath_softc *sc,
- struct ath_beacon_config *conf)
+static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
+ struct ath_beacon_config *conf)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- u32 tsf, intval, nexttbtt;
+ u32 intval, nexttbtt;
ath9k_reset_beacon_status(sc);
- if (!(sc->sc_flags & SC_OP_BEACONS))
- ath9k_hw_settsf64(ah, sc->beacon.bc_tstamp);
intval = TU_TO_USEC(conf->beacon_interval);
- tsf = roundup(ath9k_hw_gettsf32(ah) + TU_TO_USEC(FUDGE), intval);
- nexttbtt = tsf + intval;
-
- ath_dbg(common, BEACON, "IBSS nexttbtt %u intval %u (%u)\n",
- nexttbtt, intval, conf->beacon_interval);
-
- /*
- * In IBSS mode enable the beacon timers but only enable SWBA interrupts
- * if we need to manually prepare beacon frames. Otherwise we use a
- * self-linked tx descriptor and let the hardware deal with things.
- */
- ah->imask |= ATH9K_INT_SWBA;
+ nexttbtt = intval;
- ath_beaconq_config(sc);
+ if (conf->enable_beacon)
+ ah->imask |= ATH9K_INT_SWBA;
+ else
+ ah->imask &= ~ATH9K_INT_SWBA;
- /* Set the computed ADHOC beacon timers */
+ ath_dbg(common, BEACON, "IBSS nexttbtt: %u intval: %u conf_intval: %u\n",
+ nexttbtt, intval, conf->beacon_interval);
- ath9k_hw_disable_interrupts(ah);
ath9k_beacon_init(sc, nexttbtt, intval);
- sc->beacon.bmisscnt = 0;
-
- ath9k_hw_set_interrupts(ah);
- ath9k_hw_enable_interrupts(ah);
}
-static bool ath9k_allow_beacon_config(struct ath_softc *sc,
- struct ieee80211_vif *vif)
+bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
{
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
struct ath_vif *avp = (void *)vif->drv_priv;
- /*
- * Can not have different beacon interval on multiple
- * AP interface case
- */
- if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) &&
- (sc->nbcnvifs > 1) &&
- (vif->type == NL80211_IFTYPE_AP) &&
- (cur_conf->beacon_interval != bss_conf->beacon_int)) {
- ath_dbg(common, CONFIG,
- "Changing beacon interval of multiple AP interfaces !\n");
- return false;
- }
- /*
- * Can not configure station vif's beacon config
- * while on AP opmode
- */
- if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) &&
- (vif->type != NL80211_IFTYPE_AP)) {
- ath_dbg(common, CONFIG,
- "STA vif's beacon not allowed on AP mode\n");
- return false;
+ if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
+ if ((vif->type != NL80211_IFTYPE_AP) ||
+ (sc->nbcnvifs > 1)) {
+ ath_dbg(common, CONFIG,
+ "An AP interface is already present !\n");
+ return false;
+ }
}
- /*
- * Do not allow beacon config if HW was already configured
- * with another STA vif
- */
- if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
- (vif->type == NL80211_IFTYPE_STATION) &&
- (sc->sc_flags & SC_OP_BEACONS) &&
- !avp->primary_sta_vif) {
- ath_dbg(common, CONFIG,
- "Beacon already configured for a station interface\n");
- return false;
+
+ if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
+ if ((vif->type == NL80211_IFTYPE_STATION) &&
+ test_bit(SC_OP_BEACONS, &sc->sc_flags) &&
+ !avp->primary_sta_vif) {
+ ath_dbg(common, CONFIG,
+ "Beacon already configured for a station interface\n");
+ return false;
+ }
}
+
return true;
}
-void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
+static void ath9k_cache_beacon_config(struct ath_softc *sc,
+ struct ieee80211_bss_conf *bss_conf)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
- struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
- if (!ath9k_allow_beacon_config(sc, vif))
- return;
+ ath_dbg(common, BEACON,
+ "Caching beacon data for BSS: %pM\n", bss_conf->bssid);
- /* Setup the beacon configuration parameters */
cur_conf->beacon_interval = bss_conf->beacon_int;
cur_conf->dtim_period = bss_conf->dtim_period;
cur_conf->listen_interval = 1;
@@ -766,73 +659,62 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
if (cur_conf->dtim_period == 0)
cur_conf->dtim_period = 1;
- ath_set_beacon(sc);
}
-static bool ath_has_valid_bslot(struct ath_softc *sc)
+void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
+ u32 changed)
{
- struct ath_vif *avp;
- int slot;
- bool found = false;
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
- for (slot = 0; slot < ATH_BCBUF; slot++) {
- if (sc->beacon.bslot[slot]) {
- avp = (void *)sc->beacon.bslot[slot]->drv_priv;
- if (avp->is_bslot_active) {
- found = true;
- break;
+ if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
+ ath9k_cache_beacon_config(sc, bss_conf);
+ ath9k_set_beacon(sc);
+ set_bit(SC_OP_BEACONS, &sc->sc_flags);
+ } else {
+ /*
+ * Take care of multiple interfaces when
+ * enabling/disabling SWBA.
+ */
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
+ if (!bss_conf->enable_beacon &&
+ (sc->nbcnvifs <= 1)) {
+ cur_conf->enable_beacon = false;
+ } else if (bss_conf->enable_beacon) {
+ cur_conf->enable_beacon = true;
+ ath9k_cache_beacon_config(sc, bss_conf);
}
}
+
+ if (cur_conf->beacon_interval) {
+ ath9k_set_beacon(sc);
+
+ if (cur_conf->enable_beacon)
+ set_bit(SC_OP_BEACONS, &sc->sc_flags);
+ else
+ clear_bit(SC_OP_BEACONS, &sc->sc_flags);
+ }
}
- return found;
}
-
-void ath_set_beacon(struct ath_softc *sc)
+void ath9k_set_beacon(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP:
- if (ath_has_valid_bslot(sc))
- ath_beacon_config_ap(sc, cur_conf);
+ ath9k_beacon_config_ap(sc, cur_conf);
break;
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
- ath_beacon_config_adhoc(sc, cur_conf);
+ ath9k_beacon_config_adhoc(sc, cur_conf);
break;
case NL80211_IFTYPE_STATION:
- ath_beacon_config_sta(sc, cur_conf);
+ ath9k_beacon_config_sta(sc, cur_conf);
break;
default:
ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
return;
}
-
- sc->sc_flags |= SC_OP_BEACONS;
-}
-
-void ath9k_set_beaconing_status(struct ath_softc *sc, bool status)
-{
- struct ath_hw *ah = sc->sc_ah;
-
- if (!ath_has_valid_bslot(sc)) {
- sc->sc_flags &= ~SC_OP_BEACONS;
- return;
- }
-
- ath9k_ps_wakeup(sc);
- if (status) {
- /* Re-enable beaconing */
- ah->imask |= ATH9K_INT_SWBA;
- ath9k_hw_set_interrupts(ah);
- } else {
- /* Disable SWBA interrupt */
- ah->imask &= ~ATH9K_INT_SWBA;
- ath9k_hw_set_interrupts(ah);
- tasklet_kill(&sc->bcon_tasklet);
- ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq);
- }
- ath9k_ps_restore(sc);
}