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