1 --- a/net80211/ieee80211_input.c
2 +++ b/net80211/ieee80211_input.c
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;
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);
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;
28 if (! accept_data_frame(vap, ni, key, skb, eh))
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;
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,13 @@
43 dev = vap->iv_xrvap->iv_dev;
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))) {
53 /* perform as a bridge within the vap */
54 /* XXX intra-vap bridging only */
55 if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
58 if (ni1->ni_vap == vap &&
59 ieee80211_node_is_authorized(ni1) &&
64 --- a/net80211/ieee80211_ioctl.h
65 +++ b/net80211/ieee80211_ioctl.h
67 IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */
68 IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */
69 IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */
70 + IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */
73 #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
74 --- a/net80211/ieee80211_node.h
75 +++ b/net80211/ieee80211_node.h
77 * the ieee80211com structure.
79 struct ieee80211_node {
80 - struct ieee80211vap *ni_vap;
81 + struct ieee80211vap *ni_vap, *ni_subif;
82 struct ieee80211com *ni_ic;
83 struct ieee80211_node_table *ni_table;
84 TAILQ_ENTRY(ieee80211_node) ni_list;
85 LIST_ENTRY(ieee80211_node) ni_hash;
86 + struct work_struct ni_destroy; /* task for destroying a subif */
88 u_int ni_scangen; /* gen# for timeout scan */
89 u_int8_t ni_authmode; /* authentication algorithm */
91 void ieee80211_node_leave(struct ieee80211_node *);
92 u_int8_t ieee80211_getrssi(struct ieee80211com *);
93 int32_t ieee80211_get_node_count(struct ieee80211com *);
94 +void ieee80211_wds_addif(struct ieee80211_node *ni);
95 #endif /* _NET80211_IEEE80211_NODE_H_ */
97 --- a/net80211/ieee80211_var.h
98 +++ b/net80211/ieee80211_var.h
100 u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
101 struct timer_list ic_inact; /* mgmt/inactivity timer */
103 + unsigned int ic_subifs;
104 u_int32_t ic_flags; /* state flags */
105 u_int32_t ic_flags_ext; /* extension of state flags */
106 u_int32_t ic_caps; /* capabilities */
108 #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */
109 #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */
110 #define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */
111 +#define IEEE80211_FEXT_WDSSEP 0x00004000 /* move wds clients into separate interfaces */
113 #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
114 #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
115 --- a/net80211/ieee80211_wireless.c
116 +++ b/net80211/ieee80211_wireless.c
117 @@ -2867,6 +2867,14 @@
119 vap->iv_minrateindex = 0;
121 + case IEEE80211_PARAM_WDS_SEP:
122 + if (vap->iv_opmode != IEEE80211_M_HOSTAP)
125 + vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP;
127 + vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
129 #ifdef ATH_REVERSE_ENGINEERING
130 case IEEE80211_PARAM_DUMPREGS:
131 ieee80211_dump_registers(dev, info, w, extra);
132 @@ -3223,6 +3231,9 @@
133 case IEEE80211_PARAM_MINRATE:
134 param[0] = vap->iv_minrateindex;
136 + case IEEE80211_PARAM_WDS_SEP:
137 + param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
142 @@ -5767,6 +5778,10 @@
143 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
144 { IEEE80211_IOCTL_SETSCANLIST,
145 IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
146 + { IEEE80211_PARAM_WDS_SEP,
147 + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
148 + { IEEE80211_PARAM_WDS_SEP,
149 + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
151 #ifdef ATH_REVERSE_ENGINEERING
153 @@ -5890,6 +5905,8 @@
154 ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
156 struct ieee80211vap *vap = dev->priv;
157 + struct ieee80211com *ic = vap->iv_ic;
158 + struct ieee80211_node *ni;
161 case SIOCG80211STATS:
162 @@ -5898,8 +5915,20 @@
163 case SIOC80211IFDESTROY:
164 if (!capable(CAP_NET_ADMIN))
166 + /* drop all node subifs */
167 + TAILQ_FOREACH(ni, &ic->ic_sta.nt_node, ni_list) {
168 + struct ieee80211vap *avp = ni->ni_subif;
170 + if (ni->ni_vap != vap)
174 + ni->ni_subif = NULL;
175 + ieee80211_stop(avp->iv_dev);
176 + ic->ic_vap_delete(avp);
178 ieee80211_stop(vap->iv_dev); /* force state before cleanup */
179 - vap->iv_ic->ic_vap_delete(vap);
180 + ic->ic_vap_delete(vap);
182 case IEEE80211_IOCTL_GETKEY:
183 return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr);
184 --- a/net80211/ieee80211_node.c
185 +++ b/net80211/ieee80211_node.c
187 #include <linux/netdevice.h>
188 #include <linux/etherdevice.h>
189 #include <linux/random.h>
190 +#include <linux/rtnetlink.h>
192 #include "if_media.h"
195 ieee80211_node_vdetach(struct ieee80211vap *vap)
197 struct ieee80211com *ic = vap->iv_ic;
198 + struct ieee80211_node *ni;
200 + ni = vap->iv_wdsnode;
202 + ni->ni_subif = NULL;
203 ieee80211_node_table_reset(&ic->ic_sta, vap);
204 if (vap->iv_bss != NULL) {
205 ieee80211_unref_node(&vap->iv_bss);
206 @@ -1134,6 +1139,40 @@
210 +#define WDSIFNAME ".sta%d"
211 +void ieee80211_wds_addif(struct ieee80211_node *ni)
213 + struct ieee80211vap *vap = ni->ni_vap;
214 + struct ieee80211com *ic = vap->iv_ic;
215 + struct ieee80211vap *avp;
218 + /* check if the node is split out already */
222 + name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL);
226 + strcpy(name, vap->iv_dev->name);
227 + strcat(name, WDSIFNAME);
229 + avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
234 + memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN);
235 + avp->iv_wdsnode = ieee80211_ref_node(ni);
236 + ni->ni_subif = avp;
244 /* Add wds address to the node table */
246 #ifdef IEEE80211_DEBUG_REFCNT
247 @@ -2254,6 +2293,28 @@
252 +ieee80211_subif_destroy(struct work_struct *work)
254 + struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy);
255 + struct ieee80211vap *vap = ni->ni_subif;
256 + struct ieee80211com *ic;
263 + ni->ni_subif = NULL;
264 + ieee80211_stop(vap->iv_dev);
265 + ic->ic_vap_delete(vap);
270 + ieee80211_unref_node(&ni);
274 * Handle bookkeeping for a station/neighbor leaving
275 * the bss when operating in ap or adhoc modes.
276 @@ -2270,6 +2331,12 @@
277 ni, "station with aid %d leaves (refcnt %u)",
278 IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt));
280 + if (ni->ni_subif) {
281 + ieee80211_ref_node(ni);
282 + IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy);
283 + schedule_work(&ni->ni_destroy);
286 /* From this point onwards we can no longer find the node,
287 * so no more references are generated
289 --- a/net80211/ieee80211_linux.h
290 +++ b/net80211/ieee80211_linux.h
295 +#ifndef container_of
296 +#define container_of(ptr, type, member) ({ \
297 + const typeof( ((type *)0)->member ) *__mptr = (ptr); \
298 + (type *)( (char *)__mptr - offsetof(type,member) );})
306 #define IEEE80211_RESCHEDULE schedule
308 +#include <linux/sched.h>
309 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
310 +#include <linux/tqueue.h>
311 +#define work_struct tq_struct
312 +#define schedule_work(t) schedule_task((t))
313 +#define flush_scheduled_work() flush_scheduled_tasks()
314 +#define IEEE80211_INIT_WORK(t, f) do { \
315 + memset((t), 0, sizeof(struct tq_struct)); \
316 + (t)->routine = (void (*)(void*)) (f); \
317 + (t)->data=(void *) (t); \
320 +#include <linux/workqueue.h>
322 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
323 +#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
325 +#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
328 +#endif /* KERNEL_VERSION < 2.5.41 */
332 /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP)
333 * because spinlocks do not exist in this configuration. Instead IRQs
335 IEEE80211_VAPS_LOCK_ASSERT(_ic); \
336 spin_unlock_bh(&(_ic)->ic_vapslock); \
338 +#define IEEE80211_VAPS_LOCK_IRQ(_ic) do { \
339 + unsigned long __vlockflags; \
340 + IEEE80211_VAPS_LOCK_CHECK(_ic); \
341 + spin_lock_irqsave(&(_ic)->ic_vapslock, __vlockflags);
342 +#define IEEE80211_VAPS_UNLOCK_IRQ(_ic) \
343 + IEEE80211_VAPS_LOCK_ASSERT(_ic); \
344 + spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags); \
346 +#define IEEE80211_VAPS_UNLOCK_IRQ_EARLY(_ic) \
347 + IEEE80211_VAPS_LOCK_ASSERT(_ic); \
348 + spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);
351 #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
352 #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \
353 --- a/net80211/ieee80211_proto.c
354 +++ b/net80211/ieee80211_proto.c
355 @@ -1081,6 +1081,8 @@
357 ieee80211_open(struct net_device *dev)
359 + struct ieee80211vap *vap = dev->priv;
361 return ieee80211_init(dev, 0);
364 @@ -1116,11 +1118,33 @@
365 struct ieee80211vap *vap = dev->priv;
366 struct ieee80211com *ic = vap->iv_ic;
367 struct net_device *parent = ic->ic_dev;
368 + struct ieee80211_node *tni, *ni;
370 IEEE80211_DPRINTF(vap,
371 IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
372 "%s\n", "stop running");
374 + /* get rid of all wds nodes while we're still locked */
378 + IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
379 + TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
380 + if (tni->ni_vap != vap)
382 + if (!tni->ni_subif)
387 + IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
392 + ieee80211_node_leave(ni);
395 ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
396 if (dev->flags & IFF_RUNNING) {
397 dev->flags &= ~IFF_RUNNING; /* mark us stopped */
398 @@ -1342,9 +1366,9 @@
399 struct ieee80211com *ic = vap->iv_ic;
402 - IEEE80211_VAPS_LOCK_BH(ic);
403 + IEEE80211_VAPS_LOCK_IRQ(ic);
404 rc = vap->iv_newstate(vap, nstate, arg);
405 - IEEE80211_VAPS_UNLOCK_BH(ic);
406 + IEEE80211_VAPS_UNLOCK_IRQ(ic);
410 --- a/net80211/ieee80211.c
411 +++ b/net80211/ieee80211.c
414 IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
415 IEEE80211_LOCK_IRQ(ic);
416 - if (vap->iv_wdsnode)
417 + if (vap->iv_wdsnode) {
418 + vap->iv_wdsnode->ni_subif = NULL;
419 ieee80211_unref_node(&vap->iv_wdsnode);
421 if ((vap->iv_opmode == IEEE80211_M_WDS) &&
422 (vap->iv_master != NULL))
423 TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
424 --- a/ath/if_athvar.h
425 +++ b/ath/if_athvar.h
427 #define tasklet_enable(t) do { (void) t; local_bh_enable(); } while (0)
428 #endif /* !DECLARE_TASKLET */
430 -#include <linux/sched.h>
431 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
432 -#include <linux/tqueue.h>
433 -#define work_struct tq_struct
434 -#define schedule_work(t) schedule_task((t))
435 -#define flush_scheduled_work() flush_scheduled_tasks()
436 -#define ATH_INIT_WORK(t, f) do { \
437 - memset((t), 0, sizeof(struct tq_struct)); \
438 - (t)->routine = (void (*)(void*)) (f); \
439 - (t)->data=(void *) (t); \
442 -#include <linux/workqueue.h>
444 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
445 -#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
447 -#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
450 -#endif /* KERNEL_VERSION < 2.5.41 */
453 * Guess how the interrupt handler should work.
455 --- a/net80211/ieee80211_output.c
456 +++ b/net80211/ieee80211_output.c
458 hdrsize = sizeof(struct ieee80211_frame);
460 SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE));
461 + if (!SKB_CB(skb)->auth_pkt && ni->ni_subif)
462 + vap = ni->ni_subif;
464 switch (vap->iv_opmode) {
465 case IEEE80211_M_IBSS: