1170530Ssam/*-
2178354Ssam * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
3170530Ssam * All rights reserved.
4170530Ssam *
5170530Ssam * Redistribution and use in source and binary forms, with or without
6170530Ssam * modification, are permitted provided that the following conditions
7170530Ssam * are met:
8170530Ssam * 1. Redistributions of source code must retain the above copyright
9170530Ssam *    notice, this list of conditions and the following disclaimer.
10170530Ssam * 2. Redistributions in binary form must reproduce the above copyright
11170530Ssam *    notice, this list of conditions and the following disclaimer in the
12170530Ssam *    documentation and/or other materials provided with the distribution.
13170530Ssam *
14170530Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15170530Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16170530Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17170530Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18170530Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19170530Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20170530Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21170530Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22170530Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23170530Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24170530Ssam */
25170530Ssam
26170530Ssam#include <sys/cdefs.h>
27170530Ssam__FBSDID("$FreeBSD$");
28170530Ssam
29170530Ssam/*
30170530Ssam * IEEE 802.11 regdomain support.
31170530Ssam */
32178354Ssam#include "opt_wlan.h"
33170530Ssam
34170530Ssam#include <sys/param.h>
35170530Ssam#include <sys/systm.h>
36170530Ssam#include <sys/kernel.h>
37170530Ssam
38170530Ssam#include <sys/socket.h>
39170530Ssam
40170530Ssam#include <net/if.h>
41170530Ssam#include <net/if_media.h>
42170530Ssam
43170530Ssam#include <net80211/ieee80211_var.h>
44170530Ssam#include <net80211/ieee80211_regdomain.h>
45170530Ssam
46178354Ssamstatic void
47187800Ssamnull_getradiocaps(struct ieee80211com *ic, int maxchan,
48187800Ssam	int *n, struct ieee80211_channel *c)
49178354Ssam{
50178354Ssam	/* just feed back the current channel list */
51187800Ssam	if (maxchan > ic->ic_nchans)
52187800Ssam		maxchan = ic->ic_nchans;
53187800Ssam	memcpy(c, ic->ic_channels, maxchan*sizeof(struct ieee80211_channel));
54187834Ssam	*n = maxchan;
55178354Ssam}
56178354Ssam
57178354Ssamstatic int
58178354Ssamnull_setregdomain(struct ieee80211com *ic,
59178354Ssam	struct ieee80211_regdomain *rd,
60178354Ssam	int nchans, struct ieee80211_channel chans[])
61178354Ssam{
62178354Ssam	return 0;		/* accept anything */
63178354Ssam}
64178354Ssam
65170530Ssamvoid
66170530Ssamieee80211_regdomain_attach(struct ieee80211com *ic)
67170530Ssam{
68178354Ssam	if (ic->ic_regdomain.regdomain == 0 &&
69178354Ssam	    ic->ic_regdomain.country == CTRY_DEFAULT) {
70178354Ssam		ic->ic_regdomain.country = CTRY_UNITED_STATES;	/* XXX */
71178354Ssam		ic->ic_regdomain.location = ' ';		/* both */
72178354Ssam		ic->ic_regdomain.isocc[0] = 'U';		/* XXX */
73178354Ssam		ic->ic_regdomain.isocc[1] = 'S';		/* XXX */
74187802Ssam		/* NB: driver calls ieee80211_init_channels or similar */
75178354Ssam	}
76178354Ssam	ic->ic_getradiocaps = null_getradiocaps;
77178354Ssam	ic->ic_setregdomain = null_setregdomain;
78170530Ssam}
79170530Ssam
80170530Ssamvoid
81170530Ssamieee80211_regdomain_detach(struct ieee80211com *ic)
82170530Ssam{
83178354Ssam	if (ic->ic_countryie != NULL) {
84178354Ssam		free(ic->ic_countryie, M_80211_NODE_IE);
85178354Ssam		ic->ic_countryie = NULL;
86178354Ssam	}
87170530Ssam}
88170530Ssam
89178354Ssamvoid
90178354Ssamieee80211_regdomain_vattach(struct ieee80211vap *vap)
91178354Ssam{
92178354Ssam}
93178354Ssam
94178354Ssamvoid
95178354Ssamieee80211_regdomain_vdetach(struct ieee80211vap *vap)
96178354Ssam{
97178354Ssam}
98178354Ssam
99170530Ssamstatic void
100170530Ssamaddchan(struct ieee80211com *ic, int ieee, int flags)
101170530Ssam{
102170530Ssam	struct ieee80211_channel *c;
103170530Ssam
104170530Ssam	c = &ic->ic_channels[ic->ic_nchans++];
105170530Ssam	c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
106170530Ssam	c->ic_ieee = ieee;
107170530Ssam	c->ic_flags = flags;
108233845Sbschmidt	if (flags & IEEE80211_CHAN_HT40U)
109233845Sbschmidt		c->ic_extieee = ieee + 4;
110233845Sbschmidt	else if (flags & IEEE80211_CHAN_HT40D)
111233845Sbschmidt		c->ic_extieee = ieee - 4;
112233845Sbschmidt	else
113233845Sbschmidt		c->ic_extieee = 0;
114170530Ssam}
115170530Ssam
116170530Ssam/*
117170530Ssam * Setup the channel list for the specified regulatory domain,
118170530Ssam * country code, and operating modes.  This interface is used
119170530Ssam * when a driver does not obtain the channel list from another
120170530Ssam * source (such as firmware).
121170530Ssam */
122178354Ssamint
123170530Ssamieee80211_init_channels(struct ieee80211com *ic,
124178354Ssam	const struct ieee80211_regdomain *rd, const uint8_t bands[])
125170530Ssam{
126170530Ssam	int i;
127170530Ssam
128170530Ssam	/* XXX just do something for now */
129170530Ssam	ic->ic_nchans = 0;
130178354Ssam	if (isset(bands, IEEE80211_MODE_11B) ||
131233845Sbschmidt	    isset(bands, IEEE80211_MODE_11G) ||
132233845Sbschmidt	    isset(bands, IEEE80211_MODE_11NG)) {
133178354Ssam		int maxchan = 11;
134178354Ssam		if (rd != NULL && rd->ecm)
135178354Ssam			maxchan = 14;
136178354Ssam		for (i = 1; i <= maxchan; i++) {
137178354Ssam			if (isset(bands, IEEE80211_MODE_11B))
138170530Ssam				addchan(ic, i, IEEE80211_CHAN_B);
139178354Ssam			if (isset(bands, IEEE80211_MODE_11G))
140170530Ssam				addchan(ic, i, IEEE80211_CHAN_G);
141233845Sbschmidt			if (isset(bands, IEEE80211_MODE_11NG)) {
142233845Sbschmidt				addchan(ic, i,
143233845Sbschmidt				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
144233845Sbschmidt			}
145233845Sbschmidt			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
146233845Sbschmidt				continue;
147233845Sbschmidt			if (i <= 7) {
148233845Sbschmidt				addchan(ic, i,
149233845Sbschmidt				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
150233845Sbschmidt				addchan(ic, i + 4,
151233845Sbschmidt				    IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
152233845Sbschmidt			}
153170530Ssam		}
154170530Ssam	}
155233845Sbschmidt	if (isset(bands, IEEE80211_MODE_11A) ||
156233845Sbschmidt	    isset(bands, IEEE80211_MODE_11NA)) {
157233845Sbschmidt		for (i = 36; i <= 64; i += 4) {
158170530Ssam			addchan(ic, i, IEEE80211_CHAN_A);
159233845Sbschmidt			if (isset(bands, IEEE80211_MODE_11NA)) {
160233845Sbschmidt				addchan(ic, i,
161233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
162233845Sbschmidt			}
163233845Sbschmidt			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
164233845Sbschmidt				continue;
165233845Sbschmidt			if ((i % 8) == 4) {
166233845Sbschmidt				addchan(ic, i,
167233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
168233845Sbschmidt				addchan(ic, i + 4,
169233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
170233845Sbschmidt			}
171233845Sbschmidt		}
172233845Sbschmidt		for (i = 100; i <= 140; i += 4) {
173170530Ssam			addchan(ic, i, IEEE80211_CHAN_A);
174233845Sbschmidt			if (isset(bands, IEEE80211_MODE_11NA)) {
175233845Sbschmidt				addchan(ic, i,
176233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
177233845Sbschmidt			}
178233845Sbschmidt			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
179233845Sbschmidt				continue;
180233845Sbschmidt			if ((i % 8) == 4 && i != 140) {
181233845Sbschmidt				addchan(ic, i,
182233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
183233845Sbschmidt				addchan(ic, i + 4,
184233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
185233845Sbschmidt			}
186233845Sbschmidt		}
187233845Sbschmidt		for (i = 149; i <= 161; i += 4) {
188170530Ssam			addchan(ic, i, IEEE80211_CHAN_A);
189233845Sbschmidt			if (isset(bands, IEEE80211_MODE_11NA)) {
190233845Sbschmidt				addchan(ic, i,
191233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
192233845Sbschmidt			}
193233845Sbschmidt			if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0)
194233845Sbschmidt				continue;
195233845Sbschmidt			if ((i % 8) == 5) {
196233845Sbschmidt				addchan(ic, i,
197233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
198233845Sbschmidt				addchan(ic, i + 4,
199233845Sbschmidt				    IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
200233845Sbschmidt			}
201233845Sbschmidt		}
202170530Ssam	}
203178354Ssam	if (rd != NULL)
204178354Ssam		ic->ic_regdomain = *rd;
205178354Ssam
206178354Ssam	return 0;
207170530Ssam}
208170530Ssam
209178354Ssamstatic __inline int
210178354Ssamchancompar(const void *a, const void *b)
211178354Ssam{
212178354Ssam	const struct ieee80211_channel *ca = a;
213178354Ssam	const struct ieee80211_channel *cb = b;
214178354Ssam
215178354Ssam	return (ca->ic_freq == cb->ic_freq) ?
216178354Ssam		(ca->ic_flags & IEEE80211_CHAN_ALL) -
217178354Ssam		    (cb->ic_flags & IEEE80211_CHAN_ALL) :
218178354Ssam		ca->ic_freq - cb->ic_freq;
219178354Ssam}
220178354Ssam
221170530Ssam/*
222178354Ssam * Insertion sort.
223170530Ssam */
224178354Ssam#define swap(_a, _b, _size) {			\
225178354Ssam	uint8_t *s = _b;			\
226178354Ssam	int i = _size;				\
227178354Ssam	do {					\
228178354Ssam		uint8_t tmp = *_a;		\
229178354Ssam		*_a++ = *s;			\
230178354Ssam		*s++ = tmp;			\
231178354Ssam	} while (--i);				\
232178354Ssam	_a -= _size;				\
233178354Ssam}
234178354Ssam
235178354Ssamstatic void
236178354Ssamsort_channels(void *a, size_t n, size_t size)
237170530Ssam{
238178354Ssam	uint8_t *aa = a;
239178354Ssam	uint8_t *ai, *t;
240178354Ssam
241178354Ssam	KASSERT(n > 0, ("no channels"));
242178354Ssam	for (ai = aa+size; --n >= 1; ai += size)
243178354Ssam		for (t = ai; t > aa; t -= size) {
244178354Ssam			uint8_t *u = t - size;
245178354Ssam			if (chancompar(u, t) <= 0)
246178354Ssam				break;
247178354Ssam			swap(u, t, size);
248178354Ssam		}
249178354Ssam}
250178354Ssam#undef swap
251178354Ssam
252178354Ssam/*
253178354Ssam * Order channels w/ the same frequency so that
254178354Ssam * b < g < htg and a < hta.  This is used to optimize
255178354Ssam * channel table lookups and some user applications
256178354Ssam * may also depend on it (though they should not).
257178354Ssam */
258178354Ssamvoid
259178354Ssamieee80211_sort_channels(struct ieee80211_channel chans[], int nchans)
260178354Ssam{
261178354Ssam	if (nchans > 0)
262178354Ssam		sort_channels(chans, nchans, sizeof(struct ieee80211_channel));
263178354Ssam}
264178354Ssam
265178354Ssam/*
266178354Ssam * Allocate and construct a Country Information IE.
267178354Ssam */
268178354Ssamstruct ieee80211_appie *
269178354Ssamieee80211_alloc_countryie(struct ieee80211com *ic)
270178354Ssam{
271170530Ssam#define	CHAN_UNINTERESTING \
272170530Ssam    (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
273170530Ssam     IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)
274170530Ssam	/* XXX what about auto? */
275188782Ssam	/* flag set of channels to be excluded (band added below) */
276170530Ssam	static const int skipflags[IEEE80211_MODE_MAX] = {
277188778Ssam	    [IEEE80211_MODE_AUTO]	= CHAN_UNINTERESTING,
278188778Ssam	    [IEEE80211_MODE_11A]	= CHAN_UNINTERESTING,
279188778Ssam	    [IEEE80211_MODE_11B]	= CHAN_UNINTERESTING,
280188778Ssam	    [IEEE80211_MODE_11G]	= CHAN_UNINTERESTING,
281188778Ssam	    [IEEE80211_MODE_FH]		= CHAN_UNINTERESTING
282188778Ssam					| IEEE80211_CHAN_OFDM
283188778Ssam					| IEEE80211_CHAN_CCK
284188778Ssam					| IEEE80211_CHAN_DYN,
285188778Ssam	    [IEEE80211_MODE_TURBO_A]	= CHAN_UNINTERESTING,
286188778Ssam	    [IEEE80211_MODE_TURBO_G]	= CHAN_UNINTERESTING,
287188778Ssam	    [IEEE80211_MODE_STURBO_A]	= CHAN_UNINTERESTING,
288188782Ssam	    [IEEE80211_MODE_HALF]	= IEEE80211_CHAN_TURBO
289188782Ssam					| IEEE80211_CHAN_STURBO,
290188782Ssam	    [IEEE80211_MODE_QUARTER]	= IEEE80211_CHAN_TURBO
291188782Ssam					| IEEE80211_CHAN_STURBO,
292188778Ssam	    [IEEE80211_MODE_11NA]	= CHAN_UNINTERESTING,
293188778Ssam	    [IEEE80211_MODE_11NG]	= CHAN_UNINTERESTING,
294170530Ssam	};
295178354Ssam	const struct ieee80211_regdomain *rd = &ic->ic_regdomain;
296178354Ssam	uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm;
297178354Ssam	struct ieee80211_appie *aie;
298178354Ssam	struct ieee80211_country_ie *ie;
299178354Ssam	int i, skip, nruns;
300170530Ssam
301178354Ssam	aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE,
302178354Ssam	    M_NOWAIT | M_ZERO);
303178354Ssam	if (aie == NULL) {
304178354Ssam		if_printf(ic->ic_ifp,
305178354Ssam		    "%s: unable to allocate memory for country ie\n", __func__);
306178354Ssam		/* XXX stat */
307178354Ssam		return NULL;
308178354Ssam	}
309178354Ssam	ie = (struct ieee80211_country_ie *) aie->ie_data;
310170530Ssam	ie->ie = IEEE80211_ELEMID_COUNTRY;
311178354Ssam	if (rd->isocc[0] == '\0') {
312178354Ssam		if_printf(ic->ic_ifp, "no ISO country string for cc %d; "
313178354Ssam			"using blanks\n", rd->country);
314178354Ssam		ie->cc[0] = ie->cc[1] = ' ';
315178354Ssam	} else {
316178354Ssam		ie->cc[0] = rd->isocc[0];
317178354Ssam		ie->cc[1] = rd->isocc[1];
318170530Ssam	}
319170530Ssam	/*
320178354Ssam	 * Indoor/Outdoor portion of country string:
321170530Ssam	 *     'I' indoor only
322170530Ssam	 *     'O' outdoor only
323170530Ssam	 *     ' ' all enviroments
324170530Ssam	 */
325178354Ssam	ie->cc[2] = (rd->location == 'I' ? 'I' :
326178354Ssam		     rd->location == 'O' ? 'O' : ' ');
327170530Ssam	/*
328170530Ssam	 * Run-length encoded channel+max tx power info.
329170530Ssam	 */
330170530Ssam	frm = (uint8_t *)&ie->band[0];
331170530Ssam	nextchan = 0;			/* NB: impossible channel # */
332178354Ssam	nruns = 0;
333170530Ssam	memset(chans, 0, sizeof(chans));
334178354Ssam	skip = skipflags[ieee80211_chan2mode(ic->ic_bsschan)];
335188778Ssam	if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
336188778Ssam		skip |= IEEE80211_CHAN_2GHZ;
337188778Ssam	else if (IEEE80211_IS_CHAN_2GHZ(ic->ic_bsschan))
338188778Ssam		skip |= IEEE80211_CHAN_5GHZ;
339170530Ssam	for (i = 0; i < ic->ic_nchans; i++) {
340170530Ssam		const struct ieee80211_channel *c = &ic->ic_channels[i];
341170530Ssam
342170530Ssam		if (isset(chans, c->ic_ieee))		/* suppress dup's */
343170530Ssam			continue;
344172204Ssam		if (c->ic_flags & skip)			/* skip band, etc. */
345170530Ssam			continue;
346170530Ssam		setbit(chans, c->ic_ieee);
347170530Ssam		if (c->ic_ieee != nextchan ||
348170530Ssam		    c->ic_maxregpower != frm[-1]) {	/* new run */
349178354Ssam			if (nruns == IEEE80211_COUNTRY_MAX_BANDS) {
350178354Ssam				if_printf(ic->ic_ifp, "%s: country ie too big, "
351178354Ssam				    "runs > max %d, truncating\n",
352178354Ssam				    __func__, IEEE80211_COUNTRY_MAX_BANDS);
353178354Ssam				/* XXX stat? fail? */
354178354Ssam				break;
355178354Ssam			}
356170530Ssam			frm[0] = c->ic_ieee;		/* starting channel # */
357170530Ssam			frm[1] = 1;			/* # channels in run */
358170530Ssam			frm[2] = c->ic_maxregpower;	/* tx power cap */
359170530Ssam			frm += 3;
360170530Ssam			nextchan = c->ic_ieee + 1;	/* overflow? */
361178354Ssam			nruns++;
362170530Ssam		} else {				/* extend run */
363170530Ssam			frm[-2]++;
364170530Ssam			nextchan++;
365170530Ssam		}
366170530Ssam	}
367170530Ssam	ie->len = frm - ie->cc;
368171985Ssephe	if (ie->len & 1) {		/* Zero pad to multiple of 2 */
369170530Ssam		ie->len++;
370171985Ssephe		*frm++ = 0;
371171985Ssephe	}
372178354Ssam	aie->ie_len = frm - aie->ie_data;
373178354Ssam
374178354Ssam	return aie;
375170530Ssam#undef CHAN_UNINTERESTING
376170530Ssam}
377170530Ssam
378178354Ssamstatic int
379178354Ssamallvapsdown(struct ieee80211com *ic)
380170530Ssam{
381178354Ssam	struct ieee80211vap *vap;
382170530Ssam
383178354Ssam	IEEE80211_LOCK_ASSERT(ic);
384178354Ssam	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
385178354Ssam		if (vap->iv_state != IEEE80211_S_INIT)
386178354Ssam			return 0;
387178354Ssam	return 1;
388170530Ssam}
389170530Ssam
390170530Ssamint
391178354Ssamieee80211_setregdomain(struct ieee80211vap *vap,
392178354Ssam    struct ieee80211_regdomain_req *reg)
393170530Ssam{
394178354Ssam	struct ieee80211com *ic = vap->iv_ic;
395178354Ssam	struct ieee80211_channel *c;
396178354Ssam	int desfreq = 0, desflags = 0;		/* XXX silence gcc complaint */
397178354Ssam	int error, i;
398170530Ssam
399178354Ssam	if (reg->rd.location != 'I' && reg->rd.location != 'O' &&
400184272Ssam	    reg->rd.location != ' ') {
401184272Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
402184272Ssam		    "%s: invalid location 0x%x\n", __func__, reg->rd.location);
403178354Ssam		return EINVAL;
404184272Ssam	}
405184272Ssam	if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0') {
406184272Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
407184272Ssam		    "%s: invalid iso cc 0x%x:0x%x\n", __func__,
408184272Ssam		    reg->rd.isocc[0], reg->rd.isocc[1]);
409178354Ssam		return EINVAL;
410184272Ssam	}
411186107Ssam	if (reg->chaninfo.ic_nchans > IEEE80211_CHAN_MAX) {
412184272Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
413184272Ssam		    "%s: too many channels %u, max %u\n", __func__,
414184272Ssam		    reg->chaninfo.ic_nchans, IEEE80211_CHAN_MAX);
415178354Ssam		return EINVAL;
416184272Ssam	}
417178354Ssam	/*
418178354Ssam	 * Calculate freq<->IEEE mapping and default max tx power
419178354Ssam	 * for channels not setup.  The driver can override these
420178354Ssam	 * setting to reflect device properties/requirements.
421178354Ssam	 */
422178354Ssam	for (i = 0; i < reg->chaninfo.ic_nchans; i++) {
423178354Ssam		c = &reg->chaninfo.ic_chans[i];
424184272Ssam		if (c->ic_freq == 0 || c->ic_flags == 0) {
425184272Ssam			IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
426184272Ssam			    "%s: invalid channel spec at [%u]\n", __func__, i);
427178354Ssam			return EINVAL;
428184272Ssam		}
429184272Ssam		if (c->ic_maxregpower == 0) {
430184272Ssam			IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
431184272Ssam			    "%s: invalid channel spec, zero maxregpower, "
432184272Ssam			    "freq %u flags 0x%x\n", __func__,
433184272Ssam			    c->ic_freq, c->ic_flags);
434178354Ssam			return EINVAL;
435184272Ssam		}
436178354Ssam		if (c->ic_ieee == 0)
437178354Ssam			c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
438178354Ssam		if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
439178354Ssam			c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
440178354Ssam			    (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
441178354Ssam			    c->ic_flags);
442178354Ssam		if (c->ic_maxpower == 0)
443178354Ssam			c->ic_maxpower = 2*c->ic_maxregpower;
444170530Ssam	}
445178354Ssam	IEEE80211_LOCK(ic);
446187635Ssam	/* XXX bandaid; a running vap will likely crash */
447187635Ssam	if (!allvapsdown(ic)) {
448187635Ssam		IEEE80211_UNLOCK(ic);
449187635Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
450187635Ssam		    "%s: reject: vaps are running\n", __func__);
451187635Ssam		return EBUSY;
452187635Ssam	}
453178354Ssam	error = ic->ic_setregdomain(ic, &reg->rd,
454178354Ssam	    reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans);
455178354Ssam	if (error != 0) {
456178354Ssam		IEEE80211_UNLOCK(ic);
457184272Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
458184272Ssam		    "%s: driver rejected request, error %u\n", __func__, error);
459178354Ssam		return error;
460178354Ssam	}
461178354Ssam	/*
462178354Ssam	 * Commit: copy in new channel table and reset media state.
463178354Ssam	 * On return the state machines will be clocked so all vaps
464178354Ssam	 * will reset their state.
465178354Ssam	 *
466178354Ssam	 * XXX ic_bsschan is marked undefined, must have vap's in
467178354Ssam	 *     INIT state or we blow up forcing stations off
468178354Ssam	 */
469178354Ssam	/*
470178354Ssam	 * Save any desired channel for restore below.  Note this
471178354Ssam	 * needs to be done for all vaps but for now we only do
472178354Ssam	 * the one where the ioctl is issued.
473178354Ssam	 */
474178354Ssam	if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
475178354Ssam		desfreq = vap->iv_des_chan->ic_freq;
476178354Ssam		desflags = vap->iv_des_chan->ic_flags;
477178354Ssam	}
478178354Ssam	/* regdomain parameters */
479178354Ssam	memcpy(&ic->ic_regdomain, &reg->rd, sizeof(reg->rd));
480178354Ssam	/* channel table */
481178354Ssam	memcpy(ic->ic_channels, reg->chaninfo.ic_chans,
482178354Ssam	    reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel));
483178354Ssam	ic->ic_nchans = reg->chaninfo.ic_nchans;
484178354Ssam	memset(&ic->ic_channels[ic->ic_nchans], 0,
485178354Ssam	    (IEEE80211_CHAN_MAX - ic->ic_nchans) *
486178354Ssam	       sizeof(struct ieee80211_channel));
487178354Ssam	ieee80211_media_init(ic);
488178354Ssam
489178354Ssam	/*
490178354Ssam	 * Invalidate channel-related state.
491178354Ssam	 */
492178354Ssam	if (ic->ic_countryie != NULL) {
493178354Ssam		free(ic->ic_countryie, M_80211_NODE_IE);
494178354Ssam		ic->ic_countryie = NULL;
495178354Ssam	}
496178354Ssam	ieee80211_scan_flush(vap);
497178354Ssam	ieee80211_dfs_reset(ic);
498178354Ssam	if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
499188427Ssam		c = ieee80211_find_channel(ic, desfreq, desflags);
500178354Ssam		/* NB: may be NULL if not present in new channel list */
501188427Ssam		vap->iv_des_chan = (c != NULL) ? c : IEEE80211_CHAN_ANYC;
502178354Ssam	}
503178354Ssam	IEEE80211_UNLOCK(ic);
504178354Ssam
505178354Ssam	return 0;
506170530Ssam}
507