diff options
author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
---|---|---|
committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-10-05 10:12:53 +0000 |
commit | 5c105d9f3fd086aff195d3849dcf847d6b0bd927 (patch) | |
tree | 1229a11f725bfa58aa7c57a76898553bb5f6654a /package/madwifi/patches/370-wdsvap.patch | |
download | openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.tar.gz openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.zip |
branch Attitude Adjustment
git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/madwifi/patches/370-wdsvap.patch')
-rw-r--r-- | package/madwifi/patches/370-wdsvap.patch | 1665 |
1 files changed, 1665 insertions, 0 deletions
diff --git a/package/madwifi/patches/370-wdsvap.patch b/package/madwifi/patches/370-wdsvap.patch new file mode 100644 index 000000000..8a0e823ae --- /dev/null +++ b/package/madwifi/patches/370-wdsvap.patch @@ -0,0 +1,1665 @@ +--- a/ath/if_ath.c ++++ b/ath/if_ath.c +@@ -124,7 +124,7 @@ enum { + }; + + static struct ieee80211vap *ath_vap_create(struct ieee80211com *, +- const char *, int, int, struct net_device *); ++ const char *, int, int, struct net_device *, struct ieee80211vap *); + static void ath_vap_delete(struct ieee80211vap *); + static int ath_init(struct net_device *); + static int ath_set_ack_bitrate(struct ath_softc *, int); +@@ -1123,8 +1123,6 @@ ath_attach(u_int16_t devid, struct net_d + autocreatemode = IEEE80211_M_IBSS; + else if (!strcmp(autocreate, "ahdemo")) + autocreatemode = IEEE80211_M_AHDEMO; +- else if (!strcmp(autocreate, "wds")) +- autocreatemode = IEEE80211_M_WDS; + else if (!strcmp(autocreate, "monitor")) + autocreatemode = IEEE80211_M_MONITOR; + else { +@@ -1137,7 +1135,7 @@ ath_attach(u_int16_t devid, struct net_d + if (autocreatemode != -1) { + rtnl_lock(); + vap = ieee80211_create_vap(ic, "ath%d", dev, +- autocreatemode, 0); ++ autocreatemode, 0, NULL); + rtnl_unlock(); + if (vap == NULL) + EPRINTF(sc, "Autocreation of %s VAP failed.", autocreate); +@@ -1230,14 +1228,14 @@ ath_detach(struct net_device *dev) + + static struct ieee80211vap * + ath_vap_create(struct ieee80211com *ic, const char *name, +- int opmode, int flags, struct net_device *mdev) ++ int opmode, int flags, struct net_device *mdev, struct ieee80211vap *master) + { + struct ath_softc *sc = ic->ic_dev->priv; + struct ath_hal *ah = sc->sc_ah; + struct net_device *dev; + struct ath_vap *avp; + struct ieee80211vap *vap; +- int ic_opmode; ++ int ic_opmode = IEEE80211_M_STA; + + if (ic->ic_dev->flags & IFF_RUNNING) { + /* needs to disable hardware too */ +@@ -1271,8 +1269,12 @@ ath_vap_create(struct ieee80211com *ic, + } else + ic_opmode = opmode; + break; +- case IEEE80211_M_HOSTAP: + case IEEE80211_M_WDS: ++ ic_opmode = ic->ic_opmode; ++ if (!master) ++ return NULL; ++ break; ++ case IEEE80211_M_HOSTAP: + /* permit multiple APs and/or WDS links */ + /* XXX sta+ap for repeater/bridge application */ + if ((sc->sc_nvaps != 0) && (ic->ic_opmode == IEEE80211_M_STA)) +@@ -1304,7 +1306,7 @@ ath_vap_create(struct ieee80211com *ic, + } + + avp = dev->priv; +- ieee80211_vap_setup(ic, dev, name, opmode, flags); ++ ieee80211_vap_setup(ic, dev, name, opmode, flags, master); + /* override with driver methods */ + vap = &avp->av_vap; + avp->av_newstate = vap->iv_newstate; +@@ -4209,8 +4211,7 @@ ath_calcrxfilter(struct ath_softc *sc) + if (ic->ic_opmode == IEEE80211_M_STA || + sc->sc_opmode == HAL_M_IBSS || /* NB: AHDEMO too */ + (sc->sc_nostabeacons) || sc->sc_scanning || +- ((ic->ic_opmode == IEEE80211_M_HOSTAP) && +- (ic->ic_protmode != IEEE80211_PROT_NONE))) ++ (ic->ic_opmode == IEEE80211_M_HOSTAP)) + rfilt |= HAL_RX_FILTER_BEACON; + if (sc->sc_nmonvaps > 0) + rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON | +@@ -9032,8 +9033,6 @@ ath_calibrate(unsigned long arg) + * set sc->beacons if we might need to restart + * them after ath_reset. */ + if (!sc->sc_beacons && +- (TAILQ_FIRST(&ic->ic_vaps)->iv_opmode != +- IEEE80211_M_WDS) && + !txcont_was_active && + !sc->sc_dfs_cac) { + sc->sc_beacons = 1; +--- a/net80211/ieee80211.c ++++ b/net80211/ieee80211.c +@@ -373,10 +373,25 @@ void + ieee80211_ifdetach(struct ieee80211com *ic) + { + struct ieee80211vap *vap; ++ int count; ++ ++ /* bring down all vaps */ ++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { ++ ieee80211_stop(vap->iv_dev); ++ } ++ ++ /* wait for all subifs to disappear */ ++ do { ++ schedule(); ++ rtnl_lock(); ++ count = ic->ic_subifs; ++ rtnl_unlock(); ++ } while (count > 0); + + rtnl_lock(); +- while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) ++ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) { + ic->ic_vap_delete(vap); ++ } + rtnl_unlock(); + + del_timer(&ic->ic_dfs_excl_timer); +@@ -396,7 +411,7 @@ EXPORT_SYMBOL(ieee80211_ifdetach); + + int + ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev, +- const char *name, int opmode, int flags) ++ const char *name, int opmode, int flags, struct ieee80211vap *master) + { + #define IEEE80211_C_OPMODE \ + (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \ +@@ -510,9 +525,18 @@ ieee80211_vap_setup(struct ieee80211com + + vap->iv_monitor_crc_errors = 0; + vap->iv_monitor_phy_errors = 0; ++ TAILQ_INIT(&vap->iv_wdslinks); + +- IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr); +- IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr); ++ if (master && (vap->iv_opmode == IEEE80211_M_WDS)) { ++ vap->iv_master = master; ++ TAILQ_INSERT_TAIL(&master->iv_wdslinks, vap, iv_wdsnext); ++ /* use the same BSSID as the master interface */ ++ IEEE80211_ADDR_COPY(vap->iv_myaddr, vap->iv_master->iv_myaddr); ++ IEEE80211_ADDR_COPY(vap->iv_bssid, vap->iv_master->iv_myaddr); ++ } else { ++ IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr); ++ IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr); ++ } + /* NB: Defer setting dev_addr so driver can override */ + + ieee80211_crypto_vattach(vap); +@@ -547,7 +571,8 @@ ieee80211_vap_attach(struct ieee80211vap + ifmedia_set(&vap->iv_media, imr.ifm_active); + + IEEE80211_LOCK_IRQ(ic); +- TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); ++ if (vap->iv_opmode != IEEE80211_M_WDS) ++ TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); + IEEE80211_UNLOCK_IRQ(ic); + + IEEE80211_ADDR_COPY(dev->dev_addr, vap->iv_myaddr); +@@ -579,10 +604,27 @@ ieee80211_vap_detach(struct ieee80211vap + { + struct ieee80211com *ic = vap->iv_ic; + struct net_device *dev = vap->iv_dev; ++ struct ieee80211vap *avp; ++ ++ /* Drop all WDS links that belong to this vap */ ++ while ((avp = TAILQ_FIRST(&vap->iv_wdslinks)) != NULL) { ++ if (avp->iv_state != IEEE80211_S_INIT) ++ ieee80211_stop(avp->iv_dev); ++ ic->ic_vap_delete(avp); ++ } + + IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq); + IEEE80211_LOCK_IRQ(ic); +- TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); ++ if (vap->iv_wdsnode) { ++ vap->iv_wdsnode->ni_subif = NULL; ++ ieee80211_unref_node(&vap->iv_wdsnode); ++ } ++ if ((vap->iv_opmode == IEEE80211_M_WDS) && ++ (vap->iv_master != NULL)) ++ TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext); ++ else ++ TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); ++ + if (TAILQ_EMPTY(&ic->ic_vaps)) /* reset to supported mode */ + ic->ic_opmode = IEEE80211_M_STA; + IEEE80211_UNLOCK_IRQ(ic); +--- a/net80211/ieee80211_ioctl.h ++++ b/net80211/ieee80211_ioctl.h +@@ -474,7 +474,7 @@ struct ieee80211req { + #define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */ + #define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */ + #define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */ +-#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */ ++#define IEEE80211_IOC_SETMAC 55 /* set interface wds mac addr */ + #define IEEE80211_IOC_FF 56 /* ATH fast frames (on, off) */ + #define IEEE80211_IOC_TURBOP 57 /* ATH turbo' (on, off) */ + #define IEEE80211_IOC_APPIEBUF 58 /* IE in the management frame */ +@@ -552,8 +552,8 @@ struct ieee80211req_scan_result { + #define IEEE80211_IOCTL_HALMAP (SIOCIWFIRSTPRIV+21) + #define IEEE80211_IOCTL_ADDMAC (SIOCIWFIRSTPRIV+22) + #define IEEE80211_IOCTL_DELMAC (SIOCIWFIRSTPRIV+24) +-#define IEEE80211_IOCTL_WDSADDMAC (SIOCIWFIRSTPRIV+26) +-#define IEEE80211_IOCTL_WDSDELMAC (SIOCIWFIRSTPRIV+28) ++#define IEEE80211_IOCTL_WDSADDMAC (SIOCIWFIRSTPRIV+25) ++#define IEEE80211_IOCTL_WDSSETMAC (SIOCIWFIRSTPRIV+26) + #define IEEE80211_IOCTL_KICKMAC (SIOCIWFIRSTPRIV+30) + #define IEEE80211_IOCTL_SETSCANLIST (SIOCIWFIRSTPRIV+31) + +@@ -649,6 +649,7 @@ enum { + IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */ + IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */ + IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */ ++ IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */ + }; + + #define SIOCG80211STATS (SIOCDEVPRIVATE+2) +--- a/net80211/ieee80211_linux.h ++++ b/net80211/ieee80211_linux.h +@@ -81,6 +81,12 @@ set_quality(struct iw_quality *iq, u_int + #endif + } + ++#ifndef container_of ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++#endif ++ + /* + * Task deferral + * +@@ -113,6 +119,29 @@ typedef void *IEEE80211_TQUEUE_ARG; + + #define IEEE80211_RESCHEDULE schedule + ++#include <linux/sched.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) ++#include <linux/tqueue.h> ++#define work_struct tq_struct ++#define schedule_work(t) schedule_task((t)) ++#define flush_scheduled_work() flush_scheduled_tasks() ++#define IEEE80211_INIT_WORK(t, f) do { \ ++ memset((t), 0, sizeof(struct tq_struct)); \ ++ (t)->routine = (void (*)(void*)) (f); \ ++ (t)->data=(void *) (t); \ ++} while (0) ++#else ++#include <linux/workqueue.h> ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t)); ++#else ++#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (_f)); ++#endif ++ ++#endif /* KERNEL_VERSION < 2.5.41 */ ++ ++ + /* Locking */ + /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP) + * because spinlocks do not exist in this configuration. Instead IRQs +@@ -167,6 +196,14 @@ typedef spinlock_t ieee80211com_lock_t; + IEEE80211_VAPS_LOCK_ASSERT(_ic); \ + spin_unlock_bh(&(_ic)->ic_vapslock); \ + } while (0) ++#define IEEE80211_VAPS_LOCK_IRQ(_ic) do { \ ++ unsigned long __ilockflags; \ ++ IEEE80211_VAPS_LOCK_CHECK(_ic); \ ++ spin_lock_irqsave(&(_ic)->ic_vapslock, __ilockflags); ++#define IEEE80211_VAPS_UNLOCK_IRQ(_ic) \ ++ IEEE80211_VAPS_LOCK_ASSERT(_ic); \ ++ spin_unlock_irqrestore(&(_ic)->ic_vapslock, __ilockflags); \ ++} while (0) + + #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked) + #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \ +@@ -650,5 +687,5 @@ struct ifreq; + int ieee80211_ioctl_create_vap(struct ieee80211com *, struct ifreq *, + struct net_device *); + struct ieee80211vap *ieee80211_create_vap(struct ieee80211com *, char *, +- struct net_device *, int, int); ++ struct net_device *, int, int, struct ieee80211vap *); + #endif /* _NET80211_IEEE80211_LINUX_H_ */ +--- a/net80211/ieee80211_var.h ++++ b/net80211/ieee80211_var.h +@@ -187,6 +187,12 @@ struct ieee80211vap { + struct ieee80211_proc_entry *iv_proc_entries; + struct vlan_group *iv_vlgrp; /* vlan group state */ + ++ /* list of wds links */ ++ TAILQ_HEAD(, ieee80211vap) iv_wdslinks; ++ TAILQ_ENTRY(ieee80211vap) iv_wdsnext; ++ struct ieee80211vap *iv_master; ++ struct ieee80211_node *iv_wdsnode; ++ + TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */ + struct ieee80211com *iv_ic; /* back ptr to common state */ + u_int32_t iv_debug; /* debug msg flags */ +@@ -316,6 +322,7 @@ struct ieee80211com { + u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; + struct timer_list ic_inact; /* mgmt/inactivity timer */ + ++ unsigned int ic_subifs; + u_int32_t ic_flags; /* state flags */ + u_int32_t ic_flags_ext; /* extension of state flags */ + u_int32_t ic_caps; /* capabilities */ +@@ -447,7 +454,7 @@ struct ieee80211com { + atomic_t ic_node_counter; + /* Virtual AP create/delete */ + struct ieee80211vap *(*ic_vap_create)(struct ieee80211com *, +- const char *, int, int, struct net_device *); ++ const char *, int, int, struct net_device *, struct ieee80211vap *); + void (*ic_vap_delete)(struct ieee80211vap *); + + /* Send/recv 802.11 management frame */ +@@ -619,6 +626,7 @@ MALLOC_DECLARE(M_80211_VAP); + #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */ + #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */ + #define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */ ++#define IEEE80211_FEXT_WDSSEP 0x00004000 /* move wds clients into separate interfaces */ + + #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD) + #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD) +@@ -703,7 +711,7 @@ MALLOC_DECLARE(M_80211_VAP); + int ieee80211_ifattach(struct ieee80211com *); + void ieee80211_ifdetach(struct ieee80211com *); + int ieee80211_vap_setup(struct ieee80211com *, struct net_device *, +- const char *, int, int); ++ const char *, int, int, struct ieee80211vap *); + int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t); + void ieee80211_vap_detach(struct ieee80211vap *); + void ieee80211_mark_dfs(struct ieee80211com *, struct ieee80211_channel *); +--- a/net80211/ieee80211_wireless.c ++++ b/net80211/ieee80211_wireless.c +@@ -2190,7 +2190,7 @@ ieee80211_setupxr(struct ieee80211vap *v + ieee80211_scan_flush(ic); /* NB: could optimize */ + + if (!(xrvap = ic->ic_vap_create(ic, name, IEEE80211_M_HOSTAP, +- IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev))) ++ IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev, NULL))) + return; + + /* We use iv_xrvap to link to the parent VAP as well */ +@@ -2867,6 +2867,14 @@ ieee80211_ioctl_setparam(struct net_devi + else + vap->iv_minrateindex = 0; + break; ++ case IEEE80211_PARAM_WDS_SEP: ++ if (vap->iv_opmode != IEEE80211_M_HOSTAP) ++ retv = -EINVAL; ++ else if (value) ++ vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP; ++ else ++ vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP; ++ break; + #ifdef ATH_REVERSE_ENGINEERING + case IEEE80211_PARAM_DUMPREGS: + ieee80211_dump_registers(dev, info, w, extra); +@@ -3223,6 +3231,9 @@ ieee80211_ioctl_getparam(struct net_devi + case IEEE80211_PARAM_MINRATE: + param[0] = vap->iv_minrateindex; + break; ++ case IEEE80211_PARAM_WDS_SEP: ++ param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP); ++ break; + default: + return -EOPNOTSUPP; + } +@@ -3801,74 +3812,54 @@ ieee80211_ioctl_setmlme(struct net_devic + return 0; + } + ++#define WDSNAME ".wds%d" + static int +-ieee80211_ioctl_wdsmac(struct net_device *dev, struct iw_request_info *info, ++ieee80211_ioctl_wdsaddmac(struct net_device *dev, struct iw_request_info *info, + void *w, char *extra) + { + struct ieee80211vap *vap = dev->priv; + struct sockaddr *sa = (struct sockaddr *)extra; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct ieee80211vap *avp; ++ char *name; + +- if (!IEEE80211_ADDR_NULL(vap->wds_mac)) { +- printk("%s: Failed to add WDS MAC: " MAC_FMT "\n", dev->name, +- MAC_ADDR(sa->sa_data)); +- printk("%s: Device already has WDS mac address attached," +- " remove first\n", dev->name); +- return -1; +- } +- +- memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN); ++ if (vap->iv_opmode != IEEE80211_M_HOSTAP) ++ return -EINVAL; + +- printk("%s: Added WDS MAC: " MAC_FMT "\n", dev->name, +- MAC_ADDR(vap->wds_mac)); ++ name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSNAME) + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; + +- if (IS_UP(vap->iv_dev)) { +- /* Force us back to scan state to force us to go back through RUN +- * state and create/pin the WDS peer node into memory. */ +- return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); +- } ++ strcpy(name, vap->iv_dev->name); ++ strcat(name, WDSNAME); ++ avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap); ++ kfree(name); ++ if (!avp) ++ return -ENOMEM; + ++ memcpy(avp->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN); + return 0; + } ++#undef WDSNAME + + static int +-ieee80211_ioctl_wdsdelmac(struct net_device *dev, struct iw_request_info *info, ++ieee80211_ioctl_wdssetmac(struct net_device *dev, struct iw_request_info *info, + void *w, char *extra) + { + struct ieee80211vap *vap = dev->priv; + struct sockaddr *sa = (struct sockaddr *)extra; +- struct ieee80211com *ic = vap->iv_ic; +- struct ieee80211_node *wds_ni; + +- /* WDS Mac address filed already? */ +- if (IEEE80211_ADDR_NULL(vap->wds_mac)) +- return 0; ++ if (vap->iv_opmode != IEEE80211_M_WDS) ++ return -EINVAL; + +- /* Compare suplied MAC address with WDS MAC of this interface +- * remove when mac address is known +- */ +- if (memcmp(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN) == 0) { +- if (IS_UP(vap->iv_dev)) { +- wds_ni = ieee80211_find_txnode(vap, vap->wds_mac); +- if (wds_ni != NULL) { +- /* Release reference created by find node */ +- ieee80211_unref_node(&wds_ni); +- /* Release reference created by transition to RUN state, +- * [pinning peer node into the table] */ +- ieee80211_unref_node(&wds_ni); +- } +- } +- memset(vap->wds_mac, 0x00, IEEE80211_ADDR_LEN); +- if (IS_UP(vap->iv_dev)) { +- /* This leaves a dead WDS node, until started again */ +- return ic->ic_reset(ic->ic_dev); +- } +- return 0; ++ memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN); ++ if (IS_UP(vap->iv_dev)) { ++ /* Force us back to scan state to force us to go back through RUN ++ * state and create/pin the WDS peer node into memory. */ ++ return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } + +- printk("%s: WDS MAC address " MAC_FMT " is not known by this interface\n", +- dev->name, MAC_ADDR(sa->sa_data)); +- +- return -1; ++ return 0; + } + + /* +@@ -4470,6 +4461,8 @@ get_sta_space(void *arg, struct ieee8021 + struct ieee80211vap *vap = ni->ni_vap; + size_t ielen; + ++ if (req->vap->iv_wdsnode && ni->ni_subif) ++ vap = ni->ni_subif; + if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap */ + return; + if ((vap->iv_opmode == IEEE80211_M_HOSTAP || +@@ -4489,6 +4482,8 @@ get_sta_info(void *arg, struct ieee80211 + size_t ielen, len; + u_int8_t *cp; + ++ if (req->vap->iv_wdsnode && ni->ni_subif) ++ vap = ni->ni_subif; + if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap (or) xrvap */ + return; + if ((vap->iv_opmode == IEEE80211_M_HOSTAP || +@@ -5391,8 +5386,8 @@ static const struct iw_priv_args ieee802 + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac"}, + { IEEE80211_IOCTL_WDSADDMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_add" }, +- { IEEE80211_IOCTL_WDSDELMAC, +- IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_del" }, ++ { IEEE80211_IOCTL_WDSSETMAC, ++ IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_set" }, + { IEEE80211_IOCTL_SETCHANLIST, + IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED, 0,"setchanlist" }, + { IEEE80211_IOCTL_GETCHANLIST, +@@ -5790,6 +5785,10 @@ static const struct iw_priv_args ieee802 + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"}, + { IEEE80211_IOCTL_SETSCANLIST, + IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"}, ++ { IEEE80211_PARAM_WDS_SEP, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"}, ++ { IEEE80211_PARAM_WDS_SEP, ++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"}, + + #ifdef ATH_REVERSE_ENGINEERING + /* +@@ -5884,8 +5883,8 @@ static const iw_handler ieee80211_priv_h + #endif + set_priv(IEEE80211_IOCTL_ADDMAC, ieee80211_ioctl_addmac), + set_priv(IEEE80211_IOCTL_DELMAC, ieee80211_ioctl_delmac), +- set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsmac), +- set_priv(IEEE80211_IOCTL_WDSDELMAC, ieee80211_ioctl_wdsdelmac), ++ set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsaddmac), ++ set_priv(IEEE80211_IOCTL_WDSSETMAC, ieee80211_ioctl_wdssetmac), + set_priv(IEEE80211_IOCTL_KICKMAC, ieee80211_ioctl_kickmac), + set_priv(IEEE80211_IOCTL_SETSCANLIST, ieee80211_ioctl_setscanlist), + #ifdef ATH_REVERSE_ENGINEERING +@@ -5913,6 +5912,8 @@ static int + ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + { + struct ieee80211vap *vap = dev->priv; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct ieee80211_node *ni; + + switch (cmd) { + case SIOCG80211STATS: +@@ -5921,8 +5922,20 @@ ieee80211_ioctl(struct net_device *dev, + case SIOC80211IFDESTROY: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; ++ /* drop all node subifs */ ++ TAILQ_FOREACH(ni, &ic->ic_sta.nt_node, ni_list) { ++ struct ieee80211vap *avp = ni->ni_subif; ++ ++ if (ni->ni_vap != vap) ++ continue; ++ if (!avp) ++ continue; ++ ni->ni_subif = NULL; ++ ieee80211_stop(avp->iv_dev); ++ ic->ic_vap_delete(avp); ++ } + ieee80211_stop(vap->iv_dev); /* force state before cleanup */ +- vap->iv_ic->ic_vap_delete(vap); ++ ic->ic_vap_delete(vap); + return 0; + case IEEE80211_IOCTL_GETKEY: + return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr); +@@ -5956,7 +5969,7 @@ ieee80211_ioctl_create_vap(struct ieee80 + + strncpy(name, cp.icp_name, sizeof(name)); + +- vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags); ++ vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags, NULL); + if (vap == NULL) + return -EIO; + +@@ -5973,9 +5986,9 @@ EXPORT_SYMBOL(ieee80211_ioctl_create_vap + */ + struct ieee80211vap* + ieee80211_create_vap(struct ieee80211com *ic, char *name, +- struct net_device *mdev, int opmode, int opflags) ++ struct net_device *mdev, int opmode, int opflags, struct ieee80211vap *master) + { +- return ic->ic_vap_create(ic, name, opmode, opflags, mdev); ++ return ic->ic_vap_create(ic, name, opmode, opflags, mdev, master); + } + EXPORT_SYMBOL(ieee80211_create_vap); + +--- a/net80211/ieee80211_input.c ++++ b/net80211/ieee80211_input.c +@@ -199,8 +199,10 @@ ieee80211_input(struct ieee80211vap * va + { + #define HAS_SEQ(type) ((type & 0x4) == 0) + struct ieee80211_node * ni = ni_or_null; +- struct ieee80211com *ic = vap->iv_ic; +- struct net_device *dev = vap->iv_dev; ++ struct ieee80211com *ic; ++ struct net_device *dev; ++ struct ieee80211_node *ni_wds = NULL; ++ struct net_device_stats *stats; + struct ieee80211_frame *wh; + struct ieee80211_key *key; + struct ether_header *eh; +@@ -212,6 +214,19 @@ ieee80211_input(struct ieee80211vap * va + u_int8_t *bssid; + u_int16_t rxseq; + ++ type = -1; /* undefined */ ++ ++ if (!vap) ++ goto out; ++ ++ ic = vap->iv_ic; ++ if (!ic) ++ goto out; ++ ++ dev = vap->iv_dev; ++ if (!dev) ++ goto out; ++ + /* initialize ni as in the previous API */ + if (ni_or_null == NULL) { + /* This function does not 'own' vap->iv_bss, so we cannot +@@ -227,7 +242,6 @@ ieee80211_input(struct ieee80211vap * va + + /* XXX adjust device in sk_buff? */ + +- type = -1; /* undefined */ + /* + * In monitor mode, send everything directly to bpf. + * Also do not process frames w/o i_addr2 any further. +@@ -434,7 +448,7 @@ ieee80211_input(struct ieee80211vap * va + + switch (type) { + case IEEE80211_FC0_TYPE_DATA: +- hdrspace = ieee80211_hdrspace(ic, wh); ++ hdrspace = ieee80211_hdrsize(wh); + if (skb->len < hdrspace) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "data", "too short: len %u, expecting %u", +@@ -444,16 +458,24 @@ ieee80211_input(struct ieee80211vap * va + } + switch (vap->iv_opmode) { + case IEEE80211_M_STA: +- if ((dir != IEEE80211_FC1_DIR_FROMDS) && +- (!((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && +- (dir == IEEE80211_FC1_DIR_DSTODS)))) { ++ switch(dir) { ++ case IEEE80211_FC1_DIR_FROMDS: ++ break; ++ case IEEE80211_FC1_DIR_DSTODS: ++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) ++ break; ++ default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "data", "invalid dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + +- if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { ++ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { ++ /* ignore 3-addr mcast if we're WDS STA */ ++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) ++ goto out; ++ + /* Discard multicast if IFF_MULTICAST not set */ + if ((0 != memcmp(wh->i_addr3, dev->broadcast, ETH_ALEN)) && + (0 == (dev->flags & IFF_MULTICAST))) { +@@ -481,24 +503,10 @@ ieee80211_input(struct ieee80211vap * va + vap->iv_stats.is_rx_mcastecho++; + goto out; + } +- /* +- * if it is brodcasted by me on behalf of +- * a station behind me, drop it. +- */ +- if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) { +- struct ieee80211_node_table *nt; +- struct ieee80211_node *ni_wds; +- nt = &ic->ic_sta; +- ni_wds = ieee80211_find_wds_node(nt, wh->i_addr3); +- if (ni_wds) { +- ieee80211_unref_node(&ni_wds); +- IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, +- wh, NULL, "%s", +- "multicast echo originated from node behind me"); +- vap->iv_stats.is_rx_mcastecho++; +- goto out; +- } +- } ++ } else { ++ /* Same BSSID, but not meant for us to receive */ ++ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) ++ goto out; + } + break; + case IEEE80211_M_IBSS: +@@ -540,16 +548,28 @@ ieee80211_input(struct ieee80211vap * va + vap->iv_stats.is_rx_notassoc++; + goto err; + } ++ + /* + * If we're a 4 address packet, make sure we have an entry in + * the node table for the packet source address (addr4). + * If not, add one. + */ ++ /* check for wds link first */ ++ if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni->ni_subif) { ++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP) { ++ ieee80211_wds_addif(ni); ++ /* we must drop frames here until the interface has ++ * been fully separated, otherwise a bridge might get ++ * confused */ ++ goto err; ++ } ++ } ++ + /* XXX: Useless node mgmt API; make better */ +- if (dir == IEEE80211_FC1_DIR_DSTODS) { +- struct ieee80211_node_table *nt; ++ if ((dir == IEEE80211_FC1_DIR_DSTODS) && !vap->iv_wdsnode && ++ !ni_wds && !ni->ni_subif) { ++ struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_frame_addr4 *wh4; +- struct ieee80211_node *ni_wds; + + if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, +@@ -557,7 +577,6 @@ ieee80211_input(struct ieee80211vap * va + goto err; + } + wh4 = (struct ieee80211_frame_addr4 *)skb->data; +- nt = &ic->ic_sta; + ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4); + /* Last call increments ref count if !NULL */ + if ((ni_wds != NULL) && (ni_wds != ni)) { +@@ -608,6 +627,11 @@ ieee80211_input(struct ieee80211vap * va + goto out; + } + ++ /* check if there is any data left */ ++ hdrspace = ieee80211_hdrspace(ic, wh); ++ if (skb->len < hdrspace) ++ goto out; ++ + /* + * Handle privacy requirements. Note that we + * must not be preempted from here until after +@@ -680,8 +704,12 @@ ieee80211_input(struct ieee80211vap * va + if (! accept_data_frame(vap, ni, key, skb, eh)) + goto out; + +- vap->iv_devstats.rx_packets++; +- vap->iv_devstats.rx_bytes += skb->len; ++ if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) ++ stats = &ni->ni_subif->iv_devstats; ++ else ++ stats = &vap->iv_devstats; ++ stats->rx_packets++; ++ stats->rx_bytes += skb->len; + IEEE80211_NODE_STAT(ni, rx_data); + IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len); + ic->ic_lastdata = jiffies; +@@ -1114,6 +1142,18 @@ ieee80211_deliver_data(struct ieee80211_ + dev = vap->iv_xrvap->iv_dev; + #endif + ++ /* if the node has a wds subif, move data frames there, ++ * but keep EAP traffic on the master */ ++ if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) { ++ if (ni->ni_vap == ni->ni_subif) { ++ ieee80211_dev_kfree_skb(&skb); ++ return; ++ } else { ++ vap = ni->ni_subif; ++ dev = vap->iv_dev; ++ } ++ } ++ + /* perform as a bridge within the vap */ + /* XXX intra-vap bridging only */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP && +@@ -1139,7 +1179,16 @@ ieee80211_deliver_data(struct ieee80211_ + if (ni1 != NULL) { + if (ni1->ni_vap == vap && + ieee80211_node_is_authorized(ni1) && ++ !ni1->ni_subif && + ni1 != vap->iv_bss) { ++ ++ /* tried to bridge to a subif, drop the packet */ ++ if (ni->ni_subif) { ++ ieee80211_unref_node(&ni1); ++ ieee80211_dev_kfree_skb(&skb); ++ return; ++ } ++ + skb1 = skb; + skb = NULL; + } +@@ -3084,8 +3133,7 @@ ieee80211_recv_mgmt(struct ieee80211vap + (vap->iv_opmode == IEEE80211_M_STA && ni->ni_associd) || + (vap->iv_opmode == IEEE80211_M_IBSS) || + ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) && +- (vap->iv_opmode == IEEE80211_M_HOSTAP) && +- (ic->ic_protmode != IEEE80211_PROT_NONE)))) { ++ (vap->iv_opmode == IEEE80211_M_HOSTAP)))) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } +@@ -3471,13 +3519,56 @@ ieee80211_recv_mgmt(struct ieee80211vap + */ + if (ic->ic_flags & IEEE80211_F_SCAN) { + ieee80211_add_scan(vap, &scan, wh, subtype, rssi, rtsf); +- return; + } +- if ((vap->iv_opmode == IEEE80211_M_IBSS) && +- (scan.capinfo & IEEE80211_CAPINFO_IBSS)) { ++ /* NB: Behavior of WDS-Link and Ad-Hoc is very similar here: ++ * When we receive a beacon that belongs to the AP that we're ++ * connected to, use it to refresh the local node info. ++ * If no node is found, go through the vap's wds link table ++ * and try to find the sub-vap that is interested in this address ++ */ ++ if (((vap->iv_opmode == IEEE80211_M_IBSS) && ++ (scan.capinfo & IEEE80211_CAPINFO_IBSS)) || ++ (((vap->iv_opmode == IEEE80211_M_HOSTAP) || ++ (vap->iv_opmode == IEEE80211_M_WDS)) && ++ (scan.capinfo & IEEE80211_CAPINFO_ESS))) { ++ struct ieee80211vap *avp = NULL; ++ int found = 0; ++ ++ IEEE80211_LOCK_IRQ(vap->iv_ic); ++ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { ++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { ++ if (!memcmp(avp->wds_mac, wh->i_addr2, IEEE80211_ADDR_LEN)) { ++ if (avp->iv_state != IEEE80211_S_RUN) ++ continue; ++ if (!avp->iv_wdsnode) ++ continue; ++ found = 1; ++ break; ++ } ++ } ++ if (found) ++ ni = ni_or_null = avp->iv_wdsnode; ++ } else if ((vap->iv_opmode == IEEE80211_M_WDS) && vap->iv_wdsnode) { ++ found = 1; ++ ni = ni_or_null = vap->iv_wdsnode; ++ } ++ IEEE80211_UNLOCK_IRQ(vap->iv_ic); ++ ++ if (!found) ++ break; ++ + if (ni_or_null == NULL) { +- /* Create a new entry in the neighbor table. */ +- ni = ieee80211_add_neighbor(vap, wh, &scan); ++ if (avp) { ++ IEEE80211_LOCK_IRQ(ic); ++ ni = ieee80211_add_neighbor(avp, wh, &scan); ++ /* force assoc */ ++ ni->ni_associd |= 0xc000; ++ avp->iv_wdsnode = ieee80211_ref_node(ni); ++ IEEE80211_UNLOCK_IRQ(ic); ++ } else if (vap->iv_opmode == IEEE80211_M_IBSS) { ++ /* Create a new entry in the neighbor table. */ ++ ni = ieee80211_add_neighbor(vap, wh, &scan); ++ } + } else { + /* + * Copy data from beacon to neighbor table. +@@ -3490,6 +3581,7 @@ ieee80211_recv_mgmt(struct ieee80211vap + IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); + memcpy(ni->ni_tstamp.data, scan.tstamp, + sizeof(ni->ni_tstamp)); ++ ni->ni_inact = ni->ni_inact_reload; + ni->ni_intval = + IEEE80211_BINTVAL_SANITISE(scan.bintval); + ni->ni_capinfo = scan.capinfo; +--- a/net80211/ieee80211_node.c ++++ b/net80211/ieee80211_node.c +@@ -47,6 +47,7 @@ + #include <linux/netdevice.h> + #include <linux/etherdevice.h> + #include <linux/random.h> ++#include <linux/rtnetlink.h> + + #include "if_media.h" + +@@ -236,7 +237,11 @@ void + ieee80211_node_vdetach(struct ieee80211vap *vap) + { + struct ieee80211com *ic = vap->iv_ic; ++ struct ieee80211_node *ni; + ++ ni = vap->iv_wdsnode; ++ if (ni) ++ ni->ni_subif = NULL; + ieee80211_node_table_reset(&ic->ic_sta, vap); + if (vap->iv_bss != NULL) { + ieee80211_unref_node(&vap->iv_bss); +@@ -309,7 +314,7 @@ ieee80211_create_ibss(struct ieee80211va + /* Check to see if we already have a node for this mac + * NB: we gain a node reference here + */ +- ni = ieee80211_find_node(&ic->ic_sta, vap->iv_myaddr); ++ ni = ieee80211_find_txnode(vap, vap->iv_myaddr); + if (ni == NULL) { + ni = ieee80211_alloc_node_table(vap, vap->iv_myaddr); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, +@@ -831,12 +836,18 @@ node_table_leave_locked(struct ieee80211 + LIST_REMOVE(ni, ni_hash); + } + ni->ni_table = NULL; ++ if (ni->ni_vap->iv_wdsnode == ni) { ++#ifdef IEEE80211_DEBUG_REFCNT ++ ieee80211_unref_node_debug(&ni->ni_vap->iv_wdsnode, func, line); ++#else ++ ieee80211_unref_node(&ni->ni_vap->iv_wdsnode); ++#endif ++ } + #ifdef IEEE80211_DEBUG_REFCNT + ieee80211_unref_node_debug(&ni, func, line); + #else + ieee80211_unref_node(&ni); + #endif +- + } + + /* This is overridden by ath_node_alloc in ath/if_ath.c, and so +@@ -1134,6 +1145,65 @@ ieee80211_alloc_node(struct ieee80211vap + return ni; + } + ++#define WDSIFNAME ".sta%d" ++static void ++ieee80211_wds_do_addif(struct work_struct *work) ++{ ++ struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_create); ++ struct ieee80211vap *vap = ni->ni_vap; ++ struct ieee80211com *ic = vap->iv_ic; ++ struct ieee80211vap *avp = NULL; ++ char *name; ++ ++ rtnl_lock(); ++ /* did we get cancelled by the destroy call? */ ++ if (!ni->ni_subif) ++ goto done; ++ ++ ni->ni_subif = NULL; ++ name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL); ++ if (!name) ++ goto done; ++ ++ strcpy(name, vap->iv_dev->name); ++ strcat(name, WDSIFNAME); ++ avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap); ++ kfree(name); ++ if (!avp) ++ goto done; ++ ++ memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN); ++ avp->iv_wdsnode = ieee80211_ref_node(ni); ++ ni->ni_subif = avp; ++ ic->ic_subifs++; ++ ++done: ++ if (avp) { ++ IEEE80211_VAPS_LOCK_IRQ(ic); ++ avp->iv_newstate(vap, IEEE80211_S_RUN, -1); ++ IEEE80211_VAPS_UNLOCK_IRQ(ic); ++ } ++ rtnl_unlock(); ++ ieee80211_unref_node(&ni); ++} ++#undef WDSIFNAME ++ ++void ieee80211_wds_addif(struct ieee80211_node *ni) ++{ ++ /* check if the node is split out already, ++ * or if we're in progress of setting up a new interface already */ ++ if (ni->ni_subif) ++ return; ++ ++ if (!ni->ni_table) ++ return; ++ ++ ieee80211_ref_node(ni); ++ ni->ni_subif = ni->ni_vap; ++ IEEE80211_INIT_WORK(&ni->ni_create, ieee80211_wds_do_addif); ++ schedule_work(&ni->ni_create); ++} ++ + /* Add wds address to the node table */ + int + #ifdef IEEE80211_DEBUG_REFCNT +@@ -1553,22 +1623,39 @@ ieee80211_find_rxnode(struct ieee80211co + ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; ++ struct ieee80211vap *vap, *avp; ++ const u_int8_t *addr; ++ ++ if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) ++ addr = wh->i_addr1; ++ else ++ addr = wh->i_addr2; ++ ++ if (IEEE80211_IS_MULTICAST(addr)) ++ return NULL; + + /* XXX check ic_bss first in station mode */ + /* XXX 4-address frames? */ + nt = &ic->ic_sta; + IEEE80211_NODE_TABLE_LOCK_IRQ(nt); +- if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) +-#ifdef IEEE80211_DEBUG_REFCNT +- ni = ieee80211_find_node_locked_debug(nt, wh->i_addr1, func, line); +-#else +- ni = ieee80211_find_node_locked(nt, wh->i_addr1); +-#endif +- else ++ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) { ++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { ++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { ++ if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac)) ++ continue; ++ ++ if (avp->iv_wdsnode) ++ return ieee80211_ref_node(avp->iv_wdsnode); ++ else ++ return NULL; ++ } ++ } ++ } ++ + #ifdef IEEE80211_DEBUG_REFCNT +- ni = ieee80211_find_node_locked_debug(nt, wh->i_addr2, func, line); ++ ni = ieee80211_find_node_locked_debug(nt, addr, func, line); + #else +- ni = ieee80211_find_node_locked(nt, wh->i_addr2); ++ ni = ieee80211_find_node_locked(nt, addr); + #endif + IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt); + +@@ -1596,9 +1683,19 @@ ieee80211_find_txnode_debug(struct ieee8 + ieee80211_find_txnode(struct ieee80211vap *vap, const u_int8_t *mac) + #endif + { ++ struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node_table *nt; + struct ieee80211_node *ni = NULL; + ++ IEEE80211_LOCK_IRQ(ic); ++ if (vap->iv_opmode == IEEE80211_M_WDS) { ++ if (vap->iv_wdsnode && (vap->iv_state == IEEE80211_S_RUN)) ++ return ieee80211_ref_node(vap->iv_wdsnode); ++ else ++ return NULL; ++ } ++ IEEE80211_UNLOCK_IRQ(ic); ++ + /* + * The destination address should be in the node table + * unless we are operating in station mode or this is a +@@ -1669,6 +1766,11 @@ ieee80211_free_node(struct ieee80211_nod + { + struct ieee80211vap *vap = ni->ni_vap; + ++ IEEE80211_LOCK_IRQ(ni->ni_ic); ++ if (vap && ni == vap->iv_wdsnode) ++ vap->iv_wdsnode = NULL; ++ IEEE80211_UNLOCK_IRQ(ni->ni_ic); ++ + atomic_dec(&ni->ni_ic->ic_node_counter); + node_print_message(IEEE80211_MSG_NODE|IEEE80211_MSG_NODE_REF, + 1 /* show counter */, +@@ -1781,22 +1883,6 @@ restart: + jiffies > ni->ni_rxfragstamp + HZ) { + ieee80211_dev_kfree_skb(&ni->ni_rxfrag); + } +- /* +- * Special case ourself; we may be idle for extended periods +- * of time and regardless reclaiming our state is wrong. +- * Special case a WDS link: it may be dead or idle, but it is +- * never ok to reclaim it, as this will block transmissions +- * and nobody will recreate the node when the WDS peer is +- * available again. */ +- if ((ni == ni->ni_vap->iv_bss) || +- (ni->ni_vap->iv_opmode == IEEE80211_M_WDS && +- !memcmp(ni->ni_macaddr, ni->ni_vap->wds_mac, ETH_ALEN))) +- { +- /* NB: don't permit it to go negative */ +- if (ni->ni_inact > 0) +- ni->ni_inact--; +- continue; +- } + ni->ni_inact--; + if (ni->ni_associd != 0 || isadhoc) { + struct ieee80211vap *vap = ni->ni_vap; +@@ -2263,6 +2349,35 @@ ieee80211_node_leave_11g(struct ieee8021 + } + } + ++static void ++ieee80211_subif_destroy(struct work_struct *work) ++{ ++ struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy); ++ struct ieee80211vap *vap; ++ struct ieee80211com *ic; ++ ++ /* wait for full initialization before we start the teardown ++ * otherwise we could leak interfaces */ ++ while (ni->ni_subif == ni->ni_vap) ++ schedule(); ++ ++ rtnl_lock(); ++ vap = ni->ni_subif; ++ ++ if (!vap) ++ goto done; ++ ++ ic = vap->iv_ic; ++ ni->ni_subif = NULL; ++ ieee80211_stop(vap->iv_dev); ++ ic->ic_vap_delete(vap); ++ ic->ic_subifs--; ++ ++done: ++ ieee80211_unref_node(&ni); ++ rtnl_unlock(); ++} ++ + /* + * Handle bookkeeping for a station/neighbor leaving + * the bss when operating in ap or adhoc modes. +@@ -2279,6 +2394,12 @@ ieee80211_node_leave(struct ieee80211_no + ni, "station with aid %d leaves (refcnt %u)", + IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt)); + ++ if (ni->ni_subif) { ++ ieee80211_ref_node(ni); ++ IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy); ++ schedule_work(&ni->ni_destroy); ++ } ++ + /* From this point onwards we can no longer find the node, + * so no more references are generated + */ +--- a/net80211/ieee80211_output.c ++++ b/net80211/ieee80211_output.c +@@ -246,15 +246,16 @@ ieee80211_hardstart(struct sk_buff *skb, + * things like power save. + */ + eh = (struct ether_header *)skb->data; +- if (vap->iv_opmode == IEEE80211_M_WDS) +- ni = ieee80211_find_txnode(vap, vap->wds_mac); +- else +- ni = ieee80211_find_txnode(vap, eh->ether_dhost); ++ ni = ieee80211_find_txnode(vap, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + goto bad; + } + ++ if (ni->ni_subif && (vap != ni->ni_subif) && ++ ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) ++ goto bad; ++ + /* calculate priority so drivers can find the TX queue */ + if (ieee80211_classify(ni, skb)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, +@@ -334,20 +335,33 @@ void ieee80211_parent_queue_xmit(struct + * constructing a frame as it sets i_fc[1]; other bits can + * then be or'd in. + */ +-static void ++static struct ieee80211_frame * + ieee80211_send_setup(struct ieee80211vap *vap, + struct ieee80211_node *ni, +- struct ieee80211_frame *wh, ++ struct sk_buff *skb, + int type, + const u_int8_t sa[IEEE80211_ADDR_LEN], + const u_int8_t da[IEEE80211_ADDR_LEN], + const u_int8_t bssid[IEEE80211_ADDR_LEN]) + { + #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) ++ struct ieee80211_frame *wh; ++ int len = sizeof(struct ieee80211_frame); ++ int opmode = vap->iv_opmode; ++ ++ if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { ++ if ((opmode == IEEE80211_M_STA) && ++ (vap->iv_flags_ext & IEEE80211_FEXT_WDS)) ++ opmode = IEEE80211_M_WDS; + ++ if (opmode == IEEE80211_M_WDS) ++ len = sizeof(struct ieee80211_frame_addr4); ++ } ++ ++ wh = (struct ieee80211_frame *)skb_push(skb, len); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; + if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { +- switch (vap->iv_opmode) { ++ switch (opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, bssid); +@@ -389,6 +403,8 @@ ieee80211_send_setup(struct ieee80211vap + *(__le16 *)&wh->i_seq[0] = + htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[0]++; ++ ++ return wh; + #undef WH4 + } + +@@ -410,9 +426,7 @@ ieee80211_mgmt_output(struct ieee80211_n + + SKB_CB(skb)->ni = ni; + +- wh = (struct ieee80211_frame *) +- skb_push(skb, sizeof(struct ieee80211_frame)); +- ieee80211_send_setup(vap, ni, wh, ++ wh = ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_MGT | type, + vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid); + /* XXX power management */ +@@ -458,6 +472,9 @@ ieee80211_send_nulldata(struct ieee80211 + struct ieee80211_frame *wh; + u_int8_t *frm; + ++ if (ni->ni_subif) ++ vap = ni->ni_subif; ++ + skb = ieee80211_getmgtframe(&frm, 0); + if (skb == NULL) { + /* XXX debug msg */ +@@ -466,9 +483,7 @@ ieee80211_send_nulldata(struct ieee80211 + return -ENOMEM; + } + +- wh = (struct ieee80211_frame *) +- skb_push(skb, sizeof(struct ieee80211_frame)); +- ieee80211_send_setup(vap, ni, wh, ++ wh = ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, + vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid); + /* NB: power management bit is never sent by an AP */ +@@ -506,6 +521,7 @@ ieee80211_send_qosnulldata(struct ieee80 + struct sk_buff *skb; + struct ieee80211_qosframe *qwh; + u_int8_t *frm; ++ u_int8_t *i_qos; + int tid; + + skb = ieee80211_getmgtframe(&frm, 2); +@@ -517,11 +533,12 @@ ieee80211_send_qosnulldata(struct ieee80 + SKB_CB(skb)->ni = ieee80211_ref_node(ni); + + skb->priority = ac; +- qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe)); + +- qwh = (struct ieee80211_qosframe *)skb->data; ++ /* grab a pointer to QoS control and also compensate for the header length ++ * difference between QoS and non-QoS frame */ ++ i_qos = skb_push(skb, sizeof(struct ieee80211_qosframe) - sizeof(struct ieee80211_frame)); + +- ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh, ++ qwh = (struct ieee80211_qosframe *) ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_DATA, + vap->iv_myaddr, /* SA */ + ni->ni_macaddr, /* DA */ +@@ -535,10 +552,10 @@ ieee80211_send_qosnulldata(struct ieee80 + + /* map from access class/queue to 11e header priority value */ + tid = WME_AC_TO_TID(ac); +- qwh->i_qos[0] = tid & IEEE80211_QOS_TID; ++ i_qos[0] = tid & IEEE80211_QOS_TID; + if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) + qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY; +- qwh->i_qos[1] = 0; ++ i_qos[1] = 0; + + IEEE80211_NODE_STAT(ni, tx_data); + +@@ -780,6 +797,8 @@ ieee80211_encap(struct ieee80211_node *n + hdrsize = sizeof(struct ieee80211_frame); + + SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE)); ++ if (ni->ni_subif) ++ vap = ni->ni_subif; + + switch (vap->iv_opmode) { + case IEEE80211_M_IBSS: +@@ -788,7 +807,7 @@ ieee80211_encap(struct ieee80211_node *n + break; + case IEEE80211_M_WDS: + use4addr = 1; +- ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr); ++ ismulticast = 0; + break; + case IEEE80211_M_HOSTAP: + if (!IEEE80211_IS_MULTICAST(eh.ether_dhost) && +@@ -799,20 +818,9 @@ ieee80211_encap(struct ieee80211_node *n + ismulticast = IEEE80211_IS_MULTICAST(eh.ether_dhost); + break; + case IEEE80211_M_STA: +- if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && +- !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { ++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) { + use4addr = 1; +- ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr); +- /* Add a WDS entry to the station VAP */ +- if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) { +- struct ieee80211_node_table *nt = &ic->ic_sta; +- struct ieee80211_node *ni_wds +- = ieee80211_find_wds_node(nt, eh.ether_shost); +- if (ni_wds) +- ieee80211_unref_node(&ni_wds); +- else +- ieee80211_add_wds_addr(nt, ni, eh.ether_shost, 0); +- } ++ ismulticast = 0; + } else + ismulticast = IEEE80211_IS_MULTICAST(vap->iv_bssid); + break; +@@ -973,7 +981,7 @@ ieee80211_encap(struct ieee80211_node *n + break; + case IEEE80211_M_WDS: + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; +- IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); ++ IEEE80211_ADDR_COPY(wh->i_addr1, vap->wds_mac); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); +@@ -1683,9 +1691,7 @@ ieee80211_send_probereq(struct ieee80211 + + SKB_CB(skb)->ni = ieee80211_ref_node(ni); + +- wh = (struct ieee80211_frame *) +- skb_push(skb, sizeof(struct ieee80211_frame)); +- ieee80211_send_setup(vap, ni, wh, ++ wh = ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, + sa, da, bssid); + /* XXX power management? */ +--- a/tools/athkey.c ++++ b/tools/athkey.c +@@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo + IOCTL_ERR(IEEE80211_IOCTL_ADDMAC), + IOCTL_ERR(IEEE80211_IOCTL_DELMAC), + IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC), +- IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC), ++ IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC), + IOCTL_ERR(IEEE80211_IOCTL_READREG), + IOCTL_ERR(IEEE80211_IOCTL_WRITEREG), + }; +--- a/tools/athchans.c ++++ b/tools/athchans.c +@@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo + IOCTL_ERR(IEEE80211_IOCTL_ADDMAC), + IOCTL_ERR(IEEE80211_IOCTL_DELMAC), + IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC), +- IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC), ++ IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC), + IOCTL_ERR(IEEE80211_IOCTL_READREG), + IOCTL_ERR(IEEE80211_IOCTL_WRITEREG), + }; +--- a/tools/wlanconfig.c ++++ b/tools/wlanconfig.c +@@ -968,7 +968,7 @@ do80211priv(struct iwreq *iwr, const cha + IOCTL_ERR(IEEE80211_IOCTL_ADDMAC), + IOCTL_ERR(IEEE80211_IOCTL_DELMAC), + IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC), +- IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC), ++ IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC), + IOCTL_ERR(IEEE80211_IOCTL_READREG), + IOCTL_ERR(IEEE80211_IOCTL_WRITEREG), + }; +--- a/net80211/ieee80211_proto.c ++++ b/net80211/ieee80211_proto.c +@@ -979,6 +979,12 @@ ieee80211_init(struct net_device *dev, i + "start running (state=%d)\n", vap->iv_state); + + ++ if (vap->iv_master && vap->iv_master->iv_state == IEEE80211_S_INIT) { ++ int ret = ieee80211_init(vap->iv_master->iv_dev, forcescan); ++ if (ret < 0) ++ return ret; ++ } ++ + if ((dev->flags & IFF_RUNNING) == 0) { + if (ic->ic_nopened++ == 0 && + (parent->flags & IFF_RUNNING) == 0) +@@ -1081,6 +1087,8 @@ ieee80211_init(struct net_device *dev, i + int + ieee80211_open(struct net_device *dev) + { ++ struct ieee80211vap *vap = dev->priv; ++ + return ieee80211_init(dev, 0); + } + +@@ -1090,7 +1098,7 @@ ieee80211_open(struct net_device *dev) + void + ieee80211_start_running(struct ieee80211com *ic) + { +- struct ieee80211vap *vap; ++ struct ieee80211vap *vap, *avp; + struct net_device *dev; + + /* XXX locking */ +@@ -1099,6 +1107,16 @@ ieee80211_start_running(struct ieee80211 + /* NB: avoid recursion */ + if ((dev->flags & IFF_UP) && !(dev->flags & IFF_RUNNING)) + ieee80211_open(dev); ++ ++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { ++ if (avp->iv_wdsnode && avp->iv_wdsnode->ni_subif == avp) ++ continue; ++ ++ dev = avp->iv_dev; ++ /* NB: avoid recursion */ ++ if ((dev->flags & IFF_UP) && !(dev->flags & IFF_RUNNING)) ++ ieee80211_open(dev); ++ } + } + } + EXPORT_SYMBOL(ieee80211_start_running); +@@ -1116,11 +1134,43 @@ ieee80211_stop(struct net_device *dev) + struct ieee80211vap *vap = dev->priv; + struct ieee80211com *ic = vap->iv_ic; + struct net_device *parent = ic->ic_dev; ++ struct ieee80211_node *tni, *ni; ++ struct ieee80211vap *avp; + + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "%s\n", "stop running"); + ++ if (vap->iv_wdsnode && !vap->iv_wdsnode->ni_subif) ++ ieee80211_unref_node(&vap->iv_wdsnode); ++ ++ /* stop wds interfaces */ ++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_next) { ++ if (avp->iv_state != IEEE80211_S_INIT) ++ ieee80211_stop(avp->iv_dev); ++ } ++ ++ /* get rid of all wds nodes while we're still locked */ ++ do { ++ ni = NULL; ++ ++ IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta); ++ TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) { ++ if (tni->ni_vap != vap) ++ continue; ++ if (!tni->ni_subif) ++ continue; ++ ni = tni; ++ break; ++ } ++ IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta); ++ ++ if (!ni) ++ break; ++ ++ ieee80211_node_leave(ni); ++ } while (1); ++ + ieee80211_new_state(vap, IEEE80211_S_INIT, -1); + if (dev->flags & IFF_RUNNING) { + dev->flags &= ~IFF_RUNNING; /* mark us stopped */ +@@ -1148,7 +1198,7 @@ EXPORT_SYMBOL(ieee80211_stop); + void + ieee80211_stop_running(struct ieee80211com *ic) + { +- struct ieee80211vap *vap; ++ struct ieee80211vap *vap, *avp; + struct net_device *dev; + + /* XXX locking */ +@@ -1156,6 +1206,12 @@ ieee80211_stop_running(struct ieee80211c + dev = vap->iv_dev; + if (dev->flags & IFF_RUNNING) /* NB: avoid recursion */ + ieee80211_stop(dev); ++ ++ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { ++ dev = avp->iv_dev; ++ if (dev->flags & IFF_RUNNING) /* NB: avoid recursion */ ++ ieee80211_stop(dev); ++ } + } + } + EXPORT_SYMBOL(ieee80211_stop_running); +@@ -1342,9 +1398,9 @@ ieee80211_new_state(struct ieee80211vap + struct ieee80211com *ic = vap->iv_ic; + int rc; + +- IEEE80211_VAPS_LOCK_BH(ic); ++ IEEE80211_VAPS_LOCK_IRQ(ic); + rc = vap->iv_newstate(vap, nstate, arg); +- IEEE80211_VAPS_UNLOCK_BH(ic); ++ IEEE80211_VAPS_UNLOCK_IRQ(ic); + return rc; + } + +@@ -1557,57 +1613,12 @@ __ieee80211_newstate(struct ieee80211vap + switch (ostate) { + case IEEE80211_S_INIT: + if (vap->iv_opmode == IEEE80211_M_MONITOR || +- vap->iv_opmode == IEEE80211_M_WDS || + vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* + * Already have a channel; bypass the + * scan and startup immediately. + */ + ieee80211_create_ibss(vap, ic->ic_curchan); +- +- /* In WDS mode, allocate and initialize peer node. */ +- if (vap->iv_opmode == IEEE80211_M_WDS) { +- /* XXX: This is horribly non-atomic. */ +- struct ieee80211_node *wds_ni = +- ieee80211_find_node(&ic->ic_sta, +- vap->wds_mac); +- +- if (wds_ni == NULL) { +- wds_ni = ieee80211_alloc_node_table( +- vap, +- vap->wds_mac); +- if (wds_ni != NULL) { +- ieee80211_add_wds_addr( +- &ic->ic_sta, +- wds_ni, +- vap->wds_mac, +- 1); +- ieee80211_ref_node(wds_ni); /* pin in memory */ +- } +- else +- IEEE80211_DPRINTF( +- vap, +- IEEE80211_MSG_NODE, +- "%s: Unable to " +- "allocate node for " +- "WDS: " MAC_FMT "\n", +- __func__, +- MAC_ADDR( +- vap->wds_mac) +- ); +- } +- +- if (wds_ni != NULL) { +- ieee80211_node_authorize(wds_ni); +- wds_ni->ni_chan = +- vap->iv_bss->ni_chan; +- wds_ni->ni_capinfo = +- ni->ni_capinfo; +- wds_ni->ni_associd = 1; +- wds_ni->ni_ath_flags = +- vap->iv_ath_cap; +- } +- } + break; + } + /* fall thru... */ +@@ -1675,6 +1686,7 @@ __ieee80211_newstate(struct ieee80211vap + */ + if (ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ni); ++ + #ifdef ATH_SUPERG_XR + /* + * fire a timer to bring up XR vap if configured. +@@ -1808,6 +1820,11 @@ ieee80211_newstate(struct ieee80211vap * + ieee80211_state_name[dstate]); + + ieee80211_update_link_status(vap, nstate, ostate); ++ ++ if ((nstate != IEEE80211_S_RUN) && vap->iv_wdsnode && ++ !vap->iv_wdsnode->ni_subif) ++ ieee80211_unref_node(&vap->iv_wdsnode); ++ + switch (nstate) { + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: +@@ -1930,8 +1947,15 @@ ieee80211_newstate(struct ieee80211vap * + if (ostate == IEEE80211_S_SCAN || + ostate == IEEE80211_S_AUTH || + ostate == IEEE80211_S_ASSOC) { ++ + /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_RUN */ + __ieee80211_newstate(vap, nstate, arg); ++ ++ /* if we're in wds, let the ap know that we're doing this */ ++ if ((vap->iv_opmode == IEEE80211_M_STA) && ++ (vap->iv_flags_ext & IEEE80211_FEXT_WDS)) ++ ieee80211_send_nulldata(ieee80211_ref_node(vap->iv_bss)); ++ + /* Then bring up all other vaps pending on the scan */ + dstate = get_dominant_state(ic); + if (dstate == IEEE80211_S_RUN) { +--- a/ath/if_athvar.h ++++ b/ath/if_athvar.h +@@ -79,28 +79,6 @@ typedef void *TQUEUE_ARG; + #define tasklet_enable(t) do { (void) t; local_bh_enable(); } while (0) + #endif /* !DECLARE_TASKLET */ + +-#include <linux/sched.h> +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) +-#include <linux/tqueue.h> +-#define work_struct tq_struct +-#define schedule_work(t) schedule_task((t)) +-#define flush_scheduled_work() flush_scheduled_tasks() +-#define ATH_INIT_WORK(t, f) do { \ +- memset((t), 0, sizeof(struct tq_struct)); \ +- (t)->routine = (void (*)(void*)) (f); \ +- (t)->data=(void *) (t); \ +-} while (0) +-#else +-#include <linux/workqueue.h> +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +-#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t)); +-#else +-#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (_f)); +-#endif +- +-#endif /* KERNEL_VERSION < 2.5.41 */ +- + /* + * Guess how the interrupt handler should work. + */ +--- a/net80211/ieee80211_linux.c ++++ b/net80211/ieee80211_linux.c +@@ -145,7 +145,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_ + struct sk_buff *skb; + u_int len; + +- len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); ++ len = roundup(sizeof(struct ieee80211_frame_addr4) + pktlen, 4); + #ifdef IEEE80211_DEBUG_REFCNT + skb = ieee80211_dev_alloc_skb_debug(len + align - 1, func, line); + #else +@@ -161,7 +161,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_ + SKB_CB(skb)->flags = 0; + SKB_CB(skb)->next = NULL; + +- skb_reserve(skb, sizeof(struct ieee80211_frame)); ++ skb_reserve(skb, sizeof(struct ieee80211_frame_addr4)); + *frm = skb_put(skb, pktlen); + } + return skb; +--- a/net80211/ieee80211_node.h ++++ b/net80211/ieee80211_node.h +@@ -92,11 +92,13 @@ struct ath_softc; + * the ieee80211com structure. + */ + struct ieee80211_node { +- struct ieee80211vap *ni_vap; ++ struct ieee80211vap *ni_vap, *ni_subif; + struct ieee80211com *ni_ic; + struct ieee80211_node_table *ni_table; + TAILQ_ENTRY(ieee80211_node) ni_list; + LIST_ENTRY(ieee80211_node) ni_hash; ++ struct work_struct ni_create; /* task for creating a subif */ ++ struct work_struct ni_destroy; /* task for destroying a subif */ + atomic_t ni_refcnt; + u_int ni_scangen; /* gen# for timeout scan */ + u_int8_t ni_authmode; /* authentication algorithm */ +@@ -430,5 +432,6 @@ void ieee80211_node_join(struct ieee8021 + void ieee80211_node_leave(struct ieee80211_node *); + u_int8_t ieee80211_getrssi(struct ieee80211com *); + int32_t ieee80211_get_node_count(struct ieee80211com *); ++void ieee80211_wds_addif(struct ieee80211_node *ni); + #endif /* _NET80211_IEEE80211_NODE_H_ */ + |