aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJavier Cardona <javier@cozybit.com>2012-03-31 14:31:32 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-10 15:20:31 -0400
commitdbf498fbafa2c23139d5a990e94ed78bafbbea19 (patch)
tree723a2ed4575ab2178cbd210d85a19d5c968a95a1 /net
parent9bdd3a6bf8513a0a9eda031d15b36e4677854243 (diff)
mac80211: Implement mesh synchronization framework
This patch adds MBSS extensible synchronization framework (Sec. 13.13.2 of IEEE Std. 802.11-2012). The framework is implemented via an ops table which defines the following functions: rx_bcn_presp() - this is called every time a mesh beacon is received. adjust_tbtt() - this is called immediately before a beacon is about to be transmitted. The default neighbor offset synchronization defined in the standard is implemented. We also provide template functions for vendor specific methods. When neighbor offset synchronization is active (which is the default) mesh neighbors in the same MBSS will track timing offsets to each other and compensate clock drift. In our tests we observed that this mesh synchronization implementation successfully corrected drifts between stations of ~2PPM while introducing a jitter of ~20us. It is also possible to test this framework on mac80211_hwsim simulated phys to see how it behaves under different topologies, over poor links, etc. Signed-off-by: Marco Porsch <marco.porsch@s2005.tu-chemnitz.de> Signed-off-by: Pavel Zubarev <pavel.zubarev@gmail.com> Signed-off-by: Javier Cardona <javier@cozybit.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/Kconfig11
-rw-r--r--net/mac80211/Makefile3
-rw-r--r--net/mac80211/debugfs_sta.c5
-rw-r--r--net/mac80211/ieee80211_i.h23
-rw-r--r--net/mac80211/mesh.c21
-rw-r--r--net/mac80211/mesh.h19
-rw-r--r--net/mac80211/mesh_sync.c296
-rw-r--r--net/mac80211/sta_info.h5
-rw-r--r--net/mac80211/tx.c5
9 files changed, 380 insertions, 8 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 96ddb72760b9..8d249d705980 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -225,6 +225,17 @@ config MAC80211_VERBOSE_MHWMP_DEBUG
225 225
226 Do not select this option. 226 Do not select this option.
227 227
228config MAC80211_VERBOSE_MESH_SYNC_DEBUG
229 bool "Verbose mesh mesh synchronization debugging"
230 depends on MAC80211_DEBUG_MENU
231 depends on MAC80211_MESH
232 ---help---
233 Selecting this option causes mac80211 to print out very verbose mesh
234 synchronization debugging messages (when mac80211 is taking part in a
235 mesh network).
236
237 Do not select this option.
238
228config MAC80211_VERBOSE_TDLS_DEBUG 239config MAC80211_VERBOSE_TDLS_DEBUG
229 bool "Verbose TDLS debugging" 240 bool "Verbose TDLS debugging"
230 depends on MAC80211_DEBUG_MENU 241 depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 1be7a454aa77..3e9d931bba35 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -38,7 +38,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
38 mesh.o \ 38 mesh.o \
39 mesh_pathtbl.o \ 39 mesh_pathtbl.o \
40 mesh_plink.o \ 40 mesh_plink.o \
41 mesh_hwmp.o 41 mesh_hwmp.o \
42 mesh_sync.o
42 43
43mac80211-$(CONFIG_PM) += pm.o 44mac80211-$(CONFIG_PM) += pm.o
44 45
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 6d45804d09bc..ceeefd424103 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -63,7 +63,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
63 test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" 63 test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
64 64
65 int res = scnprintf(buf, sizeof(buf), 65 int res = scnprintf(buf, sizeof(buf),
66 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 66 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
67 TEST(AUTH), TEST(ASSOC), TEST(PS_STA), 67 TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
68 TEST(PS_DRIVER), TEST(AUTHORIZED), 68 TEST(PS_DRIVER), TEST(AUTHORIZED),
69 TEST(SHORT_PREAMBLE), 69 TEST(SHORT_PREAMBLE),
@@ -71,7 +71,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
71 TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), 71 TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
72 TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), 72 TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
73 TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), 73 TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
74 TEST(INSERTED), TEST(RATE_CONTROL)); 74 TEST(INSERTED), TEST(RATE_CONTROL),
75 TEST(TOFFSET_KNOWN));
75#undef TEST 76#undef TEST
76 return simple_read_from_buffer(userbuf, count, ppos, buf, res); 77 return simple_read_from_buffer(userbuf, count, ppos, buf, res);
77} 78}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8e7af7cee013..ea9623cbd969 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -554,6 +554,24 @@ struct ieee80211_if_ibss {
554 } state; 554 } state;
555}; 555};
556 556
557/**
558 * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
559 *
560 * these declarations define the interface, which enables
561 * vendor-specific mesh synchronization
562 *
563 */
564struct ieee802_11_elems;
565struct ieee80211_mesh_sync_ops {
566 void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata,
567 u16 stype,
568 struct ieee80211_mgmt *mgmt,
569 struct ieee802_11_elems *elems,
570 struct ieee80211_rx_status *rx_status);
571 void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata);
572 /* add other framework functions here */
573};
574
557struct ieee80211_if_mesh { 575struct ieee80211_if_mesh {
558 struct timer_list housekeeping_timer; 576 struct timer_list housekeeping_timer;
559 struct timer_list mesh_path_timer; 577 struct timer_list mesh_path_timer;
@@ -602,6 +620,11 @@ struct ieee80211_if_mesh {
602 IEEE80211_MESH_SEC_AUTHED = 0x1, 620 IEEE80211_MESH_SEC_AUTHED = 0x1,
603 IEEE80211_MESH_SEC_SECURED = 0x2, 621 IEEE80211_MESH_SEC_SECURED = 0x2,
604 } security; 622 } security;
623 /* Extensible Synchronization Framework */
624 struct ieee80211_mesh_sync_ops *sync_ops;
625 s64 sync_offset_clockdrift_max;
626 spinlock_t sync_offset_lock;
627 bool adjusting_tbtt;
605}; 628};
606 629
607#ifdef CONFIG_MAC80211_MESH 630#ifdef CONFIG_MAC80211_MESH
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index b05fa9ef866c..386dbca1eab3 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -13,9 +13,6 @@
13#include "ieee80211_i.h" 13#include "ieee80211_i.h"
14#include "mesh.h" 14#include "mesh.h"
15 15
16#define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01
17#define MESHCONF_CAPAB_FORWARDING 0x08
18
19#define TMR_RUNNING_HK 0 16#define TMR_RUNNING_HK 0
20#define TMR_RUNNING_MP 1 17#define TMR_RUNNING_MP 1
21#define TMR_RUNNING_MPR 2 18#define TMR_RUNNING_MPR 2
@@ -251,8 +248,10 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
251 /* Mesh capability */ 248 /* Mesh capability */
252 ifmsh->accepting_plinks = mesh_plink_availables(sdata); 249 ifmsh->accepting_plinks = mesh_plink_availables(sdata);
253 *pos = MESHCONF_CAPAB_FORWARDING; 250 *pos = MESHCONF_CAPAB_FORWARDING;
254 *pos++ |= ifmsh->accepting_plinks ? 251 *pos |= ifmsh->accepting_plinks ?
255 MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; 252 MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
253 *pos++ |= ifmsh->adjusting_tbtt ?
254 MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
256 *pos++ = 0x00; 255 *pos++ = 0x00;
257 256
258 return 0; 257 return 0;
@@ -573,8 +572,11 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
573 ieee80211_configure_filter(local); 572 ieee80211_configure_filter(local);
574 573
575 ifmsh->mesh_cc_id = 0; /* Disabled */ 574 ifmsh->mesh_cc_id = 0; /* Disabled */
576 ifmsh->mesh_sp_id = 0; /* Neighbor Offset */
577 ifmsh->mesh_auth_id = 0; /* Disabled */ 575 ifmsh->mesh_auth_id = 0; /* Disabled */
576 /* register sync ops from extensible synchronization framework */
577 ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
578 ifmsh->adjusting_tbtt = false;
579 ifmsh->sync_offset_clockdrift_max = 0;
578 set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); 580 set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
579 ieee80211_mesh_root_setup(ifmsh); 581 ieee80211_mesh_root_setup(ifmsh);
580 ieee80211_queue_work(&local->hw, &sdata->work); 582 ieee80211_queue_work(&local->hw, &sdata->work);
@@ -616,6 +618,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
616 struct ieee80211_rx_status *rx_status) 618 struct ieee80211_rx_status *rx_status)
617{ 619{
618 struct ieee80211_local *local = sdata->local; 620 struct ieee80211_local *local = sdata->local;
621 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
619 struct ieee802_11_elems elems; 622 struct ieee802_11_elems elems;
620 struct ieee80211_channel *channel; 623 struct ieee80211_channel *channel;
621 u32 supp_rates = 0; 624 u32 supp_rates = 0;
@@ -654,6 +657,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
654 supp_rates = ieee80211_sta_get_rates(local, &elems, band); 657 supp_rates = ieee80211_sta_get_rates(local, &elems, band);
655 mesh_neighbour_update(mgmt->sa, supp_rates, sdata, &elems); 658 mesh_neighbour_update(mgmt->sa, supp_rates, sdata, &elems);
656 } 659 }
660
661 if (ifmsh->sync_ops)
662 ifmsh->sync_ops->rx_bcn_presp(sdata,
663 stype, mgmt, &elems, rx_status);
657} 664}
658 665
659static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, 666static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
@@ -721,6 +728,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
721 728
722 if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags)) 729 if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags))
723 ieee80211_mesh_rootpath(sdata); 730 ieee80211_mesh_rootpath(sdata);
731
732 if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
733 mesh_sync_adjust_tbtt(sdata);
724} 734}
725 735
726void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) 736void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
@@ -761,4 +771,5 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
761 (unsigned long) sdata); 771 (unsigned long) sdata);
762 INIT_LIST_HEAD(&ifmsh->preq_queue.list); 772 INIT_LIST_HEAD(&ifmsh->preq_queue.list);
763 spin_lock_init(&ifmsh->mesh_preq_queue_lock); 773 spin_lock_init(&ifmsh->mesh_preq_queue_lock);
774 spin_lock_init(&ifmsh->sync_offset_lock);
764} 775}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 3e52439ed112..fa7d9704c175 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -19,6 +19,20 @@
19/* Data structures */ 19/* Data structures */
20 20
21/** 21/**
22 * enum mesh_config_capab_flags - mesh config IE capability flags
23 *
24 * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish
25 * additional mesh peerings with other mesh STAs
26 * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
27 * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing
28 */
29enum mesh_config_capab_flags {
30 MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
31 MESHCONF_CAPAB_FORWARDING = BIT(3),
32 MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
33};
34
35/**
22 * enum mesh_path_flags - mac80211 mesh path flags 36 * enum mesh_path_flags - mac80211 mesh path flags
23 * 37 *
24 * 38 *
@@ -56,12 +70,15 @@ enum mesh_path_flags {
56 * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to 70 * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to
57 * grow 71 * grow
58 * @MESH_WORK_ROOT: the mesh root station needs to send a frame 72 * @MESH_WORK_ROOT: the mesh root station needs to send a frame
73 * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
74 * mesh nodes
59 */ 75 */
60enum mesh_deferred_task_flags { 76enum mesh_deferred_task_flags {
61 MESH_WORK_HOUSEKEEPING, 77 MESH_WORK_HOUSEKEEPING,
62 MESH_WORK_GROW_MPATH_TABLE, 78 MESH_WORK_GROW_MPATH_TABLE,
63 MESH_WORK_GROW_MPP_TABLE, 79 MESH_WORK_GROW_MPP_TABLE,
64 MESH_WORK_ROOT, 80 MESH_WORK_ROOT,
81 MESH_WORK_DRIFT_ADJUST,
65}; 82};
66 83
67/** 84/**
@@ -234,6 +251,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
234void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); 251void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
235void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); 252void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
236void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); 253void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
254struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
237 255
238/* Mesh paths */ 256/* Mesh paths */
239int mesh_nexthop_lookup(struct sk_buff *skb, 257int mesh_nexthop_lookup(struct sk_buff *skb,
@@ -327,6 +345,7 @@ void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
327void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata); 345void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
328void mesh_plink_quiesce(struct sta_info *sta); 346void mesh_plink_quiesce(struct sta_info *sta);
329void mesh_plink_restart(struct sta_info *sta); 347void mesh_plink_restart(struct sta_info *sta);
348void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata);
330#else 349#else
331#define mesh_allocated 0 350#define mesh_allocated 0
332static inline void 351static inline void
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
new file mode 100644
index 000000000000..f78b0139856f
--- /dev/null
+++ b/net/mac80211/mesh_sync.c
@@ -0,0 +1,296 @@
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
27struct 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 */
37static 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
43void 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
73static 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
188no_sync:
189 rcu_read_unlock();
190}
191
192static 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
225static 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
242static 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
256static 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 */
267static 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
284struct 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}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b1b4b1413c74..f75f5d9ac06d 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -55,6 +55,7 @@
55 * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame. 55 * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame.
56 * @WLAN_STA_INSERTED: This station is inserted into the hash table. 56 * @WLAN_STA_INSERTED: This station is inserted into the hash table.
57 * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. 57 * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station.
58 * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
58 */ 59 */
59enum ieee80211_sta_info_flags { 60enum ieee80211_sta_info_flags {
60 WLAN_STA_AUTH, 61 WLAN_STA_AUTH,
@@ -76,6 +77,7 @@ enum ieee80211_sta_info_flags {
76 WLAN_STA_4ADDR_EVENT, 77 WLAN_STA_4ADDR_EVENT,
77 WLAN_STA_INSERTED, 78 WLAN_STA_INSERTED,
78 WLAN_STA_RATE_CONTROL, 79 WLAN_STA_RATE_CONTROL,
80 WLAN_STA_TOFFSET_KNOWN,
79}; 81};
80 82
81#define STA_TID_NUM 16 83#define STA_TID_NUM 16
@@ -268,6 +270,7 @@ struct sta_ampdu_mlme {
268 * @plink_timeout: timeout of peer link 270 * @plink_timeout: timeout of peer link
269 * @plink_timer: peer link watch timer 271 * @plink_timer: peer link watch timer
270 * @plink_timer_was_running: used by suspend/resume to restore timers 272 * @plink_timer_was_running: used by suspend/resume to restore timers
273 * @t_offset: timing offset relative to this host
271 * @debugfs: debug filesystem info 274 * @debugfs: debug filesystem info
272 * @dead: set to true when sta is unlinked 275 * @dead: set to true when sta is unlinked
273 * @uploaded: set to true when sta is uploaded to the driver 276 * @uploaded: set to true when sta is uploaded to the driver
@@ -357,6 +360,8 @@ struct sta_info {
357 enum nl80211_plink_state plink_state; 360 enum nl80211_plink_state plink_state;
358 u32 plink_timeout; 361 u32 plink_timeout;
359 struct timer_list plink_timer; 362 struct timer_list plink_timer;
363 s64 t_offset;
364 s64 t_offset_setpoint;
360#endif 365#endif
361 366
362#ifdef CONFIG_MAC80211_DEBUGFS 367#ifdef CONFIG_MAC80211_DEBUGFS
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index e0b89780b472..daab5adeb93c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2373,6 +2373,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2373 IEEE80211_STYPE_BEACON); 2373 IEEE80211_STYPE_BEACON);
2374 } else if (ieee80211_vif_is_mesh(&sdata->vif)) { 2374 } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
2375 struct ieee80211_mgmt *mgmt; 2375 struct ieee80211_mgmt *mgmt;
2376 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2376 u8 *pos; 2377 u8 *pos;
2377 int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + 2378 int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
2378 sizeof(mgmt->u.beacon); 2379 sizeof(mgmt->u.beacon);
@@ -2382,6 +2383,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2382 goto out; 2383 goto out;
2383#endif 2384#endif
2384 2385
2386 if (ifmsh->sync_ops)
2387 ifmsh->sync_ops->adjust_tbtt(
2388 sdata);
2389
2385 skb = dev_alloc_skb(local->tx_headroom + 2390 skb = dev_alloc_skb(local->tx_headroom +
2386 hdr_len + 2391 hdr_len +
2387 2 + /* NULL SSID */ 2392 2 + /* NULL SSID */