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