Skip to content

Commit cfda2d8

Browse files
Benjamin Bergkvalo
Benjamin Berg
authored andcommitted
ath9k: Fix beacon configuration for addition/removal of interfaces
This patch fixes some issues with interface reconfiguration. It could for example happen that an AP interface in beacon slot 0 was removed leaving an IBSS station in one of the other slots. When this happens the driver never sends out the beacon as it only tries to send a beacon from slot 0. Appart from that the tracking of required changes to the beacon config is relatively complicated and prone to errors. The approach taken here is to solve reconfiguration issues is to reconfigure the beacons when any interface changes. This means that the complexity of deciding whether an interface change may modify the beacon configuration is gone. It also means that the beacon config will be reliably updated when an interface is removed. The issue that a single non-AP interface might not be in beacon slot 0 and wouldn't be send out is solved by moving it into the first slot. The TSF value in hardware is adjusted accordingly so that the timestamp of the beacons stay consistent. Signed-off-by: Benjamin Berg <[email protected]> Signed-off-by: Kalle Valo <[email protected]>
1 parent 11b0ac2 commit cfda2d8

File tree

4 files changed

+165
-126
lines changed

4 files changed

+165
-126
lines changed

drivers/net/wireless/ath/ath9k/ath9k.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,8 @@ struct ath9k_vif_iter_data {
637637
int nwds; /* number of WDS vifs */
638638
int nadhocs; /* number of adhoc vifs */
639639
int nocbs; /* number of OCB vifs */
640+
int nbcnvifs; /* number of beaconing vifs */
641+
struct ieee80211_vif *primary_beacon_vif;
640642
struct ieee80211_vif *primary_sta;
641643
};
642644

@@ -685,10 +687,11 @@ struct ath_beacon {
685687
};
686688

687689
void ath9k_beacon_tasklet(unsigned long data);
688-
void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
689-
u32 changed);
690+
void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif,
691+
bool beacons);
690692
void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
691693
void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
694+
void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc);
692695
void ath9k_set_beacon(struct ath_softc *sc);
693696
bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif);
694697
void ath9k_csa_update(struct ath_softc *sc);

drivers/net/wireless/ath/ath9k/beacon.c

Lines changed: 133 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
209209
}
210210

211211
sc->beacon.bslot[avp->av_bslot] = vif;
212-
sc->nbcnvifs++;
213212

214213
ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
215214
avp->av_bslot);
@@ -220,15 +219,12 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
220219
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
221220
struct ath_vif *avp = (void *)vif->drv_priv;
222221
struct ath_buf *bf = avp->av_bcbuf;
223-
struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
224222

225223
ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
226224
avp->av_bslot);
227225

228226
tasklet_disable(&sc->bcon_tasklet);
229227

230-
cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
231-
232228
if (bf && bf->bf_mpdu) {
233229
struct sk_buff *skb = bf->bf_mpdu;
234230
dma_unmap_single(sc->dev, bf->bf_buf_addr,
@@ -240,12 +236,73 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
240236

241237
avp->av_bcbuf = NULL;
242238
sc->beacon.bslot[avp->av_bslot] = NULL;
243-
sc->nbcnvifs--;
244239
list_add_tail(&bf->list, &sc->beacon.bbuf);
245240

246241
tasklet_enable(&sc->bcon_tasklet);
247242
}
248243

244+
void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc)
245+
{
246+
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
247+
struct ieee80211_vif *vif;
248+
struct ath_vif *avp;
249+
s64 tsfadjust;
250+
u32 offset;
251+
int first_slot = ATH_BCBUF;
252+
int slot;
253+
254+
tasklet_disable(&sc->bcon_tasklet);
255+
256+
/* Find first taken slot. */
257+
for (slot = 0; slot < ATH_BCBUF; slot++) {
258+
if (sc->beacon.bslot[slot]) {
259+
first_slot = slot;
260+
break;
261+
}
262+
}
263+
if (first_slot == 0)
264+
goto out;
265+
266+
/* Re-enumarate all slots, moving them forward. */
267+
for (slot = 0; slot < ATH_BCBUF; slot++) {
268+
if (slot + first_slot < ATH_BCBUF) {
269+
vif = sc->beacon.bslot[slot + first_slot];
270+
sc->beacon.bslot[slot] = vif;
271+
272+
if (vif) {
273+
avp = (void *)vif->drv_priv;
274+
avp->av_bslot = slot;
275+
}
276+
} else {
277+
sc->beacon.bslot[slot] = NULL;
278+
}
279+
}
280+
281+
vif = sc->beacon.bslot[0];
282+
if (WARN_ON(!vif))
283+
goto out;
284+
285+
/* Get the tsf_adjust value for the new first slot. */
286+
avp = (void *)vif->drv_priv;
287+
tsfadjust = le64_to_cpu(avp->tsf_adjust);
288+
289+
ath_dbg(common, CONFIG,
290+
"Adjusting global TSF after beacon slot reassignment: %lld\n",
291+
(signed long long)tsfadjust);
292+
293+
/* Modify TSF as required and update the HW. */
294+
avp->chanctx->tsf_val += tsfadjust;
295+
if (sc->cur_chan == avp->chanctx) {
296+
offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL);
297+
ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset);
298+
}
299+
300+
/* The slots tsf_adjust will be updated by ath9k_beacon_config later. */
301+
302+
out:
303+
tasklet_enable(&sc->bcon_tasklet);
304+
}
305+
249306
static int ath9k_beacon_choose_slot(struct ath_softc *sc)
250307
{
251308
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -274,26 +331,33 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
274331
return slot;
275332
}
276333

277-
static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
334+
static void ath9k_set_tsfadjust(struct ath_softc *sc,
335+
struct ath_beacon_config *cur_conf)
278336
{
279337
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
280-
struct ath_vif *avp = (void *)vif->drv_priv;
281-
struct ath_beacon_config *cur_conf = &avp->chanctx->beacon;
282338
s64 tsfadjust;
339+
int slot;
283340

284-
if (avp->av_bslot == 0)
285-
return;
341+
for (slot = 0; slot < ATH_BCBUF; slot++) {
342+
struct ath_vif *avp;
286343

287-
/* tsf_adjust is added to the TSF value. We send out the beacon late,
288-
* so need to adjust the TSF starting point to be later in time (i.e.
289-
* the theoretical first beacon has a TSF of 0 after correction).
290-
*/
291-
tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
292-
tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF;
293-
avp->tsf_adjust = cpu_to_le64(tsfadjust);
344+
if (!sc->beacon.bslot[slot])
345+
continue;
346+
347+
avp = (void *)sc->beacon.bslot[slot]->drv_priv;
294348

295-
ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n",
296-
(signed long long)tsfadjust, avp->av_bslot);
349+
/* tsf_adjust is added to the TSF value. We send out the
350+
* beacon late, so need to adjust the TSF starting point to be
351+
* later in time (i.e. the theoretical first beacon has a TSF
352+
* of 0 after correction).
353+
*/
354+
tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
355+
tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF;
356+
avp->tsf_adjust = cpu_to_le64(tsfadjust);
357+
358+
ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n",
359+
(signed long long)tsfadjust, avp->av_bslot);
360+
}
297361
}
298362

299363
bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif)
@@ -447,20 +511,28 @@ void ath9k_beacon_tasklet(unsigned long data)
447511
* Both nexttbtt and intval have to be in usecs.
448512
*/
449513
static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
450-
u32 intval, bool reset_tsf)
514+
u32 intval)
451515
{
452516
struct ath_hw *ah = sc->sc_ah;
453517

454518
ath9k_hw_disable_interrupts(ah);
455-
if (reset_tsf)
456-
ath9k_hw_reset_tsf(ah);
457519
ath9k_beaconq_config(sc);
458520
ath9k_hw_beaconinit(ah, nexttbtt, intval);
521+
ah->imask |= ATH9K_INT_SWBA;
459522
sc->beacon.bmisscnt = 0;
460523
ath9k_hw_set_interrupts(ah);
461524
ath9k_hw_enable_interrupts(ah);
462525
}
463526

527+
static void ath9k_beacon_stop(struct ath_softc *sc)
528+
{
529+
ath9k_hw_disable_interrupts(sc->sc_ah);
530+
sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
531+
sc->beacon.bmisscnt = 0;
532+
ath9k_hw_set_interrupts(sc->sc_ah);
533+
ath9k_hw_enable_interrupts(sc->sc_ah);
534+
}
535+
464536
/*
465537
* For multi-bss ap support beacons are either staggered evenly over N slots or
466538
* burst together. For the former arrange for the SWBA to be delivered for each
@@ -472,7 +544,7 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc,
472544
struct ath_hw *ah = sc->sc_ah;
473545

474546
ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF);
475-
ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, false);
547+
ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
476548
}
477549

478550
static void ath9k_beacon_config_sta(struct ath_hw *ah,
@@ -501,7 +573,7 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
501573

502574
ath9k_cmn_beacon_config_adhoc(ah, conf);
503575

504-
ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, conf->ibss_creator);
576+
ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
505577

506578
/*
507579
* Set the global 'beacon has been configured' flag for the
@@ -511,44 +583,6 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
511583
set_bit(ATH_OP_BEACONS, &common->op_flags);
512584
}
513585

514-
static bool ath9k_allow_beacon_config(struct ath_softc *sc,
515-
struct ieee80211_vif *vif)
516-
{
517-
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
518-
struct ath_vif *avp = (void *)vif->drv_priv;
519-
520-
if (ath9k_is_chanctx_enabled()) {
521-
/*
522-
* If the VIF is not present in the current channel context,
523-
* then we can't do the usual opmode checks. Allow the
524-
* beacon config for the VIF to be updated in this case and
525-
* return immediately.
526-
*/
527-
if (sc->cur_chan != avp->chanctx)
528-
return true;
529-
}
530-
531-
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
532-
if (vif->type != NL80211_IFTYPE_AP) {
533-
ath_dbg(common, CONFIG,
534-
"An AP interface is already present !\n");
535-
return false;
536-
}
537-
}
538-
539-
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
540-
if ((vif->type == NL80211_IFTYPE_STATION) &&
541-
test_bit(ATH_OP_BEACONS, &common->op_flags) &&
542-
vif != sc->cur_chan->primary_sta) {
543-
ath_dbg(common, CONFIG,
544-
"Beacon already configured for a station interface\n");
545-
return false;
546-
}
547-
}
548-
549-
return true;
550-
}
551-
552586
static void ath9k_cache_beacon_config(struct ath_softc *sc,
553587
struct ath_chanctx *ctx,
554588
struct ieee80211_bss_conf *bss_conf)
@@ -584,87 +618,79 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc,
584618
if (cur_conf->dtim_period == 0)
585619
cur_conf->dtim_period = 1;
586620

621+
ath9k_set_tsfadjust(sc, cur_conf);
587622
}
588623

