Commit 6ba81c2c authored by Bruno Randolf's avatar Bruno Randolf Committed by John W. Linville

ath5k: work around wrong beacon rx timestamp in IBSS mode

atheros hardware has a problem with the rx timestamp of some IBSS beacons when
they caused a TSF update (they have the same BSSID).

the rx timestamp is wrong especially if the beacon frames get bigger than 78
byte (at least on AR5213 and AR5414 hardware). in that case ath5k_extend_tsf()
will assume a rs_tstamp overflow and give us a timestamp too far in the past
which will cause mac80211 to merge IBSS on every beacon (which is not necessary
since the BSSID already matches). but in this case we know that the HW must
have synced to the beacons TSF and the rx timestamp must be later than that so
we can adjust mactime accordingly.

also rename the function to ath5k_check_ibss_tsf() and change comments, since
"hw merge" is better described as a TSF update.

drivers/net/wireless/ath5k/base.c:      Changes-licensed-under: 3-Clause-BSD
Signed-off-by: default avatarBruno Randolf <bruno@thinktube.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent e14296ca
...@@ -1715,8 +1715,10 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds, ...@@ -1715,8 +1715,10 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
static void static void
ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb) ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
struct ieee80211_rx_status *rxs)
{ {
u64 tsf, bc_tstamp;
u32 hw_tu; u32 hw_tu;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
...@@ -1727,16 +1729,45 @@ ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb) ...@@ -1727,16 +1729,45 @@ ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb)
le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) { memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) {
/* /*
* Received an IBSS beacon with the same BSSID. Hardware might * Received an IBSS beacon with the same BSSID. Hardware *must*
* have updated the TSF, check if we need to update timers. * have updated the local TSF. We have to work around various
* hardware bugs, though...
*/ */
hw_tu = TSF_TO_TU(ath5k_hw_get_tsf64(sc->ah)); tsf = ath5k_hw_get_tsf64(sc->ah);
if (hw_tu >= sc->nexttbtt) { bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
ath5k_beacon_update_timers(sc, hw_tu = TSF_TO_TU(tsf);
le64_to_cpu(mgmt->u.beacon.timestamp));
ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
"detected HW merge from received beacon\n"); "beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
bc_tstamp, rxs->mactime,
(rxs->mactime - bc_tstamp), tsf);
/*
* Sometimes the HW will give us a wrong tstamp in the rx
* status, causing the timestamp extension to go wrong.
* (This seems to happen especially with beacon frames bigger
* than 78 byte (incl. FCS))
* But we know that the receive timestamp must be later than the
* timestamp of the beacon since HW must have synced to that.
*
* NOTE: here we assume mactime to be after the frame was
* received, not like mac80211 which defines it at the start.
*/
if (bc_tstamp > rxs->mactime) {
ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
"fixing mactime from %llx to %llx\n",
rxs->mactime, tsf);
rxs->mactime = tsf;
} }
/*
* Local TSF might have moved higher than our beacon timers,
* in that case we have to update them to continue sending
* beacons. This also takes care of synchronizing beacon sending
* times with other stations.
*/
if (hw_tu >= sc->nexttbtt)
ath5k_beacon_update_timers(sc, bc_tstamp);
} }
} }
...@@ -1885,7 +1916,7 @@ ath5k_tasklet_rx(unsigned long data) ...@@ -1885,7 +1916,7 @@ ath5k_tasklet_rx(unsigned long data)
/* check beacons in IBSS mode */ /* check beacons in IBSS mode */
if (sc->opmode == IEEE80211_IF_TYPE_IBSS) if (sc->opmode == IEEE80211_IF_TYPE_IBSS)
ath5k_check_ibss_hw_merge(sc, skb); ath5k_check_ibss_tsf(sc, skb, &rxs);
__ieee80211_rx(sc->hw, skb, &rxs); __ieee80211_rx(sc->hw, skb, &rxs);
sc->led_rxrate = rs.rs_rate; sc->led_rxrate = rs.rs_rate;
...@@ -2118,7 +2149,7 @@ ath5k_beacon_send(struct ath5k_softc *sc) ...@@ -2118,7 +2149,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
* beacon timer registers. * beacon timer registers.
* *
* This is called in a variety of situations, e.g. when a beacon is received, * This is called in a variety of situations, e.g. when a beacon is received,
* when a HW merge has been detected, but also when an new IBSS is created or * when a TSF update has been detected, but also when an new IBSS is created or
* when we otherwise know we have to update the timers, but we keep it in this * when we otherwise know we have to update the timers, but we keep it in this
* function to have it all together in one place. * function to have it all together in one place.
*/ */
...@@ -2218,7 +2249,7 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf) ...@@ -2218,7 +2249,7 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
* another AP to associate with. * another AP to associate with.
* *
* In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
* interrupts to detect HW merges only. * interrupts to detect TSF updates only.
* *
* AP mode is missing. * AP mode is missing.
*/ */
...@@ -2238,7 +2269,7 @@ ath5k_beacon_config(struct ath5k_softc *sc) ...@@ -2238,7 +2269,7 @@ ath5k_beacon_config(struct ath5k_softc *sc)
* hardware send the beacons automatically. We have to load it * hardware send the beacons automatically. We have to load it
* only once here. * only once here.
* We use the SWBA interrupt only to keep track of the beacon * We use the SWBA interrupt only to keep track of the beacon
* timers in order to detect HW merges (automatic TSF updates). * timers in order to detect automatic TSF updates.
*/ */
ath5k_beaconq_config(sc); ath5k_beaconq_config(sc);
...@@ -2451,8 +2482,8 @@ ath5k_intr(int irq, void *dev_id) ...@@ -2451,8 +2482,8 @@ ath5k_intr(int irq, void *dev_id)
* *
* In IBSS mode we use this interrupt just to * In IBSS mode we use this interrupt just to
* keep track of the next TBTT (target beacon * keep track of the next TBTT (target beacon
* transmission time) in order to detect hardware * transmission time) in order to detect wether
* merges (TSF updates). * automatic TSF updates happened.
*/ */
if (sc->opmode == IEEE80211_IF_TYPE_IBSS) { if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
/* XXX: only if VEOL suppported */ /* XXX: only if VEOL suppported */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment