summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath5k/base.c79
-rw-r--r--drivers/net/wireless/ath5k/base.h1
2 files changed, 67 insertions, 13 deletions
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c
index c49061c51688..eb98284cba92 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -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) {
 				/*
diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h
index 20c946926090..8287ae787f12 100644
--- a/drivers/net/wireless/ath5k/base.h
+++ b/drivers/net/wireless/ath5k/base.h
@@ -166,6 +166,7 @@ struct ath5k_softc {
 				bmisscount,	/* missed beacon transmits */
 				bintval,	/* beacon interval in TU */
 				bsent;
+	unsigned int		nexttbtt;	/* next beacon time in TU */
 
 	struct timer_list	calib_tim;	/* calibration timer */
 };