|
@@ -1640,6 +1640,34 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
|
|
|
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)
|
|
|
{
|
|
@@ -1767,6 +1795,10 @@ accept:
|
|
|
|
|
|
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);
|
|
@@ -2057,6 +2089,8 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
|
|
|
}
|
|
|
#undef FUDGE
|
|
|
|
|
|
+ sc->nexttbtt = nexttbtt;
|
|
|
+
|
|
|
intval |= AR5K_BEACON_ENA;
|
|
|
ath5k_hw_init_beacon(ah, nexttbtt, intval);
|
|
|
|
|
@@ -2084,16 +2118,19 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/*
|
|
|
- * 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)
|
|
@@ -2107,17 +2144,17 @@ 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 */
|
|
@@ -2320,8 +2357,24 @@ ath5k_intr(int irq, void *dev_id)
|
|
|
* 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) {
|
|
|
/*
|