From owner-p4-projects@FreeBSD.ORG Tue Jan 15 21:31:04 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 8E0B116A421; Tue, 15 Jan 2008 21:31:04 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 527C316A419 for ; Tue, 15 Jan 2008 21:31:04 +0000 (UTC) (envelope-from sam@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 4A8AB13C447 for ; Tue, 15 Jan 2008 21:31:04 +0000 (UTC) (envelope-from sam@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id m0FLV4J9046232 for ; Tue, 15 Jan 2008 21:31:04 GMT (envelope-from sam@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id m0FLV3Xf046226 for perforce@freebsd.org; Tue, 15 Jan 2008 21:31:03 GMT (envelope-from sam@freebsd.org) Date: Tue, 15 Jan 2008 21:31:03 GMT Message-Id: <200801152131.m0FLV3Xf046226@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to sam@freebsd.org using -f From: Sam Leffler To: Perforce Change Reviews Cc: Subject: PERFORCE change 133353 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 15 Jan 2008 21:31:04 -0000 http://perforce.freebsd.org/chv.cgi?CH=133353 Change 133353 by sam@sam_ebb on 2008/01/15 21:30:15 backout mis-auto-merge Affected files ... .. //depot/projects/vap/sys/net80211/ieee80211.c#20 edit Differences ... ==== //depot/projects/vap/sys/net80211/ieee80211.c#20 (text+ko) ==== @@ -25,11 +25,12 @@ */ #include -__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.45 2007/12/07 01:46:12 kmacy Exp $"); +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.44 2007/11/23 05:57:20 sam Exp $"); /* * IEEE 802.11 generic handler */ +#include "opt_wlan.h" #include #include @@ -38,10 +39,13 @@ #include #include +#include #include +#include #include #include +#include #include @@ -57,7 +61,21 @@ "11na", /* IEEE80211_MODE_11NA */ "11ng", /* IEEE80211_MODE_11NG */ }; +static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); +static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); +static int ieee80211_media_setup(struct ieee80211com *ic, + struct ifmedia *media, int caps, int addsta, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); +static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); +static int ieee80211com_media_change(struct ifnet *); +static int media_status(enum ieee80211_opmode, + const struct ieee80211_channel *); + +MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); + /* * Default supported rates for 802.11 operation (in IEEE .5Mb units). */ @@ -75,66 +93,6 @@ { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; #undef B -static int media_status(enum ieee80211_opmode , - const struct ieee80211_channel *); - -/* list of all instances */ -SLIST_HEAD(ieee80211_list, ieee80211com); -static struct ieee80211_list ieee80211_list = - SLIST_HEAD_INITIALIZER(ieee80211_list); -static uint8_t ieee80211_vapmap[32]; /* enough for 256 */ -static struct mtx ieee80211_vap_mtx; -MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF); - -static void -ieee80211_add_vap(struct ieee80211com *ic) -{ -#define N(a) (sizeof(a)/sizeof(a[0])) - int i; - uint8_t b; - - mtx_lock(&ieee80211_vap_mtx); - ic->ic_vap = 0; - for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++) - ic->ic_vap += NBBY; - if (i == N(ieee80211_vapmap)) - panic("vap table full"); - for (b = ieee80211_vapmap[i]; b & 1; b >>= 1) - ic->ic_vap++; - setbit(ieee80211_vapmap, ic->ic_vap); - SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); - mtx_unlock(&ieee80211_vap_mtx); -#undef N -} - -static void -ieee80211_remove_vap(struct ieee80211com *ic) -{ - mtx_lock(&ieee80211_vap_mtx); - SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); - KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY, - ("invalid vap id %d", ic->ic_vap)); - KASSERT(isset(ieee80211_vapmap, ic->ic_vap), - ("vap id %d not allocated", ic->ic_vap)); - clrbit(ieee80211_vapmap, ic->ic_vap); - mtx_unlock(&ieee80211_vap_mtx); -} - -/* - * Default reset method for use with the ioctl support. This - * method is invoked after any state change in the 802.11 - * layer that should be propagated to the hardware but not - * require re-initialization of the 802.11 state machine (e.g - * rescanning for an ap). We always return ENETRESET which - * should cause the driver to re-initialize the device. Drivers - * can override this method to implement more optimized support. - */ -static int -ieee80211_default_reset(struct ifnet *ifp) -{ - return ENETRESET; -} - /* * Fill in 802.11 available channel set, mark * all available channels as active, and pick @@ -153,6 +111,7 @@ KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX, ("invalid number of channels specified: %u", ic->ic_nchans)); memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); + memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; @@ -186,9 +145,13 @@ memcpy(ic->ic_chan_active, ic->ic_chan_avail, sizeof(ic->ic_chan_avail)); - ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + /* sort channel table to allow lookup optimizations */ + ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); + + /* invalidate any previous state */ ic->ic_bsschan = IEEE80211_CHAN_ANYC; ic->ic_prevchan = NULL; + ic->ic_csa_newchan = NULL; /* arbitrarily pick the first channel */ ic->ic_curchan = &ic->ic_channels[0]; @@ -206,54 +169,45 @@ #undef DEFAULTRATES } +static void +null_update_mcast(struct ifnet *ifp) +{ + if_printf(ifp, "need multicast update callback\n"); +} + +static void +null_update_promisc(struct ifnet *ifp) +{ + if_printf(ifp, "need promiscuous mode update callback\n"); +} + +/* + * Attach/setup the common net80211 state. Called by + * the driver on attach to prior to creating any vap's. + */ void ieee80211_ifattach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; + struct sockaddr_dl *sdl; + struct ifaddr *ifa; ether_ifattach(ifp, ic->ic_myaddr); - ifp->if_output = ieee80211_output; - bpfattach2(ifp, DLT_IEEE802_11, - sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); - - /* override the 802.3 setting */ - ifp->if_hdrlen = ic->ic_headroom - + sizeof(struct ieee80211_qosframe_addr4) - + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN - + IEEE80211_WEP_EXTIVLEN; - /* XXX no way to recalculate on ifdetach */ - if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { - /* XXX sanity check... */ - max_linkhdr = ALIGN(ifp->if_hdrlen); - max_hdr = max_linkhdr + max_protohdr; - max_datalen = MHLEN - max_hdr; - } - + IEEE80211_LOCK_INIT(ic, "ieee80211com"); + TAILQ_INIT(&ic->ic_vaps); /* * Fill in 802.11 available channel set, mark all * available channels as active, and pick a default * channel if not already specified. */ - ieee80211_chan_init(ic); + ieee80211_media_init(ic, + ieee80211com_media_change, ieee80211com_media_status); - if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */ - ic->ic_flags |= IEEE80211_F_BGSCAN; -#if 0 - /* XXX not until WME+WPA issues resolved */ - if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */ - ic->ic_flags |= IEEE80211_F_WME; -#endif - if (ic->ic_caps & IEEE80211_C_BURST) - ic->ic_flags |= IEEE80211_F_BURST; - ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */ + ic->ic_update_mcast = null_update_mcast; + ic->ic_update_promisc = null_update_promisc; ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; - ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; - ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT; - IEEE80211_LOCK_INIT(ic, "ieee80211com"); - IEEE80211_BEACON_LOCK_INIT(ic, "beacon"); - ic->ic_lintval = ic->ic_bintval; ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; @@ -263,30 +217,43 @@ ieee80211_proto_attach(ic); ieee80211_ht_attach(ic); ieee80211_scan_attach(ic); + ieee80211_regdomain_attach(ic); - ieee80211_add_vap(ic); + ieee80211_sysctl_attach(ic); - ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */ - - /* - * Install a default reset method for the ioctl support. - * The driver is expected to fill this in before calling us. - */ - if (ic->ic_reset == NULL) - ic->ic_reset = ieee80211_default_reset; - - KASSERT(ifp->if_llsoftc == NULL, ("oops, hosed")); - ifp->if_llsoftc = ic; + ifp->if_type = IFT_IEEE80211; /* NB: not IFT_ETHER */ + ifp->if_addrlen = IEEE80211_ADDR_LEN; + ifp->if_hdrlen = 0; + if_attach(ifp); + /* NB: must do after 'cuz if_attach resets state */ + ifp->if_mtu = IEEE80211_MTU_MAX; + ifa = ifaddr_byindex(ifp->if_index); + KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ + sdl->sdl_alen = IEEE80211_ADDR_LEN; + IEEE80211_ADDR_COPY(LLADDR(sdl), ic->ic_myaddr); + ifp->if_broadcastaddr = ieee80211broadcastaddr; } +/* + * Detach net80211 state on device detach. Tear down + * all vap's and reclaim all common state prior to the + * device state going away. Note we may call back into + * driver; it must be prepared for this. + */ void ieee80211_ifdetach(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap; - ieee80211_remove_vap(ic); + /* XXX ieee80211_stop_all? */ + while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) + ic->ic_vap_delete(vap); ieee80211_sysctl_detach(ic); + ieee80211_regdomain_detach(ic); ieee80211_scan_detach(ic); ieee80211_ht_detach(ic); /* NB: must be called before ieee80211_node_detach */ @@ -296,13 +263,357 @@ ieee80211_node_detach(ic); ifmedia_removeall(&ic->ic_media); + bpfdetach(ifp); + ether_ifdetach(ifp); + IEEE80211_LOCK_DESTROY(ic); - IEEE80211_BEACON_LOCK_DESTROY(ic); + if_detach(ifp); +} + +/* + * Default reset method for use with the ioctl support. This + * method is invoked after any state change in the 802.11 + * layer that should be propagated to the hardware but not + * require re-initialization of the 802.11 state machine (e.g + * rescanning for an ap). We always return ENETRESET which + * should cause the driver to re-initialize the device. Drivers + * can override this method to implement more optimized support. + */ +static int +default_reset(struct ieee80211vap *vap) +{ + return ENETRESET; +} + +/* + * Prepare a vap for use. Drivers use this call to + * setup net80211 state in new vap's prior attaching + * them with ieee80211_vap_attach (below). + */ +int +ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ +#define IEEE80211_C_OPMODE \ + (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \ + IEEE80211_C_MONITOR | IEEE80211_C_WDS) + struct ifnet *ifp; + + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + /* XXX msg,stat? */ + return 0; + } + if_initname(ifp, name, unit); + ifp->if_softc = vap; /* back pointer */ + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_start = ieee80211_start; + ifp->if_ioctl = ieee80211_ioctl; + ifp->if_watchdog = NULL; /* NB: no watchdog routine */ + ifp->if_init = ieee80211_init; + /* NB: input+output filled in by ether_ifattach */ + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + vap->iv_ifp = ifp; + vap->iv_ic = ic; + vap->iv_flags = ic->ic_flags; /* propagate common flags */ + vap->iv_flags_ext = ic->ic_flags_ext; + vap->iv_flags_ven = ic->ic_flags_ven; + vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; + vap->iv_htcaps = ic->ic_htcaps; + vap->iv_opmode = opmode; + switch (opmode) { + case IEEE80211_M_STA: + /* auto-enable s/w beacon miss support */ + if (flags & IEEE80211_CLONE_NOBEACONS) + vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; + break; + case IEEE80211_M_IBSS: + vap->iv_caps |= IEEE80211_C_IBSS; + break; + case IEEE80211_M_AHDEMO: + vap->iv_caps |= IEEE80211_C_AHDEMO; + break; + case IEEE80211_M_HOSTAP: + vap->iv_caps |= IEEE80211_C_HOSTAP; + break; + case IEEE80211_M_MONITOR: + vap->iv_caps |= IEEE80211_C_MONITOR; + break; + case IEEE80211_M_WDS: + vap->iv_caps |= IEEE80211_C_WDS; + /* + * WDS links must specify the bssid of the far end. + * For legacy operation this is a static relationship. + * For non-legacy operation the station must associate + * and be authorized to pass traffic. Plumbing the + * vap to the proper node happens when the vap + * transitions to RUN state. + */ + IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); + vap->iv_flags |= IEEE80211_F_DESBSSID; + if (flags & IEEE80211_CLONE_WDSLEGACY) + vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; + break; + } + /* + * Enable various functionality by default if we're + * capable; the driver can override us if it knows better. + */ + if (vap->iv_caps & IEEE80211_C_WME) + vap->iv_flags |= IEEE80211_F_WME; + if (vap->iv_caps & IEEE80211_C_BURST) + vap->iv_flags |= IEEE80211_F_BURST; + if (vap->iv_caps & IEEE80211_C_FF) + vap->iv_flags |= IEEE80211_F_FF; + if (vap->iv_caps & IEEE80211_C_TURBOP) + vap->iv_flags |= IEEE80211_F_TURBOP; + /* NB: bg scanning only makes sense for station mode right now */ + if (vap->iv_opmode == IEEE80211_M_STA && + (vap->iv_caps & IEEE80211_C_BGSCAN)) + vap->iv_flags |= IEEE80211_F_BGSCAN; + vap->iv_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */ + /* XXX out of caps, just ena */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + vap->iv_flags_ext |= IEEE80211_FEXT_DFS; + + vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; + vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; + /* + * Install a default reset method for the ioctl support; + * the driver can override this. + */ + vap->iv_reset = default_reset; + + IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); + + ieee80211_sysctl_vattach(vap); + ieee80211_crypto_vattach(vap); + ieee80211_node_vattach(vap); + ieee80211_power_vattach(vap); + ieee80211_proto_vattach(vap); + ieee80211_ht_vattach(vap); + ieee80211_scan_vattach(vap); + ieee80211_regdomain_vattach(vap); + + return 1; +#undef IEEE80211_C_OPMODE +} + +/* + * Activate a vap. State should have been prepared with a + * call to ieee80211_vap_setup and by the driver. On return + * from this call the vap is ready for use. + */ +int +ieee80211_vap_attach(struct ieee80211vap *vap, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +{ + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211com *ic = vap->iv_ic; + struct ifmediareq imr; + int maxrate; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", + __func__, ieee80211_opmode_name[vap->iv_opmode], + ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); + + /* + * Do late attach work that cannot happen until after + * the driver has had a chance to override defaults. + */ + ieee80211_node_latevattach(vap); + ieee80211_power_latevattach(vap); + + maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, + vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); + ieee80211_media_status(ifp, &imr); + /* NB: strip explicit mode; we're actually in autoselect */ + ifmedia_set(&vap->iv_media, imr.ifm_active &~ IFM_MMASK); + if (maxrate) + ifp->if_baudrate = IF_Mbps(maxrate); + + ether_ifattach(ifp, vap->iv_myaddr); + /* hook output method setup by ether_ifattach */ + vap->iv_output = ifp->if_output; + ifp->if_output = ieee80211_output; + /* NB: if_mtu set by ether_ifattach to ETHERMTU */ + bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf); + + IEEE80211_LOCK(ic); + TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); + ieee80211_syncflag_locked(ic, IEEE80211_F_WME); + ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); + ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); + ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + IEEE80211_UNLOCK(ic); + + return 1; +} + +/* + * Tear down vap state prior to reclaiming the ifnet. + */ +void +ieee80211_vap_detach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", + __func__, ieee80211_opmode_name[vap->iv_opmode], + ic->ic_ifp->if_xname); + /* + * Mark interface down so we ignore calls by the bridge + * to turn off promiscuous mode as a result of calling + * ether_ifdetach. + */ + ifp->if_flags &= ~IFF_UP; + ieee80211_stop(vap); bpfdetach(ifp); ether_ifdetach(ifp); + + IEEE80211_LOCK(ic); + TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); + ieee80211_syncflag_locked(ic, IEEE80211_F_WME); + ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); + ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); + ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT); + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + IEEE80211_UNLOCK(ic); + + ifmedia_removeall(&vap->iv_media); + + ieee80211_regdomain_vdetach(vap); + ieee80211_scan_vdetach(vap); + ieee80211_ht_vdetach(vap); + /* NB: must be before ieee80211_node_vdetach */ + ieee80211_proto_vdetach(vap); + ieee80211_crypto_vdetach(vap); + ieee80211_power_vdetach(vap); + ieee80211_node_vdetach(vap); + ieee80211_sysctl_vdetach(vap); +} + +/* + * Synchronize flag bit state in the parent ifnet structure + * according to the state of all vap ifnet's. This is used, + * for example, to handle IFF_PROMISC and IFF_ALLMULTI. + */ +void +ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_ifp->if_flags & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_ifp->if_flags |= flag; + else + ic->ic_ifp->if_flags &= ~flag; +} + +/* + * Synchronize flag bit state in the com structure + * according to the state of all vap's. This is used, + * for example, to handle state changes via ioctls. + */ +static void +ieee80211_syncflag_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_flags & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_flags |= flag; + else + ic->ic_flags &= ~flag; } +void +ieee80211_syncflag(struct ieee80211vap *vap, int flag) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (flag < 0) { + flag = -flag; + vap->iv_flags &= ~flag; + } else + vap->iv_flags |= flag; + ieee80211_syncflag_locked(ic, flag); + IEEE80211_UNLOCK(ic); +} + +/* + * Synchronize flag bit state in the com structure + * according to the state of all vap's. This is used, + * for example, to handle state changes via ioctls. + */ +static void +ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_flags_ext & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_flags_ext |= flag; + else + ic->ic_flags_ext &= ~flag; +} + +void +ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (flag < 0) { + flag = -flag; + vap->iv_flags_ext &= ~flag; + } else + vap->iv_flags_ext |= flag; + ieee80211_syncflag_ext_locked(ic, flag); + IEEE80211_UNLOCK(ic); +} + static __inline int mapgsm(u_int freq, u_int flags) { @@ -416,7 +727,7 @@ /* * Locate a channel given a frequency+flags. We cache - * the previous lookup to optimize swithing between two + * the previous lookup to optimize switching between two * channels--as happens with dynamic turbo. */ struct ieee80211_channel * @@ -467,80 +778,58 @@ } static void -addmedia(struct ieee80211com *ic, int mode, int mword) +addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) { -#define TURBO(m) ((m) | IFM_IEEE80211_TURBO) #define ADD(_ic, _s, _o) \ - ifmedia_add(&(_ic)->ic_media, \ + ifmedia_add(media, \ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) static const u_int mopts[IEEE80211_MODE_MAX] = { - IFM_AUTO, /* IEEE80211_MODE_AUTO */ - IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */ - IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */ - IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */ - IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */ - TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */ - TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */ - TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */ - IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */ - IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */ + IFM_AUTO, + IFM_IEEE80211_11A, + IFM_IEEE80211_11B, + IFM_IEEE80211_11G, + IFM_IEEE80211_FH, + IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, + IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, + IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, + IFM_IEEE80211_11NA, + IFM_IEEE80211_11NG, }; u_int mopt; - KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode)); mopt = mopts[mode]; - KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO, - ("no media mapping for mode %u", mode)); - - ADD(ic, mword, mopt); /* e.g. 11a auto */ - if (ic->ic_caps & IEEE80211_C_IBSS) - ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); - if (ic->ic_caps & IEEE80211_C_HOSTAP) - ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); - if (ic->ic_caps & IEEE80211_C_AHDEMO) - ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); - if (ic->ic_caps & IEEE80211_C_MONITOR) - ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); + if (addsta) + ADD(ic, mword, mopt); /* STA mode has no cap */ + if (caps & IEEE80211_C_IBSS) + ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); + if (caps & IEEE80211_C_HOSTAP) + ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); + if (caps & IEEE80211_C_AHDEMO) + ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (caps & IEEE80211_C_MONITOR) + ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); + if (caps & IEEE80211_C_WDS) + ADD(media, mword, mopt | IFM_IEEE80211_WDS); #undef ADD -#undef TURBO } /* * Setup the media data structures according to the channel and - * rate tables. This must be called by the driver after - * ieee80211_attach and before most anything else. + * rate tables. */ -void -ieee80211_media_init(struct ieee80211com *ic, +static int +ieee80211_media_setup(struct ieee80211com *ic, + struct ifmedia *media, int caps, int addsta, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { - struct ifnet *ifp = ic->ic_ifp; int i, j, mode, rate, maxrate, mword, r; const struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; - /* NB: this works because the structure is initialized to zero */ - if (LIST_EMPTY(&ic->ic_media.ifm_list)) { - /* - * Do late attach work that must wait for any subclass - * (i.e. driver) work such as overriding methods. - */ - ieee80211_node_lateattach(ic); - } else { - /* - * We are re-initializing the channel list; clear - * the existing media state as the media routines - * don't suppress duplicates. - */ - ifmedia_removeall(&ic->ic_media); - ieee80211_chan_init(ic); - } - ieee80211_power_lateattach(ic); - /* * Fill in media characteristics. */ - ifmedia_init(&ic->ic_media, 0, media_change, media_stat); + ifmedia_init(media, 0, media_change, media_stat); maxrate = 0; /* * Add media for legacy operating modes. @@ -549,7 +838,7 @@ for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; - addmedia(ic, mode, IFM_AUTO); + addmedia(media, caps, addsta, mode, IFM_AUTO); if (mode == IEEE80211_MODE_AUTO) continue; rs = &ic->ic_sup_rates[mode]; @@ -558,7 +847,7 @@ mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; - addmedia(ic, mode, mword); + addmedia(media, caps, addsta, mode, mword); /* * Add legacy rate to the collection of all rates. */ @@ -582,7 +871,8 @@ if (mword == 0) continue; /* NB: remove media options from mword */ - addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); + addmedia(media, caps, addsta, + IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); } /* * Add HT/11n media. Note that we do not have enough @@ -593,24 +883,52 @@ for (; mode < IEEE80211_MODE_MAX; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; - addmedia(ic, mode, IFM_AUTO); - addmedia(ic, mode, IFM_IEEE80211_MCS); + addmedia(media, caps, addsta, mode, IFM_AUTO); + addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); } if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { - addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); + addmedia(media, caps, addsta, + IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); /* XXX could walk htrates */ /* XXX known array size */ if (ieee80211_htrates[15] > maxrate) maxrate = ieee80211_htrates[15]; } + return maxrate; +} + +void +ieee80211_media_init(struct ieee80211com *ic, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +{ + struct ifnet *ifp = ic->ic_ifp; + int maxrate; + + /* NB: this works because the structure is initialized to zero */ + if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { + /* + * We are re-initializing the channel list; clear + * the existing media state as the media routines + * don't suppress duplicates. + */ + ifmedia_removeall(&ic->ic_media); + } + ieee80211_chan_init(ic); + /* + * Recalculate media settings in case new channel list changes + * the set of available modes. + */ + maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, + ieee80211com_media_change, ieee80211com_media_status); /* NB: strip explicit mode; we're actually in autoselect */ ifmedia_set(&ic->ic_media, media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK); - if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); + + /* XXX need to propagate new media settings to vap's */ } const struct ieee80211_rateset * @@ -701,216 +1019,134 @@ } } -/* - * Find an instance by it's mac address. - */ -struct ieee80211com * -ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]) -{ - struct ieee80211com *ic; - - /* XXX lock */ - SLIST_FOREACH(ic, &ieee80211_list, ic_next) - if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) - return ic; - return NULL; -} - -static struct ieee80211com * -ieee80211_find_instance(struct ifnet *ifp) -{ - struct ieee80211com *ic; - - /* XXX lock */ - /* XXX not right for multiple instances but works for now */ - SLIST_FOREACH(ic, &ieee80211_list, ic_next) - if (ic->ic_ifp == ifp) - return ic; - return NULL; -} - static int -findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) -{ -#define IEEERATE(_ic,_m,_i) \ - ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) - int i, nrates = ic->ic_sup_rates[mode].rs_nrates; - for (i = 0; i < nrates; i++) - if (IEEERATE(ic, mode, i) == rate) - return i; - return -1; -#undef IEEERATE -} - -/* - * Convert a media specification to a rate index and possibly a mode - * (if the rate is fixed and the mode is specified as ``auto'' then - * we need to lock down the mode so the index is meanginful). - */ -static int -checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) -{ - - /* - * Check the rate table for the specified/current phy. - */ - if (mode == IEEE80211_MODE_AUTO) { - int i; - /* - * In autoselect mode search for the rate. - */ - for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { - if (isset(ic->ic_modecaps, i) && - findrate(ic, i, rate) != -1) - return 1; - } - return 0; - } else { - /* - * Mode is fixed, check for rate. - */ - return (findrate(ic, mode, rate) != -1); - } -} - -/* - * Handle a media change request. - */ -int -ieee80211_media_change(struct ifnet *ifp) +media2mode(const struct ieee80211com *ic, + const struct ifmedia_entry *ime, enum ieee80211_phymode *mode) { - struct ieee80211com *ic; - struct ifmedia_entry *ime; - enum ieee80211_opmode newopmode; - enum ieee80211_phymode newphymode; - int newrate, error = 0; - - ic = ieee80211_find_instance(ifp); - if (!ic) { - if_printf(ifp, "%s: no 802.11 instance!\n", __func__); - return EINVAL; - } - ime = ic->ic_media.ifm_cur; - /* - * First, identify the phy mode. - */ switch (IFM_MODE(ime->ifm_media)) { case IFM_IEEE80211_11A: - newphymode = IEEE80211_MODE_11A; + *mode = IEEE80211_MODE_11A; break; case IFM_IEEE80211_11B: - newphymode = IEEE80211_MODE_11B; + *mode = IEEE80211_MODE_11B; break; case IFM_IEEE80211_11G: - newphymode = IEEE80211_MODE_11G; + *mode = IEEE80211_MODE_11G; break; case IFM_IEEE80211_FH: - newphymode = IEEE80211_MODE_FH; + *mode = IEEE80211_MODE_FH; break; case IFM_IEEE80211_11NA: - newphymode = IEEE80211_MODE_11NA; + *mode = IEEE80211_MODE_11NA; break; case IFM_IEEE80211_11NG: - newphymode = IEEE80211_MODE_11NG; + *mode = IEEE80211_MODE_11NG; break; case IFM_AUTO: - newphymode = IEEE80211_MODE_AUTO; + *mode = IEEE80211_MODE_AUTO; break; default: - return EINVAL; + return 0; } /* * Turbo mode is an ``option''. * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { - if (newphymode == IEEE80211_MODE_11A) { + if (*mode == IEEE80211_MODE_11A) { if (ic->ic_flags & IEEE80211_F_TURBOP) - newphymode = IEEE80211_MODE_TURBO_A; + *mode = IEEE80211_MODE_TURBO_A; else - newphymode = IEEE80211_MODE_STURBO_A; - } else if (newphymode == IEEE80211_MODE_11G) - newphymode = IEEE80211_MODE_TURBO_G; + *mode = IEEE80211_MODE_STURBO_A; + } else if (*mode == IEEE80211_MODE_11G) + *mode = IEEE80211_MODE_TURBO_G; >>> TRUNCATED FOR MAIL (1000 lines) <<<