return 0;
}
+
+static void
+ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb)
+{
+ u32 hw_tu;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+ if ((mgmt->frame_control & IEEE80211_FCTL_FTYPE) ==
+ IEEE80211_FTYPE_MGMT &&
+ (mgmt->frame_control & IEEE80211_FCTL_STYPE) ==
+ IEEE80211_STYPE_BEACON &&
+ 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.
+ */
+ hw_tu = TSF_TO_TU(ath5k_hw_get_tsf64(sc->ah));
+ if (hw_tu >= sc->nexttbtt) {
+ ath5k_beacon_update_timers(sc,
+ mgmt->u.beacon.timestamp);
+ ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ "detected HW merge from received beacon\n");
+ }
+ }
+}
+
+
static void
ath5k_tasklet_rx(unsigned long data)
{
ath5k_debug_dump_skb(sc, skb, "RX ", 0);
+ /* check beacons in IBSS mode */
+ if (sc->opmode == IEEE80211_IF_TYPE_IBSS)
+ ath5k_check_ibss_hw_merge(sc, skb);
+
__ieee80211_rx(sc->hw, skb, &rxs);
sc->led_rxrate = ds->ds_rxstat.rs_rate;
ath5k_led_event(sc, ATH_LED_RX);
}
#undef FUDGE
+ sc->nexttbtt = nexttbtt;
+
intval |= AR5K_BEACON_ENA;
ath5k_hw_init_beacon(ah, nexttbtt, intval);
}
-/*
- * Configure the beacon timers and interrupts based on the operating mode
+/**
+ * ath5k_beacon_config - Configure the beacon queues and interrupts
+ *
+ * @sc: struct ath5k_softc pointer we are operating on
*
* When operating in station mode we want to receive a BMISS interrupt when we
* stop seeing beacons from the AP we've associated with so we can look for
* another AP to associate with.
*
- * In IBSS mode we need to configure the beacon timers and use a self-linked tx
- * descriptor if possible. If the hardware cannot deal with that we enable SWBA
- * interrupts to send the beacons from the interrupt handler.
+ * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
+ * interrupts to detect HW merges only.
+ *
+ * AP mode is missing.
*/
static void
ath5k_beacon_config(struct ath5k_softc *sc)
sc->imask |= AR5K_INT_BMISS;
} else if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
/*
- * 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. In that case we have to load it
+ * In IBSS mode we use a self-linked tx descriptor and let the
+ * 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).
*/
ath5k_beaconq_config(sc);
- if (!ath5k_hw_hasveol(ah))
- sc->imask |= AR5K_INT_SWBA;
- else
+ sc->imask |= AR5K_INT_SWBA;
+
+ if (ath5k_hw_hasveol(ah))
ath5k_beacon_send(sc);
}
/* TODO else AP */
* Handle beacon transmission directly; deferring
* this is too slow to meet timing constraints
* under load.
+ *
+ * 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).
*/
- ath5k_beacon_send(sc);
+ if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
+ /* XXX: only if VEOL suppported */
+ u64 tsf = ath5k_hw_get_tsf64(ah);
+ sc->nexttbtt += sc->bintval;
+ ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+ "SWBA nexttbtt: %x hw_tu: %x "
+ "TSF: %llx\n",
+ sc->nexttbtt,
+ TSF_TO_TU(tsf), tsf);
+ } else {
+ ath5k_beacon_send(sc);
+ }
}
if (status & AR5K_INT_RXEOL) {
/*