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;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) {
/*
- * Received an IBSS beacon with the same BSSID. Hardware might
- * have updated the TSF, check if we need to update timers.
+ * Received an IBSS beacon with the same BSSID. Hardware *must*
+ * 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));
- if (hw_tu >= sc->nexttbtt) {
- ath5k_beacon_update_timers(sc,
- le64_to_cpu(mgmt->u.beacon.timestamp));
+ tsf = ath5k_hw_get_tsf64(sc->ah);
+ bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+ hw_tu = TSF_TO_TU(tsf);
+
+ ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ "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,
- "detected HW merge from received beacon\n");
+ "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);
}
}
/* check beacons in IBSS mode */
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);
sc->led_rxrate = rs.rs_rate;
* beacon timer registers.
*
* 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
* function to have it all together in one place.
*/
* another AP to associate with.
*
* 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.
*/
* hardware send the beacons automatically. We have to load it
* only once here.
* 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);
*
* In IBSS mode we use this interrupt just to
* keep track of the next TBTT (target beacon
- * transmission time) in order to detect hardware
- * merges (TSF updates).
+ * transmission time) in order to detect wether
+ * automatic TSF updates happened.
*/
if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
/* XXX: only if VEOL suppported */