aboutsummaryrefslogtreecommitdiffstats
path: root/package/madwifi/patches/375-atim_tsf_update.patch
blob: d1313aa5645c3bbbf95701c96ee8ca1e9b42adc8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
--- a/ath/if_ath.c
+++ b/ath/if_ath.c
@@ -161,6 +161,7 @@ static void ath_beacon_send(struct ath_s
 static void ath_beacon_return(struct ath_softc *, struct ath_buf *);
 static void ath_beacon_free(struct ath_softc *);
 static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *);
+static void ath_hw_beacon_stop(struct ath_softc *sc);
 static int ath_desc_alloc(struct ath_softc *);
 static void ath_desc_free(struct ath_softc *);
 static void ath_desc_swap(struct ath_desc *);
@@ -2793,6 +2794,72 @@ ath_set_ack_bitrate(struct ath_softc *sc
 	return 1;
 }
 
+static void
+ath_hw_beacon_stop(struct ath_softc *sc)
+{
+	HAL_BEACON_TIMERS btimers;
+
+	btimers.bt_intval = 0;
+	btimers.bt_nexttbtt = 0;
+	btimers.bt_nextdba = 0xffffffff;
+	btimers.bt_nextswba = 0xffffffff;
+	btimers.bt_nextatim = 0;
+
+	ath_hal_setbeacontimers(sc->sc_ah, &btimers);
+}
+
+/* Fix up the ATIM window after TSF resync */
+static int
+ath_hw_check_atim(struct ath_softc *sc, int window, int intval)
+{
+#define AR5K_TIMER0_5210       0x802c  /* Next beacon time register */
+#define AR5K_TIMER0_5211       0x8028
+#define AR5K_TIMER3_5210       0x8038  /* End of ATIM window time register */
+#define AR5K_TIMER3_5211       0x8034
+	struct ath_hal *ah = sc->sc_ah;
+	int dev = sc->sc_ah->ah_macType;
+	unsigned int nbtt, atim;
+	int is_5210 = 0;
+
+	/*
+	 * check if the ATIM window is still correct:
+	 *   1.) usually ATIM should be NBTT + window
+	 *   2.) nbtt already updated
+	 *   3.) nbtt already updated and has wrapped around
+	 *   4.) atim has wrapped around
+	 */
+	switch(dev) {
+	case 5210:
+		nbtt = OS_REG_READ(ah, AR5K_TIMER0_5210);
+		atim = OS_REG_READ(ah, AR5K_TIMER3_5210);
+		is_5210 = 1;
+		break;
+	case 5211:
+	case 5212:
+		nbtt = OS_REG_READ(ah, AR5K_TIMER0_5211);
+		atim = OS_REG_READ(ah, AR5K_TIMER3_5211);
+		break;
+	/* NB: 5416+ doesn't do ATIM in hw */
+	case 5416:
+	default:
+		return 0;
+	}
+
+	if ((atim - nbtt != window) &&				/* 1.) */
+	    (nbtt - atim != intval - window) &&			/* 2.) */
+	    ((nbtt | 0x10000) - atim != intval - window) &&	/* 3.) */
+	    ((atim | 0x10000) - nbtt != window)) {		/* 4.) */
+		if (is_5210)
+			OS_REG_WRITE(ah, AR5K_TIMER3_5210, nbtt + window );
+		else
+			OS_REG_WRITE(ah, AR5K_TIMER3_5211, nbtt + window );
+		return atim - nbtt;
+	}
+
+	return 0;
+}
+
+
 /*
  * Reset the hardware w/o losing operational state.  This is
  * basically a more efficient way of doing ath_stop, ath_init,
@@ -5294,6 +5361,7 @@ ath_beacon_config(struct ath_softc *sc,
 	u_int64_t tsf, hw_tsf;
 	u_int32_t tsftu, hw_tsftu;
 	u_int32_t intval, nexttbtt = 0;
+	unsigned long flags;
 	int reset_tsf = 0;
 
 	if (vap == NULL)
@@ -5301,6 +5369,9 @@ ath_beacon_config(struct ath_softc *sc,
 
 	ni = vap->iv_bss;
 
+	/* TSF calculation is timing critical - we don't want to be interrupted here */
+	local_irq_save(flags);
+
 	hw_tsf = ath_hal_gettsf64(ah);
 	tsf = le64_to_cpu(ni->ni_tstamp.tsf);
 	hw_tsftu = hw_tsf >> 10;
@@ -5490,15 +5561,27 @@ ath_beacon_config(struct ath_softc *sc,
 				<= ath_hal_sw_beacon_response_time)
 			nexttbtt += intval;
 		sc->sc_nexttbtt = nexttbtt;
+
+		/* stop beacons before reconfiguring the timers to avoid race
+		 * conditions. ath_hal_beaconinit will start them again */
+		ath_hw_beacon_stop(sc);
+
 		ath_hal_beaconinit(ah, nexttbtt, intval);
 		if (intval & HAL_BEACON_RESET_TSF) {
 			sc->sc_last_tsf = 0;
 		}
 		sc->sc_bmisscount = 0;
 		ath_hal_intrset(ah, sc->sc_imask);
+
+		if ((sc->sc_opmode == HAL_M_IBSS) && ath_hw_check_atim(sc, 1, intval & HAL_BEACON_PERIOD)) {
+			DPRINTF(sc, ATH_DEBUG_BEACON,
+				"fixed atim window after beacon init\n");
+		}
 	}
 
 ath_beacon_config_debug:
+	local_irq_restore(flags);
+
 	/* We print all debug messages here, in order to preserve the
 	 * time critical aspect of this function */
 	DPRINTF(sc, ATH_DEBUG_BEACON,
@@ -6401,6 +6484,11 @@ ath_recv_mgmt(struct ieee80211vap * vap,
 			DPRINTF(sc, ATH_DEBUG_BEACON, 
 				"Updated beacon timers\n");
 		}
+		if ((sc->sc_opmode == HAL_M_IBSS) &&
+				IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid) &&
+				ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval)) {
+			DPRINTF(sc, ATH_DEBUG_ANY, "Fixed ATIM window after beacon recv\n");
+		}
 		/* NB: Fall Through */
 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
 		if (vap->iv_opmode == IEEE80211_M_IBSS &&