madwifi: wds updates - refactor wds code - add interface for separating individual...
[openwrt/openwrt.git] / package / madwifi / patches / 371-wds_sta_separation.patch
1 --- a/net80211/ieee80211_input.c
2 +++ b/net80211/ieee80211_input.c
3 @@ -202,6 +202,7 @@
4 struct ieee80211com *ic = vap->iv_ic;
5 struct net_device *dev = vap->iv_dev;
6 struct ieee80211_node *ni_wds = NULL;
7 + struct net_device_stats *stats;
8 struct ieee80211_frame *wh;
9 struct ieee80211_key *key;
10 struct ether_header *eh;
11 @@ -562,11 +563,14 @@
12 if (ni_wds != NULL) {
13 ieee80211_unref_node(&ni);
14 ni = ieee80211_ref_node(ni_wds);
15 + } else if (!ni->ni_subif &&
16 + (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP)) {
17 + ieee80211_wds_addif(ni);
18 }
19 }
20
21 /* XXX: Useless node mgmt API; make better */
22 - if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds) {
23 + if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds && !ni->ni_subif) {
24 struct ieee80211_node_table *nt = &ic->ic_sta;
25 struct ieee80211_frame_addr4 *wh4;
26
27 @@ -698,8 +702,12 @@
28 if (! accept_data_frame(vap, ni, key, skb, eh))
29 goto out;
30
31 - vap->iv_devstats.rx_packets++;
32 - vap->iv_devstats.rx_bytes += skb->len;
33 + if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
34 + stats = &ni->ni_subif->iv_devstats;
35 + else
36 + stats = &vap->iv_devstats;
37 + stats->rx_packets++;
38 + stats->rx_bytes += skb->len;
39 IEEE80211_NODE_STAT(ni, rx_data);
40 IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
41 ic->ic_lastdata = jiffies;
42 @@ -1132,6 +1140,11 @@
43 dev = vap->iv_xrvap->iv_dev;
44 #endif
45
46 + /* if the node has a wds subif, move data frames there,
47 + * but keep EAP traffic on the master */
48 + if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
49 + dev = ni->ni_subif->iv_dev;
50 +
51 /* perform as a bridge within the vap */
52 /* XXX intra-vap bridging only */
53 if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
54 @@ -1157,6 +1170,7 @@
55 if (ni1 != NULL) {
56 if (ni1->ni_vap == vap &&
57 ieee80211_node_is_authorized(ni1) &&
58 + !ni->ni_subif &&
59 ni1 != vap->iv_bss) {
60 skb1 = skb;
61 skb = NULL;
62 --- a/net80211/ieee80211_ioctl.h
63 +++ b/net80211/ieee80211_ioctl.h
64 @@ -649,6 +649,7 @@
65 IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */
66 IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */
67 IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */
68 + IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */
69 };
70
71 #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
72 --- a/net80211/ieee80211_node.h
73 +++ b/net80211/ieee80211_node.h
74 @@ -92,11 +92,12 @@
75 * the ieee80211com structure.
76 */
77 struct ieee80211_node {
78 - struct ieee80211vap *ni_vap;
79 + struct ieee80211vap *ni_vap, *ni_subif;
80 struct ieee80211com *ni_ic;
81 struct ieee80211_node_table *ni_table;
82 TAILQ_ENTRY(ieee80211_node) ni_list;
83 LIST_ENTRY(ieee80211_node) ni_hash;
84 + struct work_struct ni_destroy; /* task for destroying a subif */
85 atomic_t ni_refcnt;
86 u_int ni_scangen; /* gen# for timeout scan */
87 u_int8_t ni_authmode; /* authentication algorithm */
88 @@ -430,5 +431,6 @@
89 void ieee80211_node_leave(struct ieee80211_node *);
90 u_int8_t ieee80211_getrssi(struct ieee80211com *);
91 int32_t ieee80211_get_node_count(struct ieee80211com *);
92 +void ieee80211_wds_addif(struct ieee80211_node *ni);
93 #endif /* _NET80211_IEEE80211_NODE_H_ */
94
95 --- a/net80211/ieee80211_var.h
96 +++ b/net80211/ieee80211_var.h
97 @@ -322,6 +322,7 @@
98 u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
99 struct timer_list ic_inact; /* mgmt/inactivity timer */
100
101 + unsigned int ic_subifs;
102 u_int32_t ic_flags; /* state flags */
103 u_int32_t ic_flags_ext; /* extension of state flags */
104 u_int32_t ic_caps; /* capabilities */
105 @@ -625,6 +626,7 @@
106 #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */
107 #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */
108 #define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */
109 +#define IEEE80211_FEXT_WDSSEP 0x00004000 /* move wds clients into separate interfaces */
110
111 #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
112 #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
113 --- a/net80211/ieee80211_wireless.c
114 +++ b/net80211/ieee80211_wireless.c
115 @@ -2867,6 +2867,14 @@
116 else
117 vap->iv_minrateindex = 0;
118 break;
119 + case IEEE80211_PARAM_WDS_SEP:
120 + if (vap->iv_opmode != IEEE80211_M_HOSTAP)
121 + retv = -EINVAL;
122 + else if (value)
123 + vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP;
124 + else
125 + vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
126 + break;
127 #ifdef ATH_REVERSE_ENGINEERING
128 case IEEE80211_PARAM_DUMPREGS:
129 ieee80211_dump_registers(dev, info, w, extra);
130 @@ -3223,6 +3231,9 @@
131 case IEEE80211_PARAM_MINRATE:
132 param[0] = vap->iv_minrateindex;
133 break;
134 + case IEEE80211_PARAM_WDS_SEP:
135 + param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
136 + break;
137 default:
138 return -EOPNOTSUPP;
139 }
140 @@ -5767,6 +5778,10 @@
141 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
142 { IEEE80211_IOCTL_SETSCANLIST,
143 IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
144 + { IEEE80211_PARAM_WDS_SEP,
145 + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
146 + { IEEE80211_PARAM_WDS_SEP,
147 + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
148
149 #ifdef ATH_REVERSE_ENGINEERING
150 /*
151 --- a/net80211/ieee80211_node.c
152 +++ b/net80211/ieee80211_node.c
153 @@ -47,6 +47,7 @@
154 #include <linux/netdevice.h>
155 #include <linux/etherdevice.h>
156 #include <linux/random.h>
157 +#include <linux/rtnetlink.h>
158
159 #include "if_media.h"
160
161 @@ -236,7 +237,11 @@
162 ieee80211_node_vdetach(struct ieee80211vap *vap)
163 {
164 struct ieee80211com *ic = vap->iv_ic;
165 + struct ieee80211_node *ni;
166
167 + ni = vap->iv_wdsnode;
168 + if (ni)
169 + ni->ni_subif = NULL;
170 ieee80211_node_table_reset(&ic->ic_sta, vap);
171 if (vap->iv_bss != NULL) {
172 ieee80211_unref_node(&vap->iv_bss);
173 @@ -1134,6 +1139,40 @@
174 return ni;
175 }
176
177 +#define WDSIFNAME ".sta%d"
178 +void ieee80211_wds_addif(struct ieee80211_node *ni)
179 +{
180 + struct ieee80211vap *vap = ni->ni_vap;
181 + struct ieee80211com *ic = vap->iv_ic;
182 + struct ieee80211vap *avp;
183 + char *name;
184 +
185 + /* check if the node is split out already */
186 + if (ni->ni_subif)
187 + return;
188 +
189 + name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL);
190 + if (!name)
191 + return;
192 +
193 + strcpy(name, vap->iv_dev->name);
194 + strcat(name, WDSIFNAME);
195 + rtnl_lock();
196 + avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
197 + kfree(name);
198 + if (!avp)
199 + goto done;
200 +
201 + memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN);
202 + avp->iv_wdsnode = ieee80211_ref_node(ni);
203 + ni->ni_subif = avp;
204 + ic->ic_subifs++;
205 +
206 +done:
207 + rtnl_unlock();
208 +}
209 +#undef WDSIFNAME
210 +
211 /* Add wds address to the node table */
212 int
213 #ifdef IEEE80211_DEBUG_REFCNT
214 @@ -2254,6 +2293,28 @@
215 }
216 }
217
218 +static void
219 +ieee80211_subif_destroy(struct work_struct *work)
220 +{
221 + struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy);
222 + struct ieee80211vap *vap = ni->ni_subif;
223 + struct ieee80211com *ic;
224 +
225 + if (!vap)
226 + goto done;
227 +
228 + rtnl_lock();
229 + ic = vap->iv_ic;
230 + ni->ni_subif = NULL;
231 + ieee80211_stop(vap->iv_dev);
232 + ic->ic_vap_delete(vap);
233 + ic->ic_subifs--;
234 + rtnl_unlock();
235 +
236 +done:
237 + ieee80211_unref_node(&ni);
238 +}
239 +
240 /*
241 * Handle bookkeeping for a station/neighbor leaving
242 * the bss when operating in ap or adhoc modes.
243 @@ -2270,6 +2331,12 @@
244 ni, "station with aid %d leaves (refcnt %u)",
245 IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt));
246
247 + if (ni->ni_subif) {
248 + ieee80211_ref_node(ni);
249 + IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy);
250 + schedule_work(&ni->ni_destroy);
251 + }
252 +
253 /* From this point onwards we can no longer find the node,
254 * so no more references are generated
255 */
256 --- a/net80211/ieee80211_linux.h
257 +++ b/net80211/ieee80211_linux.h
258 @@ -81,6 +81,12 @@
259 #endif
260 }
261
262 +#ifndef container_of
263 +#define container_of(ptr, type, member) ({ \
264 + const typeof( ((type *)0)->member ) *__mptr = (ptr); \
265 + (type *)( (char *)__mptr - offsetof(type,member) );})
266 +#endif
267 +
268 /*
269 * Task deferral
270 *
271 @@ -113,6 +119,29 @@
272
273 #define IEEE80211_RESCHEDULE schedule
274
275 +#include <linux/sched.h>
276 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
277 +#include <linux/tqueue.h>
278 +#define work_struct tq_struct
279 +#define schedule_work(t) schedule_task((t))
280 +#define flush_scheduled_work() flush_scheduled_tasks()
281 +#define IEEE80211_INIT_WORK(t, f) do { \
282 + memset((t), 0, sizeof(struct tq_struct)); \
283 + (t)->routine = (void (*)(void*)) (f); \
284 + (t)->data=(void *) (t); \
285 +} while (0)
286 +#else
287 +#include <linux/workqueue.h>
288 +
289 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
290 +#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
291 +#else
292 +#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
293 +#endif
294 +
295 +#endif /* KERNEL_VERSION < 2.5.41 */
296 +
297 +
298 /* Locking */
299 /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP)
300 * because spinlocks do not exist in this configuration. Instead IRQs
301 @@ -167,6 +196,18 @@
302 IEEE80211_VAPS_LOCK_ASSERT(_ic); \
303 spin_unlock_bh(&(_ic)->ic_vapslock); \
304 } while (0)
305 +#define IEEE80211_VAPS_LOCK_IRQ(_ic) do { \
306 + unsigned long __vlockflags; \
307 + IEEE80211_VAPS_LOCK_CHECK(_ic); \
308 + spin_lock_irqsave(&(_ic)->ic_vapslock, __vlockflags);
309 +#define IEEE80211_VAPS_UNLOCK_IRQ(_ic) \
310 + IEEE80211_VAPS_LOCK_ASSERT(_ic); \
311 + spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags); \
312 +} while (0)
313 +#define IEEE80211_VAPS_UNLOCK_IRQ_EARLY(_ic) \
314 + IEEE80211_VAPS_LOCK_ASSERT(_ic); \
315 + spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);
316 +
317
318 #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
319 #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \
320 --- a/net80211/ieee80211_proto.c
321 +++ b/net80211/ieee80211_proto.c
322 @@ -1081,6 +1081,8 @@
323 int
324 ieee80211_open(struct net_device *dev)
325 {
326 + struct ieee80211vap *vap = dev->priv;
327 +
328 return ieee80211_init(dev, 0);
329 }
330
331 @@ -1116,11 +1118,33 @@
332 struct ieee80211vap *vap = dev->priv;
333 struct ieee80211com *ic = vap->iv_ic;
334 struct net_device *parent = ic->ic_dev;
335 + struct ieee80211_node *tni, *ni;
336
337 IEEE80211_DPRINTF(vap,
338 IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
339 "%s\n", "stop running");
340
341 + /* get rid of all wds nodes while we're still locked */
342 + do {
343 + ni = NULL;
344 +
345 + IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
346 + TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
347 + if (tni->ni_vap != vap)
348 + continue;
349 + if (!tni->ni_subif)
350 + continue;
351 + ni = tni;
352 + break;
353 + }
354 + IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
355 +
356 + if (!ni)
357 + break;
358 +
359 + ieee80211_node_leave(ni);
360 + } while (1);
361 +
362 ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
363 if (dev->flags & IFF_RUNNING) {
364 dev->flags &= ~IFF_RUNNING; /* mark us stopped */
365 @@ -1342,9 +1366,9 @@
366 struct ieee80211com *ic = vap->iv_ic;
367 int rc;
368
369 - IEEE80211_VAPS_LOCK_BH(ic);
370 + IEEE80211_VAPS_LOCK_IRQ(ic);
371 rc = vap->iv_newstate(vap, nstate, arg);
372 - IEEE80211_VAPS_UNLOCK_BH(ic);
373 + IEEE80211_VAPS_UNLOCK_IRQ(ic);
374 return rc;
375 }
376
377 --- a/net80211/ieee80211.c
378 +++ b/net80211/ieee80211.c
379 @@ -599,8 +599,10 @@
380
381 IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
382 IEEE80211_LOCK_IRQ(ic);
383 - if (vap->iv_wdsnode)
384 + if (vap->iv_wdsnode) {
385 + vap->iv_wdsnode->ni_subif = NULL;
386 ieee80211_unref_node(&vap->iv_wdsnode);
387 + }
388 if ((vap->iv_opmode == IEEE80211_M_WDS) &&
389 (vap->iv_master != NULL))
390 TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
391 --- a/ath/if_athvar.h
392 +++ b/ath/if_athvar.h
393 @@ -79,28 +79,6 @@
394 #define tasklet_enable(t) do { (void) t; local_bh_enable(); } while (0)
395 #endif /* !DECLARE_TASKLET */
396
397 -#include <linux/sched.h>
398 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
399 -#include <linux/tqueue.h>
400 -#define work_struct tq_struct
401 -#define schedule_work(t) schedule_task((t))
402 -#define flush_scheduled_work() flush_scheduled_tasks()
403 -#define ATH_INIT_WORK(t, f) do { \
404 - memset((t), 0, sizeof(struct tq_struct)); \
405 - (t)->routine = (void (*)(void*)) (f); \
406 - (t)->data=(void *) (t); \
407 -} while (0)
408 -#else
409 -#include <linux/workqueue.h>
410 -
411 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
412 -#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
413 -#else
414 -#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
415 -#endif
416 -
417 -#endif /* KERNEL_VERSION < 2.5.41 */
418 -
419 /*
420 * Guess how the interrupt handler should work.
421 */