ieee80211_proto.c revision 191768
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3178354Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116742Ssam *
15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25116742Ssam */
26116742Ssam
27116742Ssam#include <sys/cdefs.h>
28116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_proto.c 191768 2009-05-03 18:29:04Z thompsa $");
29116742Ssam
30116742Ssam/*
31116742Ssam * IEEE 802.11 protocol support.
32116742Ssam */
33116742Ssam
34116742Ssam#include "opt_inet.h"
35178354Ssam#include "opt_wlan.h"
36116742Ssam
37116742Ssam#include <sys/param.h>
38138568Ssam#include <sys/kernel.h>
39170530Ssam#include <sys/systm.h>
40170530Ssam
41116742Ssam#include <sys/socket.h>
42178354Ssam#include <sys/sockio.h>
43116742Ssam
44116742Ssam#include <net/if.h>
45116742Ssam#include <net/if_media.h>
46138568Ssam#include <net/ethernet.h>		/* XXX for ether_sprintf */
47116742Ssam
48116742Ssam#include <net80211/ieee80211_var.h>
49178354Ssam#include <net80211/ieee80211_adhoc.h>
50178354Ssam#include <net80211/ieee80211_sta.h>
51178354Ssam#include <net80211/ieee80211_hostap.h>
52178354Ssam#include <net80211/ieee80211_wds.h>
53178354Ssam#include <net80211/ieee80211_monitor.h>
54178354Ssam#include <net80211/ieee80211_input.h>
55116742Ssam
56138568Ssam/* XXX tunables */
57138568Ssam#define	AGGRESSIVE_MODE_SWITCH_HYSTERESIS	3	/* pkts / 100ms */
58138568Ssam#define	HIGH_PRI_SWITCH_THRESH			10	/* pkts / 100ms */
59116742Ssam
60116742Ssamconst char *ieee80211_mgt_subtype_name[] = {
61116742Ssam	"assoc_req",	"assoc_resp",	"reassoc_req",	"reassoc_resp",
62116742Ssam	"probe_req",	"probe_resp",	"reserved#6",	"reserved#7",
63116742Ssam	"beacon",	"atim",		"disassoc",	"auth",
64172230Ssam	"deauth",	"action",	"reserved#14",	"reserved#15"
65116742Ssam};
66138568Ssamconst char *ieee80211_ctl_subtype_name[] = {
67138568Ssam	"reserved#0",	"reserved#1",	"reserved#2",	"reserved#3",
68138568Ssam	"reserved#3",	"reserved#5",	"reserved#6",	"reserved#7",
69138568Ssam	"reserved#8",	"reserved#9",	"ps_poll",	"rts",
70138568Ssam	"cts",		"ack",		"cf_end",	"cf_end_ack"
71138568Ssam};
72167283Ssamconst char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
73167283Ssam	"IBSS",		/* IEEE80211_M_IBSS */
74167283Ssam	"STA",		/* IEEE80211_M_STA */
75178354Ssam	"WDS",		/* IEEE80211_M_WDS */
76167283Ssam	"AHDEMO",	/* IEEE80211_M_AHDEMO */
77167283Ssam	"HOSTAP",	/* IEEE80211_M_HOSTAP */
78167283Ssam	"MONITOR"	/* IEEE80211_M_MONITOR */
79167283Ssam};
80117811Ssamconst char *ieee80211_state_name[IEEE80211_S_MAX] = {
81117811Ssam	"INIT",		/* IEEE80211_S_INIT */
82117811Ssam	"SCAN",		/* IEEE80211_S_SCAN */
83117811Ssam	"AUTH",		/* IEEE80211_S_AUTH */
84117811Ssam	"ASSOC",	/* IEEE80211_S_ASSOC */
85172058Ssam	"CAC",		/* IEEE80211_S_CAC */
86172058Ssam	"RUN",		/* IEEE80211_S_RUN */
87172058Ssam	"CSA",		/* IEEE80211_S_CSA */
88172058Ssam	"SLEEP",	/* IEEE80211_S_SLEEP */
89117811Ssam};
90138568Ssamconst char *ieee80211_wme_acnames[] = {
91138568Ssam	"WME_AC_BE",
92138568Ssam	"WME_AC_BK",
93138568Ssam	"WME_AC_VI",
94138568Ssam	"WME_AC_VO",
95138568Ssam	"WME_UPSD",
96138568Ssam};
97116742Ssam
98191746Sthompsastatic void beacon_miss(void *, int);
99191746Sthompsastatic void beacon_swmiss(void *, int);
100178354Ssamstatic void parent_updown(void *, int);
101191746Sthompsastatic void update_mcast(void *, int);
102191746Sthompsastatic void update_promisc(void *, int);
103191746Sthompsastatic void update_channel(void *, int);
104191746Sthompsastatic void ieee80211_newstate_cb(void *, int);
105178354Ssamstatic int ieee80211_new_state_locked(struct ieee80211vap *,
106178354Ssam	enum ieee80211_state, int);
107117811Ssam
108178354Ssamstatic int
109178354Ssamnull_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
110178354Ssam	const struct ieee80211_bpf_params *params)
111172211Ssam{
112178354Ssam	struct ifnet *ifp = ni->ni_ic->ic_ifp;
113178354Ssam
114178354Ssam	if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n");
115178354Ssam	m_freem(m);
116178354Ssam	return ENETDOWN;
117172211Ssam}
118172211Ssam
119116742Ssamvoid
120138568Ssamieee80211_proto_attach(struct ieee80211com *ic)
121116742Ssam{
122138568Ssam	struct ifnet *ifp = ic->ic_ifp;
123116742Ssam
124178354Ssam	/* override the 802.3 setting */
125178354Ssam	ifp->if_hdrlen = ic->ic_headroom
126178354Ssam		+ sizeof(struct ieee80211_qosframe_addr4)
127178354Ssam		+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
128178354Ssam		+ IEEE80211_WEP_EXTIVLEN;
129178354Ssam	/* XXX no way to recalculate on ifdetach */
130178354Ssam	if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
131178354Ssam		/* XXX sanity check... */
132178354Ssam		max_linkhdr = ALIGN(ifp->if_hdrlen);
133178354Ssam		max_hdr = max_linkhdr + max_protohdr;
134178354Ssam		max_datalen = MHLEN - max_hdr;
135178354Ssam	}
136127648Ssam	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
137116742Ssam
138178354Ssam	TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp);
139191746Sthompsa	TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic);
140191746Sthompsa	TASK_INIT(&ic->ic_promisc_task, 0, update_promisc, ic);
141191746Sthompsa	TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic);
142191746Sthompsa	TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic);
143178354Ssam
144138568Ssam	ic->ic_wme.wme_hipri_switch_hysteresis =
145138568Ssam		AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
146138568Ssam
147116742Ssam	/* initialize management frame handlers */
148116742Ssam	ic->ic_send_mgmt = ieee80211_send_mgmt;
149178354Ssam	ic->ic_raw_xmit = null_raw_xmit;
150178354Ssam
151178354Ssam	ieee80211_adhoc_attach(ic);
152178354Ssam	ieee80211_sta_attach(ic);
153178354Ssam	ieee80211_wds_attach(ic);
154178354Ssam	ieee80211_hostap_attach(ic);
155178354Ssam	ieee80211_monitor_attach(ic);
156116742Ssam}
157116742Ssam
158116742Ssamvoid
159138568Ssamieee80211_proto_detach(struct ieee80211com *ic)
160116742Ssam{
161178354Ssam	ieee80211_monitor_detach(ic);
162178354Ssam	ieee80211_hostap_detach(ic);
163178354Ssam	ieee80211_wds_detach(ic);
164178354Ssam	ieee80211_adhoc_detach(ic);
165178354Ssam	ieee80211_sta_detach(ic);
166178354Ssam}
167116742Ssam
168178354Ssamstatic void
169178354Ssamnull_update_beacon(struct ieee80211vap *vap, int item)
170178354Ssam{
171178354Ssam}
172178354Ssam
173178354Ssamvoid
174178354Ssamieee80211_proto_vattach(struct ieee80211vap *vap)
175178354Ssam{
176178354Ssam	struct ieee80211com *ic = vap->iv_ic;
177178354Ssam	struct ifnet *ifp = vap->iv_ifp;
178178354Ssam	int i;
179178354Ssam
180178354Ssam	/* override the 802.3 setting */
181178354Ssam	ifp->if_hdrlen = ic->ic_ifp->if_hdrlen;
182178354Ssam
183178354Ssam	vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT;
184178354Ssam	vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT;
185178354Ssam	vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
186178354Ssam	callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE);
187178354Ssam	callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE);
188191746Sthompsa	TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap);
189191746Sthompsa	TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
190138568Ssam	/*
191178354Ssam	 * Install default tx rate handling: no fixed rate, lowest
192178354Ssam	 * supported rate for mgmt and multicast frames.  Default
193178354Ssam	 * max retry count.  These settings can be changed by the
194178354Ssam	 * driver and/or user applications.
195178354Ssam	 */
196188779Ssam	for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
197178354Ssam		const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i];
198178354Ssam
199178354Ssam		vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
200188779Ssam		if (i == IEEE80211_MODE_11NA || i == IEEE80211_MODE_11NG) {
201188779Ssam			vap->iv_txparms[i].mgmtrate = 0 | IEEE80211_RATE_MCS;
202188779Ssam			vap->iv_txparms[i].mcastrate = 0 | IEEE80211_RATE_MCS;
203188779Ssam		} else {
204188779Ssam			vap->iv_txparms[i].mgmtrate =
205188779Ssam			    rs->rs_rates[0] & IEEE80211_RATE_VAL;
206188779Ssam			vap->iv_txparms[i].mcastrate =
207188779Ssam			    rs->rs_rates[0] & IEEE80211_RATE_VAL;
208188779Ssam		}
209178354Ssam		vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
210178354Ssam	}
211178354Ssam	vap->iv_roaming = IEEE80211_ROAMING_AUTO;
212178354Ssam
213178354Ssam	vap->iv_update_beacon = null_update_beacon;
214178354Ssam	vap->iv_deliver_data = ieee80211_deliver_data;
215178354Ssam
216178354Ssam	/* attach support for operating mode */
217178354Ssam	ic->ic_vattach[vap->iv_opmode](vap);
218178354Ssam}
219178354Ssam
220178354Ssamvoid
221178354Ssamieee80211_proto_vdetach(struct ieee80211vap *vap)
222178354Ssam{
223178354Ssam#define	FREEAPPIE(ie) do { \
224178354Ssam	if (ie != NULL) \
225186302Ssam		free(ie, M_80211_NODE_IE); \
226178354Ssam} while (0)
227178354Ssam	/*
228178354Ssam	 * Detach operating mode module.
229178354Ssam	 */
230178354Ssam	if (vap->iv_opdetach != NULL)
231178354Ssam		vap->iv_opdetach(vap);
232178354Ssam	/*
233138568Ssam	 * This should not be needed as we detach when reseting
234138568Ssam	 * the state but be conservative here since the
235138568Ssam	 * authenticator may do things like spawn kernel threads.
236138568Ssam	 */
237178354Ssam	if (vap->iv_auth->ia_detach != NULL)
238178354Ssam		vap->iv_auth->ia_detach(vap);
239138568Ssam	/*
240138568Ssam	 * Detach any ACL'ator.
241138568Ssam	 */
242178354Ssam	if (vap->iv_acl != NULL)
243178354Ssam		vap->iv_acl->iac_detach(vap);
244178354Ssam
245178354Ssam	FREEAPPIE(vap->iv_appie_beacon);
246178354Ssam	FREEAPPIE(vap->iv_appie_probereq);
247178354Ssam	FREEAPPIE(vap->iv_appie_proberesp);
248178354Ssam	FREEAPPIE(vap->iv_appie_assocreq);
249178354Ssam	FREEAPPIE(vap->iv_appie_assocresp);
250178354Ssam	FREEAPPIE(vap->iv_appie_wpa);
251178354Ssam#undef FREEAPPIE
252116742Ssam}
253116742Ssam
254138568Ssam/*
255138568Ssam * Simple-minded authenticator module support.
256138568Ssam */
257138568Ssam
258138568Ssam#define	IEEE80211_AUTH_MAX	(IEEE80211_AUTH_WPA+1)
259138568Ssam/* XXX well-known names */
260138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = {
261138568Ssam	"wlan_internal",	/* IEEE80211_AUTH_NONE */
262138568Ssam	"wlan_internal",	/* IEEE80211_AUTH_OPEN */
263138568Ssam	"wlan_internal",	/* IEEE80211_AUTH_SHARED */
264138568Ssam	"wlan_xauth",		/* IEEE80211_AUTH_8021X	 */
265138568Ssam	"wlan_internal",	/* IEEE80211_AUTH_AUTO */
266138568Ssam	"wlan_xauth",		/* IEEE80211_AUTH_WPA */
267138568Ssam};
268138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX];
269138568Ssam
270138568Ssamstatic const struct ieee80211_authenticator auth_internal = {
271138568Ssam	.ia_name		= "wlan_internal",
272138568Ssam	.ia_attach		= NULL,
273138568Ssam	.ia_detach		= NULL,
274138568Ssam	.ia_node_join		= NULL,
275138568Ssam	.ia_node_leave		= NULL,
276138568Ssam};
277138568Ssam
278138568Ssam/*
279138568Ssam * Setup internal authenticators once; they are never unregistered.
280138568Ssam */
281138568Ssamstatic void
282138568Ssamieee80211_auth_setup(void)
283138568Ssam{
284138568Ssam	ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal);
285138568Ssam	ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal);
286138568Ssam	ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal);
287138568Ssam}
288138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL);
289138568Ssam
290138568Ssamconst struct ieee80211_authenticator *
291138568Ssamieee80211_authenticator_get(int auth)
292138568Ssam{
293138568Ssam	if (auth >= IEEE80211_AUTH_MAX)
294138568Ssam		return NULL;
295138568Ssam	if (authenticators[auth] == NULL)
296138568Ssam		ieee80211_load_module(auth_modnames[auth]);
297138568Ssam	return authenticators[auth];
298138568Ssam}
299138568Ssam
300116742Ssamvoid
301138568Ssamieee80211_authenticator_register(int type,
302138568Ssam	const struct ieee80211_authenticator *auth)
303116742Ssam{
304138568Ssam	if (type >= IEEE80211_AUTH_MAX)
305138568Ssam		return;
306138568Ssam	authenticators[type] = auth;
307138568Ssam}
308138568Ssam
309138568Ssamvoid
310138568Ssamieee80211_authenticator_unregister(int type)
311138568Ssam{
312138568Ssam
313138568Ssam	if (type >= IEEE80211_AUTH_MAX)
314138568Ssam		return;
315138568Ssam	authenticators[type] = NULL;
316138568Ssam}
317138568Ssam
318138568Ssam/*
319138568Ssam * Very simple-minded ACL module support.
320138568Ssam */
321138568Ssam/* XXX just one for now */
322138568Ssamstatic	const struct ieee80211_aclator *acl = NULL;
323138568Ssam
324138568Ssamvoid
325138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac)
326138568Ssam{
327138568Ssam	printf("wlan: %s acl policy registered\n", iac->iac_name);
328138568Ssam	acl = iac;
329138568Ssam}
330138568Ssam
331138568Ssamvoid
332138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac)
333138568Ssam{
334138568Ssam	if (acl == iac)
335138568Ssam		acl = NULL;
336138568Ssam	printf("wlan: %s acl policy unregistered\n", iac->iac_name);
337138568Ssam}
338138568Ssam
339138568Ssamconst struct ieee80211_aclator *
340138568Ssamieee80211_aclator_get(const char *name)
341138568Ssam{
342138568Ssam	if (acl == NULL)
343138568Ssam		ieee80211_load_module("wlan_acl");
344138568Ssam	return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL;
345138568Ssam}
346138568Ssam
347138568Ssamvoid
348170530Ssamieee80211_print_essid(const uint8_t *essid, int len)
349138568Ssam{
350170530Ssam	const uint8_t *p;
351116742Ssam	int i;
352116742Ssam
353116742Ssam	if (len > IEEE80211_NWID_LEN)
354116742Ssam		len = IEEE80211_NWID_LEN;
355116742Ssam	/* determine printable or not */
356116742Ssam	for (i = 0, p = essid; i < len; i++, p++) {
357116742Ssam		if (*p < ' ' || *p > 0x7e)
358116742Ssam			break;
359116742Ssam	}
360116742Ssam	if (i == len) {
361116742Ssam		printf("\"");
362116742Ssam		for (i = 0, p = essid; i < len; i++, p++)
363116742Ssam			printf("%c", *p);
364116742Ssam		printf("\"");
365116742Ssam	} else {
366116742Ssam		printf("0x");
367116742Ssam		for (i = 0, p = essid; i < len; i++, p++)
368116742Ssam			printf("%02x", *p);
369116742Ssam	}
370116742Ssam}
371116742Ssam
372116742Ssamvoid
373170530Ssamieee80211_dump_pkt(struct ieee80211com *ic,
374170530Ssam	const uint8_t *buf, int len, int rate, int rssi)
375116742Ssam{
376138568Ssam	const struct ieee80211_frame *wh;
377116742Ssam	int i;
378116742Ssam
379138568Ssam	wh = (const struct ieee80211_frame *)buf;
380116742Ssam	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
381116742Ssam	case IEEE80211_FC1_DIR_NODS:
382116742Ssam		printf("NODS %s", ether_sprintf(wh->i_addr2));
383116742Ssam		printf("->%s", ether_sprintf(wh->i_addr1));
384116742Ssam		printf("(%s)", ether_sprintf(wh->i_addr3));
385116742Ssam		break;
386116742Ssam	case IEEE80211_FC1_DIR_TODS:
387116742Ssam		printf("TODS %s", ether_sprintf(wh->i_addr2));
388116742Ssam		printf("->%s", ether_sprintf(wh->i_addr3));
389116742Ssam		printf("(%s)", ether_sprintf(wh->i_addr1));
390116742Ssam		break;
391116742Ssam	case IEEE80211_FC1_DIR_FROMDS:
392116742Ssam		printf("FRDS %s", ether_sprintf(wh->i_addr3));
393116742Ssam		printf("->%s", ether_sprintf(wh->i_addr1));
394116742Ssam		printf("(%s)", ether_sprintf(wh->i_addr2));
395116742Ssam		break;
396116742Ssam	case IEEE80211_FC1_DIR_DSTODS:
397170530Ssam		printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1]));
398116742Ssam		printf("->%s", ether_sprintf(wh->i_addr3));
399116742Ssam		printf("(%s", ether_sprintf(wh->i_addr2));
400116742Ssam		printf("->%s)", ether_sprintf(wh->i_addr1));
401116742Ssam		break;
402116742Ssam	}
403116742Ssam	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
404116742Ssam	case IEEE80211_FC0_TYPE_DATA:
405116742Ssam		printf(" data");
406116742Ssam		break;
407116742Ssam	case IEEE80211_FC0_TYPE_MGT:
408116742Ssam		printf(" %s", ieee80211_mgt_subtype_name[
409116742Ssam		    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
410116742Ssam		    >> IEEE80211_FC0_SUBTYPE_SHIFT]);
411116742Ssam		break;
412116742Ssam	default:
413116742Ssam		printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
414116742Ssam		break;
415116742Ssam	}
416170530Ssam	if (IEEE80211_QOS_HAS_SEQ(wh)) {
417170530Ssam		const struct ieee80211_qosframe *qwh =
418170530Ssam			(const struct ieee80211_qosframe *)buf;
419170530Ssam		printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID,
420170530Ssam			qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : "");
421170530Ssam	}
422138568Ssam	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
423170530Ssam		int off;
424170530Ssam
425170530Ssam		off = ieee80211_anyhdrspace(ic, wh);
426170530Ssam		printf(" WEP [IV %.02x %.02x %.02x",
427170530Ssam			buf[off+0], buf[off+1], buf[off+2]);
428170530Ssam		if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV)
429170530Ssam			printf(" %.02x %.02x %.02x",
430170530Ssam				buf[off+4], buf[off+5], buf[off+6]);
431170530Ssam		printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6);
432138568Ssam	}
433116742Ssam	if (rate >= 0)
434116742Ssam		printf(" %dM", rate / 2);
435116742Ssam	if (rssi >= 0)
436116742Ssam		printf(" +%d", rssi);
437116742Ssam	printf("\n");
438116742Ssam	if (len > 0) {
439116742Ssam		for (i = 0; i < len; i++) {
440116742Ssam			if ((i & 1) == 0)
441116742Ssam				printf(" ");
442116742Ssam			printf("%02x", buf[i]);
443116742Ssam		}
444116742Ssam		printf("\n");
445116742Ssam	}
446116742Ssam}
447116742Ssam
448165887Ssamstatic __inline int
449165887Ssamfindrix(const struct ieee80211_rateset *rs, int r)
450165887Ssam{
451165887Ssam	int i;
452165887Ssam
453165887Ssam	for (i = 0; i < rs->rs_nrates; i++)
454165887Ssam		if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r)
455165887Ssam			return i;
456165887Ssam	return -1;
457165887Ssam}
458165887Ssam
459116742Ssamint
460167442Ssamieee80211_fix_rate(struct ieee80211_node *ni,
461167442Ssam	struct ieee80211_rateset *nrs, int flags)
462116742Ssam{
463116742Ssam#define	RV(v)	((v) & IEEE80211_RATE_VAL)
464178354Ssam	struct ieee80211vap *vap = ni->ni_vap;
465148299Ssam	struct ieee80211com *ic = ni->ni_ic;
466165887Ssam	int i, j, rix, error;
467178354Ssam	int okrate, badrate, fixedrate, ucastrate;
468165569Ssam	const struct ieee80211_rateset *srs;
469170530Ssam	uint8_t r;
470116742Ssam
471116742Ssam	error = 0;
472170530Ssam	okrate = badrate = 0;
473178354Ssam	ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate;
474178354Ssam	if (ucastrate != IEEE80211_FIXED_RATE_NONE) {
475178354Ssam		/*
476178354Ssam		 * Workaround awkwardness with fixed rate.  We are called
477178354Ssam		 * to check both the legacy rate set and the HT rate set
478178354Ssam		 * but we must apply any legacy fixed rate check only to the
479178354Ssam		 * legacy rate set and vice versa.  We cannot tell what type
480178354Ssam		 * of rate set we've been given (legacy or HT) but we can
481178354Ssam		 * distinguish the fixed rate type (MCS have 0x80 set).
482178354Ssam		 * So to deal with this the caller communicates whether to
483178354Ssam		 * check MCS or legacy rate using the flags and we use the
484178354Ssam		 * type of any fixed rate to avoid applying an MCS to a
485178354Ssam		 * legacy rate and vice versa.
486178354Ssam		 */
487178354Ssam		if (ucastrate & 0x80) {
488178354Ssam			if (flags & IEEE80211_F_DOFRATE)
489178354Ssam				flags &= ~IEEE80211_F_DOFRATE;
490178354Ssam		} else if ((ucastrate & 0x80) == 0) {
491178354Ssam			if (flags & IEEE80211_F_DOFMCS)
492178354Ssam				flags &= ~IEEE80211_F_DOFMCS;
493178354Ssam		}
494178354Ssam		/* NB: required to make MCS match below work */
495178354Ssam		ucastrate &= IEEE80211_RATE_VAL;
496178354Ssam	}
497170530Ssam	fixedrate = IEEE80211_FIXED_RATE_NONE;
498178354Ssam	/*
499178354Ssam	 * XXX we are called to process both MCS and legacy rates;
500178354Ssam	 * we must use the appropriate basic rate set or chaos will
501178354Ssam	 * ensue; for now callers that want MCS must supply
502178354Ssam	 * IEEE80211_F_DOBRS; at some point we'll need to split this
503178354Ssam	 * function so there are two variants, one for MCS and one
504178354Ssam	 * for legacy rates.
505178354Ssam	 */
506178354Ssam	if (flags & IEEE80211_F_DOBRS)
507178354Ssam		srs = (const struct ieee80211_rateset *)
508178354Ssam		    ieee80211_get_suphtrates(ic, ni->ni_chan);
509178354Ssam	else
510178354Ssam		srs = ieee80211_get_suprates(ic, ni->ni_chan);
511120482Ssam	for (i = 0; i < nrs->rs_nrates; ) {
512116742Ssam		if (flags & IEEE80211_F_DOSORT) {
513116742Ssam			/*
514116742Ssam			 * Sort rates.
515116742Ssam			 */
516116742Ssam			for (j = i + 1; j < nrs->rs_nrates; j++) {
517116742Ssam				if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) {
518116742Ssam					r = nrs->rs_rates[i];
519116742Ssam					nrs->rs_rates[i] = nrs->rs_rates[j];
520116742Ssam					nrs->rs_rates[j] = r;
521116742Ssam				}
522116742Ssam			}
523116742Ssam		}
524116742Ssam		r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
525116742Ssam		badrate = r;
526165887Ssam		/*
527170530Ssam		 * Check for fixed rate.
528170530Ssam		 */
529178354Ssam		if (r == ucastrate)
530170530Ssam			fixedrate = r;
531170530Ssam		/*
532165887Ssam		 * Check against supported rates.
533165887Ssam		 */
534165887Ssam		rix = findrix(srs, r);
535116742Ssam		if (flags & IEEE80211_F_DONEGO) {
536165887Ssam			if (rix < 0) {
537120482Ssam				/*
538120482Ssam				 * A rate in the node's rate set is not
539120482Ssam				 * supported.  If this is a basic rate and we
540165887Ssam				 * are operating as a STA then this is an error.
541120482Ssam				 * Otherwise we just discard/ignore the rate.
542120482Ssam				 */
543165887Ssam				if ((flags & IEEE80211_F_JOIN) &&
544120482Ssam				    (nrs->rs_rates[i] & IEEE80211_RATE_BASIC))
545116742Ssam					error++;
546165887Ssam			} else if ((flags & IEEE80211_F_JOIN) == 0) {
547165887Ssam				/*
548165887Ssam				 * Overwrite with the supported rate
549165887Ssam				 * value so any basic rate bit is set.
550165887Ssam				 */
551165887Ssam				nrs->rs_rates[i] = srs->rs_rates[rix];
552116742Ssam			}
553116742Ssam		}
554165887Ssam		if ((flags & IEEE80211_F_DODEL) && rix < 0) {
555116742Ssam			/*
556116742Ssam			 * Delete unacceptable rates.
557116742Ssam			 */
558165887Ssam			nrs->rs_nrates--;
559165887Ssam			for (j = i; j < nrs->rs_nrates; j++)
560165887Ssam				nrs->rs_rates[j] = nrs->rs_rates[j + 1];
561165887Ssam			nrs->rs_rates[j] = 0;
562165887Ssam			continue;
563116742Ssam		}
564165887Ssam		if (rix >= 0)
565116742Ssam			okrate = nrs->rs_rates[i];
566116742Ssam		i++;
567116742Ssam	}
568138568Ssam	if (okrate == 0 || error != 0 ||
569178354Ssam	    ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) &&
570178354Ssam	     fixedrate != ucastrate)) {
571178354Ssam		IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
572178354Ssam		    "%s: flags 0x%x okrate %d error %d fixedrate 0x%x "
573178354Ssam		    "ucastrate %x\n", __func__, fixedrate, ucastrate, flags);
574116742Ssam		return badrate | IEEE80211_RATE_BASIC;
575178354Ssam	} else
576116742Ssam		return RV(okrate);
577116742Ssam#undef RV
578116742Ssam}
579116742Ssam
580138568Ssam/*
581138568Ssam * Reset 11g-related state.
582138568Ssam */
583138568Ssamvoid
584138568Ssamieee80211_reset_erp(struct ieee80211com *ic)
585138568Ssam{
586138568Ssam	ic->ic_flags &= ~IEEE80211_F_USEPROT;
587138568Ssam	ic->ic_nonerpsta = 0;
588138568Ssam	ic->ic_longslotsta = 0;
589138568Ssam	/*
590138568Ssam	 * Short slot time is enabled only when operating in 11g
591138568Ssam	 * and not in an IBSS.  We must also honor whether or not
592138568Ssam	 * the driver is capable of doing it.
593138568Ssam	 */
594138568Ssam	ieee80211_set_shortslottime(ic,
595170530Ssam		IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
596170530Ssam		IEEE80211_IS_CHAN_HT(ic->ic_curchan) ||
597170530Ssam		(IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
598138568Ssam		ic->ic_opmode == IEEE80211_M_HOSTAP &&
599138568Ssam		(ic->ic_caps & IEEE80211_C_SHSLOT)));
600138568Ssam	/*
601138568Ssam	 * Set short preamble and ERP barker-preamble flags.
602138568Ssam	 */
603170530Ssam	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
604138568Ssam	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
605138568Ssam		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
606138568Ssam		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
607138568Ssam	} else {
608138568Ssam		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
609138568Ssam		ic->ic_flags |= IEEE80211_F_USEBARKER;
610138568Ssam	}
611138568Ssam}
612138568Ssam
613138568Ssam/*
614138568Ssam * Set the short slot time state and notify the driver.
615138568Ssam */
616138568Ssamvoid
617138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
618138568Ssam{
619138568Ssam	if (onoff)
620138568Ssam		ic->ic_flags |= IEEE80211_F_SHSLOT;
621138568Ssam	else
622138568Ssam		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
623138568Ssam	/* notify driver */
624138568Ssam	if (ic->ic_updateslot != NULL)
625138568Ssam		ic->ic_updateslot(ic->ic_ifp);
626138568Ssam}
627138568Ssam
628138568Ssam/*
629138568Ssam * Check if the specified rate set supports ERP.
630138568Ssam * NB: the rate set is assumed to be sorted.
631138568Ssam */
632138568Ssamint
633178354Ssamieee80211_iserp_rateset(const struct ieee80211_rateset *rs)
634138568Ssam{
635138568Ssam#define N(a)	(sizeof(a) / sizeof(a[0]))
636138568Ssam	static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
637138568Ssam	int i, j;
638138568Ssam
639138568Ssam	if (rs->rs_nrates < N(rates))
640138568Ssam		return 0;
641138568Ssam	for (i = 0; i < N(rates); i++) {
642138568Ssam		for (j = 0; j < rs->rs_nrates; j++) {
643138568Ssam			int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
644138568Ssam			if (rates[i] == r)
645138568Ssam				goto next;
646138568Ssam			if (r > rates[i])
647138568Ssam				return 0;
648138568Ssam		}
649138568Ssam		return 0;
650138568Ssam	next:
651138568Ssam		;
652138568Ssam	}
653138568Ssam	return 1;
654138568Ssam#undef N
655138568Ssam}
656138568Ssam
657138568Ssam/*
658178354Ssam * Mark the basic rates for the rate table based on the
659138568Ssam * operating mode.  For real 11g we mark all the 11b rates
660138568Ssam * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
661138568Ssam * 11b rates.  There's also a pseudo 11a-mode used to mark only
662138568Ssam * the basic OFDM rates.
663138568Ssam */
664178354Ssamstatic void
665178354Ssamsetbasicrates(struct ieee80211_rateset *rs,
666178354Ssam    enum ieee80211_phymode mode, int add)
667138568Ssam{
668170530Ssam	static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = {
669188780Ssam	    [IEEE80211_MODE_11A]	= { 3, { 12, 24, 48 } },
670188780Ssam	    [IEEE80211_MODE_11B]	= { 2, { 2, 4 } },
671188780Ssam					    /* NB: mixed b/g */
672188780Ssam	    [IEEE80211_MODE_11G]	= { 4, { 2, 4, 11, 22 } },
673188780Ssam	    [IEEE80211_MODE_TURBO_A]	= { 3, { 12, 24, 48 } },
674188780Ssam	    [IEEE80211_MODE_TURBO_G]	= { 4, { 2, 4, 11, 22 } },
675188780Ssam	    [IEEE80211_MODE_STURBO_A]	= { 3, { 12, 24, 48 } },
676188782Ssam	    [IEEE80211_MODE_HALF]	= { 3, { 6, 12, 24 } },
677188782Ssam	    [IEEE80211_MODE_QUARTER]	= { 3, { 3, 6, 12 } },
678188780Ssam	    [IEEE80211_MODE_11NA]	= { 3, { 12, 24, 48 } },
679188780Ssam					    /* NB: mixed b/g */
680188780Ssam	    [IEEE80211_MODE_11NG]	= { 4, { 2, 4, 11, 22 } },
681138568Ssam	};
682138568Ssam	int i, j;
683138568Ssam
684138568Ssam	for (i = 0; i < rs->rs_nrates; i++) {
685178354Ssam		if (!add)
686178354Ssam			rs->rs_rates[i] &= IEEE80211_RATE_VAL;
687138568Ssam		for (j = 0; j < basic[mode].rs_nrates; j++)
688138568Ssam			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
689138568Ssam				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
690138568Ssam				break;
691138568Ssam			}
692138568Ssam	}
693138568Ssam}
694138568Ssam
695138568Ssam/*
696178354Ssam * Set the basic rates in a rate set.
697138568Ssam */
698178354Ssamvoid
699178354Ssamieee80211_setbasicrates(struct ieee80211_rateset *rs,
700178354Ssam    enum ieee80211_phymode mode)
701178354Ssam{
702178354Ssam	setbasicrates(rs, mode, 0);
703178354Ssam}
704178354Ssam
705178354Ssam/*
706178354Ssam * Add basic rates to a rate set.
707178354Ssam */
708178354Ssamvoid
709178354Ssamieee80211_addbasicrates(struct ieee80211_rateset *rs,
710178354Ssam    enum ieee80211_phymode mode)
711178354Ssam{
712178354Ssam	setbasicrates(rs, mode, 1);
713178354Ssam}
714178354Ssam
715178354Ssam/*
716178354Ssam * WME protocol support.
717178354Ssam *
718178354Ssam * The default 11a/b/g/n parameters come from the WiFi Alliance WMM
719178354Ssam * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n
720178354Ssam * Draft 2.0 Test Plan (Appendix D).
721178354Ssam *
722178354Ssam * Static/Dynamic Turbo mode settings come from Atheros.
723178354Ssam */
724138568Ssamtypedef struct phyParamType {
725178354Ssam	uint8_t		aifsn;
726178354Ssam	uint8_t		logcwmin;
727178354Ssam	uint8_t		logcwmax;
728178354Ssam	uint16_t	txopLimit;
729178354Ssam	uint8_t 	acm;
730138568Ssam} paramType;
731138568Ssam
732138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
733188780Ssam	[IEEE80211_MODE_AUTO]	= { 3, 4,  6,  0, 0 },
734188780Ssam	[IEEE80211_MODE_11A]	= { 3, 4,  6,  0, 0 },
735188780Ssam	[IEEE80211_MODE_11B]	= { 3, 4,  6,  0, 0 },
736188780Ssam	[IEEE80211_MODE_11G]	= { 3, 4,  6,  0, 0 },
737188780Ssam	[IEEE80211_MODE_FH]	= { 3, 4,  6,  0, 0 },
738188780Ssam	[IEEE80211_MODE_TURBO_A]= { 2, 3,  5,  0, 0 },
739188780Ssam	[IEEE80211_MODE_TURBO_G]= { 2, 3,  5,  0, 0 },
740188780Ssam	[IEEE80211_MODE_STURBO_A]={ 2, 3,  5,  0, 0 },
741188782Ssam	[IEEE80211_MODE_HALF]	= { 3, 4,  6,  0, 0 },
742188782Ssam	[IEEE80211_MODE_QUARTER]= { 3, 4,  6,  0, 0 },
743188780Ssam	[IEEE80211_MODE_11NA]	= { 3, 4,  6,  0, 0 },
744188780Ssam	[IEEE80211_MODE_11NG]	= { 3, 4,  6,  0, 0 },
745138568Ssam};
746138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
747188780Ssam	[IEEE80211_MODE_AUTO]	= { 7, 4, 10,  0, 0 },
748188780Ssam	[IEEE80211_MODE_11A]	= { 7, 4, 10,  0, 0 },
749188780Ssam	[IEEE80211_MODE_11B]	= { 7, 4, 10,  0, 0 },
750188780Ssam	[IEEE80211_MODE_11G]	= { 7, 4, 10,  0, 0 },
751188780Ssam	[IEEE80211_MODE_FH]	= { 7, 4, 10,  0, 0 },
752188780Ssam	[IEEE80211_MODE_TURBO_A]= { 7, 3, 10,  0, 0 },
753188780Ssam	[IEEE80211_MODE_TURBO_G]= { 7, 3, 10,  0, 0 },
754188780Ssam	[IEEE80211_MODE_STURBO_A]={ 7, 3, 10,  0, 0 },
755188782Ssam	[IEEE80211_MODE_HALF]	= { 7, 4, 10,  0, 0 },
756188782Ssam	[IEEE80211_MODE_QUARTER]= { 7, 4, 10,  0, 0 },
757188780Ssam	[IEEE80211_MODE_11NA]	= { 7, 4, 10,  0, 0 },
758188780Ssam	[IEEE80211_MODE_11NG]	= { 7, 4, 10,  0, 0 },
759138568Ssam};
760138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
761188780Ssam	[IEEE80211_MODE_AUTO]	= { 1, 3, 4,  94, 0 },
762188780Ssam	[IEEE80211_MODE_11A]	= { 1, 3, 4,  94, 0 },
763188780Ssam	[IEEE80211_MODE_11B]	= { 1, 3, 4, 188, 0 },
764188780Ssam	[IEEE80211_MODE_11G]	= { 1, 3, 4,  94, 0 },
765188780Ssam	[IEEE80211_MODE_FH]	= { 1, 3, 4, 188, 0 },
766188780Ssam	[IEEE80211_MODE_TURBO_A]= { 1, 2, 3,  94, 0 },
767188780Ssam	[IEEE80211_MODE_TURBO_G]= { 1, 2, 3,  94, 0 },
768188780Ssam	[IEEE80211_MODE_STURBO_A]={ 1, 2, 3,  94, 0 },
769188782Ssam	[IEEE80211_MODE_HALF]	= { 1, 3, 4,  94, 0 },
770188782Ssam	[IEEE80211_MODE_QUARTER]= { 1, 3, 4,  94, 0 },
771188780Ssam	[IEEE80211_MODE_11NA]	= { 1, 3, 4,  94, 0 },
772188780Ssam	[IEEE80211_MODE_11NG]	= { 1, 3, 4,  94, 0 },
773138568Ssam};
774138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
775188780Ssam	[IEEE80211_MODE_AUTO]	= { 1, 2, 3,  47, 0 },
776188780Ssam	[IEEE80211_MODE_11A]	= { 1, 2, 3,  47, 0 },
777188780Ssam	[IEEE80211_MODE_11B]	= { 1, 2, 3, 102, 0 },
778188780Ssam	[IEEE80211_MODE_11G]	= { 1, 2, 3,  47, 0 },
779188780Ssam	[IEEE80211_MODE_FH]	= { 1, 2, 3, 102, 0 },
780188780Ssam	[IEEE80211_MODE_TURBO_A]= { 1, 2, 2,  47, 0 },
781188780Ssam	[IEEE80211_MODE_TURBO_G]= { 1, 2, 2,  47, 0 },
782188780Ssam	[IEEE80211_MODE_STURBO_A]={ 1, 2, 2,  47, 0 },
783188782Ssam	[IEEE80211_MODE_HALF]	= { 1, 2, 3,  47, 0 },
784188782Ssam	[IEEE80211_MODE_QUARTER]= { 1, 2, 3,  47, 0 },
785188780Ssam	[IEEE80211_MODE_11NA]	= { 1, 2, 3,  47, 0 },
786188780Ssam	[IEEE80211_MODE_11NG]	= { 1, 2, 3,  47, 0 },
787138568Ssam};
788138568Ssam
789138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = {
790188780Ssam	[IEEE80211_MODE_AUTO]	= { 3, 4, 10,  0, 0 },
791188780Ssam	[IEEE80211_MODE_11A]	= { 3, 4, 10,  0, 0 },
792188780Ssam	[IEEE80211_MODE_11B]	= { 3, 4, 10,  0, 0 },
793188780Ssam	[IEEE80211_MODE_11G]	= { 3, 4, 10,  0, 0 },
794188780Ssam	[IEEE80211_MODE_FH]	= { 3, 4, 10,  0, 0 },
795188780Ssam	[IEEE80211_MODE_TURBO_A]= { 2, 3, 10,  0, 0 },
796188780Ssam	[IEEE80211_MODE_TURBO_G]= { 2, 3, 10,  0, 0 },
797188780Ssam	[IEEE80211_MODE_STURBO_A]={ 2, 3, 10,  0, 0 },
798188782Ssam	[IEEE80211_MODE_HALF]	= { 3, 4, 10,  0, 0 },
799188782Ssam	[IEEE80211_MODE_QUARTER]= { 3, 4, 10,  0, 0 },
800188780Ssam	[IEEE80211_MODE_11NA]	= { 3, 4, 10,  0, 0 },
801188780Ssam	[IEEE80211_MODE_11NG]	= { 3, 4, 10,  0, 0 },
802138568Ssam};
803138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = {
804188780Ssam	[IEEE80211_MODE_AUTO]	= { 2, 3, 4,  94, 0 },
805188780Ssam	[IEEE80211_MODE_11A]	= { 2, 3, 4,  94, 0 },
806188780Ssam	[IEEE80211_MODE_11B]	= { 2, 3, 4, 188, 0 },
807188780Ssam	[IEEE80211_MODE_11G]	= { 2, 3, 4,  94, 0 },
808188780Ssam	[IEEE80211_MODE_FH]	= { 2, 3, 4, 188, 0 },
809188780Ssam	[IEEE80211_MODE_TURBO_A]= { 2, 2, 3,  94, 0 },
810188780Ssam	[IEEE80211_MODE_TURBO_G]= { 2, 2, 3,  94, 0 },
811188780Ssam	[IEEE80211_MODE_STURBO_A]={ 2, 2, 3,  94, 0 },
812188782Ssam	[IEEE80211_MODE_HALF]	= { 2, 3, 4,  94, 0 },
813188782Ssam	[IEEE80211_MODE_QUARTER]= { 2, 3, 4,  94, 0 },
814188780Ssam	[IEEE80211_MODE_11NA]	= { 2, 3, 4,  94, 0 },
815188780Ssam	[IEEE80211_MODE_11NG]	= { 2, 3, 4,  94, 0 },
816138568Ssam};
817138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
818188780Ssam	[IEEE80211_MODE_AUTO]	= { 2, 2, 3,  47, 0 },
819188780Ssam	[IEEE80211_MODE_11A]	= { 2, 2, 3,  47, 0 },
820188780Ssam	[IEEE80211_MODE_11B]	= { 2, 2, 3, 102, 0 },
821188780Ssam	[IEEE80211_MODE_11G]	= { 2, 2, 3,  47, 0 },
822188780Ssam	[IEEE80211_MODE_FH]	= { 2, 2, 3, 102, 0 },
823188780Ssam	[IEEE80211_MODE_TURBO_A]= { 1, 2, 2,  47, 0 },
824188780Ssam	[IEEE80211_MODE_TURBO_G]= { 1, 2, 2,  47, 0 },
825188780Ssam	[IEEE80211_MODE_STURBO_A]={ 1, 2, 2,  47, 0 },
826188782Ssam	[IEEE80211_MODE_HALF]	= { 2, 2, 3,  47, 0 },
827188782Ssam	[IEEE80211_MODE_QUARTER]= { 2, 2, 3,  47, 0 },
828188780Ssam	[IEEE80211_MODE_11NA]	= { 2, 2, 3,  47, 0 },
829188780Ssam	[IEEE80211_MODE_11NG]	= { 2, 2, 3,  47, 0 },
830138568Ssam};
831138568Ssam
832178354Ssamstatic void
833188863Ssam_setifsparams(struct wmeParams *wmep, const paramType *phy)
834188863Ssam{
835188863Ssam	wmep->wmep_aifsn = phy->aifsn;
836188863Ssam	wmep->wmep_logcwmin = phy->logcwmin;
837188863Ssam	wmep->wmep_logcwmax = phy->logcwmax;
838188863Ssam	wmep->wmep_txopLimit = phy->txopLimit;
839188863Ssam}
840188863Ssam
841188863Ssamstatic void
842188863Ssamsetwmeparams(struct ieee80211vap *vap, const char *type, int ac,
843188863Ssam	struct wmeParams *wmep, const paramType *phy)
844188863Ssam{
845188863Ssam	wmep->wmep_acm = phy->acm;
846188863Ssam	_setifsparams(wmep, phy);
847188863Ssam
848188863Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
849188863Ssam	    "set %s (%s) [acm %u aifsn %u logcwmin %u logcwmax %u txop %u]\n",
850188863Ssam	    ieee80211_wme_acnames[ac], type,
851188863Ssam	    wmep->wmep_acm, wmep->wmep_aifsn, wmep->wmep_logcwmin,
852188863Ssam	    wmep->wmep_logcwmax, wmep->wmep_txopLimit);
853188863Ssam}
854188863Ssam
855188863Ssamstatic void
856178354Ssamieee80211_wme_initparams_locked(struct ieee80211vap *vap)
857138568Ssam{
858178354Ssam	struct ieee80211com *ic = vap->iv_ic;
859138568Ssam	struct ieee80211_wme_state *wme = &ic->ic_wme;
860138568Ssam	const paramType *pPhyParam, *pBssPhyParam;
861138568Ssam	struct wmeParams *wmep;
862170530Ssam	enum ieee80211_phymode mode;
863138568Ssam	int i;
864138568Ssam
865178354Ssam	IEEE80211_LOCK_ASSERT(ic);
866178354Ssam
867188864Ssam	if ((ic->ic_caps & IEEE80211_C_WME) == 0 || ic->ic_nrunning > 1)
868138568Ssam		return;
869138568Ssam
870170530Ssam	/*
871170530Ssam	 * Select mode; we can be called early in which case we
872170530Ssam	 * always use auto mode.  We know we'll be called when
873170530Ssam	 * entering the RUN state with bsschan setup properly
874170530Ssam	 * so state will eventually get set correctly
875170530Ssam	 */
876170530Ssam	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
877170530Ssam		mode = ieee80211_chan2mode(ic->ic_bsschan);
878170530Ssam	else
879170530Ssam		mode = IEEE80211_MODE_AUTO;
880138568Ssam	for (i = 0; i < WME_NUM_AC; i++) {
881138568Ssam		switch (i) {
882138568Ssam		case WME_AC_BK:
883170530Ssam			pPhyParam = &phyParamForAC_BK[mode];
884170530Ssam			pBssPhyParam = &phyParamForAC_BK[mode];
885138568Ssam			break;
886138568Ssam		case WME_AC_VI:
887170530Ssam			pPhyParam = &phyParamForAC_VI[mode];
888170530Ssam			pBssPhyParam = &bssPhyParamForAC_VI[mode];
889138568Ssam			break;
890138568Ssam		case WME_AC_VO:
891170530Ssam			pPhyParam = &phyParamForAC_VO[mode];
892170530Ssam			pBssPhyParam = &bssPhyParamForAC_VO[mode];
893138568Ssam			break;
894138568Ssam		case WME_AC_BE:
895138568Ssam		default:
896170530Ssam			pPhyParam = &phyParamForAC_BE[mode];
897170530Ssam			pBssPhyParam = &bssPhyParamForAC_BE[mode];
898138568Ssam			break;
899138568Ssam		}
900138568Ssam		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
901138568Ssam		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
902188863Ssam			setwmeparams(vap, "chan", i, wmep, pPhyParam);
903138568Ssam		} else {
904188863Ssam			setwmeparams(vap, "chan", i, wmep, pBssPhyParam);
905138568Ssam		}
906138568Ssam		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
907188863Ssam		setwmeparams(vap, "bss ", i, wmep, pBssPhyParam);
908138568Ssam	}
909138568Ssam	/* NB: check ic_bss to avoid NULL deref on initial attach */
910178354Ssam	if (vap->iv_bss != NULL) {
911138568Ssam		/*
912138568Ssam		 * Calculate agressive mode switching threshold based
913138568Ssam		 * on beacon interval.  This doesn't need locking since
914138568Ssam		 * we're only called before entering the RUN state at
915138568Ssam		 * which point we start sending beacon frames.
916138568Ssam		 */
917138568Ssam		wme->wme_hipri_switch_thresh =
918178354Ssam			(HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100;
919188864Ssam		wme->wme_flags &= ~WME_F_AGGRMODE;
920178354Ssam		ieee80211_wme_updateparams(vap);
921138568Ssam	}
922138568Ssam}
923138568Ssam
924178354Ssamvoid
925178354Ssamieee80211_wme_initparams(struct ieee80211vap *vap)
926178354Ssam{
927178354Ssam	struct ieee80211com *ic = vap->iv_ic;
928178354Ssam
929178354Ssam	IEEE80211_LOCK(ic);
930178354Ssam	ieee80211_wme_initparams_locked(vap);
931178354Ssam	IEEE80211_UNLOCK(ic);
932178354Ssam}
933178354Ssam
934138568Ssam/*
935138568Ssam * Update WME parameters for ourself and the BSS.
936138568Ssam */
937138568Ssamvoid
938178354Ssamieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
939138568Ssam{
940188863Ssam	static const paramType aggrParam[IEEE80211_MODE_MAX] = {
941188780Ssam	    [IEEE80211_MODE_AUTO]	= { 2, 4, 10, 64, 0 },
942188780Ssam	    [IEEE80211_MODE_11A]	= { 2, 4, 10, 64, 0 },
943188780Ssam	    [IEEE80211_MODE_11B]	= { 2, 5, 10, 64, 0 },
944188780Ssam	    [IEEE80211_MODE_11G]	= { 2, 4, 10, 64, 0 },
945188780Ssam	    [IEEE80211_MODE_FH]		= { 2, 5, 10, 64, 0 },
946188780Ssam	    [IEEE80211_MODE_TURBO_A]	= { 1, 3, 10, 64, 0 },
947188780Ssam	    [IEEE80211_MODE_TURBO_G]	= { 1, 3, 10, 64, 0 },
948188780Ssam	    [IEEE80211_MODE_STURBO_A]	= { 1, 3, 10, 64, 0 },
949188782Ssam	    [IEEE80211_MODE_HALF]	= { 2, 4, 10, 64, 0 },
950188782Ssam	    [IEEE80211_MODE_QUARTER]	= { 2, 4, 10, 64, 0 },
951188780Ssam	    [IEEE80211_MODE_11NA]	= { 2, 4, 10, 64, 0 },	/* XXXcheck*/
952188780Ssam	    [IEEE80211_MODE_11NG]	= { 2, 4, 10, 64, 0 },	/* XXXcheck*/
953138568Ssam	};
954178354Ssam	struct ieee80211com *ic = vap->iv_ic;
955138568Ssam	struct ieee80211_wme_state *wme = &ic->ic_wme;
956138568Ssam	const struct wmeParams *wmep;
957138568Ssam	struct wmeParams *chanp, *bssp;
958170530Ssam	enum ieee80211_phymode mode;
959138568Ssam	int i;
960138568Ssam
961188863Ssam       	/*
962188863Ssam	 * Set up the channel access parameters for the physical
963188863Ssam	 * device.  First populate the configured settings.
964188863Ssam	 */
965138568Ssam	for (i = 0; i < WME_NUM_AC; i++) {
966138568Ssam		chanp = &wme->wme_chanParams.cap_wmeParams[i];
967138568Ssam		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
968138568Ssam		chanp->wmep_aifsn = wmep->wmep_aifsn;
969138568Ssam		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
970138568Ssam		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
971138568Ssam		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
972138568Ssam
973138568Ssam		chanp = &wme->wme_bssChanParams.cap_wmeParams[i];
974138568Ssam		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
975138568Ssam		chanp->wmep_aifsn = wmep->wmep_aifsn;
976138568Ssam		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
977138568Ssam		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
978138568Ssam		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
979138568Ssam	}
980138568Ssam
981138568Ssam	/*
982170530Ssam	 * Select mode; we can be called early in which case we
983170530Ssam	 * always use auto mode.  We know we'll be called when
984170530Ssam	 * entering the RUN state with bsschan setup properly
985170530Ssam	 * so state will eventually get set correctly
986170530Ssam	 */
987170530Ssam	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
988170530Ssam		mode = ieee80211_chan2mode(ic->ic_bsschan);
989170530Ssam	else
990170530Ssam		mode = IEEE80211_MODE_AUTO;
991170530Ssam
992170530Ssam	/*
993138568Ssam	 * This implements agressive mode as found in certain
994138568Ssam	 * vendors' AP's.  When there is significant high
995138568Ssam	 * priority (VI/VO) traffic in the BSS throttle back BE
996138568Ssam	 * traffic by using conservative parameters.  Otherwise
997138568Ssam	 * BE uses agressive params to optimize performance of
998138568Ssam	 * legacy/non-QoS traffic.
999138568Ssam	 */
1000178354Ssam        if ((vap->iv_opmode == IEEE80211_M_HOSTAP &&
1001156524Ssam	     (wme->wme_flags & WME_F_AGGRMODE) != 0) ||
1002178354Ssam	    (vap->iv_opmode == IEEE80211_M_STA &&
1003178354Ssam	     (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
1004178354Ssam	    (vap->iv_flags & IEEE80211_F_WME) == 0) {
1005138568Ssam		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
1006138568Ssam		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
1007138568Ssam
1008188863Ssam		chanp->wmep_aifsn = bssp->wmep_aifsn = aggrParam[mode].aifsn;
1009138568Ssam		chanp->wmep_logcwmin = bssp->wmep_logcwmin =
1010188863Ssam		    aggrParam[mode].logcwmin;
1011138568Ssam		chanp->wmep_logcwmax = bssp->wmep_logcwmax =
1012188863Ssam		    aggrParam[mode].logcwmax;
1013138568Ssam		chanp->wmep_txopLimit = bssp->wmep_txopLimit =
1014188863Ssam		    (vap->iv_flags & IEEE80211_F_BURST) ?
1015188863Ssam			aggrParam[mode].txopLimit : 0;
1016178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
1017188863Ssam		    "update %s (chan+bss) [acm %u aifsn %u logcwmin %u "
1018188863Ssam		    "logcwmax %u txop %u]\n", ieee80211_wme_acnames[WME_AC_BE],
1019188863Ssam		    chanp->wmep_acm, chanp->wmep_aifsn, chanp->wmep_logcwmin,
1020188863Ssam		    chanp->wmep_logcwmax, chanp->wmep_txopLimit);
1021138568Ssam	}
1022138568Ssam
1023178354Ssam	if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
1024156524Ssam	    ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) {
1025188780Ssam		static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
1026188780Ssam		    [IEEE80211_MODE_AUTO]	= 3,
1027188780Ssam		    [IEEE80211_MODE_11A]	= 3,
1028188780Ssam		    [IEEE80211_MODE_11B]	= 4,
1029188780Ssam		    [IEEE80211_MODE_11G]	= 3,
1030188780Ssam		    [IEEE80211_MODE_FH]		= 4,
1031188780Ssam		    [IEEE80211_MODE_TURBO_A]	= 3,
1032188780Ssam		    [IEEE80211_MODE_TURBO_G]	= 3,
1033188780Ssam		    [IEEE80211_MODE_STURBO_A]	= 3,
1034188782Ssam		    [IEEE80211_MODE_HALF]	= 3,
1035188782Ssam		    [IEEE80211_MODE_QUARTER]	= 3,
1036188780Ssam		    [IEEE80211_MODE_11NA]	= 3,
1037188780Ssam		    [IEEE80211_MODE_11NG]	= 3,
1038138568Ssam		};
1039138568Ssam		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
1040138568Ssam		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
1041138568Ssam
1042170530Ssam		chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode];
1043178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
1044188863Ssam		    "update %s (chan+bss) logcwmin %u\n",
1045188863Ssam		    ieee80211_wme_acnames[WME_AC_BE], chanp->wmep_logcwmin);
1046138568Ssam    	}
1047178354Ssam	if (vap->iv_opmode == IEEE80211_M_HOSTAP) {	/* XXX ibss? */
1048138568Ssam		/*
1049138568Ssam		 * Arrange for a beacon update and bump the parameter
1050138568Ssam		 * set number so associated stations load the new values.
1051138568Ssam		 */
1052138568Ssam		wme->wme_bssChanParams.cap_info =
1053138568Ssam			(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
1054178354Ssam		ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
1055138568Ssam	}
1056138568Ssam
1057138568Ssam	wme->wme_update(ic);
1058138568Ssam
1059178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
1060188863Ssam	    "%s: WME params updated, cap_info 0x%x\n", __func__,
1061188863Ssam	    vap->iv_opmode == IEEE80211_M_STA ?
1062188863Ssam		wme->wme_wmeChanParams.cap_info :
1063188863Ssam		wme->wme_bssChanParams.cap_info);
1064138568Ssam}
1065138568Ssam
1066138568Ssamvoid
1067178354Ssamieee80211_wme_updateparams(struct ieee80211vap *vap)
1068138568Ssam{
1069178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1070138568Ssam
1071138568Ssam	if (ic->ic_caps & IEEE80211_C_WME) {
1072178354Ssam		IEEE80211_LOCK(ic);
1073178354Ssam		ieee80211_wme_updateparams_locked(vap);
1074178354Ssam		IEEE80211_UNLOCK(ic);
1075138568Ssam	}
1076138568Ssam}
1077138568Ssam
1078178354Ssamstatic void
1079178354Ssamparent_updown(void *arg, int npending)
1080178354Ssam{
1081178354Ssam	struct ifnet *parent = arg;
1082178354Ssam
1083178354Ssam	parent->if_ioctl(parent, SIOCSIFFLAGS, NULL);
1084178354Ssam}
1085178354Ssam
1086191746Sthompsastatic void
1087191746Sthompsaupdate_mcast(void *arg, int npending)
1088191746Sthompsa{
1089191746Sthompsa	struct ieee80211com *ic = arg;
1090191746Sthompsa	struct ifnet *parent = ic->ic_ifp;
1091191746Sthompsa
1092191746Sthompsa	ic->ic_update_mcast(parent);
1093191746Sthompsa}
1094191746Sthompsa
1095191746Sthompsastatic void
1096191746Sthompsaupdate_promisc(void *arg, int npending)
1097191746Sthompsa{
1098191746Sthompsa	struct ieee80211com *ic = arg;
1099191746Sthompsa	struct ifnet *parent = ic->ic_ifp;
1100191746Sthompsa
1101191746Sthompsa	ic->ic_update_promisc(parent);
1102191746Sthompsa}
1103191746Sthompsa
1104191746Sthompsastatic void
1105191746Sthompsaupdate_channel(void *arg, int npending)
1106191746Sthompsa{
1107191746Sthompsa	struct ieee80211com *ic = arg;
1108191746Sthompsa
1109191746Sthompsa	ic->ic_set_channel(ic);
1110191746Sthompsa}
1111191746Sthompsa
1112170530Ssam/*
1113188533Sthompsa * Block until the parent is in a known state.  This is
1114188533Sthompsa * used after any operations that dispatch a task (e.g.
1115188533Sthompsa * to auto-configure the parent device up/down).
1116188533Sthompsa */
1117188533Sthompsavoid
1118188533Sthompsaieee80211_waitfor_parent(struct ieee80211com *ic)
1119188533Sthompsa{
1120191746Sthompsa	taskqueue_block(ic->ic_tq);
1121191746Sthompsa	ieee80211_draintask(ic, &ic->ic_parent_task);
1122191746Sthompsa	ieee80211_draintask(ic, &ic->ic_mcast_task);
1123191746Sthompsa	ieee80211_draintask(ic, &ic->ic_promisc_task);
1124191746Sthompsa	ieee80211_draintask(ic, &ic->ic_chan_task);
1125191746Sthompsa	ieee80211_draintask(ic, &ic->ic_bmiss_task);
1126191746Sthompsa	taskqueue_unblock(ic->ic_tq);
1127188533Sthompsa}
1128188533Sthompsa
1129188533Sthompsa/*
1130178354Ssam * Start a vap running.  If this is the first vap to be
1131178354Ssam * set running on the underlying device then we
1132178354Ssam * automatically bring the device up.
1133170530Ssam */
1134178354Ssamvoid
1135178354Ssamieee80211_start_locked(struct ieee80211vap *vap)
1136170530Ssam{
1137178354Ssam	struct ifnet *ifp = vap->iv_ifp;
1138178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1139178354Ssam	struct ifnet *parent = ic->ic_ifp;
1140170530Ssam
1141178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1142178354Ssam
1143178354Ssam	IEEE80211_DPRINTF(vap,
1144170530Ssam		IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
1145178354Ssam		"start running, %d vaps running\n", ic->ic_nrunning);
1146170530Ssam
1147178354Ssam	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1148178354Ssam		/*
1149178354Ssam		 * Mark us running.  Note that it's ok to do this first;
1150178354Ssam		 * if we need to bring the parent device up we defer that
1151178354Ssam		 * to avoid dropping the com lock.  We expect the device
1152178354Ssam		 * to respond to being marked up by calling back into us
1153178354Ssam		 * through ieee80211_start_all at which point we'll come
1154178354Ssam		 * back in here and complete the work.
1155178354Ssam		 */
1156178354Ssam		ifp->if_drv_flags |= IFF_DRV_RUNNING;
1157178354Ssam		/*
1158178354Ssam		 * We are not running; if this we are the first vap
1159178354Ssam		 * to be brought up auto-up the parent if necessary.
1160178354Ssam		 */
1161178354Ssam		if (ic->ic_nrunning++ == 0 &&
1162178354Ssam		    (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1163178354Ssam			IEEE80211_DPRINTF(vap,
1164178354Ssam			    IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
1165178354Ssam			    "%s: up parent %s\n", __func__, parent->if_xname);
1166178354Ssam			parent->if_flags |= IFF_UP;
1167191746Sthompsa			ieee80211_runtask(ic, &ic->ic_parent_task);
1168178354Ssam			return;
1169178354Ssam		}
1170178354Ssam	}
1171170530Ssam	/*
1172178354Ssam	 * If the parent is up and running, then kick the
1173178354Ssam	 * 802.11 state machine as appropriate.
1174170530Ssam	 */
1175178354Ssam	if ((parent->if_drv_flags & IFF_DRV_RUNNING) &&
1176178354Ssam	    vap->iv_roaming != IEEE80211_ROAMING_MANUAL) {
1177178354Ssam		if (vap->iv_opmode == IEEE80211_M_STA) {
1178178354Ssam#if 0
1179178354Ssam			/* XXX bypasses scan too easily; disable for now */
1180178354Ssam			/*
1181178354Ssam			 * Try to be intelligent about clocking the state
1182178354Ssam			 * machine.  If we're currently in RUN state then
1183178354Ssam			 * we should be able to apply any new state/parameters
1184178354Ssam			 * simply by re-associating.  Otherwise we need to
1185178354Ssam			 * re-scan to select an appropriate ap.
1186178354Ssam			 */
1187178354Ssam			if (vap->iv_state >= IEEE80211_S_RUN)
1188178354Ssam				ieee80211_new_state_locked(vap,
1189178354Ssam				    IEEE80211_S_ASSOC, 1);
1190178354Ssam			else
1191178354Ssam#endif
1192178354Ssam				ieee80211_new_state_locked(vap,
1193178354Ssam				    IEEE80211_S_SCAN, 0);
1194170530Ssam		} else {
1195170530Ssam			/*
1196178354Ssam			 * For monitor+wds mode there's nothing to do but
1197178354Ssam			 * start running.  Otherwise if this is the first
1198170530Ssam			 * vap to be brought up, start a scan which may be
1199170530Ssam			 * preempted if the station is locked to a particular
1200170530Ssam			 * channel.
1201170530Ssam			 */
1202191746Sthompsa			vap->iv_flags_ext |= IEEE80211_FEXT_REINIT;
1203178354Ssam			if (vap->iv_opmode == IEEE80211_M_MONITOR ||
1204178354Ssam			    vap->iv_opmode == IEEE80211_M_WDS)
1205178354Ssam				ieee80211_new_state_locked(vap,
1206178354Ssam				    IEEE80211_S_RUN, -1);
1207178354Ssam			else
1208178354Ssam				ieee80211_new_state_locked(vap,
1209178354Ssam				    IEEE80211_S_SCAN, 0);
1210170530Ssam		}
1211170530Ssam	}
1212170530Ssam}
1213170530Ssam
1214170530Ssam/*
1215178354Ssam * Start a single vap.
1216178354Ssam */
1217178354Ssamvoid
1218178354Ssamieee80211_init(void *arg)
1219178354Ssam{
1220178354Ssam	struct ieee80211vap *vap = arg;
1221178354Ssam
1222178354Ssam	/*
1223178354Ssam	 * This routine is publicly accessible through the vap's
1224178354Ssam	 * if_init method so guard against calls during detach.
1225178354Ssam	 * ieee80211_vap_detach null's the backpointer before
1226178354Ssam	 * tearing down state to signal any callback should be
1227178354Ssam	 * rejected/ignored.
1228178354Ssam	 */
1229178354Ssam	if (vap != NULL) {
1230178354Ssam		IEEE80211_DPRINTF(vap,
1231178354Ssam		    IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
1232178354Ssam		    "%s\n", __func__);
1233178354Ssam
1234178354Ssam		IEEE80211_LOCK(vap->iv_ic);
1235178354Ssam		ieee80211_start_locked(vap);
1236178354Ssam		IEEE80211_UNLOCK(vap->iv_ic);
1237178354Ssam	}
1238178354Ssam}
1239178354Ssam
1240178354Ssam/*
1241178354Ssam * Start all runnable vap's on a device.
1242178354Ssam */
1243178354Ssamvoid
1244178354Ssamieee80211_start_all(struct ieee80211com *ic)
1245178354Ssam{
1246178354Ssam	struct ieee80211vap *vap;
1247178354Ssam
1248178354Ssam	IEEE80211_LOCK(ic);
1249178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1250178354Ssam		struct ifnet *ifp = vap->iv_ifp;
1251178354Ssam		if (IFNET_IS_UP_RUNNING(ifp))	/* NB: avoid recursion */
1252178354Ssam			ieee80211_start_locked(vap);
1253178354Ssam	}
1254178354Ssam	IEEE80211_UNLOCK(ic);
1255178354Ssam}
1256178354Ssam
1257178354Ssam/*
1258178354Ssam * Stop a vap.  We force it down using the state machine
1259178354Ssam * then mark it's ifnet not running.  If this is the last
1260178354Ssam * vap running on the underlying device then we close it
1261178354Ssam * too to insure it will be properly initialized when the
1262178354Ssam * next vap is brought up.
1263178354Ssam */
1264178354Ssamvoid
1265178354Ssamieee80211_stop_locked(struct ieee80211vap *vap)
1266178354Ssam{
1267178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1268178354Ssam	struct ifnet *ifp = vap->iv_ifp;
1269178354Ssam	struct ifnet *parent = ic->ic_ifp;
1270178354Ssam
1271178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1272178354Ssam
1273178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
1274178354Ssam	    "stop running, %d vaps running\n", ic->ic_nrunning);
1275178354Ssam
1276178354Ssam	ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1);
1277178354Ssam	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1278178354Ssam		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;	/* mark us stopped */
1279178354Ssam		if (--ic->ic_nrunning == 0 &&
1280178354Ssam		    (parent->if_drv_flags & IFF_DRV_RUNNING)) {
1281178354Ssam			IEEE80211_DPRINTF(vap,
1282178354Ssam			    IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
1283178354Ssam			    "down parent %s\n", parent->if_xname);
1284178354Ssam			parent->if_flags &= ~IFF_UP;
1285191746Sthompsa			ieee80211_runtask(ic, &ic->ic_parent_task);
1286178354Ssam		}
1287178354Ssam	}
1288178354Ssam}
1289178354Ssam
1290178354Ssamvoid
1291178354Ssamieee80211_stop(struct ieee80211vap *vap)
1292178354Ssam{
1293178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1294178354Ssam
1295178354Ssam	IEEE80211_LOCK(ic);
1296178354Ssam	ieee80211_stop_locked(vap);
1297178354Ssam	IEEE80211_UNLOCK(ic);
1298178354Ssam}
1299178354Ssam
1300178354Ssam/*
1301178354Ssam * Stop all vap's running on a device.
1302178354Ssam */
1303178354Ssamvoid
1304178354Ssamieee80211_stop_all(struct ieee80211com *ic)
1305178354Ssam{
1306178354Ssam	struct ieee80211vap *vap;
1307178354Ssam
1308178354Ssam	IEEE80211_LOCK(ic);
1309178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1310178354Ssam		struct ifnet *ifp = vap->iv_ifp;
1311178354Ssam		if (IFNET_IS_UP_RUNNING(ifp))	/* NB: avoid recursion */
1312178354Ssam			ieee80211_stop_locked(vap);
1313178354Ssam	}
1314178354Ssam	IEEE80211_UNLOCK(ic);
1315188533Sthompsa
1316188533Sthompsa	ieee80211_waitfor_parent(ic);
1317178354Ssam}
1318178354Ssam
1319178354Ssam/*
1320179391Ssam * Stop all vap's running on a device and arrange
1321179391Ssam * for those that were running to be resumed.
1322179391Ssam */
1323179391Ssamvoid
1324179391Ssamieee80211_suspend_all(struct ieee80211com *ic)
1325179391Ssam{
1326179391Ssam	struct ieee80211vap *vap;
1327179391Ssam
1328179391Ssam	IEEE80211_LOCK(ic);
1329179391Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1330179391Ssam		struct ifnet *ifp = vap->iv_ifp;
1331179391Ssam		if (IFNET_IS_UP_RUNNING(ifp)) {	/* NB: avoid recursion */
1332179391Ssam			vap->iv_flags_ext |= IEEE80211_FEXT_RESUME;
1333179391Ssam			ieee80211_stop_locked(vap);
1334179391Ssam		}
1335179391Ssam	}
1336179391Ssam	IEEE80211_UNLOCK(ic);
1337188533Sthompsa
1338188533Sthompsa	ieee80211_waitfor_parent(ic);
1339179391Ssam}
1340179391Ssam
1341179391Ssam/*
1342179391Ssam * Start all vap's marked for resume.
1343179391Ssam */
1344179391Ssamvoid
1345179391Ssamieee80211_resume_all(struct ieee80211com *ic)
1346179391Ssam{
1347179391Ssam	struct ieee80211vap *vap;
1348179391Ssam
1349179391Ssam	IEEE80211_LOCK(ic);
1350179391Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1351179391Ssam		struct ifnet *ifp = vap->iv_ifp;
1352179391Ssam		if (!IFNET_IS_UP_RUNNING(ifp) &&
1353179391Ssam		    (vap->iv_flags_ext & IEEE80211_FEXT_RESUME)) {
1354179391Ssam			vap->iv_flags_ext &= ~IEEE80211_FEXT_RESUME;
1355179391Ssam			ieee80211_start_locked(vap);
1356179391Ssam		}
1357179391Ssam	}
1358179391Ssam	IEEE80211_UNLOCK(ic);
1359179391Ssam}
1360179391Ssam
1361153349Ssamvoid
1362153349Ssamieee80211_beacon_miss(struct ieee80211com *ic)
1363153349Ssam{
1364191746Sthompsa	IEEE80211_LOCK(ic);
1365191746Sthompsa	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
1366191746Sthompsa		/* Process in a taskq, the handler may reenter the driver */
1367191746Sthompsa		ieee80211_runtask(ic, &ic->ic_bmiss_task);
1368191746Sthompsa	}
1369191746Sthompsa	IEEE80211_UNLOCK(ic);
1370191746Sthompsa}
1371191746Sthompsa
1372191746Sthompsastatic void
1373191746Sthompsabeacon_miss(void *arg, int npending)
1374191746Sthompsa{
1375191746Sthompsa	struct ieee80211com *ic = arg;
1376178354Ssam	struct ieee80211vap *vap;
1377153349Ssam
1378178354Ssam	/* XXX locking */
1379178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1380153349Ssam		/*
1381178354Ssam		 * We only pass events through for sta vap's in RUN state;
1382178354Ssam		 * may be too restrictive but for now this saves all the
1383178354Ssam		 * handlers duplicating these checks.
1384153349Ssam		 */
1385178354Ssam		if (vap->iv_opmode == IEEE80211_M_STA &&
1386178354Ssam		    vap->iv_state == IEEE80211_S_RUN &&
1387178354Ssam		    vap->iv_bmiss != NULL)
1388178354Ssam			vap->iv_bmiss(vap);
1389153349Ssam	}
1390153349Ssam}
1391153349Ssam
1392191746Sthompsastatic void
1393191746Sthompsabeacon_swmiss(void *arg, int npending)
1394191746Sthompsa{
1395191746Sthompsa	struct ieee80211vap *vap = arg;
1396191746Sthompsa
1397191746Sthompsa	if (vap->iv_state != IEEE80211_S_RUN)
1398191746Sthompsa		return;
1399191746Sthompsa
1400191746Sthompsa	/* XXX Call multiple times if npending > zero? */
1401191746Sthompsa	vap->iv_bmiss(vap);
1402191746Sthompsa}
1403191746Sthompsa
1404154736Ssam/*
1405154736Ssam * Software beacon miss handling.  Check if any beacons
1406154736Ssam * were received in the last period.  If not post a
1407154736Ssam * beacon miss; otherwise reset the counter.
1408154736Ssam */
1409178354Ssamvoid
1410154736Ssamieee80211_swbmiss(void *arg)
1411154736Ssam{
1412178354Ssam	struct ieee80211vap *vap = arg;
1413179217Ssam	struct ieee80211com *ic = vap->iv_ic;
1414154736Ssam
1415179217Ssam	/* XXX sleep state? */
1416179217Ssam	KASSERT(vap->iv_state == IEEE80211_S_RUN,
1417179217Ssam	    ("wrong state %d", vap->iv_state));
1418179217Ssam
1419179217Ssam	if (ic->ic_flags & IEEE80211_F_SCAN) {
1420179217Ssam		/*
1421179217Ssam		 * If scanning just ignore and reset state.  If we get a
1422179217Ssam		 * bmiss after coming out of scan because we haven't had
1423179217Ssam		 * time to receive a beacon then we should probe the AP
1424179217Ssam		 * before posting a real bmiss (unless iv_bmiss_max has
1425179217Ssam		 * been artifiically lowered).  A cleaner solution might
1426179217Ssam		 * be to disable the timer on scan start/end but to handle
1427179217Ssam		 * case of multiple sta vap's we'd need to disable the
1428179217Ssam		 * timers of all affected vap's.
1429179217Ssam		 */
1430179217Ssam		vap->iv_swbmiss_count = 0;
1431179217Ssam	} else if (vap->iv_swbmiss_count == 0) {
1432178354Ssam		if (vap->iv_bmiss != NULL)
1433191746Sthompsa			ieee80211_runtask(ic, &vap->iv_swbmiss_task);
1434178354Ssam		if (vap->iv_bmiss_count == 0)	/* don't re-arm timer */
1435154736Ssam			return;
1436154736Ssam	} else
1437178354Ssam		vap->iv_swbmiss_count = 0;
1438178354Ssam	callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
1439178354Ssam		ieee80211_swbmiss, vap);
1440154736Ssam}
1441154736Ssam
1442178354Ssam/*
1443178354Ssam * Start an 802.11h channel switch.  We record the parameters,
1444178354Ssam * mark the operation pending, notify each vap through the
1445178354Ssam * beacon update mechanism so it can update the beacon frame
1446178354Ssam * contents, and then switch vap's to CSA state to block outbound
1447178354Ssam * traffic.  Devices that handle CSA directly can use the state
1448178354Ssam * switch to do the right thing so long as they call
1449178354Ssam * ieee80211_csa_completeswitch when it's time to complete the
1450178354Ssam * channel change.  Devices that depend on the net80211 layer can
1451178354Ssam * use ieee80211_beacon_update to handle the countdown and the
1452178354Ssam * channel switch.
1453178354Ssam */
1454178354Ssamvoid
1455178354Ssamieee80211_csa_startswitch(struct ieee80211com *ic,
1456178354Ssam	struct ieee80211_channel *c, int mode, int count)
1457178354Ssam{
1458178354Ssam	struct ieee80211vap *vap;
1459178354Ssam
1460178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1461178354Ssam
1462178354Ssam	ic->ic_csa_newchan = c;
1463178354Ssam	ic->ic_csa_count = count;
1464178354Ssam	/* XXX record mode? */
1465178354Ssam	ic->ic_flags |= IEEE80211_F_CSAPENDING;
1466178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1467178354Ssam		if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
1468178354Ssam		    vap->iv_opmode == IEEE80211_M_IBSS)
1469178354Ssam			ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA);
1470178354Ssam		/* switch to CSA state to block outbound traffic */
1471178354Ssam		if (vap->iv_state == IEEE80211_S_RUN)
1472178354Ssam			ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0);
1473178354Ssam	}
1474178354Ssam	ieee80211_notify_csa(ic, c, mode, count);
1475178354Ssam}
1476178354Ssam
1477178354Ssam/*
1478178354Ssam * Complete an 802.11h channel switch started by ieee80211_csa_startswitch.
1479178354Ssam * We clear state and move all vap's in CSA state to RUN state
1480178354Ssam * so they can again transmit.
1481178354Ssam */
1482178354Ssamvoid
1483178354Ssamieee80211_csa_completeswitch(struct ieee80211com *ic)
1484178354Ssam{
1485178354Ssam	struct ieee80211vap *vap;
1486178354Ssam
1487178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1488178354Ssam
1489178354Ssam	KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending"));
1490178354Ssam
1491178354Ssam	ieee80211_setcurchan(ic, ic->ic_csa_newchan);
1492178354Ssam	ic->ic_csa_newchan = NULL;
1493178354Ssam	ic->ic_flags &= ~IEEE80211_F_CSAPENDING;
1494178354Ssam
1495178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
1496178354Ssam		if (vap->iv_state == IEEE80211_S_CSA)
1497178354Ssam			ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
1498178354Ssam}
1499178354Ssam
1500178354Ssam/*
1501178354Ssam * Complete a DFS CAC started by ieee80211_dfs_cac_start.
1502178354Ssam * We clear state and move all vap's in CAC state to RUN state.
1503178354Ssam */
1504178354Ssamvoid
1505178354Ssamieee80211_cac_completeswitch(struct ieee80211vap *vap0)
1506178354Ssam{
1507178354Ssam	struct ieee80211com *ic = vap0->iv_ic;
1508178354Ssam	struct ieee80211vap *vap;
1509178354Ssam
1510178354Ssam	IEEE80211_LOCK(ic);
1511178354Ssam	/*
1512178354Ssam	 * Complete CAC state change for lead vap first; then
1513178354Ssam	 * clock all the other vap's waiting.
1514178354Ssam	 */
1515178354Ssam	KASSERT(vap0->iv_state == IEEE80211_S_CAC,
1516178354Ssam	    ("wrong state %d", vap0->iv_state));
1517178354Ssam	ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0);
1518178354Ssam
1519178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
1520178354Ssam		if (vap->iv_state == IEEE80211_S_CAC)
1521178354Ssam			ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
1522178354Ssam	IEEE80211_UNLOCK(ic);
1523178354Ssam}
1524178354Ssam
1525178354Ssam/*
1526178354Ssam * Force all vap's other than the specified vap to the INIT state
1527178354Ssam * and mark them as waiting for a scan to complete.  These vaps
1528178354Ssam * will be brought up when the scan completes and the scanning vap
1529178354Ssam * reaches RUN state by wakeupwaiting.
1530178354Ssam */
1531154736Ssamstatic void
1532178354Ssammarkwaiting(struct ieee80211vap *vap0)
1533147765Ssam{
1534178354Ssam	struct ieee80211com *ic = vap0->iv_ic;
1535178354Ssam	struct ieee80211vap *vap;
1536147765Ssam
1537178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1538178354Ssam
1539191746Sthompsa	/*
1540191746Sthompsa	 * A vap list entry can not disappear since we are running on the
1541191746Sthompsa	 * taskqueue and a vap destroy will queue and drain another state
1542191746Sthompsa	 * change task.
1543191746Sthompsa	 */
1544178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1545178354Ssam		if (vap == vap0)
1546178354Ssam			continue;
1547178354Ssam		if (vap->iv_state != IEEE80211_S_INIT) {
1548191746Sthompsa			/* NB: iv_newstate may drop the lock */
1549178354Ssam			vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
1550178354Ssam			vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
1551178354Ssam		}
1552147765Ssam	}
1553147765Ssam}
1554147765Ssam
1555178354Ssam/*
1556178354Ssam * Wakeup all vap's waiting for a scan to complete.  This is the
1557178354Ssam * companion to markwaiting (above) and is used to coordinate
1558178354Ssam * multiple vaps scanning.
1559191746Sthompsa * This is called from the state taskqueue.
1560178354Ssam */
1561147765Ssamstatic void
1562178354Ssamwakeupwaiting(struct ieee80211vap *vap0)
1563147765Ssam{
1564178354Ssam	struct ieee80211com *ic = vap0->iv_ic;
1565178354Ssam	struct ieee80211vap *vap;
1566147765Ssam
1567178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1568178354Ssam
1569191746Sthompsa	/*
1570191746Sthompsa	 * A vap list entry can not disappear since we are running on the
1571191746Sthompsa	 * taskqueue and a vap destroy will queue and drain another state
1572191746Sthompsa	 * change task.
1573191746Sthompsa	 */
1574178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
1575178354Ssam		if (vap == vap0)
1576178354Ssam			continue;
1577178354Ssam		if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) {
1578178354Ssam			vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
1579178354Ssam			/* NB: sta's cannot go INIT->RUN */
1580191746Sthompsa			/* NB: iv_newstate may drop the lock */
1581178354Ssam			vap->iv_newstate(vap,
1582178354Ssam			    vap->iv_opmode == IEEE80211_M_STA ?
1583178354Ssam			        IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
1584178354Ssam		}
1585178354Ssam	}
1586147765Ssam}
1587147765Ssam
1588170530Ssam/*
1589178354Ssam * Handle post state change work common to all operating modes.
1590170530Ssam */
1591170530Ssamstatic void
1592191746Sthompsaieee80211_newstate_cb(void *xvap, int npending)
1593170530Ssam{
1594191746Sthompsa	struct ieee80211vap *vap = xvap;
1595178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1596191746Sthompsa	enum ieee80211_state nstate, ostate;
1597191746Sthompsa	int arg, rc;
1598178354Ssam
1599191746Sthompsa	IEEE80211_LOCK(ic);
1600191746Sthompsa	nstate = vap->iv_nstate;
1601191746Sthompsa	arg = vap->iv_nstate_arg;
1602178354Ssam
1603191746Sthompsa	if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) {
1604191746Sthompsa		/*
1605191746Sthompsa		 * We have been requested to drop back to the INIT before
1606191746Sthompsa		 * proceeding to the new state.
1607191746Sthompsa		 */
1608191746Sthompsa		IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1609191746Sthompsa		    "%s: %s -> %s arg %d\n", __func__,
1610191746Sthompsa		    ieee80211_state_name[vap->iv_state],
1611191746Sthompsa		    ieee80211_state_name[IEEE80211_S_INIT], arg);
1612191746Sthompsa		vap->iv_newstate(vap, IEEE80211_S_INIT, arg);
1613191746Sthompsa		vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT;
1614191746Sthompsa	}
1615191746Sthompsa
1616191746Sthompsa	ostate = vap->iv_state;
1617191746Sthompsa	if (nstate == IEEE80211_S_SCAN && ostate != IEEE80211_S_INIT) {
1618191746Sthompsa		/*
1619191746Sthompsa		 * SCAN was forced; e.g. on beacon miss.  Force other running
1620191746Sthompsa		 * vap's to INIT state and mark them as waiting for the scan to
1621191746Sthompsa		 * complete.  This insures they don't interfere with our
1622191746Sthompsa		 * scanning.  Since we are single threaded the vaps can not
1623191746Sthompsa		 * transition again while we are executing.
1624191746Sthompsa		 *
1625191746Sthompsa		 * XXX not always right, assumes ap follows sta
1626191746Sthompsa		 */
1627191746Sthompsa		markwaiting(vap);
1628191746Sthompsa	}
1629178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1630191746Sthompsa	    "%s: %s -> %s arg %d\n", __func__,
1631191746Sthompsa	    ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg);
1632178354Ssam
1633191746Sthompsa	rc = vap->iv_newstate(vap, nstate, arg);
1634191746Sthompsa	vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT;
1635191746Sthompsa	if (rc != 0) {
1636191746Sthompsa		/* State transition failed */
1637191746Sthompsa		KASSERT(rc != EINPROGRESS, ("iv_newstate was deferred"));
1638191746Sthompsa		KASSERT(nstate != IEEE80211_S_INIT,
1639191746Sthompsa		    ("INIT state change failed"));
1640191746Sthompsa		IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1641191746Sthompsa		    "%s: %s returned error %d\n", __func__,
1642191746Sthompsa		    ieee80211_state_name[nstate], rc);
1643191746Sthompsa		goto done;
1644191746Sthompsa	}
1645191746Sthompsa
1646191746Sthompsa	/* No actual transition, skip post processing */
1647191746Sthompsa	if (ostate == nstate)
1648191746Sthompsa		goto done;
1649191746Sthompsa
1650178354Ssam	if (nstate == IEEE80211_S_RUN) {
1651178354Ssam		/*
1652178354Ssam		 * OACTIVE may be set on the vap if the upper layer
1653178354Ssam		 * tried to transmit (e.g. IPv6 NDP) before we reach
1654178354Ssam		 * RUN state.  Clear it and restart xmit.
1655178354Ssam		 *
1656178354Ssam		 * Note this can also happen as a result of SLEEP->RUN
1657178354Ssam		 * (i.e. coming out of power save mode).
1658178354Ssam		 */
1659178354Ssam		vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1660178354Ssam		if_start(vap->iv_ifp);
1661178354Ssam
1662178354Ssam		/* bring up any vaps waiting on us */
1663178354Ssam		wakeupwaiting(vap);
1664178354Ssam	} else if (nstate == IEEE80211_S_INIT) {
1665178354Ssam		/*
1666178354Ssam		 * Flush the scan cache if we did the last scan (XXX?)
1667178354Ssam		 * and flush any frames on send queues from this vap.
1668178354Ssam		 * Note the mgt q is used only for legacy drivers and
1669178354Ssam		 * will go away shortly.
1670178354Ssam		 */
1671178354Ssam		ieee80211_scan_flush(vap);
1672178354Ssam
1673178354Ssam		/* XXX NB: cast for altq */
1674178354Ssam		ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
1675170530Ssam	}
1676191746Sthompsadone:
1677191746Sthompsa	IEEE80211_UNLOCK(ic);
1678170530Ssam}
1679170530Ssam
1680178354Ssam/*
1681178354Ssam * Public interface for initiating a state machine change.
1682178354Ssam * This routine single-threads the request and coordinates
1683178354Ssam * the scheduling of multiple vaps for the purpose of selecting
1684178354Ssam * an operating channel.  Specifically the following scenarios
1685178354Ssam * are handled:
1686178354Ssam * o only one vap can be selecting a channel so on transition to
1687178354Ssam *   SCAN state if another vap is already scanning then
1688178354Ssam *   mark the caller for later processing and return without
1689178354Ssam *   doing anything (XXX? expectations by caller of synchronous operation)
1690178354Ssam * o only one vap can be doing CAC of a channel so on transition to
1691178354Ssam *   CAC state if another vap is already scanning for radar then
1692178354Ssam *   mark the caller for later processing and return without
1693178354Ssam *   doing anything (XXX? expectations by caller of synchronous operation)
1694178354Ssam * o if another vap is already running when a request is made
1695178354Ssam *   to SCAN then an operating channel has been chosen; bypass
1696178354Ssam *   the scan and just join the channel
1697178354Ssam *
1698178354Ssam * Note that the state change call is done through the iv_newstate
1699178354Ssam * method pointer so any driver routine gets invoked.  The driver
1700178354Ssam * will normally call back into operating mode-specific
1701178354Ssam * ieee80211_newstate routines (below) unless it needs to completely
1702178354Ssam * bypass the state machine (e.g. because the firmware has it's
1703178354Ssam * own idea how things should work).  Bypassing the net80211 layer
1704178354Ssam * is usually a mistake and indicates lack of proper integration
1705178354Ssam * with the net80211 layer.
1706178354Ssam */
1707117811Ssamstatic int
1708178354Ssamieee80211_new_state_locked(struct ieee80211vap *vap,
1709178354Ssam	enum ieee80211_state nstate, int arg)
1710116742Ssam{
1711178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1712178354Ssam	struct ieee80211vap *vp;
1713117811Ssam	enum ieee80211_state ostate;
1714191746Sthompsa	int nrunning, nscanning;
1715116742Ssam
1716178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1717178354Ssam
1718191746Sthompsa	if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) {
1719191746Sthompsa		if (vap->iv_nstate == IEEE80211_S_INIT) {
1720191746Sthompsa			/*
1721191746Sthompsa			 * XXX The vap is being stopped, do no allow any other
1722191746Sthompsa			 * state changes until this is completed.
1723191746Sthompsa			 */
1724191746Sthompsa			return -1;
1725191768Sthompsa		} else if (vap->iv_state != vap->iv_nstate) {
1726191746Sthompsa#if 0
1727191768Sthompsa			/* Warn if the previous state hasn't completed. */
1728191768Sthompsa			IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1729191768Sthompsa			    "%s: pending %s -> %s transition lost\n", __func__,
1730191768Sthompsa			    ieee80211_state_name[vap->iv_state],
1731191768Sthompsa			    ieee80211_state_name[vap->iv_nstate]);
1732191746Sthompsa#else
1733191768Sthompsa			/* XXX temporarily enable to identify issues */
1734191768Sthompsa			if_printf(vap->iv_ifp,
1735191768Sthompsa			    "%s: pending %s -> %s transition lost\n",
1736191768Sthompsa			    __func__, ieee80211_state_name[vap->iv_state],
1737191768Sthompsa			    ieee80211_state_name[vap->iv_nstate]);
1738191746Sthompsa#endif
1739191768Sthompsa		}
1740191746Sthompsa	}
1741191746Sthompsa
1742178354Ssam	nrunning = nscanning = 0;
1743178354Ssam	/* XXX can track this state instead of calculating */
1744178354Ssam	TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) {
1745178354Ssam		if (vp != vap) {
1746178354Ssam			if (vp->iv_state >= IEEE80211_S_RUN)
1747178354Ssam				nrunning++;
1748178354Ssam			/* XXX doesn't handle bg scan */
1749178354Ssam			/* NB: CAC+AUTH+ASSOC treated like SCAN */
1750178354Ssam			else if (vp->iv_state > IEEE80211_S_INIT)
1751178354Ssam				nscanning++;
1752178354Ssam		}
1753178354Ssam	}
1754178354Ssam	ostate = vap->iv_state;
1755178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1756178354Ssam	    "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__,
1757178354Ssam	    ieee80211_state_name[ostate], ieee80211_state_name[nstate],
1758178354Ssam	    nrunning, nscanning);
1759116742Ssam	switch (nstate) {
1760116742Ssam	case IEEE80211_S_SCAN:
1761178354Ssam		if (ostate == IEEE80211_S_INIT) {
1762178354Ssam			/*
1763178354Ssam			 * INIT -> SCAN happens on initial bringup.
1764178354Ssam			 */
1765178354Ssam			KASSERT(!(nscanning && nrunning),
1766178354Ssam			    ("%d scanning and %d running", nscanning, nrunning));
1767178354Ssam			if (nscanning) {
1768116742Ssam				/*
1769178354Ssam				 * Someone is scanning, defer our state
1770178354Ssam				 * change until the work has completed.
1771116742Ssam				 */
1772178354Ssam				IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1773178354Ssam				    "%s: defer %s -> %s\n",
1774178354Ssam				    __func__, ieee80211_state_name[ostate],
1775178354Ssam				    ieee80211_state_name[nstate]);
1776178354Ssam				vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
1777191746Sthompsa				return 0;
1778116742Ssam			}
1779178354Ssam			if (nrunning) {
1780170530Ssam				/*
1781178354Ssam				 * Someone is operating; just join the channel
1782178354Ssam				 * they have chosen.
1783170530Ssam				 */
1784178354Ssam				/* XXX kill arg? */
1785178354Ssam				/* XXX check each opmode, adhoc? */
1786178354Ssam				if (vap->iv_opmode == IEEE80211_M_STA)
1787178354Ssam					nstate = IEEE80211_S_SCAN;
1788178354Ssam				else
1789178354Ssam					nstate = IEEE80211_S_RUN;
1790138568Ssam#ifdef IEEE80211_DEBUG
1791178354Ssam				if (nstate != IEEE80211_S_SCAN) {
1792178354Ssam					IEEE80211_DPRINTF(vap,
1793178354Ssam					    IEEE80211_MSG_STATE,
1794178354Ssam					    "%s: override, now %s -> %s\n",
1795178354Ssam					    __func__,
1796178354Ssam					    ieee80211_state_name[ostate],
1797178354Ssam					    ieee80211_state_name[nstate]);
1798178354Ssam				}
1799138568Ssam#endif
1800170530Ssam			}
1801116742Ssam		}
1802178354Ssam		break;
1803178354Ssam	case IEEE80211_S_RUN:
1804178354Ssam		if (vap->iv_opmode == IEEE80211_M_WDS &&
1805178354Ssam		    (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) &&
1806178354Ssam		    nscanning) {
1807154736Ssam			/*
1808178354Ssam			 * Legacy WDS with someone else scanning; don't
1809178354Ssam			 * go online until that completes as we should
1810178354Ssam			 * follow the other vap to the channel they choose.
1811154736Ssam			 */
1812178354Ssam			IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1813178354Ssam			     "%s: defer %s -> %s (legacy WDS)\n", __func__,
1814178354Ssam			     ieee80211_state_name[ostate],
1815178354Ssam			     ieee80211_state_name[nstate]);
1816178354Ssam			vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
1817191746Sthompsa			return 0;
1818154736Ssam		}
1819178354Ssam		if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
1820178354Ssam		    IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
1821178354Ssam		    (vap->iv_flags_ext & IEEE80211_FEXT_DFS) &&
1822178354Ssam		    !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) {
1823178354Ssam			/*
1824178354Ssam			 * This is a DFS channel, transition to CAC state
1825178354Ssam			 * instead of RUN.  This allows us to initiate
1826178354Ssam			 * Channel Availability Check (CAC) as specified
1827178354Ssam			 * by 11h/DFS.
1828178354Ssam			 */
1829178354Ssam			nstate = IEEE80211_S_CAC;
1830178354Ssam			IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
1831178354Ssam			     "%s: override %s -> %s (DFS)\n", __func__,
1832178354Ssam			     ieee80211_state_name[ostate],
1833178354Ssam			     ieee80211_state_name[nstate]);
1834138568Ssam		}
1835116742Ssam		break;
1836178354Ssam	case IEEE80211_S_INIT:
1837178354Ssam		if (ostate == IEEE80211_S_INIT ) {
1838178354Ssam			/* XXX don't believe this */
1839178354Ssam			/* INIT -> INIT. nothing to do */
1840178354Ssam			vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
1841178354Ssam		}
1842178354Ssam		/* fall thru... */
1843172058Ssam	default:
1844172058Ssam		break;
1845116742Ssam	}
1846191746Sthompsa	/* defer the state change to a thread */
1847191746Sthompsa	vap->iv_nstate = nstate;
1848191746Sthompsa	vap->iv_nstate_arg = arg;
1849191746Sthompsa	vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT;
1850191746Sthompsa	ieee80211_runtask(ic, &vap->iv_nstate_task);
1851191746Sthompsa	return EINPROGRESS;
1852116742Ssam}
1853178354Ssam
1854178354Ssamint
1855178354Ssamieee80211_new_state(struct ieee80211vap *vap,
1856178354Ssam	enum ieee80211_state nstate, int arg)
1857178354Ssam{
1858178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1859178354Ssam	int rc;
1860178354Ssam
1861178354Ssam	IEEE80211_LOCK(ic);
1862178354Ssam	rc = ieee80211_new_state_locked(vap, nstate, arg);
1863178354Ssam	IEEE80211_UNLOCK(ic);
1864178354Ssam	return rc;
1865178354Ssam}
1866