2012-03-31 22:31:32 +04:00
/*
* Copyright 2011 - 2012 , Pavel Zubarev < pavel . zubarev @ gmail . com >
* Copyright 2011 - 2012 , Marco Porsch < marco . porsch @ s2005 . tu - chemnitz . de >
* Copyright 2011 - 2012 , cozybit Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include "ieee80211_i.h"
# include "mesh.h"
# include "driver-ops.h"
2016-12-08 04:15:51 +03:00
/* This is not in the standard. It represents a tolerable tsf drift below
2012-03-31 22:31:32 +04:00
* which we do no TSF adjustment .
*/
2012-04-13 01:32:22 +04:00
# define TOFFSET_MINIMUM_ADJUSTMENT 10
2012-04-13 01:32:23 +04:00
/* This is not in the standard. It is a margin added to the
* Toffset setpoint to mitigate TSF overcorrection
* introduced by TSF adjustment latency .
*/
# define TOFFSET_SET_MARGIN 20
2012-04-13 01:32:22 +04:00
/* This is not in the standard. It represents the maximum Toffset jump above
* which we ' ll invalidate the Toffset setpoint and choose a new setpoint . This
* could be , for instance , in case a neighbor is restarted and its TSF counter
* reset .
2012-04-13 01:32:23 +04:00
*/
2016-09-29 02:56:31 +03:00
# define TOFFSET_MAXIMUM_ADJUSTMENT 800 /* 0.8 ms */
2012-03-31 22:31:32 +04:00
struct sync_method {
u8 method ;
struct ieee80211_mesh_sync_ops ops ;
} ;
/**
* mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT
*
* @ ie : information elements of a management frame from the mesh peer
*/
static bool mesh_peer_tbtt_adjusting ( struct ieee802_11_elems * ie )
{
return ( ie - > mesh_config - > meshconf_cap &
2013-02-15 17:40:31 +04:00
IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING ) ! = 0 ;
2012-03-31 22:31:32 +04:00
}
2016-12-08 04:15:51 +03:00
void mesh_sync_adjust_tsf ( struct ieee80211_sub_if_data * sdata )
2012-03-31 22:31:32 +04:00
{
struct ieee80211_local * local = sdata - > local ;
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
/* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */
u64 beacon_int_fraction = sdata - > vif . bss_conf . beacon_int * 1024 / 2500 ;
u64 tsf ;
u64 tsfdelta ;
spin_lock_bh ( & ifmsh - > sync_offset_lock ) ;
if ( ifmsh - > sync_offset_clockdrift_max < beacon_int_fraction ) {
2016-12-08 04:15:51 +03:00
msync_dbg ( sdata , " TSF : max clockdrift=%lld; adjusting \n " ,
2012-06-22 13:29:50 +04:00
( long long ) ifmsh - > sync_offset_clockdrift_max ) ;
2012-03-31 22:31:32 +04:00
tsfdelta = - ifmsh - > sync_offset_clockdrift_max ;
ifmsh - > sync_offset_clockdrift_max = 0 ;
} else {
2016-12-08 04:15:51 +03:00
msync_dbg ( sdata , " TSF : max clockdrift=%lld; adjusting by %llu \n " ,
2012-06-22 13:29:50 +04:00
( long long ) ifmsh - > sync_offset_clockdrift_max ,
( unsigned long long ) beacon_int_fraction ) ;
2012-03-31 22:31:32 +04:00
tsfdelta = - beacon_int_fraction ;
ifmsh - > sync_offset_clockdrift_max - = beacon_int_fraction ;
}
2012-10-06 04:57:39 +04:00
spin_unlock_bh ( & ifmsh - > sync_offset_lock ) ;
2012-03-31 22:31:32 +04:00
2016-09-29 02:56:28 +03:00
if ( local - > ops - > offset_tsf ) {
drv_offset_tsf ( local , sdata , tsfdelta ) ;
} else {
tsf = drv_get_tsf ( local , sdata ) ;
if ( tsf ! = - 1ULL )
drv_set_tsf ( local , sdata , tsf + tsfdelta ) ;
}
2012-03-31 22:31:32 +04:00
}
static void mesh_sync_offset_rx_bcn_presp ( struct ieee80211_sub_if_data * sdata ,
u16 stype ,
struct ieee80211_mgmt * mgmt ,
struct ieee802_11_elems * elems ,
struct ieee80211_rx_status * rx_status )
{
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
struct ieee80211_local * local = sdata - > local ;
struct sta_info * sta ;
u64 t_t , t_r ;
WARN_ON ( ifmsh - > mesh_sp_id ! = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET ) ;
/* standard mentions only beacons */
if ( stype ! = IEEE80211_STYPE_BEACON )
return ;
2013-11-19 02:25:28 +04:00
/*
* Get time when timestamp field was received . If we don ' t
* have rx timestamps , then use current tsf as an approximation .
* drv_get_tsf ( ) must be called before entering the rcu - read
* section .
*/
if ( ieee80211_have_rx_timestamp ( rx_status ) )
t_r = ieee80211_calculate_rx_timestamp ( local , rx_status ,
24 + 12 +
elems - > total_len +
FCS_LEN ,
24 ) ;
else
t_r = drv_get_tsf ( local , sdata ) ;
2012-03-31 22:31:32 +04:00
rcu_read_lock ( ) ;
sta = sta_info_get ( sdata , mgmt - > sa ) ;
if ( ! sta )
goto no_sync ;
/* check offset sync conditions (13.13.2.2.1)
*
* TODO also sync to
* dot11MeshNbrOffsetMaxNeighbor non - peer non - MBSS neighbors
*/
if ( elems - > mesh_config & & mesh_peer_tbtt_adjusting ( elems ) ) {
2013-02-15 17:40:31 +04:00
msync_dbg ( sdata , " STA %pM : is adjusting TBTT \n " ,
sta - > sta . addr ) ;
2012-03-31 22:31:32 +04:00
goto no_sync ;
}
/* Timing offset calculation (see 13.13.2.2.2) */
t_t = le64_to_cpu ( mgmt - > u . beacon . timestamp ) ;
2015-06-17 11:31:00 +03:00
sta - > mesh - > t_offset = t_t - t_r ;
2012-03-31 22:31:32 +04:00
if ( test_sta_flag ( sta , WLAN_STA_TOFFSET_KNOWN ) ) {
2015-06-17 11:31:00 +03:00
s64 t_clockdrift = sta - > mesh - > t_offset_setpoint - sta - > mesh - > t_offset ;
2012-06-22 13:29:50 +04:00
msync_dbg ( sdata ,
2015-06-17 11:31:00 +03:00
" STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld \n " ,
sta - > sta . addr , ( long long ) sta - > mesh - > t_offset ,
( long long ) sta - > mesh - > t_offset_setpoint ,
2012-03-31 22:31:32 +04:00
( long long ) t_clockdrift ) ;
2012-04-13 01:32:22 +04:00
if ( t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT | |
2013-02-15 17:40:31 +04:00
t_clockdrift < - TOFFSET_MAXIMUM_ADJUSTMENT ) {
2012-06-22 13:29:50 +04:00
msync_dbg ( sdata ,
" STA %pM : t_clockdrift=%lld too large, setpoint reset \n " ,
2012-04-13 01:32:22 +04:00
sta - > sta . addr ,
( long long ) t_clockdrift ) ;
clear_sta_flag ( sta , WLAN_STA_TOFFSET_KNOWN ) ;
goto no_sync ;
}
2012-03-31 22:31:32 +04:00
spin_lock_bh ( & ifmsh - > sync_offset_lock ) ;
2013-02-15 17:40:31 +04:00
if ( t_clockdrift > ifmsh - > sync_offset_clockdrift_max )
ifmsh - > sync_offset_clockdrift_max = t_clockdrift ;
2012-03-31 22:31:32 +04:00
spin_unlock_bh ( & ifmsh - > sync_offset_lock ) ;
} else {
2015-06-17 11:31:00 +03:00
sta - > mesh - > t_offset_setpoint = sta - > mesh - > t_offset - TOFFSET_SET_MARGIN ;
2012-03-31 22:31:32 +04:00
set_sta_flag ( sta , WLAN_STA_TOFFSET_KNOWN ) ;
2012-06-22 13:29:50 +04:00
msync_dbg ( sdata ,
2015-06-17 11:31:00 +03:00
" STA %pM : offset was invalid, t_offset=%lld \n " ,
2012-03-31 22:31:32 +04:00
sta - > sta . addr ,
2015-06-17 11:31:00 +03:00
( long long ) sta - > mesh - > t_offset ) ;
2012-03-31 22:31:32 +04:00
}
no_sync :
rcu_read_unlock ( ) ;
}
2016-12-08 04:15:51 +03:00
static void mesh_sync_offset_adjust_tsf ( struct ieee80211_sub_if_data * sdata ,
2013-12-16 01:14:16 +04:00
struct beacon_data * beacon )
2012-03-31 22:31:32 +04:00
{
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2013-02-15 17:40:31 +04:00
WARN_ON ( ifmsh - > mesh_sp_id ! = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET ) ;
2014-04-29 19:55:26 +04:00
WARN_ON ( ! rcu_read_lock_held ( ) ) ;
2012-03-31 22:31:32 +04:00
spin_lock_bh ( & ifmsh - > sync_offset_lock ) ;
2013-02-15 17:40:31 +04:00
if ( ifmsh - > sync_offset_clockdrift_max > TOFFSET_MINIMUM_ADJUSTMENT ) {
2012-03-31 22:31:32 +04:00
/* Since ajusting the tsf here would
* require a possibly blocking call
* to the driver tsf setter , we punt
* the tsf adjustment to the mesh tasklet
*/
2012-06-22 13:29:50 +04:00
msync_dbg ( sdata ,
2016-12-08 04:15:51 +03:00
" TSF : kicking off TSF adjustment with clockdrift_max=%lld \n " ,
2012-06-22 13:29:50 +04:00
ifmsh - > sync_offset_clockdrift_max ) ;
2013-02-15 17:40:31 +04:00
set_bit ( MESH_WORK_DRIFT_ADJUST , & ifmsh - > wrkq_flags ) ;
2012-03-31 22:31:32 +04:00
} else {
2012-06-22 13:29:50 +04:00
msync_dbg ( sdata ,
2016-12-08 04:15:51 +03:00
" TSF : max clockdrift=%lld; too small to adjust \n " ,
2012-06-22 13:29:50 +04:00
( long long ) ifmsh - > sync_offset_clockdrift_max ) ;
2012-03-31 22:31:32 +04:00
ifmsh - > sync_offset_clockdrift_max = 0 ;
}
spin_unlock_bh ( & ifmsh - > sync_offset_lock ) ;
}
2012-09-30 19:07:19 +04:00
static const struct sync_method sync_methods [ ] = {
2012-03-31 22:31:32 +04:00
{
. method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET ,
. ops = {
. rx_bcn_presp = & mesh_sync_offset_rx_bcn_presp ,
2016-12-08 04:15:51 +03:00
. adjust_tsf = & mesh_sync_offset_adjust_tsf ,
2012-03-31 22:31:32 +04:00
}
} ,
} ;
2012-09-30 19:07:19 +04:00
const struct ieee80211_mesh_sync_ops * ieee80211_mesh_sync_ops_get ( u8 method )
2012-03-31 22:31:32 +04:00
{
2013-02-15 17:40:31 +04:00
int i ;
2012-03-31 22:31:32 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( sync_methods ) ; + + i ) {
2013-02-15 17:40:31 +04:00
if ( sync_methods [ i ] . method = = method )
return & sync_methods [ i ] . ops ;
2012-03-31 22:31:32 +04:00
}
2013-02-15 17:40:31 +04:00
return NULL ;
2012-03-31 22:31:32 +04:00
}