mesh_sync.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com>
00003  * Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
00004  * Copyright 2011-2012, cozybit Inc.
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License version 2 as
00008  * published by the Free Software Foundation.
00009  */
00010 
00011 #include "ieee80211_i.h"
00012 #include "mesh.h"
00013 #include "driver-ops.h"
00014 
00015 #ifdef CONFIG_MAC80211_VERBOSE_MESH_SYNC_DEBUG
00016 #define msync_dbg(fmt, args...) \
00017         printk(KERN_DEBUG "Mesh sync (%s): " fmt "\n", sdata->name, ##args)
00018 #else
00019 #define msync_dbg(fmt, args...)   do { (void)(0); } while (0)
00020 #endif
00021 
00022 /* This is not in the standard.  It represents a tolerable tbtt drift below
00023  * which we do no TSF adjustment.
00024  */
00025 #define TOFFSET_MINIMUM_ADJUSTMENT 10
00026 
00027 /* This is not in the standard. It is a margin added to the
00028  * Toffset setpoint to mitigate TSF overcorrection
00029  * introduced by TSF adjustment latency.
00030  */
00031 #define TOFFSET_SET_MARGIN 20
00032 
00033 /* This is not in the standard.  It represents the maximum Toffset jump above
00034  * which we'll invalidate the Toffset setpoint and choose a new setpoint.  This
00035  * could be, for instance, in case a neighbor is restarted and its TSF counter
00036  * reset.
00037  */
00038 #define TOFFSET_MAXIMUM_ADJUSTMENT 30000                /* 30 ms */
00039 
00040 struct sync_method {
00041         u8 method;
00042         struct ieee80211_mesh_sync_ops ops;
00043 };
00044 
00050 static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
00051 {
00052         return (ie->mesh_config->meshconf_cap &
00053             MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
00054 }
00055 
00056 void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
00057 {
00058         struct ieee80211_local *local = sdata->local;
00059         struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
00060         /* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */
00061         u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500;
00062         u64 tsf;
00063         u64 tsfdelta;
00064 
00065         spin_lock_bh(&ifmsh->sync_offset_lock);
00066 
00067         if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) {
00068                 msync_dbg("TBTT : max clockdrift=%lld; adjusting",
00069                         (long long) ifmsh->sync_offset_clockdrift_max);
00070                 tsfdelta = -ifmsh->sync_offset_clockdrift_max;
00071                 ifmsh->sync_offset_clockdrift_max = 0;
00072         } else {
00073                 msync_dbg("TBTT : max clockdrift=%lld; adjusting by %llu",
00074                         (long long) ifmsh->sync_offset_clockdrift_max,
00075                         (unsigned long long) beacon_int_fraction);
00076                 tsfdelta = -beacon_int_fraction;
00077                 ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction;
00078         }
00079 
00080         tsf = drv_get_tsf(local, sdata);
00081         if (tsf != -1ULL)
00082                 drv_set_tsf(local, sdata, tsf + tsfdelta);
00083         spin_unlock_bh(&ifmsh->sync_offset_lock);
00084 }
00085 
00086 static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
00087                                    u16 stype,
00088                                    struct ieee80211_mgmt *mgmt,
00089                                    struct ieee802_11_elems *elems,
00090                                    struct ieee80211_rx_status *rx_status)
00091 {
00092         struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
00093         struct ieee80211_local *local = sdata->local;
00094         struct sta_info *sta;
00095         u64 t_t, t_r;
00096 
00097         WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
00098 
00099         /* standard mentions only beacons */
00100         if (stype != IEEE80211_STYPE_BEACON)
00101                 return;
00102 
00103         /* The current tsf is a first approximation for the timestamp
00104          * for the received beacon.  Further down we try to get a
00105          * better value from the rx_status->mactime field if
00106          * available. Also we have to call drv_get_tsf() before
00107          * entering the rcu-read section.*/
00108         t_r = drv_get_tsf(local, sdata);
00109 
00110         rcu_read_lock();
00111         sta = sta_info_get(sdata, mgmt->sa);
00112         if (!sta)
00113                 goto no_sync;
00114 
00115         /* check offset sync conditions (13.13.2.2.1)
00116          *
00117          * TODO also sync to
00118          * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors
00119          */
00120 
00121         if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) {
00122                 clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
00123                 msync_dbg("STA %pM : is adjusting TBTT", sta->sta.addr);
00124                 goto no_sync;
00125         }
00126 
00127         if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) {
00128                 /*
00129                  * The mactime is defined as the time the first data symbol
00130                  * of the frame hits the PHY, and the timestamp of the beacon
00131                  * is defined as "the time that the data symbol containing the
00132                  * first bit of the timestamp is transmitted to the PHY plus
00133                  * the transmitting STA's delays through its local PHY from the
00134                  * MAC-PHY interface to its interface with the WM" (802.11
00135                  * 11.1.2)
00136                  *
00137                  * T_r, in 13.13.2.2.2, is just defined as "the frame reception
00138                  * time" but we unless we interpret that time to be the same
00139                  * time of the beacon timestamp, the offset calculation will be
00140                  * off.  Below we adjust t_r to be "the time at which the first
00141                  * symbol of the timestamp element in the beacon is received".
00142                  * This correction depends on the rate.
00143                  *
00144                  * Based on similar code in ibss.c
00145                  */
00146                 int rate;
00147 
00148                 if (rx_status->flag & RX_FLAG_HT) {
00149                         /* TODO:
00150                          * In principle there could be HT-beacons (Dual Beacon
00151                          * HT Operation options), but for now ignore them and
00152                          * just use the primary (i.e. non-HT) beacons for
00153                          * synchronization.
00154                          * */
00155                         goto no_sync;
00156                 } else
00157                         rate = local->hw.wiphy->bands[rx_status->band]->
00158                                 bitrates[rx_status->rate_idx].bitrate;
00159 
00160                 /* 24 bytes of header * 8 bits/byte *
00161                  * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/
00162                 t_r = rx_status->mactime + (24 * 8 * 10 / rate);
00163         }
00164 
00165         /* Timing offset calculation (see 13.13.2.2.2) */
00166         t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
00167         sta->t_offset = t_t - t_r;
00168 
00169         if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
00170                 s64 t_clockdrift = sta->t_offset_setpoint
00171                                    - sta->t_offset;
00172                 msync_dbg("STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld",
00173                           sta->sta.addr,
00174                           (long long) sta->t_offset,
00175                           (long long)
00176                           sta->t_offset_setpoint,
00177                           (long long) t_clockdrift);
00178 
00179                 if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT ||
00180                         t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) {
00181                         msync_dbg("STA %pM : t_clockdrift=%lld too large, setpoint reset",
00182                                   sta->sta.addr,
00183                                   (long long) t_clockdrift);
00184                         clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
00185                         goto no_sync;
00186                 }
00187 
00188                 rcu_read_unlock();
00189 
00190                 spin_lock_bh(&ifmsh->sync_offset_lock);
00191                 if (t_clockdrift >
00192                     ifmsh->sync_offset_clockdrift_max)
00193                         ifmsh->sync_offset_clockdrift_max
00194                                 = t_clockdrift;
00195                 spin_unlock_bh(&ifmsh->sync_offset_lock);
00196 
00197         } else {
00198                 sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN;
00199                 set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
00200                 msync_dbg("STA %pM : offset was invalid, "
00201                           " sta->t_offset=%lld",
00202                           sta->sta.addr,
00203                           (long long) sta->t_offset);
00204                 rcu_read_unlock();
00205         }
00206         return;
00207 
00208 no_sync:
00209         rcu_read_unlock();
00210 }
00211 
00212 static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
00213 {
00214         struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
00215 
00216         WARN_ON(ifmsh->mesh_sp_id
00217                 != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
00218         BUG_ON(!rcu_read_lock_held());
00219 
00220         spin_lock_bh(&ifmsh->sync_offset_lock);
00221 
00222         if (ifmsh->sync_offset_clockdrift_max >
00223                 TOFFSET_MINIMUM_ADJUSTMENT) {
00224                 /* Since ajusting the tsf here would
00225                  * require a possibly blocking call
00226                  * to the driver tsf setter, we punt
00227                  * the tsf adjustment to the mesh tasklet
00228                  */
00229                 msync_dbg("TBTT : kicking off TBTT "
00230                           "adjustment with "
00231                           "clockdrift_max=%lld",
00232                   ifmsh->sync_offset_clockdrift_max);
00233                 set_bit(MESH_WORK_DRIFT_ADJUST,
00234                         &ifmsh->wrkq_flags);
00235         } else {
00236                 msync_dbg("TBTT : max clockdrift=%lld; "
00237                           "too small to adjust",
00238                           (long long)
00239                        ifmsh->sync_offset_clockdrift_max);
00240                 ifmsh->sync_offset_clockdrift_max = 0;
00241         }
00242         spin_unlock_bh(&ifmsh->sync_offset_lock);
00243 }
00244 
00245 static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata)
00246 {
00247         struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
00248         u8 offset;
00249 
00250         if (!ifmsh->ie || !ifmsh->ie_len)
00251                 return NULL;
00252 
00253         offset = ieee80211_ie_split_vendor(ifmsh->ie,
00254                                         ifmsh->ie_len, 0);
00255 
00256         if (!offset)
00257                 return NULL;
00258 
00259         return ifmsh->ie + offset + 2;
00260 }
00261 
00262 static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
00263                                    u16 stype,
00264                                    struct ieee80211_mgmt *mgmt,
00265                                    struct ieee802_11_elems *elems,
00266                                    struct ieee80211_rx_status *rx_status)
00267 {
00268         const u8 *oui;
00269 
00270         WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
00271         msync_dbg("called mesh_sync_vendor_rx_bcn_presp");
00272         oui = mesh_get_vendor_oui(sdata);
00273         /*  here you would implement the vendor offset tracking for this oui */
00274 }
00275 
00276 static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
00277 {
00278         const u8 *oui;
00279 
00280         WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
00281         msync_dbg("called mesh_sync_vendor_adjust_tbtt");
00282         oui = mesh_get_vendor_oui(sdata);
00283         /*  here you would implement the vendor tsf adjustment for this oui */
00284 }
00285 
00286 /* global variable */
00287 static struct sync_method sync_methods[] = {
00288         {
00289                 .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
00290                 .ops = {
00291                         .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp,
00292                         .adjust_tbtt = &mesh_sync_offset_adjust_tbtt,
00293                 }
00294         },
00295         {
00296                 .method = IEEE80211_SYNC_METHOD_VENDOR,
00297                 .ops = {
00298                         .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp,
00299                         .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt,
00300                 }
00301         },
00302 };
00303 
00304 struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
00305 {
00306         struct ieee80211_mesh_sync_ops *ops = NULL;
00307         u8 i;
00308 
00309         for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) {
00310                 if (sync_methods[i].method == method) {
00311                         ops = &sync_methods[i].ops;
00312                         break;
00313                 }
00314         }
00315         return ops;
00316 }


ros_rt_wmp
Author(s): Danilo Tardioli, dantard@unizar.es
autogenerated on Mon Oct 6 2014 08:27:10