]>
Commit | Line | Data |
---|---|---|
dbf498fb JC |
1 | /* |
2 | * Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com> | |
3 | * Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de> | |
4 | * Copyright 2011-2012, cozybit Inc. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include "ieee80211_i.h" | |
12 | #include "mesh.h" | |
13 | #include "driver-ops.h" | |
14 | ||
15 | #ifdef CONFIG_MAC80211_VERBOSE_MESH_SYNC_DEBUG | |
16 | #define msync_dbg(fmt, args...) \ | |
f0d23208 | 17 | pr_debug("Mesh sync (%s): " fmt "\n", sdata->name, ##args) |
dbf498fb JC |
18 | #else |
19 | #define msync_dbg(fmt, args...) do { (void)(0); } while (0) | |
20 | #endif | |
21 | ||
22 | /* This is not in the standard. It represents a tolerable tbtt drift below | |
23 | * which we do no TSF adjustment. | |
24 | */ | |
a802a6eb JC |
25 | #define TOFFSET_MINIMUM_ADJUSTMENT 10 |
26 | ||
ec14bcd2 JC |
27 | /* This is not in the standard. It is a margin added to the |
28 | * Toffset setpoint to mitigate TSF overcorrection | |
29 | * introduced by TSF adjustment latency. | |
30 | */ | |
31 | #define TOFFSET_SET_MARGIN 20 | |
32 | ||
a802a6eb JC |
33 | /* This is not in the standard. It represents the maximum Toffset jump above |
34 | * which we'll invalidate the Toffset setpoint and choose a new setpoint. This | |
35 | * could be, for instance, in case a neighbor is restarted and its TSF counter | |
36 | * reset. | |
ec14bcd2 | 37 | */ |
a802a6eb | 38 | #define TOFFSET_MAXIMUM_ADJUSTMENT 30000 /* 30 ms */ |
dbf498fb JC |
39 | |
40 | struct sync_method { | |
41 | u8 method; | |
42 | struct ieee80211_mesh_sync_ops ops; | |
43 | }; | |
44 | ||
45 | /** | |
46 | * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT | |
47 | * | |
48 | * @ie: information elements of a management frame from the mesh peer | |
49 | */ | |
50 | static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie) | |
51 | { | |
52 | return (ie->mesh_config->meshconf_cap & | |
53 | MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; | |
54 | } | |
55 | ||
56 | void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) | |
57 | { | |
58 | struct ieee80211_local *local = sdata->local; | |
59 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | |
60 | /* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */ | |
61 | u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500; | |
62 | u64 tsf; | |
63 | u64 tsfdelta; | |
64 | ||
65 | spin_lock_bh(&ifmsh->sync_offset_lock); | |
66 | ||
67 | if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) { | |
68 | msync_dbg("TBTT : max clockdrift=%lld; adjusting", | |
69 | (long long) ifmsh->sync_offset_clockdrift_max); | |
70 | tsfdelta = -ifmsh->sync_offset_clockdrift_max; | |
71 | ifmsh->sync_offset_clockdrift_max = 0; | |
72 | } else { | |
73 | msync_dbg("TBTT : max clockdrift=%lld; adjusting by %llu", | |
74 | (long long) ifmsh->sync_offset_clockdrift_max, | |
75 | (unsigned long long) beacon_int_fraction); | |
76 | tsfdelta = -beacon_int_fraction; | |
77 | ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction; | |
78 | } | |
79 | ||
80 | tsf = drv_get_tsf(local, sdata); | |
81 | if (tsf != -1ULL) | |
82 | drv_set_tsf(local, sdata, tsf + tsfdelta); | |
83 | spin_unlock_bh(&ifmsh->sync_offset_lock); | |
84 | } | |
85 | ||
86 | static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, | |
87 | u16 stype, | |
88 | struct ieee80211_mgmt *mgmt, | |
89 | struct ieee802_11_elems *elems, | |
90 | struct ieee80211_rx_status *rx_status) | |
91 | { | |
92 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | |
93 | struct ieee80211_local *local = sdata->local; | |
94 | struct sta_info *sta; | |
95 | u64 t_t, t_r; | |
96 | ||
97 | WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); | |
98 | ||
99 | /* standard mentions only beacons */ | |
100 | if (stype != IEEE80211_STYPE_BEACON) | |
101 | return; | |
102 | ||
103 | /* The current tsf is a first approximation for the timestamp | |
104 | * for the received beacon. Further down we try to get a | |
105 | * better value from the rx_status->mactime field if | |
106 | * available. Also we have to call drv_get_tsf() before | |
107 | * entering the rcu-read section.*/ | |
108 | t_r = drv_get_tsf(local, sdata); | |
109 | ||
110 | rcu_read_lock(); | |
111 | sta = sta_info_get(sdata, mgmt->sa); | |
112 | if (!sta) | |
113 | goto no_sync; | |
114 | ||
115 | /* check offset sync conditions (13.13.2.2.1) | |
116 | * | |
117 | * TODO also sync to | |
118 | * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors | |
119 | */ | |
120 | ||
121 | if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { | |
122 | clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); | |
123 | msync_dbg("STA %pM : is adjusting TBTT", sta->sta.addr); | |
124 | goto no_sync; | |
125 | } | |
126 | ||
127 | if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) { | |
128 | /* | |
129 | * The mactime is defined as the time the first data symbol | |
130 | * of the frame hits the PHY, and the timestamp of the beacon | |
131 | * is defined as "the time that the data symbol containing the | |
132 | * first bit of the timestamp is transmitted to the PHY plus | |
133 | * the transmitting STA's delays through its local PHY from the | |
134 | * MAC-PHY interface to its interface with the WM" (802.11 | |
135 | * 11.1.2) | |
136 | * | |
137 | * T_r, in 13.13.2.2.2, is just defined as "the frame reception | |
138 | * time" but we unless we interpret that time to be the same | |
139 | * time of the beacon timestamp, the offset calculation will be | |
140 | * off. Below we adjust t_r to be "the time at which the first | |
141 | * symbol of the timestamp element in the beacon is received". | |
142 | * This correction depends on the rate. | |
143 | * | |
144 | * Based on similar code in ibss.c | |
145 | */ | |
146 | int rate; | |
147 | ||
148 | if (rx_status->flag & RX_FLAG_HT) { | |
149 | /* TODO: | |
150 | * In principle there could be HT-beacons (Dual Beacon | |
151 | * HT Operation options), but for now ignore them and | |
152 | * just use the primary (i.e. non-HT) beacons for | |
153 | * synchronization. | |
154 | * */ | |
155 | goto no_sync; | |
156 | } else | |
157 | rate = local->hw.wiphy->bands[rx_status->band]-> | |
158 | bitrates[rx_status->rate_idx].bitrate; | |
159 | ||
160 | /* 24 bytes of header * 8 bits/byte * | |
161 | * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/ | |
162 | t_r = rx_status->mactime + (24 * 8 * 10 / rate); | |
163 | } | |
164 | ||
165 | /* Timing offset calculation (see 13.13.2.2.2) */ | |
166 | t_t = le64_to_cpu(mgmt->u.beacon.timestamp); | |
167 | sta->t_offset = t_t - t_r; | |
168 | ||
169 | if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { | |
170 | s64 t_clockdrift = sta->t_offset_setpoint | |
171 | - sta->t_offset; | |
a802a6eb | 172 | msync_dbg("STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld", |
dbf498fb JC |
173 | sta->sta.addr, |
174 | (long long) sta->t_offset, | |
175 | (long long) | |
176 | sta->t_offset_setpoint, | |
177 | (long long) t_clockdrift); | |
a802a6eb JC |
178 | |
179 | if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || | |
180 | t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { | |
181 | msync_dbg("STA %pM : t_clockdrift=%lld too large, setpoint reset", | |
182 | sta->sta.addr, | |
183 | (long long) t_clockdrift); | |
184 | clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); | |
185 | goto no_sync; | |
186 | } | |
187 | ||
dbf498fb JC |
188 | rcu_read_unlock(); |
189 | ||
190 | spin_lock_bh(&ifmsh->sync_offset_lock); | |
191 | if (t_clockdrift > | |
192 | ifmsh->sync_offset_clockdrift_max) | |
193 | ifmsh->sync_offset_clockdrift_max | |
194 | = t_clockdrift; | |
195 | spin_unlock_bh(&ifmsh->sync_offset_lock); | |
196 | ||
197 | } else { | |
6ac95b57 | 198 | sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN; |
dbf498fb JC |
199 | set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); |
200 | msync_dbg("STA %pM : offset was invalid, " | |
201 | " sta->t_offset=%lld", | |
202 | sta->sta.addr, | |
203 | (long long) sta->t_offset); | |
204 | rcu_read_unlock(); | |
205 | } | |
206 | return; | |
207 | ||
208 | no_sync: | |
209 | rcu_read_unlock(); | |
210 | } | |
211 | ||
212 | static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) | |
213 | { | |
214 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | |
215 | ||
216 | WARN_ON(ifmsh->mesh_sp_id | |
217 | != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); | |
218 | BUG_ON(!rcu_read_lock_held()); | |
219 | ||
220 | spin_lock_bh(&ifmsh->sync_offset_lock); | |
221 | ||
222 | if (ifmsh->sync_offset_clockdrift_max > | |
a802a6eb | 223 | TOFFSET_MINIMUM_ADJUSTMENT) { |
dbf498fb JC |
224 | /* Since ajusting the tsf here would |
225 | * require a possibly blocking call | |
226 | * to the driver tsf setter, we punt | |
227 | * the tsf adjustment to the mesh tasklet | |
228 | */ | |
229 | msync_dbg("TBTT : kicking off TBTT " | |
230 | "adjustment with " | |
231 | "clockdrift_max=%lld", | |
232 | ifmsh->sync_offset_clockdrift_max); | |
233 | set_bit(MESH_WORK_DRIFT_ADJUST, | |
234 | &ifmsh->wrkq_flags); | |
235 | } else { | |
236 | msync_dbg("TBTT : max clockdrift=%lld; " | |
237 | "too small to adjust", | |
238 | (long long) | |
239 | ifmsh->sync_offset_clockdrift_max); | |
240 | ifmsh->sync_offset_clockdrift_max = 0; | |
241 | } | |
242 | spin_unlock_bh(&ifmsh->sync_offset_lock); | |
243 | } | |
244 | ||
245 | static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata) | |
246 | { | |
247 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | |
248 | u8 offset; | |
249 | ||
250 | if (!ifmsh->ie || !ifmsh->ie_len) | |
251 | return NULL; | |
252 | ||
253 | offset = ieee80211_ie_split_vendor(ifmsh->ie, | |
254 | ifmsh->ie_len, 0); | |
255 | ||
256 | if (!offset) | |
257 | return NULL; | |
258 | ||
259 | return ifmsh->ie + offset + 2; | |
260 | } | |
261 | ||
262 | static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, | |
263 | u16 stype, | |
264 | struct ieee80211_mgmt *mgmt, | |
265 | struct ieee802_11_elems *elems, | |
266 | struct ieee80211_rx_status *rx_status) | |
267 | { | |
268 | const u8 *oui; | |
269 | ||
270 | WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); | |
271 | msync_dbg("called mesh_sync_vendor_rx_bcn_presp"); | |
272 | oui = mesh_get_vendor_oui(sdata); | |
273 | /* here you would implement the vendor offset tracking for this oui */ | |
274 | } | |
275 | ||
276 | static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata) | |
277 | { | |
278 | const u8 *oui; | |
279 | ||
280 | WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); | |
281 | msync_dbg("called mesh_sync_vendor_adjust_tbtt"); | |
282 | oui = mesh_get_vendor_oui(sdata); | |
283 | /* here you would implement the vendor tsf adjustment for this oui */ | |
284 | } | |
285 | ||
286 | /* global variable */ | |
287 | static struct sync_method sync_methods[] = { | |
288 | { | |
289 | .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, | |
290 | .ops = { | |
291 | .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp, | |
292 | .adjust_tbtt = &mesh_sync_offset_adjust_tbtt, | |
293 | } | |
294 | }, | |
295 | { | |
296 | .method = IEEE80211_SYNC_METHOD_VENDOR, | |
297 | .ops = { | |
298 | .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp, | |
299 | .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt, | |
300 | } | |
301 | }, | |
302 | }; | |
303 | ||
304 | struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) | |
305 | { | |
306 | struct ieee80211_mesh_sync_ops *ops = NULL; | |
307 | u8 i; | |
308 | ||
309 | for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) { | |
310 | if (sync_methods[i].method == method) { | |
311 | ops = &sync_methods[i].ops; | |
312 | break; | |
313 | } | |
314 | } | |
315 | return ops; | |
316 | } |