ieee80211_regdomain.c revision 170530
10SN/A/*-
2477SN/A * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
30SN/A * All rights reserved.
40SN/A *
50SN/A * Redistribution and use in source and binary forms, with or without
60SN/A * modification, are permitted provided that the following conditions
7157SN/A * are met:
80SN/A * 1. Redistributions of source code must retain the above copyright
9157SN/A *    notice, this list of conditions and the following disclaimer.
100SN/A * 2. Redistributions in binary form must reproduce the above copyright
110SN/A *    notice, this list of conditions and the following disclaimer in the
120SN/A *    documentation and/or other materials provided with the distribution.
130SN/A *
140SN/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
150SN/A * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
160SN/A * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
170SN/A * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
180SN/A * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
190SN/A * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
200SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21157SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22157SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23157SN/A * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
240SN/A */
250SN/A
260SN/A#include <sys/cdefs.h>
270SN/A__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_regdomain.c 170530 2007-06-11 03:36:55Z sam $");
280SN/A
290SN/A/*
300SN/A * IEEE 802.11 regdomain support.
310SN/A */
320SN/A
330SN/A#include <sys/param.h>
340SN/A#include <sys/systm.h>
350SN/A#include <sys/kernel.h>
360SN/A
370SN/A#include <sys/socket.h>
380SN/A
390SN/A#include <net/if.h>
400SN/A#include <net/if_arp.h>
410SN/A#include <net/if_dl.h>
420SN/A#include <net/if_media.h>
430SN/A#include <net/if_types.h>
440SN/A#include <net/ethernet.h>
450SN/A
460SN/A#include <net80211/ieee80211_var.h>
470SN/A#include <net80211/ieee80211_regdomain.h>
480SN/A
490SN/Avoid
500SN/Aieee80211_regdomain_attach(struct ieee80211com *ic)
510SN/A{
520SN/A	ic->ic_regdomain = 0;			/* XXX */
530SN/A	ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */
540SN/A	ic->ic_location = 1+2;			/* both */
550SN/A}
560SN/A
570SN/Avoid
580SN/Aieee80211_regdomain_detach(struct ieee80211com *ic)
590SN/A{
600SN/A}
610SN/A
620SN/Astatic void
630SN/Aaddchan(struct ieee80211com *ic, int ieee, int flags)
640SN/A{
650SN/A	struct ieee80211_channel *c;
660SN/A
670SN/A	c = &ic->ic_channels[ic->ic_nchans++];
680SN/A	c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
690SN/A	c->ic_ieee = ieee;
700SN/A	c->ic_flags = flags;
710SN/A}
720SN/A
730SN/A/*
740SN/A * Setup the channel list for the specified regulatory domain,
750SN/A * country code, and operating modes.  This interface is used
760SN/A * when a driver does not obtain the channel list from another
770SN/A * source (such as firmware).
780SN/A */
790SN/Avoid
800SN/Aieee80211_init_channels(struct ieee80211com *ic,
810SN/A	int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm)
820SN/A{
830SN/A	int i;
840SN/A
850SN/A	/* XXX just do something for now */
860SN/A	ic->ic_nchans = 0;
870SN/A	if (isset(&bands, IEEE80211_MODE_11B) ||
880SN/A	    isset(&bands, IEEE80211_MODE_11G)) {
890SN/A		for (i = 1; i <= (ecm ? 14 : 11); i++) {
900SN/A			if (isset(&bands, IEEE80211_MODE_11B))
910SN/A				addchan(ic, i, IEEE80211_CHAN_B);
920SN/A			if (isset(&bands, IEEE80211_MODE_11G))
930SN/A				addchan(ic, i, IEEE80211_CHAN_G);
940SN/A		}
950SN/A	}
960SN/A	if (isset(&bands, IEEE80211_MODE_11A)) {
970SN/A		for (i = 36; i <= 64; i += 4)
980SN/A			addchan(ic, i, IEEE80211_CHAN_A);
990SN/A		for (i = 100; i <= 140; i += 4)
1000SN/A			addchan(ic, i, IEEE80211_CHAN_A);
1010SN/A		for (i = 149; i <= 161; i += 4)
1020SN/A			addchan(ic, i, IEEE80211_CHAN_A);
1030SN/A	}
1040SN/A	ic->ic_regdomain = rd;
1050SN/A	ic->ic_countrycode = cc;
1060SN/A	ic->ic_location = outdoor;
1070SN/A}
1080SN/A
1090SN/A/*
1100SN/A * Add Country Information IE.
1110SN/A */
1120SN/Auint8_t *
1130SN/Aieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
1140SN/A	enum ISOCountryCode cc, int location)
1150SN/A{
1160SN/A#define	CHAN_UNINTERESTING \
1170SN/A    (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
1180SN/A     IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)
1190SN/A	/* XXX what about auto? */
1200SN/A	/* flag set of channels to be excluded */
1210SN/A	static const int skipflags[IEEE80211_MODE_MAX] = {
1220SN/A	    CHAN_UNINTERESTING,				/* MODE_AUTO */
1230SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_11A */
1240SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11B */
1250SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11G */
1260SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_OFDM |	/* MODE_FH */
1270SN/A	        IEEE80211_CHAN_CCK | IEEE80211_CHAN_DYN,
1280SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_TURBO_A */
1290SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_TURBO_G */
1300SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_STURBO_A */
1310SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_11NA */
1320SN/A	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11NG */
1330SN/A	};
1340SN/A	struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm;
1350SN/A	const char *iso_name;
1360SN/A	uint8_t nextchan, chans[IEEE80211_CHAN_BYTES];
1370SN/A	int i, skip;
1380SN/A
1390SN/A	ie->ie = IEEE80211_ELEMID_COUNTRY;
1400SN/A	iso_name = ieee80211_cctoiso(cc);
1410SN/A	if (iso_name == NULL) {
1420SN/A		if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc);
1430SN/A		iso_name = "  ";
1440SN/A	}
1450SN/A	ie->cc[0] = iso_name[0];
1460SN/A	ie->cc[1] = iso_name[1];
1470SN/A	/*
1480SN/A	 * Indoor/Outdoor portion of country string.
1490SN/A	 * NB: this is not quite right, since we should have one of:
1500SN/A	 *     'I' indoor only
1510SN/A	 *     'O' outdoor only
1520SN/A	 *     ' ' all enviroments
1530SN/A	 */
1540SN/A	ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O');
1550SN/A
1560SN/A	/*
1570SN/A	 * Run-length encoded channel+max tx power info.
1580SN/A	 */
1590SN/A	frm = (uint8_t *)&ie->band[0];
1600SN/A	nextchan = 0;			/* NB: impossible channel # */
1610SN/A	memset(chans, 0, sizeof(chans));
1620SN/A	skip = skipflags[ic->ic_curmode];
1630SN/A	for (i = 0; i < ic->ic_nchans; i++) {
1640SN/A		const struct ieee80211_channel *c = &ic->ic_channels[i];
1650SN/A
1660SN/A		if (isset(chans, c->ic_ieee))		/* suppress dup's */
1670SN/A			continue;
1680SN/A		if ((c->ic_flags & skip) == 0)		/* skip band, etc. */
1690SN/A			continue;
1700SN/A		setbit(chans, c->ic_ieee);
1710SN/A		if (c->ic_ieee != nextchan ||
1720SN/A		    c->ic_maxregpower != frm[-1]) {	/* new run */
1730SN/A			/* XXX max of 83 runs */
1740SN/A			frm[0] = c->ic_ieee;		/* starting channel # */
1750SN/A			frm[1] = 1;			/* # channels in run */
1760SN/A			frm[2] = c->ic_maxregpower;	/* tx power cap */
1770SN/A			frm += 3;
1780SN/A			nextchan = c->ic_ieee + 1;	/* overflow? */
1790SN/A		} else {				/* extend run */
1800SN/A			frm[-2]++;
1810SN/A			nextchan++;
1820SN/A		}
1830SN/A	}
1840SN/A	ie->len = frm - ie->cc;
1850SN/A	if (ie->len & 1)		/* pad to multiple of 2 */
1860SN/A		ie->len++;
1870SN/A	return frm;
1880SN/A#undef CHAN_UNINTERESTING
1890SN/A}
1900SN/A
1910SN/A/*
1920SN/A * Country Code Table for code-to-string conversion.
1930SN/A */
1940SN/Astatic const struct {
1950SN/A	enum ISOCountryCode iso_code;
1960SN/A	const char*	iso_name;
1970SN/A} country_strings[] = {
1980SN/A    { CTRY_DEBUG,	 	"DB" },		/* NB: nonstandard */
1990SN/A    { CTRY_DEFAULT,	 	"NA" },		/* NB: nonstandard */
2000SN/A    { CTRY_ALBANIA,		"AL" },
2010SN/A    { CTRY_ALGERIA,		"DZ" },
2020SN/A    { CTRY_ARGENTINA,		"AR" },
2030SN/A    { CTRY_ARMENIA,		"AM" },
2040SN/A    { CTRY_AUSTRALIA,		"AU" },
2050SN/A    { CTRY_AUSTRIA,		"AT" },
2060SN/A    { CTRY_AZERBAIJAN,		"AZ" },
2070SN/A    { CTRY_BAHRAIN,		"BH" },
2080SN/A    { CTRY_BELARUS,		"BY" },
2090SN/A    { CTRY_BELGIUM,		"BE" },
2100SN/A    { CTRY_BELIZE,		"BZ" },
2110SN/A    { CTRY_BOLIVIA,		"BO" },
2120SN/A    { CTRY_BRAZIL,		"BR" },
2130SN/A    { CTRY_BRUNEI_DARUSSALAM,	"BN" },
2140SN/A    { CTRY_BULGARIA,		"BG" },
2150SN/A    { CTRY_CANADA,		"CA" },
2160SN/A    { CTRY_CHILE,		"CL" },
2170SN/A    { CTRY_CHINA,		"CN" },
2180SN/A    { CTRY_COLOMBIA,		"CO" },
2190SN/A    { CTRY_COSTA_RICA,		"CR" },
2200SN/A    { CTRY_CROATIA,		"HR" },
2210SN/A    { CTRY_CYPRUS,		"CY" },
2220SN/A    { CTRY_CZECH,		"CZ" },
2230SN/A    { CTRY_DENMARK,		"DK" },
2240SN/A    { CTRY_DOMINICAN_REPUBLIC,	"DO" },
2250SN/A    { CTRY_ECUADOR,		"EC" },
2260SN/A    { CTRY_EGYPT,		"EG" },
2270SN/A    { CTRY_EL_SALVADOR,		"SV" },
2280SN/A    { CTRY_ESTONIA,		"EE" },
2290SN/A    { CTRY_FINLAND,		"FI" },
2300SN/A    { CTRY_FRANCE,		"FR" },
2310SN/A    { CTRY_FRANCE2,		"F2" },
2320SN/A    { CTRY_GEORGIA,		"GE" },
2330SN/A    { CTRY_GERMANY,		"DE" },
2340SN/A    { CTRY_GREECE,		"GR" },
2350SN/A    { CTRY_GUATEMALA,		"GT" },
2360SN/A    { CTRY_HONDURAS,		"HN" },
2370SN/A    { CTRY_HONG_KONG,		"HK" },
2380SN/A    { CTRY_HUNGARY,		"HU" },
2390SN/A    { CTRY_ICELAND,		"IS" },
2400SN/A    { CTRY_INDIA,		"IN" },
2410SN/A    { CTRY_INDONESIA,		"ID" },
2420SN/A    { CTRY_IRAN,		"IR" },
2430SN/A    { CTRY_IRELAND,		"IE" },
2440SN/A    { CTRY_ISRAEL,		"IL" },
2450SN/A    { CTRY_ITALY,		"IT" },
2460SN/A    { CTRY_JAMAICA,		"JM" },
2470SN/A    { CTRY_JAPAN,		"JP" },
2480SN/A    { CTRY_JAPAN1,		"J1" },
2490SN/A    { CTRY_JAPAN2,		"J2" },
2500SN/A    { CTRY_JAPAN3,		"J3" },
2510SN/A    { CTRY_JAPAN4,		"J4" },
2520SN/A    { CTRY_JAPAN5,		"J5" },
2530SN/A    { CTRY_JORDAN,		"JO" },
2540SN/A    { CTRY_KAZAKHSTAN,		"KZ" },
2550SN/A    { CTRY_KOREA_NORTH,		"KP" },
2560SN/A    { CTRY_KOREA_ROC,		"KR" },
2570SN/A    { CTRY_KOREA_ROC2,		"K2" },
2580SN/A    { CTRY_KUWAIT,		"KW" },
2590SN/A    { CTRY_LATVIA,		"LV" },
2600SN/A    { CTRY_LEBANON,		"LB" },
2610SN/A    { CTRY_LIECHTENSTEIN,	"LI" },
2620SN/A    { CTRY_LITHUANIA,		"LT" },
2630SN/A    { CTRY_LUXEMBOURG,		"LU" },
2640SN/A    { CTRY_MACAU,		"MO" },
2650SN/A    { CTRY_MACEDONIA,		"MK" },
2660SN/A    { CTRY_MALAYSIA,		"MY" },
2670SN/A    { CTRY_MEXICO,		"MX" },
2680SN/A    { CTRY_MONACO,		"MC" },
2690SN/A    { CTRY_MOROCCO,		"MA" },
2700SN/A    { CTRY_NETHERLANDS,		"NL" },
2710SN/A    { CTRY_NEW_ZEALAND,		"NZ" },
2720SN/A    { CTRY_NORWAY,		"NO" },
2730SN/A    { CTRY_OMAN,		"OM" },
2740SN/A    { CTRY_PAKISTAN,		"PK" },
2750SN/A    { CTRY_PANAMA,		"PA" },
2760SN/A    { CTRY_PERU,		"PE" },
2770SN/A    { CTRY_PHILIPPINES,		"PH" },
2780SN/A    { CTRY_POLAND,		"PL" },
2790SN/A    { CTRY_PORTUGAL,		"PT" },
2800SN/A    { CTRY_PUERTO_RICO,		"PR" },
2810SN/A    { CTRY_QATAR,		"QA" },
2820SN/A    { CTRY_ROMANIA,		"RO" },
2830SN/A    { CTRY_RUSSIA,		"RU" },
2840SN/A    { CTRY_SAUDI_ARABIA,	"SA" },
2850SN/A    { CTRY_SINGAPORE,		"SG" },
2860SN/A    { CTRY_SLOVAKIA,		"SK" },
2870SN/A    { CTRY_SLOVENIA,		"SI" },
2880SN/A    { CTRY_SOUTH_AFRICA,	"ZA" },
2890SN/A    { CTRY_SPAIN,		"ES" },
2900SN/A    { CTRY_SWEDEN,		"SE" },
2910SN/A    { CTRY_SWITZERLAND,		"CH" },
2920SN/A    { CTRY_SYRIA,		"SY" },
2930SN/A    { CTRY_TAIWAN,		"TW" },
2940SN/A    { CTRY_THAILAND,		"TH" },
2950SN/A    { CTRY_TRINIDAD_Y_TOBAGO,	"TT" },
2960SN/A    { CTRY_TUNISIA,		"TN" },
2970SN/A    { CTRY_TURKEY,		"TR" },
2980SN/A    { CTRY_UKRAINE,		"UA" },
2990SN/A    { CTRY_UAE,			"AE" },
3000SN/A    { CTRY_UNITED_KINGDOM,	"GB" },
3010SN/A    { CTRY_UNITED_STATES,	"US" },
3020SN/A    { CTRY_URUGUAY,		"UY" },
3030SN/A    { CTRY_UZBEKISTAN,		"UZ" },
3040SN/A    { CTRY_VENEZUELA,		"VE" },
3050SN/A    { CTRY_VIET_NAM,		"VN" },
3060SN/A    { CTRY_YEMEN,		"YE" },
3070SN/A    { CTRY_ZIMBABWE,		"ZW" }
3080SN/A};
3090SN/A
3100SN/Aconst char *
3110SN/Aieee80211_cctoiso(enum ISOCountryCode cc)
3120SN/A{
3130SN/A#define	N(a)	(sizeof(a) / sizeof(a[0]))
3140SN/A	int i;
3150SN/A
3160SN/A	for (i = 0; i < N(country_strings); i++) {
3170SN/A		if (country_strings[i].iso_code == cc)
3180SN/A			return country_strings[i].iso_name;
3190SN/A	}
3200SN/A	return NULL;
3210SN/A#undef N
3220SN/A}
3230SN/A
3240SN/Aint
3250SN/Aieee80211_isotocc(const char iso[2])
3260SN/A{
3270SN/A#define	N(a)	(sizeof(a) / sizeof(a[0]))
3280SN/A	int i;
3290SN/A
3300SN/A	for (i = 0; i < N(country_strings); i++) {
3310SN/A		if (country_strings[i].iso_name[0] == iso[0] &&
3320SN/A		    country_strings[i].iso_name[1] == iso[1])
3330SN/A			return country_strings[i].iso_code;
3340SN/A	}
3350SN/A	return -1;
3360SN/A#undef N
3370SN/A}
3380SN/A