168e8e04eSSam Leffler /*- 2b032f27cSSam Leffler * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting 368e8e04eSSam Leffler * All rights reserved. 468e8e04eSSam Leffler * 568e8e04eSSam Leffler * Redistribution and use in source and binary forms, with or without 668e8e04eSSam Leffler * modification, are permitted provided that the following conditions 768e8e04eSSam Leffler * are met: 868e8e04eSSam Leffler * 1. Redistributions of source code must retain the above copyright 968e8e04eSSam Leffler * notice, this list of conditions and the following disclaimer. 1068e8e04eSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 1168e8e04eSSam Leffler * notice, this list of conditions and the following disclaimer in the 1268e8e04eSSam Leffler * documentation and/or other materials provided with the distribution. 1368e8e04eSSam Leffler * 1468e8e04eSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1568e8e04eSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1668e8e04eSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1768e8e04eSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1868e8e04eSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1968e8e04eSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2068e8e04eSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2168e8e04eSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2268e8e04eSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2368e8e04eSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2468e8e04eSSam Leffler */ 2568e8e04eSSam Leffler 2668e8e04eSSam Leffler #include <sys/cdefs.h> 2768e8e04eSSam Leffler __FBSDID("$FreeBSD$"); 2868e8e04eSSam Leffler 2968e8e04eSSam Leffler /* 3068e8e04eSSam Leffler * IEEE 802.11 regdomain support. 3168e8e04eSSam Leffler */ 32b032f27cSSam Leffler #include "opt_wlan.h" 3368e8e04eSSam Leffler 3468e8e04eSSam Leffler #include <sys/param.h> 3568e8e04eSSam Leffler #include <sys/systm.h> 3668e8e04eSSam Leffler #include <sys/kernel.h> 3768e8e04eSSam Leffler 3868e8e04eSSam Leffler #include <sys/socket.h> 3968e8e04eSSam Leffler 4068e8e04eSSam Leffler #include <net/if.h> 4168e8e04eSSam Leffler #include <net/if_media.h> 4268e8e04eSSam Leffler 4368e8e04eSSam Leffler #include <net80211/ieee80211_var.h> 4468e8e04eSSam Leffler #include <net80211/ieee80211_regdomain.h> 4568e8e04eSSam Leffler 46b032f27cSSam Leffler static void 475fe9f044SSam Leffler null_getradiocaps(struct ieee80211com *ic, int maxchan, 485fe9f044SSam Leffler int *n, struct ieee80211_channel *c) 49b032f27cSSam Leffler { 50b032f27cSSam Leffler /* just feed back the current channel list */ 515fe9f044SSam Leffler if (maxchan > ic->ic_nchans) 525fe9f044SSam Leffler maxchan = ic->ic_nchans; 535fe9f044SSam Leffler memcpy(c, ic->ic_channels, maxchan*sizeof(struct ieee80211_channel)); 54a6f34da7SSam Leffler *n = maxchan; 55b032f27cSSam Leffler } 56b032f27cSSam Leffler 57b032f27cSSam Leffler static int 58b032f27cSSam Leffler null_setregdomain(struct ieee80211com *ic, 59b032f27cSSam Leffler struct ieee80211_regdomain *rd, 60b032f27cSSam Leffler int nchans, struct ieee80211_channel chans[]) 61b032f27cSSam Leffler { 62b032f27cSSam Leffler return 0; /* accept anything */ 63b032f27cSSam Leffler } 64b032f27cSSam Leffler 6568e8e04eSSam Leffler void 6668e8e04eSSam Leffler ieee80211_regdomain_attach(struct ieee80211com *ic) 6768e8e04eSSam Leffler { 68b032f27cSSam Leffler if (ic->ic_regdomain.regdomain == 0 && 69b032f27cSSam Leffler ic->ic_regdomain.country == CTRY_DEFAULT) { 70b032f27cSSam Leffler ic->ic_regdomain.country = CTRY_UNITED_STATES; /* XXX */ 71b032f27cSSam Leffler ic->ic_regdomain.location = ' '; /* both */ 72b032f27cSSam Leffler ic->ic_regdomain.isocc[0] = 'U'; /* XXX */ 73b032f27cSSam Leffler ic->ic_regdomain.isocc[1] = 'S'; /* XXX */ 74b83f2cf3SSam Leffler /* NB: driver calls ieee80211_init_channels or similar */ 75b032f27cSSam Leffler } 76b032f27cSSam Leffler ic->ic_getradiocaps = null_getradiocaps; 77b032f27cSSam Leffler ic->ic_setregdomain = null_setregdomain; 7868e8e04eSSam Leffler } 7968e8e04eSSam Leffler 8068e8e04eSSam Leffler void 8168e8e04eSSam Leffler ieee80211_regdomain_detach(struct ieee80211com *ic) 8268e8e04eSSam Leffler { 83b032f27cSSam Leffler if (ic->ic_countryie != NULL) { 84b032f27cSSam Leffler free(ic->ic_countryie, M_80211_NODE_IE); 85b032f27cSSam Leffler ic->ic_countryie = NULL; 86b032f27cSSam Leffler } 87b032f27cSSam Leffler } 88b032f27cSSam Leffler 89b032f27cSSam Leffler void 90b032f27cSSam Leffler ieee80211_regdomain_vattach(struct ieee80211vap *vap) 91b032f27cSSam Leffler { 92b032f27cSSam Leffler } 93b032f27cSSam Leffler 94b032f27cSSam Leffler void 95b032f27cSSam Leffler ieee80211_regdomain_vdetach(struct ieee80211vap *vap) 96b032f27cSSam Leffler { 9768e8e04eSSam Leffler } 9868e8e04eSSam Leffler 9968e8e04eSSam Leffler static void 10068e8e04eSSam Leffler addchan(struct ieee80211com *ic, int ieee, int flags) 10168e8e04eSSam Leffler { 10268e8e04eSSam Leffler struct ieee80211_channel *c; 10368e8e04eSSam Leffler 10468e8e04eSSam Leffler c = &ic->ic_channels[ic->ic_nchans++]; 10568e8e04eSSam Leffler c->ic_freq = ieee80211_ieee2mhz(ieee, flags); 10668e8e04eSSam Leffler c->ic_ieee = ieee; 10768e8e04eSSam Leffler c->ic_flags = flags; 108*a2a4a2aaSBernhard Schmidt if (flags & IEEE80211_CHAN_HT40U) 109*a2a4a2aaSBernhard Schmidt c->ic_extieee = ieee + 4; 110*a2a4a2aaSBernhard Schmidt else if (flags & IEEE80211_CHAN_HT40D) 111*a2a4a2aaSBernhard Schmidt c->ic_extieee = ieee - 4; 112*a2a4a2aaSBernhard Schmidt else 113b032f27cSSam Leffler c->ic_extieee = 0; 11468e8e04eSSam Leffler } 11568e8e04eSSam Leffler 11668e8e04eSSam Leffler /* 11768e8e04eSSam Leffler * Setup the channel list for the specified regulatory domain, 11868e8e04eSSam Leffler * country code, and operating modes. This interface is used 11968e8e04eSSam Leffler * when a driver does not obtain the channel list from another 12068e8e04eSSam Leffler * source (such as firmware). 12168e8e04eSSam Leffler */ 122b032f27cSSam Leffler int 12368e8e04eSSam Leffler ieee80211_init_channels(struct ieee80211com *ic, 124b032f27cSSam Leffler const struct ieee80211_regdomain *rd, const uint8_t bands[]) 12568e8e04eSSam Leffler { 12668e8e04eSSam Leffler int i; 12768e8e04eSSam Leffler 12868e8e04eSSam Leffler /* XXX just do something for now */ 12968e8e04eSSam Leffler ic->ic_nchans = 0; 130b032f27cSSam Leffler if (isset(bands, IEEE80211_MODE_11B) || 131*a2a4a2aaSBernhard Schmidt isset(bands, IEEE80211_MODE_11G) || 132*a2a4a2aaSBernhard Schmidt isset(bands, IEEE80211_MODE_11NG)) { 133b032f27cSSam Leffler int maxchan = 11; 134b032f27cSSam Leffler if (rd != NULL && rd->ecm) 135b032f27cSSam Leffler maxchan = 14; 136b032f27cSSam Leffler for (i = 1; i <= maxchan; i++) { 137b032f27cSSam Leffler if (isset(bands, IEEE80211_MODE_11B)) 13868e8e04eSSam Leffler addchan(ic, i, IEEE80211_CHAN_B); 139b032f27cSSam Leffler if (isset(bands, IEEE80211_MODE_11G)) 14068e8e04eSSam Leffler addchan(ic, i, IEEE80211_CHAN_G); 141*a2a4a2aaSBernhard Schmidt if (isset(bands, IEEE80211_MODE_11NG)) { 142*a2a4a2aaSBernhard Schmidt addchan(ic, i, 143*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_G | IEEE80211_CHAN_HT20); 144*a2a4a2aaSBernhard Schmidt } 145*a2a4a2aaSBernhard Schmidt if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) 146*a2a4a2aaSBernhard Schmidt continue; 147*a2a4a2aaSBernhard Schmidt if (i <= 7) { 148*a2a4a2aaSBernhard Schmidt addchan(ic, i, 149*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U); 150*a2a4a2aaSBernhard Schmidt addchan(ic, i + 4, 151*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D); 15268e8e04eSSam Leffler } 15368e8e04eSSam Leffler } 154*a2a4a2aaSBernhard Schmidt } 155*a2a4a2aaSBernhard Schmidt if (isset(bands, IEEE80211_MODE_11A) || 156*a2a4a2aaSBernhard Schmidt isset(bands, IEEE80211_MODE_11NA)) { 157*a2a4a2aaSBernhard Schmidt for (i = 36; i <= 64; i += 4) { 15868e8e04eSSam Leffler addchan(ic, i, IEEE80211_CHAN_A); 159*a2a4a2aaSBernhard Schmidt if (isset(bands, IEEE80211_MODE_11NA)) { 160*a2a4a2aaSBernhard Schmidt addchan(ic, i, 161*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT20); 162*a2a4a2aaSBernhard Schmidt } 163*a2a4a2aaSBernhard Schmidt if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) 164*a2a4a2aaSBernhard Schmidt continue; 165*a2a4a2aaSBernhard Schmidt if ((i % 8) == 4) { 166*a2a4a2aaSBernhard Schmidt addchan(ic, i, 167*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U); 168*a2a4a2aaSBernhard Schmidt addchan(ic, i + 4, 169*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D); 170*a2a4a2aaSBernhard Schmidt } 171*a2a4a2aaSBernhard Schmidt } 172*a2a4a2aaSBernhard Schmidt for (i = 100; i <= 140; i += 4) { 17368e8e04eSSam Leffler addchan(ic, i, IEEE80211_CHAN_A); 174*a2a4a2aaSBernhard Schmidt if (isset(bands, IEEE80211_MODE_11NA)) { 175*a2a4a2aaSBernhard Schmidt addchan(ic, i, 176*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT20); 177*a2a4a2aaSBernhard Schmidt } 178*a2a4a2aaSBernhard Schmidt if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) 179*a2a4a2aaSBernhard Schmidt continue; 180*a2a4a2aaSBernhard Schmidt if ((i % 8) == 4 && i != 140) { 181*a2a4a2aaSBernhard Schmidt addchan(ic, i, 182*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U); 183*a2a4a2aaSBernhard Schmidt addchan(ic, i + 4, 184*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D); 185*a2a4a2aaSBernhard Schmidt } 186*a2a4a2aaSBernhard Schmidt } 187*a2a4a2aaSBernhard Schmidt for (i = 149; i <= 161; i += 4) { 18868e8e04eSSam Leffler addchan(ic, i, IEEE80211_CHAN_A); 189*a2a4a2aaSBernhard Schmidt if (isset(bands, IEEE80211_MODE_11NA)) { 190*a2a4a2aaSBernhard Schmidt addchan(ic, i, 191*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT20); 192*a2a4a2aaSBernhard Schmidt } 193*a2a4a2aaSBernhard Schmidt if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) 194*a2a4a2aaSBernhard Schmidt continue; 195*a2a4a2aaSBernhard Schmidt if ((i % 8) == 5) { 196*a2a4a2aaSBernhard Schmidt addchan(ic, i, 197*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U); 198*a2a4a2aaSBernhard Schmidt addchan(ic, i + 4, 199*a2a4a2aaSBernhard Schmidt IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D); 200*a2a4a2aaSBernhard Schmidt } 201*a2a4a2aaSBernhard Schmidt } 20268e8e04eSSam Leffler } 203b032f27cSSam Leffler if (rd != NULL) 204b032f27cSSam Leffler ic->ic_regdomain = *rd; 205b032f27cSSam Leffler 206b032f27cSSam Leffler return 0; 207b032f27cSSam Leffler } 208b032f27cSSam Leffler 209b032f27cSSam Leffler static __inline int 210b032f27cSSam Leffler chancompar(const void *a, const void *b) 211b032f27cSSam Leffler { 212b032f27cSSam Leffler const struct ieee80211_channel *ca = a; 213b032f27cSSam Leffler const struct ieee80211_channel *cb = b; 214b032f27cSSam Leffler 215b032f27cSSam Leffler return (ca->ic_freq == cb->ic_freq) ? 216b032f27cSSam Leffler (ca->ic_flags & IEEE80211_CHAN_ALL) - 217b032f27cSSam Leffler (cb->ic_flags & IEEE80211_CHAN_ALL) : 218b032f27cSSam Leffler ca->ic_freq - cb->ic_freq; 21968e8e04eSSam Leffler } 22068e8e04eSSam Leffler 22168e8e04eSSam Leffler /* 222b032f27cSSam Leffler * Insertion sort. 22368e8e04eSSam Leffler */ 224b032f27cSSam Leffler #define swap(_a, _b, _size) { \ 225b032f27cSSam Leffler uint8_t *s = _b; \ 226b032f27cSSam Leffler int i = _size; \ 227b032f27cSSam Leffler do { \ 228b032f27cSSam Leffler uint8_t tmp = *_a; \ 229b032f27cSSam Leffler *_a++ = *s; \ 230b032f27cSSam Leffler *s++ = tmp; \ 231b032f27cSSam Leffler } while (--i); \ 232b032f27cSSam Leffler _a -= _size; \ 233b032f27cSSam Leffler } 234b032f27cSSam Leffler 235b032f27cSSam Leffler static void 236b032f27cSSam Leffler sort_channels(void *a, size_t n, size_t size) 237b032f27cSSam Leffler { 238b032f27cSSam Leffler uint8_t *aa = a; 239b032f27cSSam Leffler uint8_t *ai, *t; 240b032f27cSSam Leffler 241b032f27cSSam Leffler KASSERT(n > 0, ("no channels")); 242b032f27cSSam Leffler for (ai = aa+size; --n >= 1; ai += size) 243b032f27cSSam Leffler for (t = ai; t > aa; t -= size) { 244b032f27cSSam Leffler uint8_t *u = t - size; 245b032f27cSSam Leffler if (chancompar(u, t) <= 0) 246b032f27cSSam Leffler break; 247b032f27cSSam Leffler swap(u, t, size); 248b032f27cSSam Leffler } 249b032f27cSSam Leffler } 250b032f27cSSam Leffler #undef swap 251b032f27cSSam Leffler 252b032f27cSSam Leffler /* 253b032f27cSSam Leffler * Order channels w/ the same frequency so that 254b032f27cSSam Leffler * b < g < htg and a < hta. This is used to optimize 255b032f27cSSam Leffler * channel table lookups and some user applications 256b032f27cSSam Leffler * may also depend on it (though they should not). 257b032f27cSSam Leffler */ 258b032f27cSSam Leffler void 259b032f27cSSam Leffler ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans) 260b032f27cSSam Leffler { 261b032f27cSSam Leffler if (nchans > 0) 262b032f27cSSam Leffler sort_channels(chans, nchans, sizeof(struct ieee80211_channel)); 263b032f27cSSam Leffler } 264b032f27cSSam Leffler 265b032f27cSSam Leffler /* 266b032f27cSSam Leffler * Allocate and construct a Country Information IE. 267b032f27cSSam Leffler */ 268b032f27cSSam Leffler struct ieee80211_appie * 269b032f27cSSam Leffler ieee80211_alloc_countryie(struct ieee80211com *ic) 27068e8e04eSSam Leffler { 27168e8e04eSSam Leffler #define CHAN_UNINTERESTING \ 27268e8e04eSSam Leffler (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \ 27368e8e04eSSam Leffler IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER) 27468e8e04eSSam Leffler /* XXX what about auto? */ 2756a76ae21SSam Leffler /* flag set of channels to be excluded (band added below) */ 27668e8e04eSSam Leffler static const int skipflags[IEEE80211_MODE_MAX] = { 277da92f159SSam Leffler [IEEE80211_MODE_AUTO] = CHAN_UNINTERESTING, 278da92f159SSam Leffler [IEEE80211_MODE_11A] = CHAN_UNINTERESTING, 279da92f159SSam Leffler [IEEE80211_MODE_11B] = CHAN_UNINTERESTING, 280da92f159SSam Leffler [IEEE80211_MODE_11G] = CHAN_UNINTERESTING, 281da92f159SSam Leffler [IEEE80211_MODE_FH] = CHAN_UNINTERESTING 282da92f159SSam Leffler | IEEE80211_CHAN_OFDM 283da92f159SSam Leffler | IEEE80211_CHAN_CCK 284da92f159SSam Leffler | IEEE80211_CHAN_DYN, 285da92f159SSam Leffler [IEEE80211_MODE_TURBO_A] = CHAN_UNINTERESTING, 286da92f159SSam Leffler [IEEE80211_MODE_TURBO_G] = CHAN_UNINTERESTING, 287da92f159SSam Leffler [IEEE80211_MODE_STURBO_A] = CHAN_UNINTERESTING, 2886a76ae21SSam Leffler [IEEE80211_MODE_HALF] = IEEE80211_CHAN_TURBO 2896a76ae21SSam Leffler | IEEE80211_CHAN_STURBO, 2906a76ae21SSam Leffler [IEEE80211_MODE_QUARTER] = IEEE80211_CHAN_TURBO 2916a76ae21SSam Leffler | IEEE80211_CHAN_STURBO, 292da92f159SSam Leffler [IEEE80211_MODE_11NA] = CHAN_UNINTERESTING, 293da92f159SSam Leffler [IEEE80211_MODE_11NG] = CHAN_UNINTERESTING, 29468e8e04eSSam Leffler }; 295b032f27cSSam Leffler const struct ieee80211_regdomain *rd = &ic->ic_regdomain; 296b032f27cSSam Leffler uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm; 297b032f27cSSam Leffler struct ieee80211_appie *aie; 298b032f27cSSam Leffler struct ieee80211_country_ie *ie; 299b032f27cSSam Leffler int i, skip, nruns; 30068e8e04eSSam Leffler 301b032f27cSSam Leffler aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE, 302b032f27cSSam Leffler M_NOWAIT | M_ZERO); 303b032f27cSSam Leffler if (aie == NULL) { 304b032f27cSSam Leffler if_printf(ic->ic_ifp, 305b032f27cSSam Leffler "%s: unable to allocate memory for country ie\n", __func__); 306b032f27cSSam Leffler /* XXX stat */ 307b032f27cSSam Leffler return NULL; 30868e8e04eSSam Leffler } 309b032f27cSSam Leffler ie = (struct ieee80211_country_ie *) aie->ie_data; 310b032f27cSSam Leffler ie->ie = IEEE80211_ELEMID_COUNTRY; 311b032f27cSSam Leffler if (rd->isocc[0] == '\0') { 312b032f27cSSam Leffler if_printf(ic->ic_ifp, "no ISO country string for cc %d; " 313b032f27cSSam Leffler "using blanks\n", rd->country); 314b032f27cSSam Leffler ie->cc[0] = ie->cc[1] = ' '; 315b032f27cSSam Leffler } else { 316b032f27cSSam Leffler ie->cc[0] = rd->isocc[0]; 317b032f27cSSam Leffler ie->cc[1] = rd->isocc[1]; 318b032f27cSSam Leffler } 31968e8e04eSSam Leffler /* 320b032f27cSSam Leffler * Indoor/Outdoor portion of country string: 32168e8e04eSSam Leffler * 'I' indoor only 32268e8e04eSSam Leffler * 'O' outdoor only 32368e8e04eSSam Leffler * ' ' all enviroments 32468e8e04eSSam Leffler */ 325b032f27cSSam Leffler ie->cc[2] = (rd->location == 'I' ? 'I' : 326b032f27cSSam Leffler rd->location == 'O' ? 'O' : ' '); 32768e8e04eSSam Leffler /* 32868e8e04eSSam Leffler * Run-length encoded channel+max tx power info. 32968e8e04eSSam Leffler */ 33068e8e04eSSam Leffler frm = (uint8_t *)&ie->band[0]; 33168e8e04eSSam Leffler nextchan = 0; /* NB: impossible channel # */ 332b032f27cSSam Leffler nruns = 0; 33368e8e04eSSam Leffler memset(chans, 0, sizeof(chans)); 334b032f27cSSam Leffler skip = skipflags[ieee80211_chan2mode(ic->ic_bsschan)]; 335da92f159SSam Leffler if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) 336da92f159SSam Leffler skip |= IEEE80211_CHAN_2GHZ; 337da92f159SSam Leffler else if (IEEE80211_IS_CHAN_2GHZ(ic->ic_bsschan)) 338da92f159SSam Leffler skip |= IEEE80211_CHAN_5GHZ; 33968e8e04eSSam Leffler for (i = 0; i < ic->ic_nchans; i++) { 34068e8e04eSSam Leffler const struct ieee80211_channel *c = &ic->ic_channels[i]; 34168e8e04eSSam Leffler 34268e8e04eSSam Leffler if (isset(chans, c->ic_ieee)) /* suppress dup's */ 34368e8e04eSSam Leffler continue; 3445ef9cd6cSSam Leffler if (c->ic_flags & skip) /* skip band, etc. */ 34568e8e04eSSam Leffler continue; 34668e8e04eSSam Leffler setbit(chans, c->ic_ieee); 34768e8e04eSSam Leffler if (c->ic_ieee != nextchan || 34868e8e04eSSam Leffler c->ic_maxregpower != frm[-1]) { /* new run */ 349b032f27cSSam Leffler if (nruns == IEEE80211_COUNTRY_MAX_BANDS) { 350b032f27cSSam Leffler if_printf(ic->ic_ifp, "%s: country ie too big, " 351b032f27cSSam Leffler "runs > max %d, truncating\n", 352b032f27cSSam Leffler __func__, IEEE80211_COUNTRY_MAX_BANDS); 353b032f27cSSam Leffler /* XXX stat? fail? */ 354b032f27cSSam Leffler break; 355b032f27cSSam Leffler } 35668e8e04eSSam Leffler frm[0] = c->ic_ieee; /* starting channel # */ 35768e8e04eSSam Leffler frm[1] = 1; /* # channels in run */ 35868e8e04eSSam Leffler frm[2] = c->ic_maxregpower; /* tx power cap */ 35968e8e04eSSam Leffler frm += 3; 36068e8e04eSSam Leffler nextchan = c->ic_ieee + 1; /* overflow? */ 361b032f27cSSam Leffler nruns++; 36268e8e04eSSam Leffler } else { /* extend run */ 36368e8e04eSSam Leffler frm[-2]++; 36468e8e04eSSam Leffler nextchan++; 36568e8e04eSSam Leffler } 36668e8e04eSSam Leffler } 36768e8e04eSSam Leffler ie->len = frm - ie->cc; 368f05ba5eeSSepherosa Ziehau if (ie->len & 1) { /* Zero pad to multiple of 2 */ 36968e8e04eSSam Leffler ie->len++; 370f05ba5eeSSepherosa Ziehau *frm++ = 0; 371f05ba5eeSSepherosa Ziehau } 372b032f27cSSam Leffler aie->ie_len = frm - aie->ie_data; 373b032f27cSSam Leffler 374b032f27cSSam Leffler return aie; 37568e8e04eSSam Leffler #undef CHAN_UNINTERESTING 37668e8e04eSSam Leffler } 37768e8e04eSSam Leffler 378b032f27cSSam Leffler static int 379b032f27cSSam Leffler allvapsdown(struct ieee80211com *ic) 38068e8e04eSSam Leffler { 381b032f27cSSam Leffler struct ieee80211vap *vap; 38268e8e04eSSam Leffler 383b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 384b032f27cSSam Leffler TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 385b032f27cSSam Leffler if (vap->iv_state != IEEE80211_S_INIT) 386b032f27cSSam Leffler return 0; 387b032f27cSSam Leffler return 1; 38868e8e04eSSam Leffler } 38968e8e04eSSam Leffler 39068e8e04eSSam Leffler int 391b032f27cSSam Leffler ieee80211_setregdomain(struct ieee80211vap *vap, 392b032f27cSSam Leffler struct ieee80211_regdomain_req *reg) 39368e8e04eSSam Leffler { 394b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 395b032f27cSSam Leffler struct ieee80211_channel *c; 396b032f27cSSam Leffler int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */ 397b032f27cSSam Leffler int error, i; 39868e8e04eSSam Leffler 399b032f27cSSam Leffler if (reg->rd.location != 'I' && reg->rd.location != 'O' && 400fad0f46dSSam Leffler reg->rd.location != ' ') { 401fad0f46dSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, 402fad0f46dSSam Leffler "%s: invalid location 0x%x\n", __func__, reg->rd.location); 403b032f27cSSam Leffler return EINVAL; 404fad0f46dSSam Leffler } 405fad0f46dSSam Leffler if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0') { 406fad0f46dSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, 407fad0f46dSSam Leffler "%s: invalid iso cc 0x%x:0x%x\n", __func__, 408fad0f46dSSam Leffler reg->rd.isocc[0], reg->rd.isocc[1]); 409b032f27cSSam Leffler return EINVAL; 410fad0f46dSSam Leffler } 41131378b1cSSam Leffler if (reg->chaninfo.ic_nchans > IEEE80211_CHAN_MAX) { 412fad0f46dSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, 413fad0f46dSSam Leffler "%s: too many channels %u, max %u\n", __func__, 414fad0f46dSSam Leffler reg->chaninfo.ic_nchans, IEEE80211_CHAN_MAX); 415b032f27cSSam Leffler return EINVAL; 416fad0f46dSSam Leffler } 417b032f27cSSam Leffler /* 418b032f27cSSam Leffler * Calculate freq<->IEEE mapping and default max tx power 419b032f27cSSam Leffler * for channels not setup. The driver can override these 420b032f27cSSam Leffler * setting to reflect device properties/requirements. 421b032f27cSSam Leffler */ 422b032f27cSSam Leffler for (i = 0; i < reg->chaninfo.ic_nchans; i++) { 423b032f27cSSam Leffler c = ®->chaninfo.ic_chans[i]; 424fad0f46dSSam Leffler if (c->ic_freq == 0 || c->ic_flags == 0) { 425fad0f46dSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, 426fad0f46dSSam Leffler "%s: invalid channel spec at [%u]\n", __func__, i); 427b032f27cSSam Leffler return EINVAL; 428fad0f46dSSam Leffler } 429fad0f46dSSam Leffler if (c->ic_maxregpower == 0) { 430fad0f46dSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, 431fad0f46dSSam Leffler "%s: invalid channel spec, zero maxregpower, " 432fad0f46dSSam Leffler "freq %u flags 0x%x\n", __func__, 433fad0f46dSSam Leffler c->ic_freq, c->ic_flags); 434b032f27cSSam Leffler return EINVAL; 435fad0f46dSSam Leffler } 436b032f27cSSam Leffler if (c->ic_ieee == 0) 437b032f27cSSam Leffler c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); 438b032f27cSSam Leffler if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) 439b032f27cSSam Leffler c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + 440b032f27cSSam Leffler (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), 441b032f27cSSam Leffler c->ic_flags); 442b032f27cSSam Leffler if (c->ic_maxpower == 0) 443b032f27cSSam Leffler c->ic_maxpower = 2*c->ic_maxregpower; 44468e8e04eSSam Leffler } 445b032f27cSSam Leffler IEEE80211_LOCK(ic); 446a5985e8bSSam Leffler /* XXX bandaid; a running vap will likely crash */ 447a5985e8bSSam Leffler if (!allvapsdown(ic)) { 448a5985e8bSSam Leffler IEEE80211_UNLOCK(ic); 449a5985e8bSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, 450a5985e8bSSam Leffler "%s: reject: vaps are running\n", __func__); 451a5985e8bSSam Leffler return EBUSY; 452a5985e8bSSam Leffler } 453b032f27cSSam Leffler error = ic->ic_setregdomain(ic, ®->rd, 454b032f27cSSam Leffler reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans); 455b032f27cSSam Leffler if (error != 0) { 456b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 457fad0f46dSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, 458fad0f46dSSam Leffler "%s: driver rejected request, error %u\n", __func__, error); 459b032f27cSSam Leffler return error; 460b032f27cSSam Leffler } 461b032f27cSSam Leffler /* 462b032f27cSSam Leffler * Commit: copy in new channel table and reset media state. 463b032f27cSSam Leffler * On return the state machines will be clocked so all vaps 464b032f27cSSam Leffler * will reset their state. 465b032f27cSSam Leffler * 466b032f27cSSam Leffler * XXX ic_bsschan is marked undefined, must have vap's in 467b032f27cSSam Leffler * INIT state or we blow up forcing stations off 468b032f27cSSam Leffler */ 469b032f27cSSam Leffler /* 470b032f27cSSam Leffler * Save any desired channel for restore below. Note this 471b032f27cSSam Leffler * needs to be done for all vaps but for now we only do 472b032f27cSSam Leffler * the one where the ioctl is issued. 473b032f27cSSam Leffler */ 474b032f27cSSam Leffler if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { 475b032f27cSSam Leffler desfreq = vap->iv_des_chan->ic_freq; 476b032f27cSSam Leffler desflags = vap->iv_des_chan->ic_flags; 477b032f27cSSam Leffler } 478b032f27cSSam Leffler /* regdomain parameters */ 479b032f27cSSam Leffler memcpy(&ic->ic_regdomain, ®->rd, sizeof(reg->rd)); 480b032f27cSSam Leffler /* channel table */ 481b032f27cSSam Leffler memcpy(ic->ic_channels, reg->chaninfo.ic_chans, 482b032f27cSSam Leffler reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel)); 483b032f27cSSam Leffler ic->ic_nchans = reg->chaninfo.ic_nchans; 484b032f27cSSam Leffler memset(&ic->ic_channels[ic->ic_nchans], 0, 485b032f27cSSam Leffler (IEEE80211_CHAN_MAX - ic->ic_nchans) * 486b032f27cSSam Leffler sizeof(struct ieee80211_channel)); 487b032f27cSSam Leffler ieee80211_media_init(ic); 488b032f27cSSam Leffler 489b032f27cSSam Leffler /* 490b032f27cSSam Leffler * Invalidate channel-related state. 491b032f27cSSam Leffler */ 492b032f27cSSam Leffler if (ic->ic_countryie != NULL) { 493b032f27cSSam Leffler free(ic->ic_countryie, M_80211_NODE_IE); 494b032f27cSSam Leffler ic->ic_countryie = NULL; 495b032f27cSSam Leffler } 496b032f27cSSam Leffler ieee80211_scan_flush(vap); 497b032f27cSSam Leffler ieee80211_dfs_reset(ic); 498b032f27cSSam Leffler if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { 49924a90e6bSSam Leffler c = ieee80211_find_channel(ic, desfreq, desflags); 500b032f27cSSam Leffler /* NB: may be NULL if not present in new channel list */ 50124a90e6bSSam Leffler vap->iv_des_chan = (c != NULL) ? c : IEEE80211_CHAN_ANYC; 502b032f27cSSam Leffler } 503b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 504b032f27cSSam Leffler 505b032f27cSSam Leffler return 0; 50668e8e04eSSam Leffler } 507