18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com> 48c2ecf20Sopenharmony_ci * Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de> 58c2ecf20Sopenharmony_ci * Copyright 2011-2012, cozybit Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 98c2ecf20Sopenharmony_ci#include "mesh.h" 108c2ecf20Sopenharmony_ci#include "driver-ops.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* This is not in the standard. It represents a tolerable tsf drift below 138c2ecf20Sopenharmony_ci * which we do no TSF adjustment. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci#define TOFFSET_MINIMUM_ADJUSTMENT 10 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* This is not in the standard. It is a margin added to the 188c2ecf20Sopenharmony_ci * Toffset setpoint to mitigate TSF overcorrection 198c2ecf20Sopenharmony_ci * introduced by TSF adjustment latency. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci#define TOFFSET_SET_MARGIN 20 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* This is not in the standard. It represents the maximum Toffset jump above 248c2ecf20Sopenharmony_ci * which we'll invalidate the Toffset setpoint and choose a new setpoint. This 258c2ecf20Sopenharmony_ci * could be, for instance, in case a neighbor is restarted and its TSF counter 268c2ecf20Sopenharmony_ci * reset. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#define TOFFSET_MAXIMUM_ADJUSTMENT 800 /* 0.8 ms */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct sync_method { 318c2ecf20Sopenharmony_ci u8 method; 328c2ecf20Sopenharmony_ci struct ieee80211_mesh_sync_ops ops; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/** 368c2ecf20Sopenharmony_ci * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * @ie: information elements of a management frame from the mesh peer 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci return (ie->mesh_config->meshconf_cap & 438c2ecf20Sopenharmony_ci IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 498c2ecf20Sopenharmony_ci struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 508c2ecf20Sopenharmony_ci /* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */ 518c2ecf20Sopenharmony_ci u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500; 528c2ecf20Sopenharmony_ci u64 tsf; 538c2ecf20Sopenharmony_ci u64 tsfdelta; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci spin_lock_bh(&ifmsh->sync_offset_lock); 568c2ecf20Sopenharmony_ci if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) { 578c2ecf20Sopenharmony_ci msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting\n", 588c2ecf20Sopenharmony_ci (long long) ifmsh->sync_offset_clockdrift_max); 598c2ecf20Sopenharmony_ci tsfdelta = -ifmsh->sync_offset_clockdrift_max; 608c2ecf20Sopenharmony_ci ifmsh->sync_offset_clockdrift_max = 0; 618c2ecf20Sopenharmony_ci } else { 628c2ecf20Sopenharmony_ci msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting by %llu\n", 638c2ecf20Sopenharmony_ci (long long) ifmsh->sync_offset_clockdrift_max, 648c2ecf20Sopenharmony_ci (unsigned long long) beacon_int_fraction); 658c2ecf20Sopenharmony_ci tsfdelta = -beacon_int_fraction; 668c2ecf20Sopenharmony_ci ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci spin_unlock_bh(&ifmsh->sync_offset_lock); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (local->ops->offset_tsf) { 718c2ecf20Sopenharmony_ci drv_offset_tsf(local, sdata, tsfdelta); 728c2ecf20Sopenharmony_ci } else { 738c2ecf20Sopenharmony_ci tsf = drv_get_tsf(local, sdata); 748c2ecf20Sopenharmony_ci if (tsf != -1ULL) 758c2ecf20Sopenharmony_ci drv_set_tsf(local, sdata, tsf + tsfdelta); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, 808c2ecf20Sopenharmony_ci u16 stype, 818c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt, 828c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 838c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 868c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 878c2ecf20Sopenharmony_ci struct sta_info *sta; 888c2ecf20Sopenharmony_ci u64 t_t, t_r; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* standard mentions only beacons */ 938c2ecf20Sopenharmony_ci if (stype != IEEE80211_STYPE_BEACON) 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * Get time when timestamp field was received. If we don't 988c2ecf20Sopenharmony_ci * have rx timestamps, then use current tsf as an approximation. 998c2ecf20Sopenharmony_ci * drv_get_tsf() must be called before entering the rcu-read 1008c2ecf20Sopenharmony_ci * section. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci if (ieee80211_have_rx_timestamp(rx_status)) 1038c2ecf20Sopenharmony_ci t_r = ieee80211_calculate_rx_timestamp(local, rx_status, 1048c2ecf20Sopenharmony_ci 24 + 12 + 1058c2ecf20Sopenharmony_ci elems->total_len + 1068c2ecf20Sopenharmony_ci FCS_LEN, 1078c2ecf20Sopenharmony_ci 24); 1088c2ecf20Sopenharmony_ci else 1098c2ecf20Sopenharmony_ci t_r = drv_get_tsf(local, sdata); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci rcu_read_lock(); 1128c2ecf20Sopenharmony_ci sta = sta_info_get(sdata, mgmt->sa); 1138c2ecf20Sopenharmony_ci if (!sta) 1148c2ecf20Sopenharmony_ci goto no_sync; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* check offset sync conditions (13.13.2.2.1) 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * TODO also sync to 1198c2ecf20Sopenharmony_ci * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { 1238c2ecf20Sopenharmony_ci msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", 1248c2ecf20Sopenharmony_ci sta->sta.addr); 1258c2ecf20Sopenharmony_ci goto no_sync; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Timing offset calculation (see 13.13.2.2.2) */ 1298c2ecf20Sopenharmony_ci t_t = le64_to_cpu(mgmt->u.beacon.timestamp); 1308c2ecf20Sopenharmony_ci sta->mesh->t_offset = t_t - t_r; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { 1338c2ecf20Sopenharmony_ci s64 t_clockdrift = sta->mesh->t_offset_setpoint - sta->mesh->t_offset; 1348c2ecf20Sopenharmony_ci msync_dbg(sdata, 1358c2ecf20Sopenharmony_ci "STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld\n", 1368c2ecf20Sopenharmony_ci sta->sta.addr, (long long) sta->mesh->t_offset, 1378c2ecf20Sopenharmony_ci (long long) sta->mesh->t_offset_setpoint, 1388c2ecf20Sopenharmony_ci (long long) t_clockdrift); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || 1418c2ecf20Sopenharmony_ci t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { 1428c2ecf20Sopenharmony_ci msync_dbg(sdata, 1438c2ecf20Sopenharmony_ci "STA %pM : t_clockdrift=%lld too large, setpoint reset\n", 1448c2ecf20Sopenharmony_ci sta->sta.addr, 1458c2ecf20Sopenharmony_ci (long long) t_clockdrift); 1468c2ecf20Sopenharmony_ci clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); 1478c2ecf20Sopenharmony_ci goto no_sync; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spin_lock_bh(&ifmsh->sync_offset_lock); 1518c2ecf20Sopenharmony_ci if (t_clockdrift > ifmsh->sync_offset_clockdrift_max) 1528c2ecf20Sopenharmony_ci ifmsh->sync_offset_clockdrift_max = t_clockdrift; 1538c2ecf20Sopenharmony_ci spin_unlock_bh(&ifmsh->sync_offset_lock); 1548c2ecf20Sopenharmony_ci } else { 1558c2ecf20Sopenharmony_ci sta->mesh->t_offset_setpoint = sta->mesh->t_offset - TOFFSET_SET_MARGIN; 1568c2ecf20Sopenharmony_ci set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); 1578c2ecf20Sopenharmony_ci msync_dbg(sdata, 1588c2ecf20Sopenharmony_ci "STA %pM : offset was invalid, t_offset=%lld\n", 1598c2ecf20Sopenharmony_ci sta->sta.addr, 1608c2ecf20Sopenharmony_ci (long long) sta->mesh->t_offset); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cino_sync: 1648c2ecf20Sopenharmony_ci rcu_read_unlock(); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void mesh_sync_offset_adjust_tsf(struct ieee80211_sub_if_data *sdata, 1688c2ecf20Sopenharmony_ci struct beacon_data *beacon) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); 1738c2ecf20Sopenharmony_ci WARN_ON(!rcu_read_lock_held()); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci spin_lock_bh(&ifmsh->sync_offset_lock); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (ifmsh->sync_offset_clockdrift_max > TOFFSET_MINIMUM_ADJUSTMENT) { 1788c2ecf20Sopenharmony_ci /* Since ajusting the tsf here would 1798c2ecf20Sopenharmony_ci * require a possibly blocking call 1808c2ecf20Sopenharmony_ci * to the driver tsf setter, we punt 1818c2ecf20Sopenharmony_ci * the tsf adjustment to the mesh tasklet 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci msync_dbg(sdata, 1848c2ecf20Sopenharmony_ci "TSF : kicking off TSF adjustment with clockdrift_max=%lld\n", 1858c2ecf20Sopenharmony_ci ifmsh->sync_offset_clockdrift_max); 1868c2ecf20Sopenharmony_ci set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci msync_dbg(sdata, 1898c2ecf20Sopenharmony_ci "TSF : max clockdrift=%lld; too small to adjust\n", 1908c2ecf20Sopenharmony_ci (long long)ifmsh->sync_offset_clockdrift_max); 1918c2ecf20Sopenharmony_ci ifmsh->sync_offset_clockdrift_max = 0; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci spin_unlock_bh(&ifmsh->sync_offset_lock); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic const struct sync_method sync_methods[] = { 1978c2ecf20Sopenharmony_ci { 1988c2ecf20Sopenharmony_ci .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, 1998c2ecf20Sopenharmony_ci .ops = { 2008c2ecf20Sopenharmony_ci .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp, 2018c2ecf20Sopenharmony_ci .adjust_tsf = &mesh_sync_offset_adjust_tsf, 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciconst struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) { 2118c2ecf20Sopenharmony_ci if (sync_methods[i].method == method) 2128c2ecf20Sopenharmony_ci return &sync_methods[i].ops; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci return NULL; 2158c2ecf20Sopenharmony_ci} 216