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 pr_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 TOFFSET_MINIMUM_ADJUSTMENT 10 26 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 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. 37 */ 38 #define TOFFSET_MAXIMUM_ADJUSTMENT 30000 /* 30 ms */ 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; 172 msync_dbg("STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld", 173 sta->sta.addr, 174 (long long) sta->t_offset, 175 (long long) 176 sta->t_offset_setpoint, 177 (long long) t_clockdrift); 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 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 { 198 sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN; 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 > 223 TOFFSET_MINIMUM_ADJUSTMENT) { 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 } 317