fix ocf compile error
[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 @@ -435,7 +436,7 @@
12  
13         switch (type) {
14         case IEEE80211_FC0_TYPE_DATA:
15 -               hdrspace = ieee80211_hdrspace(ic, wh);
16 +               hdrspace = ieee80211_hdrsize(wh);
17                 if (skb->len < hdrspace) {
18                         IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
19                                 wh, "data", "too short: len %u, expecting %u",
20 @@ -446,15 +447,20 @@
21                 switch (vap->iv_opmode) {
22                 case IEEE80211_M_STA:
23                         if ((dir != IEEE80211_FC1_DIR_FROMDS) &&
24 -                           (!((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
25 -                           (dir == IEEE80211_FC1_DIR_DSTODS)))) {
26 +                           (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
27 +                            (dir == IEEE80211_FC1_DIR_DSTODS))) {
28                                 IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
29                                         wh, "data", "invalid dir 0x%x", dir);
30                                 vap->iv_stats.is_rx_wrongdir++;
31                                 goto out;
32                         }
33  
34 -                       if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
35 +                       if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
36 +                               /* ignore 3-addr mcast if we're WDS STA */
37 +                               if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
38 +                                       (dir != IEEE80211_FC1_DIR_DSTODS))
39 +                                       goto out;
40 +
41                                 /* Discard multicast if IFF_MULTICAST not set */
42                                 if ((0 != memcmp(wh->i_addr3, dev->broadcast, ETH_ALEN)) && 
43                                         (0 == (dev->flags & IFF_MULTICAST))) {
44 @@ -482,24 +488,6 @@
45                                         vap->iv_stats.is_rx_mcastecho++;
46                                         goto out;
47                                 }
48 -                               /* 
49 -                                * if it is brodcasted by me on behalf of
50 -                                * a station behind me, drop it.
51 -                                */
52 -                               if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) {
53 -                                       struct ieee80211_node_table *nt;
54 -                                       struct ieee80211_node *ni_wds;
55 -                                       nt = &ic->ic_sta;
56 -                                       ni_wds = ieee80211_find_wds_node(nt, wh->i_addr3);
57 -                                       if (ni_wds) {
58 -                                               ieee80211_unref_node(&ni_wds);
59 -                                               IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
60 -                                                       wh, NULL, "%s",
61 -                                                       "multicast echo originated from node behind me");
62 -                                               vap->iv_stats.is_rx_mcastecho++;
63 -                                               goto out;
64 -                                       }
65 -                               }
66                         }
67                         break;
68                 case IEEE80211_M_IBSS:
69 @@ -548,7 +536,7 @@
70                          */
71  
72                         /* check for wds link first */
73 -                       if (dir == IEEE80211_FC1_DIR_DSTODS) {
74 +                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni->ni_subif) {
75                                 struct ieee80211vap *avp;
76  
77                                 TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
78 @@ -562,11 +550,13 @@
79                                 if (ni_wds != NULL) {
80                                         ieee80211_unref_node(&ni);
81                                         ni = ieee80211_ref_node(ni_wds);
82 +                               } else if (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP) {
83 +                                       ieee80211_wds_addif(ni);
84                                 }
85                         }
86  
87                         /* XXX: Useless node mgmt API; make better */
88 -                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds) {
89 +                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds && !ni->ni_subif) {
90                                 struct ieee80211_node_table *nt = &ic->ic_sta;
91                                 struct ieee80211_frame_addr4 *wh4;
92  
93 @@ -626,6 +616,11 @@
94                         goto out;
95                 }
96  
97 +               /* check if there is any data left */
98 +               hdrspace = ieee80211_hdrspace(ic, wh);
99 +               if (skb->len < hdrspace)
100 +                       goto out;
101 +
102                 /*
103                  * Handle privacy requirements.  Note that we
104                  * must not be preempted from here until after
105 @@ -698,8 +693,12 @@
106                 if (! accept_data_frame(vap, ni, key, skb, eh))
107                         goto out;
108  
109 -               vap->iv_devstats.rx_packets++;
110 -               vap->iv_devstats.rx_bytes += skb->len;
111 +               if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
112 +                       stats = &ni->ni_subif->iv_devstats;
113 +               else
114 +                       stats = &vap->iv_devstats;
115 +               stats->rx_packets++;
116 +               stats->rx_bytes += skb->len;
117                 IEEE80211_NODE_STAT(ni, rx_data);
118                 IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
119                 ic->ic_lastdata = jiffies;
120 @@ -1132,6 +1131,13 @@
121                 dev = vap->iv_xrvap->iv_dev;
122  #endif
123  
124 +       /* if the node has a wds subif, move data frames there,
125 +        * but keep EAP traffic on the master */
126 +       if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) {
127 +               vap = ni->ni_subif;
128 +               dev = vap->iv_dev;
129 +       }
130 +
131         /* perform as a bridge within the vap */
132         /* XXX intra-vap bridging only */
133         if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
134 @@ -1157,6 +1163,7 @@
135                         if (ni1 != NULL) {
136                                 if (ni1->ni_vap == vap &&
137                                     ieee80211_node_is_authorized(ni1) &&
138 +                                       !ni->ni_subif &&
139                                     ni1 != vap->iv_bss) {
140                                         skb1 = skb;
141                                         skb = NULL;
142 --- a/net80211/ieee80211_ioctl.h
143 +++ b/net80211/ieee80211_ioctl.h
144 @@ -649,6 +649,7 @@
145         IEEE80211_PARAM_BGSCAN_THRESH           = 79,   /* bg scan rssi threshold */
146         IEEE80211_PARAM_RSSI_DIS_THR    = 80,   /* rssi threshold for disconnection */
147         IEEE80211_PARAM_RSSI_DIS_COUNT  = 81,   /* counter for rssi threshold */
148 +       IEEE80211_PARAM_WDS_SEP                 = 82,   /* move wds stations into separate interfaces */
149  };
150  
151  #define        SIOCG80211STATS                 (SIOCDEVPRIVATE+2)
152 --- a/net80211/ieee80211_node.h
153 +++ b/net80211/ieee80211_node.h
154 @@ -92,11 +92,13 @@
155   * the ieee80211com structure.
156   */
157  struct ieee80211_node {
158 -       struct ieee80211vap *ni_vap;
159 +       struct ieee80211vap *ni_vap, *ni_subif;
160         struct ieee80211com *ni_ic;
161         struct ieee80211_node_table *ni_table;
162         TAILQ_ENTRY(ieee80211_node) ni_list;
163         LIST_ENTRY(ieee80211_node) ni_hash;
164 +       struct work_struct ni_create;   /* task for creating a subif */
165 +       struct work_struct ni_destroy;  /* task for destroying a subif */
166         atomic_t ni_refcnt;
167         u_int ni_scangen;                       /* gen# for timeout scan */
168         u_int8_t ni_authmode;                   /* authentication algorithm */
169 @@ -430,5 +432,6 @@
170  void ieee80211_node_leave(struct ieee80211_node *);
171  u_int8_t ieee80211_getrssi(struct ieee80211com *);
172  int32_t ieee80211_get_node_count(struct ieee80211com *);
173 +void ieee80211_wds_addif(struct ieee80211_node *ni);
174  #endif /* _NET80211_IEEE80211_NODE_H_ */
175  
176 --- a/net80211/ieee80211_var.h
177 +++ b/net80211/ieee80211_var.h
178 @@ -322,6 +322,7 @@
179         u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
180         struct timer_list ic_inact;             /* mgmt/inactivity timer */
181  
182 +       unsigned int ic_subifs;
183         u_int32_t ic_flags;                     /* state flags */
184         u_int32_t ic_flags_ext;                 /* extension of state flags */
185         u_int32_t ic_caps;                      /* capabilities */
186 @@ -625,6 +626,7 @@
187  #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800      /* CONF: drop unencrypted eapol frames */
188  #define IEEE80211_FEXT_APPIE_UPDATE    0x00001000      /* STATE: beacon APP IE updated */
189  #define IEEE80211_FEXT_BGSCAN_THR      0x00002000      /* bgscan due to low rssi */
190 +#define IEEE80211_FEXT_WDSSEP          0x00004000      /* move wds clients into separate interfaces */
191  
192  #define IEEE80211_COM_UAPSD_ENABLE(_ic)                ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
193  #define IEEE80211_COM_UAPSD_DISABLE(_ic)       ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
194 --- a/net80211/ieee80211_wireless.c
195 +++ b/net80211/ieee80211_wireless.c
196 @@ -2867,6 +2867,14 @@
197                 else
198                         vap->iv_minrateindex = 0;
199                 break;
200 +       case IEEE80211_PARAM_WDS_SEP:
201 +               if (vap->iv_opmode != IEEE80211_M_HOSTAP)
202 +                       retv = -EINVAL;
203 +               else if (value)
204 +                       vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP;
205 +               else
206 +                       vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
207 +               break;
208  #ifdef ATH_REVERSE_ENGINEERING
209         case IEEE80211_PARAM_DUMPREGS:
210                 ieee80211_dump_registers(dev, info, w, extra);
211 @@ -3223,6 +3231,9 @@
212         case IEEE80211_PARAM_MINRATE:
213                 param[0] = vap->iv_minrateindex;
214                 break;
215 +       case IEEE80211_PARAM_WDS_SEP:
216 +               param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
217 +               break;
218         default:
219                 return -EOPNOTSUPP;
220         }
221 @@ -5767,6 +5778,10 @@
222          0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
223         { IEEE80211_IOCTL_SETSCANLIST,
224          IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
225 +       { IEEE80211_PARAM_WDS_SEP,
226 +        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
227 +       { IEEE80211_PARAM_WDS_SEP,
228 +        0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
229  
230  #ifdef ATH_REVERSE_ENGINEERING
231         /*
232 @@ -5890,6 +5905,8 @@
233  ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
234  {
235         struct ieee80211vap *vap = dev->priv;
236 +       struct ieee80211com *ic = vap->iv_ic;
237 +       struct ieee80211_node *ni;
238  
239         switch (cmd) {
240         case SIOCG80211STATS:
241 @@ -5898,8 +5915,20 @@
242         case SIOC80211IFDESTROY:
243                 if (!capable(CAP_NET_ADMIN))
244                         return -EPERM;
245 +               /* drop all node subifs */
246 +               TAILQ_FOREACH(ni, &ic->ic_sta.nt_node, ni_list) {
247 +                       struct ieee80211vap *avp = ni->ni_subif;
248 +
249 +                       if (ni->ni_vap != vap)
250 +                               continue;
251 +                       if (!avp)
252 +                               continue;
253 +                       ni->ni_subif = NULL;
254 +                       ieee80211_stop(avp->iv_dev);
255 +                       ic->ic_vap_delete(avp);
256 +               }
257                 ieee80211_stop(vap->iv_dev);    /* force state before cleanup */
258 -               vap->iv_ic->ic_vap_delete(vap);
259 +               ic->ic_vap_delete(vap);
260                 return 0;
261         case IEEE80211_IOCTL_GETKEY:
262                 return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr);
263 --- a/net80211/ieee80211_node.c
264 +++ b/net80211/ieee80211_node.c
265 @@ -47,6 +47,7 @@
266  #include <linux/netdevice.h>
267  #include <linux/etherdevice.h>
268  #include <linux/random.h>
269 +#include <linux/rtnetlink.h>
270  
271  #include "if_media.h"
272  
273 @@ -236,7 +237,11 @@
274  ieee80211_node_vdetach(struct ieee80211vap *vap)
275  {
276         struct ieee80211com *ic = vap->iv_ic;
277 +       struct ieee80211_node *ni;
278  
279 +       ni = vap->iv_wdsnode;
280 +       if (ni)
281 +               ni->ni_subif = NULL;
282         ieee80211_node_table_reset(&ic->ic_sta, vap);
283         if (vap->iv_bss != NULL) {
284                 ieee80211_unref_node(&vap->iv_bss);
285 @@ -1134,6 +1139,57 @@
286         return ni;
287  }
288  
289 +#define WDSIFNAME ".sta%d"
290 +static void
291 +ieee80211_wds_do_addif(struct work_struct *work)
292 +{
293 +       struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_create);
294 +       struct ieee80211vap *vap = ni->ni_vap;
295 +       struct ieee80211com *ic = vap->iv_ic;
296 +       struct ieee80211vap *avp;
297 +       char *name;
298 +
299 +       rtnl_lock();
300 +       /* did we get cancelled by the destroy call? */
301 +       if (!ni->ni_subif)
302 +               goto done;
303 +
304 +       ni->ni_subif = NULL;
305 +       name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL);
306 +       if (!name)
307 +               goto done;
308 +
309 +       strcpy(name, vap->iv_dev->name);
310 +       strcat(name, WDSIFNAME);
311 +       avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
312 +       kfree(name);
313 +       if (!avp)
314 +               goto done;
315 +
316 +       memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN);
317 +       avp->iv_wdsnode = ieee80211_ref_node(ni);
318 +       ni->ni_subif = avp;
319 +       ic->ic_subifs++;
320 +
321 +done:
322 +       rtnl_unlock();
323 +       ieee80211_unref_node(&ni);
324 +}
325 +#undef WDSIFNAME
326 +
327 +void ieee80211_wds_addif(struct ieee80211_node *ni)
328 +{
329 +       /* check if the node is split out already,
330 +        * or if we're in progress of setting up a new interface already */
331 +       if (ni->ni_subif)
332 +               return;
333 +
334 +       ieee80211_ref_node(ni);
335 +       ni->ni_subif = ni->ni_vap;
336 +       IEEE80211_INIT_WORK(&ni->ni_create, ieee80211_wds_do_addif);
337 +       schedule_work(&ni->ni_create);
338 +}
339 +
340  /* Add wds address to the node table */
341  int
342  #ifdef IEEE80211_DEBUG_REFCNT
343 @@ -2254,6 +2310,36 @@
344         }
345  }
346  
347 +static void
348 +ieee80211_subif_destroy(struct work_struct *work)
349 +{
350 +       struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy);
351 +       struct ieee80211vap *vap;
352 +       struct ieee80211com *ic;
353 +
354 +       rtnl_lock();
355 +       vap = ni->ni_subif;
356 +
357 +       /* if addif is waiting for the timer to fire, cancel! */
358 +       if (vap == ni->ni_vap) {
359 +               ni->ni_subif = NULL;
360 +               goto done;
361 +       }
362 +
363 +       if (!vap)
364 +               goto done;
365 +
366 +       ic = vap->iv_ic;
367 +       ni->ni_subif = NULL;
368 +       ieee80211_stop(vap->iv_dev);
369 +       ic->ic_vap_delete(vap);
370 +       ic->ic_subifs--;
371 +
372 +done:
373 +       ieee80211_unref_node(&ni);
374 +       rtnl_unlock();
375 +}
376 +
377  /*
378   * Handle bookkeeping for a station/neighbor leaving
379   * the bss when operating in ap or adhoc modes.
380 @@ -2270,6 +2356,12 @@
381                         ni, "station with aid %d leaves (refcnt %u)",
382                         IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt));
383  
384 +       if (ni->ni_subif) {
385 +               ieee80211_ref_node(ni);
386 +               IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy);
387 +               schedule_work(&ni->ni_destroy);
388 +       }
389 +
390         /* From this point onwards we can no longer find the node,
391          * so no more references are generated
392          */
393 --- a/net80211/ieee80211_linux.h
394 +++ b/net80211/ieee80211_linux.h
395 @@ -81,6 +81,12 @@
396  #endif
397  }
398  
399 +#ifndef container_of
400 +#define container_of(ptr, type, member) ({          \
401 +    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
402 +           (type *)( (char *)__mptr - offsetof(type,member) );})
403 +#endif
404 +
405  /*
406   * Task deferral
407   *
408 @@ -113,6 +119,29 @@
409  
410  #define        IEEE80211_RESCHEDULE    schedule
411  
412 +#include <linux/sched.h>
413 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
414 +#include <linux/tqueue.h>
415 +#define work_struct                    tq_struct
416 +#define schedule_work(t)               schedule_task((t))
417 +#define flush_scheduled_work()         flush_scheduled_tasks()
418 +#define IEEE80211_INIT_WORK(t, f) do {                         \
419 +       memset((t), 0, sizeof(struct tq_struct)); \
420 +       (t)->routine = (void (*)(void*)) (f);   \
421 +       (t)->data=(void *) (t);                 \
422 +} while (0)
423 +#else
424 +#include <linux/workqueue.h>
425 +
426 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
427 +#define IEEE80211_INIT_WORK(_t, _f)    INIT_WORK((_t), (void (*)(void *))(_f), (_t));
428 +#else
429 +#define IEEE80211_INIT_WORK(_t, _f)    INIT_WORK((_t), (_f));
430 +#endif
431 +
432 +#endif /* KERNEL_VERSION < 2.5.41 */
433 +
434 +
435  /* Locking */
436  /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP)
437   * because spinlocks do not exist in this configuration. Instead IRQs 
438 @@ -167,6 +196,18 @@
439         IEEE80211_VAPS_LOCK_ASSERT(_ic);                \
440         spin_unlock_bh(&(_ic)->ic_vapslock);            \
441  } while (0)
442 +#define        IEEE80211_VAPS_LOCK_IRQ(_ic) do {                                       \
443 +       unsigned long __vlockflags;                                     \
444 +       IEEE80211_VAPS_LOCK_CHECK(_ic);                                 \
445 +       spin_lock_irqsave(&(_ic)->ic_vapslock, __vlockflags);
446 +#define        IEEE80211_VAPS_UNLOCK_IRQ(_ic)                                  \
447 +       IEEE80211_VAPS_LOCK_ASSERT(_ic);                                        \
448 +       spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);      \
449 +} while (0)
450 +#define        IEEE80211_VAPS_UNLOCK_IRQ_EARLY(_ic)                                    \
451 +       IEEE80211_VAPS_LOCK_ASSERT(_ic);                                        \
452 +       spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);
453 +
454  
455  #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
456  #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \
457 --- a/net80211/ieee80211_proto.c
458 +++ b/net80211/ieee80211_proto.c
459 @@ -1081,6 +1081,8 @@
460  int
461  ieee80211_open(struct net_device *dev)
462  {
463 +       struct ieee80211vap *vap = dev->priv;
464 +
465         return ieee80211_init(dev, 0);
466  }
467  
468 @@ -1116,11 +1118,33 @@
469         struct ieee80211vap *vap = dev->priv;
470         struct ieee80211com *ic = vap->iv_ic;
471         struct net_device *parent = ic->ic_dev;
472 +       struct ieee80211_node *tni, *ni;
473  
474         IEEE80211_DPRINTF(vap,
475                 IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
476                 "%s\n", "stop running");
477  
478 +       /* get rid of all wds nodes while we're still locked */
479 +       do {
480 +               ni = NULL;
481 +
482 +               IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
483 +               TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
484 +                       if (tni->ni_vap != vap)
485 +                               continue;
486 +                       if (!tni->ni_subif)
487 +                               continue;
488 +                       ni = tni;
489 +                       break;
490 +               }
491 +               IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
492 +
493 +               if (!ni)
494 +                       break;
495 +
496 +               ieee80211_node_leave(ni);
497 +       } while (1);
498 +
499         ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
500         if (dev->flags & IFF_RUNNING) {
501                 dev->flags &= ~IFF_RUNNING;             /* mark us stopped */
502 @@ -1342,9 +1366,9 @@
503         struct ieee80211com *ic = vap->iv_ic;
504         int rc;
505  
506 -       IEEE80211_VAPS_LOCK_BH(ic);
507 +       IEEE80211_VAPS_LOCK_IRQ(ic);
508         rc = vap->iv_newstate(vap, nstate, arg);
509 -       IEEE80211_VAPS_UNLOCK_BH(ic);
510 +       IEEE80211_VAPS_UNLOCK_IRQ(ic);
511         return rc;
512  }
513  
514 @@ -1630,6 +1654,7 @@
515                  */
516                 if (ni->ni_authmode != IEEE80211_AUTH_8021X)
517                         ieee80211_node_authorize(ni);
518 +
519  #ifdef ATH_SUPERG_XR
520                 /*
521                  * fire a timer to bring up XR vap if configured.
522 @@ -1885,8 +1910,15 @@
523                 if (ostate == IEEE80211_S_SCAN || 
524                     ostate == IEEE80211_S_AUTH ||
525                     ostate == IEEE80211_S_ASSOC) {
526 +
527                         /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_RUN */
528                         __ieee80211_newstate(vap, nstate, arg);
529 +
530 +                       /* if we're in wds, let the ap know that we're doing this */
531 +                       if ((vap->iv_opmode == IEEE80211_M_STA) &&
532 +                               (vap->iv_flags_ext & IEEE80211_FEXT_WDS))
533 +                                       ieee80211_send_nulldata(ieee80211_ref_node(vap->iv_bss));
534 +
535                         /* Then bring up all other vaps pending on the scan */
536                         dstate = get_dominant_state(ic);
537                         if (dstate == IEEE80211_S_RUN) {
538 --- a/net80211/ieee80211.c
539 +++ b/net80211/ieee80211.c
540 @@ -373,10 +373,25 @@
541  ieee80211_ifdetach(struct ieee80211com *ic)
542  {
543         struct ieee80211vap *vap;
544 +       int count;
545 +
546 +       /* bring down all vaps */
547 +       TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
548 +               ieee80211_stop(vap->iv_dev);
549 +       }
550 +
551 +       /* wait for all subifs to disappear */
552 +       do {
553 +               schedule();
554 +               rtnl_lock();
555 +               count = ic->ic_subifs;
556 +               rtnl_unlock();
557 +       } while (count > 0);
558  
559         rtnl_lock();
560 -       while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
561 +       while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) {
562                 ic->ic_vap_delete(vap);
563 +       }
564         rtnl_unlock();
565  
566         del_timer(&ic->ic_dfs_excl_timer);
567 @@ -599,8 +614,10 @@
568  
569         IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
570         IEEE80211_LOCK_IRQ(ic);
571 -       if (vap->iv_wdsnode)
572 +       if (vap->iv_wdsnode) {
573 +               vap->iv_wdsnode->ni_subif = NULL;
574                 ieee80211_unref_node(&vap->iv_wdsnode);
575 +       }
576         if ((vap->iv_opmode == IEEE80211_M_WDS) &&
577                 (vap->iv_master != NULL))
578                 TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
579 --- a/ath/if_athvar.h
580 +++ b/ath/if_athvar.h
581 @@ -79,28 +79,6 @@
582  #define        tasklet_enable(t)       do { (void) t; local_bh_enable(); } while (0)
583  #endif /* !DECLARE_TASKLET */
584  
585 -#include <linux/sched.h>
586 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
587 -#include <linux/tqueue.h>
588 -#define work_struct                    tq_struct
589 -#define schedule_work(t)               schedule_task((t))
590 -#define flush_scheduled_work()         flush_scheduled_tasks()
591 -#define ATH_INIT_WORK(t, f) do {                       \
592 -       memset((t), 0, sizeof(struct tq_struct)); \
593 -       (t)->routine = (void (*)(void*)) (f);   \
594 -       (t)->data=(void *) (t);                 \
595 -} while (0)
596 -#else
597 -#include <linux/workqueue.h>
598 -
599 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
600 -#define ATH_INIT_WORK(_t, _f)  INIT_WORK((_t), (void (*)(void *))(_f), (_t));
601 -#else
602 -#define ATH_INIT_WORK(_t, _f)  INIT_WORK((_t), (_f));
603 -#endif
604 -
605 -#endif /* KERNEL_VERSION < 2.5.41 */
606 -
607  /*
608   * Guess how the interrupt handler should work.
609   */
610 --- a/net80211/ieee80211_output.c
611 +++ b/net80211/ieee80211_output.c
612 @@ -261,6 +261,10 @@
613                 goto bad;
614         }
615  
616 +       if (ni->ni_subif && (vap != ni->ni_subif) &&
617 +               ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
618 +               goto bad;
619 +
620         /* calculate priority so drivers can find the TX queue */
621         if (ieee80211_classify(ni, skb)) {
622                 IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
623 @@ -340,20 +344,33 @@
624   * constructing a frame as it sets i_fc[1]; other bits can
625   * then be or'd in.
626   */
627 -static void
628 +static struct ieee80211_frame *
629  ieee80211_send_setup(struct ieee80211vap *vap,
630         struct ieee80211_node *ni,
631 -       struct ieee80211_frame *wh,
632 +       struct sk_buff *skb,
633         int type,
634         const u_int8_t sa[IEEE80211_ADDR_LEN],
635         const u_int8_t da[IEEE80211_ADDR_LEN],
636         const u_int8_t bssid[IEEE80211_ADDR_LEN])
637  {
638  #define        WH4(wh) ((struct ieee80211_frame_addr4 *)wh)
639 +       struct ieee80211_frame *wh;
640 +       int len = sizeof(struct ieee80211_frame);
641 +       int opmode = vap->iv_opmode;
642 +
643 +       if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
644 +               if ((opmode == IEEE80211_M_STA) &&
645 +                       (vap->iv_flags_ext & IEEE80211_FEXT_WDS))
646 +                       opmode = IEEE80211_M_WDS;
647 +
648 +               if (opmode == IEEE80211_M_WDS)
649 +                       len = sizeof(struct ieee80211_frame_addr4);
650 +       }
651  
652 +       wh = (struct ieee80211_frame *)skb_push(skb, len);
653         wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
654         if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
655 -               switch (vap->iv_opmode) {
656 +               switch (opmode) {
657                 case IEEE80211_M_STA:
658                         wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
659                         IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
660 @@ -395,6 +412,8 @@
661         *(__le16 *)&wh->i_seq[0] =
662             htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
663         ni->ni_txseqs[0]++;
664 +
665 +       return wh;
666  #undef WH4
667  }
668  
669 @@ -416,9 +435,7 @@
670  
671         SKB_CB(skb)->ni = ni;
672  
673 -       wh = (struct ieee80211_frame *)
674 -               skb_push(skb, sizeof(struct ieee80211_frame));
675 -       ieee80211_send_setup(vap, ni, wh,
676 +       wh = ieee80211_send_setup(vap, ni, skb,
677                 IEEE80211_FC0_TYPE_MGT | type,
678                 vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid);
679         /* XXX power management */
680 @@ -464,6 +481,9 @@
681         struct ieee80211_frame *wh;
682         u_int8_t *frm;
683  
684 +       if (ni->ni_subif)
685 +               vap = ni->ni_subif;
686 +
687         skb = ieee80211_getmgtframe(&frm, 0);
688         if (skb == NULL) {
689                 /* XXX debug msg */
690 @@ -472,9 +492,7 @@
691                 return -ENOMEM;
692         }
693  
694 -       wh = (struct ieee80211_frame *)
695 -               skb_push(skb, sizeof(struct ieee80211_frame));
696 -       ieee80211_send_setup(vap, ni, wh,
697 +       wh = ieee80211_send_setup(vap, ni, skb,
698                 IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
699                 vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid);
700         /* NB: power management bit is never sent by an AP */
701 @@ -512,6 +530,7 @@
702         struct sk_buff *skb;
703         struct ieee80211_qosframe *qwh;
704         u_int8_t *frm;
705 +       u_int8_t *i_qos;
706         int tid;
707  
708         skb = ieee80211_getmgtframe(&frm, 2);
709 @@ -523,11 +542,12 @@
710         SKB_CB(skb)->ni = ieee80211_ref_node(ni);
711  
712         skb->priority = ac;
713 -       qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe));
714  
715 -       qwh = (struct ieee80211_qosframe *)skb->data;
716 +       /* grab a pointer to QoS control and also compensate for the header length
717 +        * difference between QoS and non-QoS frame */
718 +       i_qos = skb_push(skb, sizeof(struct ieee80211_qosframe) - sizeof(struct ieee80211_frame));
719  
720 -       ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh,
721 +       qwh = (struct ieee80211_qosframe *) ieee80211_send_setup(vap, ni, skb,
722                 IEEE80211_FC0_TYPE_DATA,
723                 vap->iv_myaddr, /* SA */
724                 ni->ni_macaddr, /* DA */
725 @@ -541,10 +561,10 @@
726  
727         /* map from access class/queue to 11e header priority value */
728         tid = WME_AC_TO_TID(ac);
729 -       qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
730 +       i_qos[0] = tid & IEEE80211_QOS_TID;
731         if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
732                 qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY;
733 -       qwh->i_qos[1] = 0;
734 +       i_qos[1] = 0;
735  
736         IEEE80211_NODE_STAT(ni, tx_data);
737  
738 @@ -786,6 +806,8 @@
739                 hdrsize = sizeof(struct ieee80211_frame);
740  
741         SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE));
742 +       if (!SKB_CB(skb)->auth_pkt && ni->ni_subif)
743 +               vap = ni->ni_subif;
744  
745         switch (vap->iv_opmode) {
746         case IEEE80211_M_IBSS:
747 @@ -805,20 +827,9 @@
748                         ismulticast = IEEE80211_IS_MULTICAST(eh.ether_dhost);
749                 break;
750         case IEEE80211_M_STA:
751 -               if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
752 -                   !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) {
753 +               if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) {
754                         use4addr = 1;
755 -                       ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr);
756 -                       /* Add a WDS entry to the station VAP */
757 -                       if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
758 -                               struct ieee80211_node_table *nt = &ic->ic_sta;
759 -                               struct ieee80211_node *ni_wds 
760 -                                       = ieee80211_find_wds_node(nt, eh.ether_shost);
761 -                               if (ni_wds)
762 -                                       ieee80211_unref_node(&ni_wds);
763 -                               else
764 -                                       ieee80211_add_wds_addr(nt, ni, eh.ether_shost, 0);
765 -                       }
766 +                       ismulticast = 0;
767                 } else
768                         ismulticast = IEEE80211_IS_MULTICAST(vap->iv_bssid);
769                 break;
770 @@ -1689,9 +1700,7 @@
771  
772         SKB_CB(skb)->ni = ieee80211_ref_node(ni);
773  
774 -       wh = (struct ieee80211_frame *)
775 -               skb_push(skb, sizeof(struct ieee80211_frame));
776 -       ieee80211_send_setup(vap, ni, wh,
777 +       wh = ieee80211_send_setup(vap, ni, skb,
778                 IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
779                 sa, da, bssid);
780         /* XXX power management? */
781 --- a/net80211/ieee80211_linux.c
782 +++ b/net80211/ieee80211_linux.c
783 @@ -145,7 +145,7 @@
784         struct sk_buff *skb;
785         u_int len;
786  
787 -       len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
788 +       len = roundup(sizeof(struct ieee80211_frame_addr4) + pktlen, 4);
789  #ifdef IEEE80211_DEBUG_REFCNT
790         skb = ieee80211_dev_alloc_skb_debug(len + align - 1, func, line);
791  #else
792 @@ -161,7 +161,7 @@
793                 SKB_CB(skb)->flags = 0;
794                 SKB_CB(skb)->next = NULL;
795  
796 -               skb_reserve(skb, sizeof(struct ieee80211_frame));
797 +               skb_reserve(skb, sizeof(struct ieee80211_frame_addr4));
798                 *frm = skb_put(skb, pktlen);
799         }
800         return skb;