589-
void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
590-
u32 changed)
624+
void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif,
625+
bool beacons)
591626
{
592-
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
593-
struct ath_hw *ah = sc->sc_ah;
594-
struct ath_common *common = ath9k_hw_common(ah);
595-
struct ath_vif *avp = (void *)vif->drv_priv;
596-
struct ath_chanctx *ctx = avp->chanctx;
627+
struct ath_hw *ah = sc->sc_ah;
628+
struct ath_common *common = ath9k_hw_common(ah);
629+
struct ath_vif *avp;
630+
struct ath_chanctx *ctx;
597631
struct ath_beacon_config *cur_conf;
598632
unsigned long flags;
633+
bool enabled;
599634
bool skip_beacon = false;
600635

601-
if (!ctx)
636+
if (!beacons) {
637+
clear_bit(ATH_OP_BEACONS, &common->op_flags);
638+
ath9k_beacon_stop(sc);
602639
return;
640+
}
603641

604-
cur_conf = &avp->chanctx->beacon;
605-
if (vif->type == NL80211_IFTYPE_AP)
606-
ath9k_set_tsfadjust(sc, vif);
607-
608-
if (!ath9k_allow_beacon_config(sc, vif))
642+
if (WARN_ON(!main_vif))
609643
return;
610644

611-
if (vif->type == NL80211_IFTYPE_STATION) {
612-
ath9k_cache_beacon_config(sc, ctx, bss_conf);
613-
if (ctx != sc->cur_chan)
614-
return;
645+
avp = (void *)main_vif->drv_priv;
646+
ctx = avp->chanctx;
647+
cur_conf = &ctx->beacon;
648+
enabled = cur_conf->enable_beacon;
649+
cur_conf->enable_beacon = beacons;
650+
651+
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
652+
ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf);
615653

616654
ath9k_set_beacon(sc);
617655
set_bit(ATH_OP_BEACONS, &common->op_flags);
618656
return;
619657
}
620658

621-
/*
622-
* Take care of multiple interfaces when
623-
* enabling/disabling SWBA.
624-
*/
625-
if (changed & BSS_CHANGED_BEACON_ENABLED) {
626-
bool enabled = cur_conf->enable_beacon;
627-
628-
if (!bss_conf->enable_beacon) {
629-
cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
630-
} else {
631-
cur_conf->enable_beacon |= BIT(avp->av_bslot);
632-
if (!enabled)
633-
ath9k_cache_beacon_config(sc, ctx, bss_conf);
634-
}
635-
}
636-
637-
if (ctx != sc->cur_chan)
638-
return;
659+
/* Update the beacon configuration. */
660+
ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf);
639661

640662
/*
641663
* Configure the HW beacon registers only when we have a valid
642664
* beacon interval.
643665
*/
644666
if (cur_conf->beacon_interval) {
645-
/*
646-
* If we are joining an existing IBSS network, start beaconing
647-
* only after a TSF-sync has taken place. Ensure that this
648-
* happens by setting the appropriate flags.
667+
/* Special case to sync the TSF when joining an existing IBSS.
668+
* This is only done if no AP interface is active.
669+
* Note that mac80211 always resets the TSF when creating a new
670+
* IBSS interface.
649671
*/
650-
if ((changed & BSS_CHANGED_IBSS) && !bss_conf->ibss_creator &&
651-
bss_conf->enable_beacon) {
672+
if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
673+
!enabled && beacons && !main_vif->bss_conf.ibss_creator) {
652674
spin_lock_irqsave(&sc->sc_pm_lock, flags);
653675
sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
654676
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
655677
skip_beacon = true;
656-
} else {
657-
ath9k_set_beacon(sc);
658678
}
659679

660680
/*
661681
* Do not set the ATH_OP_BEACONS flag for IBSS joiner mode
662682
* here, it is done in ath9k_beacon_config_adhoc().
663683
*/
664-
if (cur_conf->enable_beacon && !skip_beacon)
684+
if (beacons && !skip_beacon) {
665685
set_bit(ATH_OP_BEACONS, &common->op_flags);
666-
else
686+
ath9k_set_beacon(sc);
687+
} else {
667688
clear_bit(ATH_OP_BEACONS, &common->op_flags);
689+
ath9k_beacon_stop(sc);
690+
}
691+
} else {
692+
clear_bit(ATH_OP_BEACONS, &common->op_flags);
693+
ath9k_beacon_stop(sc);
668694
}
669695
}
670696

drivers/net/wireless/ath/ath9k/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
5151

5252
struct ath_beacon_config {
53+
struct ieee80211_vif *main_vif;
5354
int beacon_interval;
5455
u16 dtim_period;
5556
u16 bmiss_timeout;

0 commit comments

Comments
 (0)