diff options
author | Felix Fietkau <nbd@nbd.name> | 2016-07-05 10:45:17 +0200 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2016-07-05 10:45:17 +0200 |
commit | 92d856f50a71d0376e491644894507458bc26050 (patch) | |
tree | 78191f330ece4bd2412b327135fb43a751af8aa0 | |
parent | 71753a8286dcb4dedbb6d4792b0fd5dd305c3338 (diff) | |
download | upstream-92d856f50a71d0376e491644894507458bc26050.tar.gz upstream-92d856f50a71d0376e491644894507458bc26050.tar.bz2 upstream-92d856f50a71d0376e491644894507458bc26050.zip |
ath9k: add beacon related stability fixes
Signed-off-by: Felix Fietkau <nbd@nbd.name>
10 files changed, 908 insertions, 15 deletions
diff --git a/package/kernel/mac80211/patches/312-ath9k-Correct-TSF-adjustment-to-align-the-beacon-tim.patch b/package/kernel/mac80211/patches/312-ath9k-Correct-TSF-adjustment-to-align-the-beacon-tim.patch new file mode 100644 index 0000000000..df43105cb8 --- /dev/null +++ b/package/kernel/mac80211/patches/312-ath9k-Correct-TSF-adjustment-to-align-the-beacon-tim.patch @@ -0,0 +1,45 @@ +From: Benjamin Berg <benjamin.berg@open-mesh.com> +Date: Mon, 4 Jul 2016 14:37:20 +0200 +Subject: [PATCH] ath9k: Correct TSF adjustment to align the beacon time + correctly + +Beacons were not send out at (timestamp % beacon_time == 0) for interfaces +other than the primary one. To send out beacons with the correct timestamp +according to 10.1.3.2 of the 802.11 standard the tsf_adjustment has to be +set to the negative time difference instead of positive. This way the +later beacons get corrected to have a lower (and similar) timestamp with +regard to the beacon from slot 0. + +I am not aware about any issues that have been caused by this. + +Signed-off-by: Benjamin Berg <benjamin.berg@open-mesh.com> +--- + +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -279,17 +279,21 @@ static void ath9k_set_tsfadjust(struct a + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_beacon_config *cur_conf = &avp->chanctx->beacon; +- u32 tsfadjust; ++ s64 tsfadjust; + + if (avp->av_bslot == 0) + return; + ++ /* tsf_adjust is added to the TSF value. We send out the beacon late, ++ * so need to adjust the TSF starting point to be later in time (i.e. ++ * the theoretical first beacon has a TSF of 0 after correction). ++ */ + tsfadjust = cur_conf->beacon_interval * avp->av_bslot; +- tsfadjust = TU_TO_USEC(tsfadjust) / ATH_BCBUF; ++ tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF; + avp->tsf_adjust = cpu_to_le64(tsfadjust); + +- ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n", +- (unsigned long long)tsfadjust, avp->av_bslot); ++ ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n", ++ (signed long long)tsfadjust, avp->av_bslot); + } + + bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif) diff --git a/package/kernel/mac80211/patches/313-ath9k-Handle-channel-context-in-get_-set_-reset_tsf.patch b/package/kernel/mac80211/patches/313-ath9k-Handle-channel-context-in-get_-set_-reset_tsf.patch new file mode 100644 index 0000000000..ef0afbea9c --- /dev/null +++ b/package/kernel/mac80211/patches/313-ath9k-Handle-channel-context-in-get_-set_-reset_tsf.patch @@ -0,0 +1,70 @@ +From: Benjamin Berg <benjamin.berg@open-mesh.com> +Date: Mon, 4 Jul 2016 14:37:21 +0200 +Subject: [PATCH] ath9k: Handle channel context in get_/set_/reset_tsf + +The ath9k TSF handling routines need to be aware of the channel context that +is being modified. With this change the TSF related values that are stored +in each channel context will be correctly tracked and the harware will only +be updated if the modified context is currently the active one. + +Without this change the TSF modifications done using these routines would +for example be lost during a hardware reset as done by ath_complete_reset. + +Signed-off-by: Benjamin Berg <benjamin.berg@open-mesh.com> +--- + +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -1823,11 +1823,18 @@ static void ath9k_bss_info_changed(struc + static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) + { + struct ath_softc *sc = hw->priv; ++ struct ath_vif *avp = (void *)vif->drv_priv; + u64 tsf; + + mutex_lock(&sc->mutex); + ath9k_ps_wakeup(sc); +- tsf = ath9k_hw_gettsf64(sc->sc_ah); ++ /* Get current TSF either from HW or kernel time. */ ++ if (sc->cur_chan == avp->chanctx) { ++ tsf = ath9k_hw_gettsf64(sc->sc_ah); ++ } else { ++ tsf = sc->cur_chan->tsf_val + ++ ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); ++ } + ath9k_ps_restore(sc); + mutex_unlock(&sc->mutex); + +@@ -1839,10 +1846,14 @@ static void ath9k_set_tsf(struct ieee802 + u64 tsf) + { + struct ath_softc *sc = hw->priv; ++ struct ath_vif *avp = (void *)vif->drv_priv; + + mutex_lock(&sc->mutex); + ath9k_ps_wakeup(sc); +- ath9k_hw_settsf64(sc->sc_ah, tsf); ++ getrawmonotonic(&avp->chanctx->tsf_ts); ++ if (sc->cur_chan == avp->chanctx) ++ ath9k_hw_settsf64(sc->sc_ah, tsf); ++ avp->chanctx->tsf_val = tsf; + ath9k_ps_restore(sc); + mutex_unlock(&sc->mutex); + } +@@ -1850,11 +1861,15 @@ static void ath9k_set_tsf(struct ieee802 + static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) + { + struct ath_softc *sc = hw->priv; ++ struct ath_vif *avp = (void *)vif->drv_priv; + + mutex_lock(&sc->mutex); + + ath9k_ps_wakeup(sc); +- ath9k_hw_reset_tsf(sc->sc_ah); ++ getrawmonotonic(&avp->chanctx->tsf_ts); ++ if (sc->cur_chan == avp->chanctx) ++ ath9k_hw_reset_tsf(sc->sc_ah); ++ avp->chanctx->tsf_val = 0; + ath9k_ps_restore(sc); + + mutex_unlock(&sc->mutex); diff --git a/package/kernel/mac80211/patches/314-ath9k-Use-tsf-offset-helper-in-ath9k_hw_reset.patch b/package/kernel/mac80211/patches/314-ath9k-Use-tsf-offset-helper-in-ath9k_hw_reset.patch new file mode 100644 index 0000000000..e725a8bd60 --- /dev/null +++ b/package/kernel/mac80211/patches/314-ath9k-Use-tsf-offset-helper-in-ath9k_hw_reset.patch @@ -0,0 +1,65 @@ +From: Benjamin Berg <benjamin.berg@open-mesh.com> +Date: Mon, 4 Jul 2016 14:37:22 +0200 +Subject: [PATCH] ath9k: Use tsf offset helper in ath9k_hw_reset + +These changes make ath9k_hw_reset more consistent with other places that +handle the TSF value by using the same helper routine. + +A slight improvement is to not assume that a fixed time of 1.5ms has +passed for the initval writes when compared to the first write attempt. +Instead the TSF value is re-calculated which will yield a higher accuracy +of the restored TSF timer. + +Signed-off-by: Benjamin Berg <benjamin.berg@open-mesh.com> +--- + +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -1832,8 +1832,9 @@ int ath9k_hw_reset(struct ath_hw *ah, st + u32 saveLedState; + u32 saveDefAntenna; + u32 macStaId1; ++ struct timespec tsf_ts; ++ u32 tsf_offset; + u64 tsf = 0; +- s64 usec = 0; + int r; + bool start_mci_reset = false; + bool save_fullsleep = ah->chip_fullsleep; +@@ -1877,8 +1878,8 @@ int ath9k_hw_reset(struct ath_hw *ah, st + macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; + + /* Save TSF before chip reset, a cold reset clears it */ ++ getrawmonotonic(&tsf_ts); + tsf = ath9k_hw_gettsf64(ah); +- usec = ktime_to_us(ktime_get_raw()); + + saveLedState = REG_READ(ah, AR_CFG_LED) & + (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | +@@ -1911,8 +1912,8 @@ int ath9k_hw_reset(struct ath_hw *ah, st + } + + /* Restore TSF */ +- usec = ktime_to_us(ktime_get_raw()) - usec; +- ath9k_hw_settsf64(ah, tsf + usec); ++ tsf_offset = ath9k_hw_get_tsf_offset(&tsf_ts, NULL); ++ ath9k_hw_settsf64(ah, tsf + tsf_offset); + + if (AR_SREV_9280_20_OR_LATER(ah)) + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); +@@ -1932,12 +1933,11 @@ int ath9k_hw_reset(struct ath_hw *ah, st + /* + * Some AR91xx SoC devices frequently fail to accept TSF writes + * right after the chip reset. When that happens, write a new +- * value after the initvals have been applied, with an offset +- * based on measured time difference ++ * value after the initvals have been applied. + */ + if (AR_SREV_9100(ah) && (ath9k_hw_gettsf64(ah) < tsf)) { +- tsf += 1500; +- ath9k_hw_settsf64(ah, tsf); ++ tsf_offset = ath9k_hw_get_tsf_offset(&tsf_ts, NULL); ++ ath9k_hw_settsf64(ah, tsf + tsf_offset); + } + + ath9k_hw_init_mfp(ah); diff --git a/package/kernel/mac80211/patches/315-ath9k-Expose-tsf_adjustment-in-mac80211-tsf-getters-.patch b/package/kernel/mac80211/patches/315-ath9k-Expose-tsf_adjustment-in-mac80211-tsf-getters-.patch new file mode 100644 index 0000000000..c95ab7e644 --- /dev/null +++ b/package/kernel/mac80211/patches/315-ath9k-Expose-tsf_adjustment-in-mac80211-tsf-getters-.patch @@ -0,0 +1,32 @@ +From: Benjamin Berg <benjamin.berg@open-mesh.com> +Date: Mon, 4 Jul 2016 14:37:23 +0200 +Subject: [PATCH] ath9k: Expose tsf_adjustment in mac80211 tsf getters and + setters. + +The ath9k driver modifies the TSF for VIFs for the purpose of sending +beacons in a staggered fashion. This patch exposes this VIF specific +adjustment of the TSF value to mac80211. Without the change the TSF +routines handle the hardware TSF value instead of the actual TSF value as +seen on the air. + +Signed-off-by: Benjamin Berg <benjamin.berg@open-mesh.com> +--- + +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -1835,6 +1835,7 @@ static u64 ath9k_get_tsf(struct ieee8021 + tsf = sc->cur_chan->tsf_val + + ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); + } ++ tsf += le64_to_cpu(avp->tsf_adjust); + ath9k_ps_restore(sc); + mutex_unlock(&sc->mutex); + +@@ -1850,6 +1851,7 @@ static void ath9k_set_tsf(struct ieee802 + + mutex_lock(&sc->mutex); + ath9k_ps_wakeup(sc); ++ tsf -= le64_to_cpu(avp->tsf_adjust); + getrawmonotonic(&avp->chanctx->tsf_ts); + if (sc->cur_chan == avp->chanctx) + ath9k_hw_settsf64(sc->sc_ah, tsf); diff --git a/package/kernel/mac80211/patches/316-ath9k-Remove-some-defined-constants-to-decrease-verb.patch b/package/kernel/mac80211/patches/316-ath9k-Remove-some-defined-constants-to-decrease-verb.patch new file mode 100644 index 0000000000..36aaa1030e --- /dev/null +++ b/package/kernel/mac80211/patches/316-ath9k-Remove-some-defined-constants-to-decrease-verb.patch @@ -0,0 +1,137 @@ +From: Benjamin Berg <benjamin.berg@open-mesh.com> +Date: Mon, 4 Jul 2016 14:37:24 +0200 +Subject: [PATCH] ath9k: Remove some #defined constants to decrease + verbosity + +The removed ATH9K_SLOT_TIME_X constants simply map the value in microseconds +to the same integer. These constants were not used consistently, so fix the +inconsistency issue by replacing all occurances with the integer equivalent. + +Signed-off-by: Benjamin Berg <benjamin.berg@open-mesh.com> +--- + +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -50,7 +50,7 @@ static void ath9k_beaconq_config(struct + txq = sc->tx.txq_map[IEEE80211_AC_BE]; + ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); + qi.tqi_aifs = qi_be.tqi_aifs; +- if (ah->slottime == ATH9K_SLOT_TIME_20) ++ if (ah->slottime == 20) + qi.tqi_cwmin = 2*qi_be.tqi_cwmin; + else + qi.tqi_cwmin = 4*qi_be.tqi_cwmin; +--- a/drivers/net/wireless/ath/ath9k/dynack.c ++++ b/drivers/net/wireless/ath/ath9k/dynack.c +@@ -280,7 +280,7 @@ EXPORT_SYMBOL(ath_dynack_sample_ack_ts); + void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an) + { + /* ackto = slottime + sifs + air delay */ +- u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64; ++ u32 ackto = 9 + 16 + 64; + struct ath_dynack *da = &ah->dynack; + + an->ackto = ackto; +@@ -315,7 +315,7 @@ EXPORT_SYMBOL(ath_dynack_node_deinit); + void ath_dynack_reset(struct ath_hw *ah) + { + /* ackto = slottime + sifs + air delay */ +- u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64; ++ u32 ackto = 9 + 16 + 64; + struct ath_dynack *da = &ah->dynack; + + da->lto = jiffies; +--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +@@ -45,7 +45,7 @@ void ath9k_htc_beaconq_config(struct ath + * Long slot time : 2x cwmin + * Short slot time : 4x cwmin + */ +- if (ah->slottime == ATH9K_SLOT_TIME_20) ++ if (ah->slottime == 20) + qi.tqi_cwmin = 2*qi_be.tqi_cwmin; + else + qi.tqi_cwmin = 4*qi_be.tqi_cwmin; +--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c +@@ -678,7 +678,7 @@ static int ath9k_init_priv(struct ath9k_ + + for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) + priv->beacon.bslot[i] = NULL; +- priv->beacon.slottime = ATH9K_SLOT_TIME_9; ++ priv->beacon.slottime = 9; + + ath9k_cmn_init_channels_rates(common); + ath9k_cmn_init_crypto(ah); +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -454,7 +454,7 @@ static void ath9k_hw_init_defaults(struc + if (AR_SREV_9100(ah)) + ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX; + +- ah->slottime = ATH9K_SLOT_TIME_9; ++ ah->slottime = 9; + ah->globaltxtimeout = (u32) -1; + ah->power_mode = ATH9K_PM_UNDEFINED; + ah->htc_reset_init = true; +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -372,7 +372,7 @@ static void ath9k_init_misc(struct ath_s + + common->last_rssi = ATH_RSSI_DUMMY_MARKER; + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); +- sc->beacon.slottime = ATH9K_SLOT_TIME_9; ++ sc->beacon.slottime = 9; + + for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) + sc->beacon.bslot[i] = NULL; +--- a/drivers/net/wireless/ath/ath9k/mac.h ++++ b/drivers/net/wireless/ath/ath9k/mac.h +@@ -65,10 +65,6 @@ + #define INIT_SSH_RETRY 32 + #define INIT_SLG_RETRY 32 + +-#define ATH9K_SLOT_TIME_6 6 +-#define ATH9K_SLOT_TIME_9 9 +-#define ATH9K_SLOT_TIME_20 20 +- + #define ATH9K_TXERR_XRETRY 0x01 + #define ATH9K_TXERR_FILT 0x02 + #define ATH9K_TXERR_FIFO 0x04 +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -926,7 +926,7 @@ static void ath9k_vif_iter(struct ath9k_ + } + + if (!vif->bss_conf.use_short_slot) +- iter_data->slottime = ATH9K_SLOT_TIME_20; ++ iter_data->slottime = 20; + + switch (vif->type) { + case NL80211_IFTYPE_AP: +@@ -999,7 +999,7 @@ void ath9k_calculate_iter_data(struct at + */ + memset(iter_data, 0, sizeof(*iter_data)); + eth_broadcast_addr(iter_data->mask); +- iter_data->slottime = ATH9K_SLOT_TIME_9; ++ iter_data->slottime = 9; + + list_for_each_entry(avp, &ctx->vifs, list) + ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif); +@@ -1061,7 +1061,7 @@ static void ath9k_set_offchannel_state(s + ah->opmode = vif->type; + ah->imask &= ~ATH9K_INT_SWBA; + ah->imask &= ~ATH9K_INT_TSFOOR; +- ah->slottime = ATH9K_SLOT_TIME_9; ++ ah->slottime = 9; + + ath_hw_setbssidmask(common); + ath9k_hw_setopmode(ah); +@@ -1788,6 +1788,7 @@ static void ath9k_bss_info_changed(struc + slottime = 9; + else + slottime = 20; ++ + if (vif->type == NL80211_IFTYPE_AP) { + /* + * Defer update, so that connected stations can adjust diff --git a/package/kernel/mac80211/patches/317-ath9k-Fix-beacon-configuration-for-addition-removal-.patch b/package/kernel/mac80211/patches/317-ath9k-Fix-beacon-configuration-for-addition-removal-.patch new file mode 100644 index 0000000000..360dfbfafb --- /dev/null +++ b/package/kernel/mac80211/patches/317-ath9k-Fix-beacon-configuration-for-addition-removal-.patch @@ -0,0 +1,544 @@ +From: Benjamin Berg <benjamin.berg@open-mesh.com> +Date: Mon, 4 Jul 2016 14:37:25 +0200 +Subject: [PATCH] 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 <benjamin.berg@open-mesh.com> +--- + +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -637,6 +637,8 @@ struct ath9k_vif_iter_data { + int nwds; /* number of WDS vifs */ + int nadhocs; /* number of adhoc vifs */ + int nocbs; /* number of OCB vifs */ ++ int nbcnvifs; /* number of beaconing vifs */ ++ struct ieee80211_vif *primary_beacon_vif; + struct ieee80211_vif *primary_sta; + }; + +@@ -685,10 +687,11 @@ struct ath_beacon { + }; + + void ath9k_beacon_tasklet(unsigned long data); +-void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, +- u32 changed); ++void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif, ++ bool beacons); + void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif); + void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif); ++void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc); + void ath9k_set_beacon(struct ath_softc *sc); + bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif); + void ath9k_csa_update(struct ath_softc *sc); +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -209,7 +209,6 @@ void ath9k_beacon_assign_slot(struct ath + } + + sc->beacon.bslot[avp->av_bslot] = vif; +- sc->nbcnvifs++; + + ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n", + avp->av_bslot); +@@ -220,15 +219,12 @@ void ath9k_beacon_remove_slot(struct ath + 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; +- struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; + + ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", + avp->av_bslot); + + tasklet_disable(&sc->bcon_tasklet); + +- cur_conf->enable_beacon &= ~BIT(avp->av_bslot); +- + if (bf && bf->bf_mpdu) { + struct sk_buff *skb = bf->bf_mpdu; + dma_unmap_single(sc->dev, bf->bf_buf_addr, +@@ -240,12 +236,73 @@ void ath9k_beacon_remove_slot(struct ath + + avp->av_bcbuf = NULL; + sc->beacon.bslot[avp->av_bslot] = NULL; +- sc->nbcnvifs--; + list_add_tail(&bf->list, &sc->beacon.bbuf); + + tasklet_enable(&sc->bcon_tasklet); + } + ++void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc) ++{ ++ struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++ struct ieee80211_vif *vif; ++ struct ath_vif *avp; ++ s64 tsfadjust; ++ u32 offset; ++ int first_slot = ATH_BCBUF; ++ int slot; ++ ++ tasklet_disable(&sc->bcon_tasklet); ++ ++ /* Find first taken slot. */ ++ for (slot = 0; slot < ATH_BCBUF; slot++) { ++ if (sc->beacon.bslot[slot]) { ++ first_slot = slot; ++ break; ++ } ++ } ++ if (first_slot == 0) ++ goto out; ++ ++ /* Re-enumarate all slots, moving them forward. */ ++ for (slot = 0; slot < ATH_BCBUF; slot++) { ++ if (slot + first_slot < ATH_BCBUF) { ++ vif = sc->beacon.bslot[slot + first_slot]; ++ sc->beacon.bslot[slot] = vif; ++ ++ if (vif) { ++ avp = (void *)vif->drv_priv; ++ avp->av_bslot = slot; ++ } ++ } else { ++ sc->beacon.bslot[slot] = NULL; ++ } ++ } ++ ++ vif = sc->beacon.bslot[0]; ++ if (WARN_ON(!vif)) ++ goto out; ++ ++ /* Get the tsf_adjust value for the new first slot. */ ++ avp = (void *)vif->drv_priv; ++ tsfadjust = le64_to_cpu(avp->tsf_adjust); ++ ++ ath_dbg(common, CONFIG, ++ "Adjusting global TSF after beacon slot reassignment: %lld\n", ++ (signed long long)tsfadjust); ++ ++ /* Modify TSF as required and update the HW. */ ++ avp->chanctx->tsf_val += tsfadjust; ++ if (sc->cur_chan == avp->chanctx) { ++ offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL); ++ ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset); ++ } ++ ++ /* The slots tsf_adjust will be updated by ath9k_beacon_config later. */ ++ ++out: ++ tasklet_enable(&sc->bcon_tasklet); ++} ++ + static int ath9k_beacon_choose_slot(struct ath_softc *sc) + { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); +@@ -274,26 +331,33 @@ static int ath9k_beacon_choose_slot(stru + return slot; + } + +-static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) ++static void ath9k_set_tsfadjust(struct ath_softc *sc, ++ struct ath_beacon_config *cur_conf) + { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); +- struct ath_vif *avp = (void *)vif->drv_priv; +- struct ath_beacon_config *cur_conf = &avp->chanctx->beacon; + s64 tsfadjust; ++ int slot; + +- if (avp->av_bslot == 0) +- return; ++ for (slot = 0; slot < ATH_BCBUF; slot++) { ++ struct ath_vif *avp; + +- /* tsf_adjust is added to the TSF value. We send out the beacon late, +- * so need to adjust the TSF starting point to be later in time (i.e. +- * the theoretical first beacon has a TSF of 0 after correction). +- */ +- tsfadjust = cur_conf->beacon_interval * avp->av_bslot; +- tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF; +- avp->tsf_adjust = cpu_to_le64(tsfadjust); ++ if (!sc->beacon.bslot[slot]) ++ continue; + +- ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n", +- (signed long long)tsfadjust, avp->av_bslot); ++ avp = (void *)sc->beacon.bslot[slot]->drv_priv; ++ ++ /* tsf_adjust is added to the TSF value. We send out the ++ * beacon late, so need to adjust the TSF starting point to be ++ * later in time (i.e. the theoretical first beacon has a TSF ++ * of 0 after correction). ++ */ ++ tsfadjust = cur_conf->beacon_interval * avp->av_bslot; ++ tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF; ++ avp->tsf_adjust = cpu_to_le64(tsfadjust); ++ ++ ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n", ++ (signed long long)tsfadjust, avp->av_bslot); ++ } + } + + bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif) +@@ -447,20 +511,28 @@ void ath9k_beacon_tasklet(unsigned long + * Both nexttbtt and intval have to be in usecs. + */ + static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, +- u32 intval, bool reset_tsf) ++ u32 intval) + { + struct ath_hw *ah = sc->sc_ah; + + ath9k_hw_disable_interrupts(ah); +- if (reset_tsf) +- ath9k_hw_reset_tsf(ah); + ath9k_beaconq_config(sc); + ath9k_hw_beaconinit(ah, nexttbtt, intval); ++ ah->imask |= ATH9K_INT_SWBA; + sc->beacon.bmisscnt = 0; + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + } + ++static void ath9k_beacon_stop(struct ath_softc *sc) ++{ ++ ath9k_hw_disable_interrupts(sc->sc_ah); ++ sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); ++ sc->beacon.bmisscnt = 0; ++ ath9k_hw_set_interrupts(sc->sc_ah); ++ ath9k_hw_enable_interrupts(sc->sc_ah); ++} ++ + /* + * For multi-bss ap support beacons are either staggered evenly over N slots or + * burst together. For the former arrange for the SWBA to be delivered for each +@@ -472,7 +544,7 @@ static void ath9k_beacon_config_ap(struc + struct ath_hw *ah = sc->sc_ah; + + ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF); +- ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, false); ++ ath9k_beacon_init(sc, conf->nexttbtt, conf->intval); + } + + static void ath9k_beacon_config_sta(struct ath_hw *ah, +@@ -501,7 +573,7 @@ static void ath9k_beacon_config_adhoc(st + + ath9k_cmn_beacon_config_adhoc(ah, conf); + +- ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, conf->ibss_creator); ++ ath9k_beacon_init(sc, conf->nexttbtt, conf->intval); + + /* + * Set the global 'beacon has been configured' flag for the +@@ -511,44 +583,6 @@ static void ath9k_beacon_config_adhoc(st + set_bit(ATH_OP_BEACONS, &common->op_flags); + } + +-static bool ath9k_allow_beacon_config(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; +- +- if (ath9k_is_chanctx_enabled()) { +- /* +- * If the VIF is not present in the current channel context, +- * then we can't do the usual opmode checks. Allow the +- * beacon config for the VIF to be updated in this case and +- * return immediately. +- */ +- if (sc->cur_chan != avp->chanctx) +- return true; +- } +- +- if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { +- if (vif->type != NL80211_IFTYPE_AP) { +- ath_dbg(common, CONFIG, +- "An AP interface is already present !\n"); +- return false; +- } +- } +- +- if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { +- if ((vif->type == NL80211_IFTYPE_STATION) && +- test_bit(ATH_OP_BEACONS, &common->op_flags) && +- vif != sc->cur_chan->primary_sta) { +- ath_dbg(common, CONFIG, +- "Beacon already configured for a station interface\n"); +- return false; +- } +- } +- +- return true; +-} +- + static void ath9k_cache_beacon_config(struct ath_softc *sc, + struct ath_chanctx *ctx, + struct ieee80211_bss_conf *bss_conf) +@@ -584,87 +618,79 @@ static void ath9k_cache_beacon_config(st + if (cur_conf->dtim_period == 0) + cur_conf->dtim_period = 1; + ++ ath9k_set_tsfadjust(sc, cur_conf); + } + +-void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, +- u32 changed) ++void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif, ++ bool beacons) + { +- struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; +- struct ath_hw *ah = sc->sc_ah; +- struct ath_common *common = ath9k_hw_common(ah); +- struct ath_vif *avp = (void *)vif->drv_priv; +- struct ath_chanctx *ctx = avp->chanctx; ++ struct ath_hw *ah = sc->sc_ah; ++ struct ath_common *common = ath9k_hw_common(ah); ++ struct ath_vif *avp; ++ struct ath_chanctx *ctx; + struct ath_beacon_config *cur_conf; + unsigned long flags; ++ bool enabled; + bool skip_beacon = false; + +- if (!ctx) ++ if (!beacons) { ++ clear_bit(ATH_OP_BEACONS, &common->op_flags); ++ ath9k_beacon_stop(sc); + return; ++ } + +- cur_conf = &avp->chanctx->beacon; +- if (vif->type == NL80211_IFTYPE_AP) +- ath9k_set_tsfadjust(sc, vif); +- +- if (!ath9k_allow_beacon_config(sc, vif)) ++ if (WARN_ON(!main_vif)) + return; + +- if (vif->type == NL80211_IFTYPE_STATION) { +- ath9k_cache_beacon_config(sc, ctx, bss_conf); +- if (ctx != sc->cur_chan) +- return; ++ avp = (void *)main_vif->drv_priv; ++ ctx = avp->chanctx; ++ cur_conf = &ctx->beacon; ++ enabled = cur_conf->enable_beacon; ++ cur_conf->enable_beacon = beacons; ++ ++ if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { ++ ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf); + + ath9k_set_beacon(sc); + set_bit(ATH_OP_BEACONS, &common->op_flags); + return; + } + +- /* +- * Take care of multiple interfaces when +- * enabling/disabling SWBA. +- */ +- if (changed & BSS_CHANGED_BEACON_ENABLED) { +- bool enabled = cur_conf->enable_beacon; +- +- if (!bss_conf->enable_beacon) { +- cur_conf->enable_beacon &= ~BIT(avp->av_bslot); +- } else { +- cur_conf->enable_beacon |= BIT(avp->av_bslot); +- if (!enabled) +- ath9k_cache_beacon_config(sc, ctx, bss_conf); +- } +- } +- +- if (ctx != sc->cur_chan) +- return; ++ /* Update the beacon configuration. */ ++ ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf); + + /* + * Configure the HW beacon registers only when we have a valid + * beacon interval. + */ + if (cur_conf->beacon_interval) { +- /* +- * If we are joining an existing IBSS network, start beaconing +- * only after a TSF-sync has taken place. Ensure that this +- * happens by setting the appropriate flags. ++ /* Special case to sync the TSF when joining an existing IBSS. ++ * This is only done if no AP interface is active. ++ * Note that mac80211 always resets the TSF when creating a new ++ * IBSS interface. + */ +- if ((changed & BSS_CHANGED_IBSS) && !bss_conf->ibss_creator && +- bss_conf->enable_beacon) { ++ if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC && ++ !enabled && beacons && !main_vif->bss_conf.ibss_creator) { + spin_lock_irqsave(&sc->sc_pm_lock, flags); + sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + skip_beacon = true; +- } else { +- ath9k_set_beacon(sc); + } + + /* + * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode + * here, it is done in ath9k_beacon_config_adhoc(). + */ +- if (cur_conf->enable_beacon && !skip_beacon) ++ if (beacons && !skip_beacon) { + set_bit(ATH_OP_BEACONS, &common->op_flags); +- else ++ ath9k_set_beacon(sc); ++ } else { + clear_bit(ATH_OP_BEACONS, &common->op_flags); ++ ath9k_beacon_stop(sc); ++ } ++ } else { ++ clear_bit(ATH_OP_BEACONS, &common->op_flags); ++ ath9k_beacon_stop(sc); + } + } + +--- a/drivers/net/wireless/ath/ath9k/common.h ++++ b/drivers/net/wireless/ath/ath9k/common.h +@@ -50,6 +50,7 @@ + #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) + + struct ath_beacon_config { ++ struct ieee80211_vif *main_vif; + int beacon_interval; + u16 dtim_period; + u16 bmiss_timeout; +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -910,6 +910,22 @@ static bool ath9k_uses_beacons(int type) + } + } + ++static void ath9k_vif_iter_set_beacon(struct ath9k_vif_iter_data *iter_data, ++ struct ieee80211_vif *vif) ++{ ++ /* Use the first (configured) interface, but prefering AP interfaces. */ ++ if (!iter_data->primary_beacon_vif) { ++ iter_data->primary_beacon_vif = vif; ++ } else { ++ if (iter_data->primary_beacon_vif->type != NL80211_IFTYPE_AP && ++ vif->type == NL80211_IFTYPE_AP) ++ iter_data->primary_beacon_vif = vif; ++ } ++ ++ iter_data->beacons = true; ++ iter_data->nbcnvifs += 1; ++} ++ + static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data, + u8 *mac, struct ieee80211_vif *vif) + { +@@ -931,6 +947,8 @@ static void ath9k_vif_iter(struct ath9k_ + switch (vif->type) { + case NL80211_IFTYPE_AP: + iter_data->naps++; ++ if (vif->bss_conf.enable_beacon) ++ ath9k_vif_iter_set_beacon(iter_data, vif); + break; + case NL80211_IFTYPE_STATION: + iter_data->nstations++; +@@ -943,12 +961,12 @@ static void ath9k_vif_iter(struct ath9k_ + case NL80211_IFTYPE_ADHOC: + iter_data->nadhocs++; + if (vif->bss_conf.enable_beacon) +- iter_data->beacons = true; ++ ath9k_vif_iter_set_beacon(iter_data, vif); + break; + case NL80211_IFTYPE_MESH_POINT: + iter_data->nmeshes++; + if (vif->bss_conf.enable_beacon) +- iter_data->beacons = true; ++ ath9k_vif_iter_set_beacon(iter_data, vif); + break; + case NL80211_IFTYPE_WDS: + iter_data->nwds++; +@@ -1081,7 +1099,6 @@ void ath9k_calculate_summary_state(struc + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_vif_iter_data iter_data; +- struct ath_beacon_config *cur_conf; + + ath_chanctx_check_active(sc, ctx); + +@@ -1103,13 +1120,12 @@ void ath9k_calculate_summary_state(struc + ath_hw_setbssidmask(common); + + if (iter_data.naps > 0) { +- cur_conf = &ctx->beacon; + ath9k_hw_set_tsfadjust(ah, true); + ah->opmode = NL80211_IFTYPE_AP; +- if (cur_conf->enable_beacon) +- iter_data.beacons = true; + } else { + ath9k_hw_set_tsfadjust(ah, false); ++ if (iter_data.beacons) ++ ath9k_beacon_ensure_primary_slot(sc); + + if (iter_data.nmeshes) + ah->opmode = NL80211_IFTYPE_MESH_POINT; +@@ -1134,7 +1150,6 @@ void ath9k_calculate_summary_state(struc + ctx->switch_after_beacon = true; + } + +- ah->imask &= ~ATH9K_INT_SWBA; + if (ah->opmode == NL80211_IFTYPE_STATION) { + bool changed = (iter_data.primary_sta != ctx->primary_sta); + +@@ -1151,16 +1166,12 @@ void ath9k_calculate_summary_state(struc + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + ath9k_mci_update_wlan_channels(sc, true); + } +- } else if (iter_data.beacons) { +- ah->imask |= ATH9K_INT_SWBA; + } ++ sc->nbcnvifs = iter_data.nbcnvifs; ++ ath9k_beacon_config(sc, iter_data.primary_beacon_vif, ++ iter_data.beacons); + ath9k_hw_set_interrupts(ah); + +- if (iter_data.beacons) +- set_bit(ATH_OP_BEACONS, &common->op_flags); +- else +- clear_bit(ATH_OP_BEACONS, &common->op_flags); +- + if (ah->slottime != iter_data.slottime) { + ah->slottime = iter_data.slottime; + ath9k_hw_init_global_settings(ah); +@@ -1777,9 +1788,7 @@ static void ath9k_bss_info_changed(struc + if ((changed & BSS_CHANGED_BEACON_ENABLED) || + (changed & BSS_CHANGED_BEACON_INT) || + (changed & BSS_CHANGED_BEACON_INFO)) { +- ath9k_beacon_config(sc, vif, changed); +- if (changed & BSS_CHANGED_BEACON_ENABLED) +- ath9k_calculate_summary_state(sc, avp->chanctx); ++ ath9k_calculate_summary_state(sc, avp->chanctx); + } + + if ((avp->chanctx == sc->cur_chan) && diff --git a/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch b/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch index 0204fa0556..a8a8a4cc7b 100644 --- a/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch +++ b/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h -@@ -813,6 +813,9 @@ static inline int ath9k_dump_btcoex(stru +@@ -816,6 +816,9 @@ static inline int ath9k_dump_btcoex(stru #ifdef CPTCFG_MAC80211_LEDS void ath_init_leds(struct ath_softc *sc); void ath_deinit_leds(struct ath_softc *sc); @@ -10,7 +10,7 @@ #else static inline void ath_init_leds(struct ath_softc *sc) { -@@ -949,6 +952,13 @@ void ath_ant_comb_scan(struct ath_softc +@@ -952,6 +955,13 @@ void ath_ant_comb_scan(struct ath_softc #define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */ @@ -24,7 +24,7 @@ struct ath_softc { struct ieee80211_hw *hw; struct device *dev; -@@ -1001,9 +1011,8 @@ struct ath_softc { +@@ -1004,9 +1014,8 @@ struct ath_softc { spinlock_t chan_lock; #ifdef CPTCFG_MAC80211_LEDS @@ -112,7 +112,8 @@ + GFP_KERNEL); + if (!led) + return -ENOMEM; -+ + +- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val); + led->gpio = gpio = (struct gpio_led *) (led + 1); + _name = (char *) (led->gpio + 1); + @@ -125,8 +126,7 @@ + ret = ath_add_led(sc, led); + if (unlikely(ret < 0)) + kfree(led); - -- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val); ++ + return ret; } @@ -134,11 +134,11 @@ { - if (!sc->led_registered) - return; -+ struct ath_led *led; - +- - ath_led_brightness(&sc->led_cdev, LED_OFF); - led_classdev_unregister(&sc->led_cdev); -- ++ struct ath_led *led; + - ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin); + while (!list_empty(&sc->leds)) { + led = list_first_entry(&sc->leds, struct ath_led, list); diff --git a/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch b/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch index 116860d75c..405ca11cb9 100644 --- a/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch +++ b/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch @@ -18,7 +18,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> #include "common.h" #include "debug.h" -@@ -959,6 +960,14 @@ struct ath_led { +@@ -962,6 +963,14 @@ struct ath_led { struct led_classdev cdev; }; @@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> struct ath_softc { struct ieee80211_hw *hw; struct device *dev; -@@ -1013,6 +1022,9 @@ struct ath_softc { +@@ -1016,6 +1025,9 @@ struct ath_softc { #ifdef CPTCFG_MAC80211_LEDS const char *led_default_trigger; struct list_head leds; diff --git a/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch b/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch index d331b15b52..d962eda978 100644 --- a/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch +++ b/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> --- --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h -@@ -1024,6 +1024,7 @@ struct ath_softc { +@@ -1027,6 +1027,7 @@ struct ath_softc { struct list_head leds; #ifdef CONFIG_GPIOLIB struct ath9k_gpio_chip *gpiochip; diff --git a/package/kernel/mac80211/patches/910-01-add-support-for-mt7620.patch b/package/kernel/mac80211/patches/910-01-add-support-for-mt7620.patch index b53102bfed..be210f2d4b 100644 --- a/package/kernel/mac80211/patches/910-01-add-support-for-mt7620.patch +++ b/package/kernel/mac80211/patches/910-01-add-support-for-mt7620.patch @@ -91,7 +91,8 @@ + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM_MT7620, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE_MT7620, 1); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY_MT7620, 1); -+ + +- rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + } + break; @@ -103,8 +104,7 @@ + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); - -- rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); ++ + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + } + break; |