xref: /freebsd/sys/net80211/ieee80211_regdomain.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /*-
2  * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 /*
30  * IEEE 802.11 regdomain support.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 
37 #include <sys/socket.h>
38 
39 #include <net/if.h>
40 #include <net/if_arp.h>
41 #include <net/if_dl.h>
42 #include <net/if_media.h>
43 #include <net/if_types.h>
44 #include <net/ethernet.h>
45 
46 #include <net80211/ieee80211_var.h>
47 #include <net80211/ieee80211_regdomain.h>
48 
49 void
50 ieee80211_regdomain_attach(struct ieee80211com *ic)
51 {
52 	ic->ic_regdomain = 0;			/* XXX */
53 	ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */
54 	ic->ic_location = 1+2;			/* both */
55 }
56 
57 void
58 ieee80211_regdomain_detach(struct ieee80211com *ic)
59 {
60 }
61 
62 static void
63 addchan(struct ieee80211com *ic, int ieee, int flags)
64 {
65 	struct ieee80211_channel *c;
66 
67 	c = &ic->ic_channels[ic->ic_nchans++];
68 	c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
69 	c->ic_ieee = ieee;
70 	c->ic_flags = flags;
71 }
72 
73 /*
74  * Setup the channel list for the specified regulatory domain,
75  * country code, and operating modes.  This interface is used
76  * when a driver does not obtain the channel list from another
77  * source (such as firmware).
78  */
79 void
80 ieee80211_init_channels(struct ieee80211com *ic,
81 	int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm)
82 {
83 	int i;
84 
85 	/* XXX just do something for now */
86 	ic->ic_nchans = 0;
87 	if (isset(&bands, IEEE80211_MODE_11B) ||
88 	    isset(&bands, IEEE80211_MODE_11G)) {
89 		for (i = 1; i <= (ecm ? 14 : 11); i++) {
90 			if (isset(&bands, IEEE80211_MODE_11B))
91 				addchan(ic, i, IEEE80211_CHAN_B);
92 			if (isset(&bands, IEEE80211_MODE_11G))
93 				addchan(ic, i, IEEE80211_CHAN_G);
94 		}
95 	}
96 	if (isset(&bands, IEEE80211_MODE_11A)) {
97 		for (i = 36; i <= 64; i += 4)
98 			addchan(ic, i, IEEE80211_CHAN_A);
99 		for (i = 100; i <= 140; i += 4)
100 			addchan(ic, i, IEEE80211_CHAN_A);
101 		for (i = 149; i <= 161; i += 4)
102 			addchan(ic, i, IEEE80211_CHAN_A);
103 	}
104 	ic->ic_regdomain = rd;
105 	ic->ic_countrycode = cc;
106 	ic->ic_location = outdoor;
107 }
108 
109 /*
110  * Add Country Information IE.
111  */
112 uint8_t *
113 ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
114 	enum ISOCountryCode cc, int location)
115 {
116 #define	CHAN_UNINTERESTING \
117     (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
118      IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)
119 	/* XXX what about auto? */
120 	/* flag set of channels to be excluded */
121 	static const int skipflags[IEEE80211_MODE_MAX] = {
122 	    CHAN_UNINTERESTING,				/* MODE_AUTO */
123 	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_11A */
124 	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11B */
125 	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11G */
126 	    CHAN_UNINTERESTING | IEEE80211_CHAN_OFDM |	/* MODE_FH */
127 	        IEEE80211_CHAN_CCK | IEEE80211_CHAN_DYN,
128 	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_TURBO_A */
129 	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_TURBO_G */
130 	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_STURBO_A */
131 	    CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ,	/* MODE_11NA */
132 	    CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ,	/* MODE_11NG */
133 	};
134 	struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm;
135 	const char *iso_name;
136 	uint8_t nextchan, chans[IEEE80211_CHAN_BYTES];
137 	int i, skip;
138 
139 	ie->ie = IEEE80211_ELEMID_COUNTRY;
140 	iso_name = ieee80211_cctoiso(cc);
141 	if (iso_name == NULL) {
142 		if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc);
143 		iso_name = "  ";
144 	}
145 	ie->cc[0] = iso_name[0];
146 	ie->cc[1] = iso_name[1];
147 	/*
148 	 * Indoor/Outdoor portion of country string.
149 	 * NB: this is not quite right, since we should have one of:
150 	 *     'I' indoor only
151 	 *     'O' outdoor only
152 	 *     ' ' all enviroments
153 	 */
154 	ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O');
155 
156 	/*
157 	 * Run-length encoded channel+max tx power info.
158 	 */
159 	frm = (uint8_t *)&ie->band[0];
160 	nextchan = 0;			/* NB: impossible channel # */
161 	memset(chans, 0, sizeof(chans));
162 	skip = skipflags[ic->ic_curmode];
163 	for (i = 0; i < ic->ic_nchans; i++) {
164 		const struct ieee80211_channel *c = &ic->ic_channels[i];
165 
166 		if (isset(chans, c->ic_ieee))		/* suppress dup's */
167 			continue;
168 		if (c->ic_flags & skip)			/* skip band, etc. */
169 			continue;
170 		setbit(chans, c->ic_ieee);
171 		if (c->ic_ieee != nextchan ||
172 		    c->ic_maxregpower != frm[-1]) {	/* new run */
173 			/* XXX max of 83 runs */
174 			frm[0] = c->ic_ieee;		/* starting channel # */
175 			frm[1] = 1;			/* # channels in run */
176 			frm[2] = c->ic_maxregpower;	/* tx power cap */
177 			frm += 3;
178 			nextchan = c->ic_ieee + 1;	/* overflow? */
179 		} else {				/* extend run */
180 			frm[-2]++;
181 			nextchan++;
182 		}
183 	}
184 	ie->len = frm - ie->cc;
185 	if (ie->len & 1) {		/* Zero pad to multiple of 2 */
186 		ie->len++;
187 		*frm++ = 0;
188 	}
189 	return frm;
190 #undef CHAN_UNINTERESTING
191 }
192 
193 /*
194  * Country Code Table for code-to-string conversion.
195  */
196 static const struct {
197 	enum ISOCountryCode iso_code;
198 	const char*	iso_name;
199 } country_strings[] = {
200     { CTRY_DEBUG,	 	"DB" },		/* NB: nonstandard */
201     { CTRY_DEFAULT,	 	"NA" },		/* NB: nonstandard */
202     { CTRY_ALBANIA,		"AL" },
203     { CTRY_ALGERIA,		"DZ" },
204     { CTRY_ARGENTINA,		"AR" },
205     { CTRY_ARMENIA,		"AM" },
206     { CTRY_AUSTRALIA,		"AU" },
207     { CTRY_AUSTRIA,		"AT" },
208     { CTRY_AZERBAIJAN,		"AZ" },
209     { CTRY_BAHRAIN,		"BH" },
210     { CTRY_BELARUS,		"BY" },
211     { CTRY_BELGIUM,		"BE" },
212     { CTRY_BELIZE,		"BZ" },
213     { CTRY_BOLIVIA,		"BO" },
214     { CTRY_BRAZIL,		"BR" },
215     { CTRY_BRUNEI_DARUSSALAM,	"BN" },
216     { CTRY_BULGARIA,		"BG" },
217     { CTRY_CANADA,		"CA" },
218     { CTRY_CHILE,		"CL" },
219     { CTRY_CHINA,		"CN" },
220     { CTRY_COLOMBIA,		"CO" },
221     { CTRY_COSTA_RICA,		"CR" },
222     { CTRY_CROATIA,		"HR" },
223     { CTRY_CYPRUS,		"CY" },
224     { CTRY_CZECH,		"CZ" },
225     { CTRY_DENMARK,		"DK" },
226     { CTRY_DOMINICAN_REPUBLIC,	"DO" },
227     { CTRY_ECUADOR,		"EC" },
228     { CTRY_EGYPT,		"EG" },
229     { CTRY_EL_SALVADOR,		"SV" },
230     { CTRY_ESTONIA,		"EE" },
231     { CTRY_FINLAND,		"FI" },
232     { CTRY_FRANCE,		"FR" },
233     { CTRY_FRANCE2,		"F2" },
234     { CTRY_GEORGIA,		"GE" },
235     { CTRY_GERMANY,		"DE" },
236     { CTRY_GREECE,		"GR" },
237     { CTRY_GUATEMALA,		"GT" },
238     { CTRY_HONDURAS,		"HN" },
239     { CTRY_HONG_KONG,		"HK" },
240     { CTRY_HUNGARY,		"HU" },
241     { CTRY_ICELAND,		"IS" },
242     { CTRY_INDIA,		"IN" },
243     { CTRY_INDONESIA,		"ID" },
244     { CTRY_IRAN,		"IR" },
245     { CTRY_IRELAND,		"IE" },
246     { CTRY_ISRAEL,		"IL" },
247     { CTRY_ITALY,		"IT" },
248     { CTRY_JAMAICA,		"JM" },
249     { CTRY_JAPAN,		"JP" },
250     { CTRY_JAPAN1,		"J1" },
251     { CTRY_JAPAN2,		"J2" },
252     { CTRY_JAPAN3,		"J3" },
253     { CTRY_JAPAN4,		"J4" },
254     { CTRY_JAPAN5,		"J5" },
255     { CTRY_JORDAN,		"JO" },
256     { CTRY_KAZAKHSTAN,		"KZ" },
257     { CTRY_KOREA_NORTH,		"KP" },
258     { CTRY_KOREA_ROC,		"KR" },
259     { CTRY_KOREA_ROC2,		"K2" },
260     { CTRY_KUWAIT,		"KW" },
261     { CTRY_LATVIA,		"LV" },
262     { CTRY_LEBANON,		"LB" },
263     { CTRY_LIECHTENSTEIN,	"LI" },
264     { CTRY_LITHUANIA,		"LT" },
265     { CTRY_LUXEMBOURG,		"LU" },
266     { CTRY_MACAU,		"MO" },
267     { CTRY_MACEDONIA,		"MK" },
268     { CTRY_MALAYSIA,		"MY" },
269     { CTRY_MEXICO,		"MX" },
270     { CTRY_MONACO,		"MC" },
271     { CTRY_MOROCCO,		"MA" },
272     { CTRY_NETHERLANDS,		"NL" },
273     { CTRY_NEW_ZEALAND,		"NZ" },
274     { CTRY_NORWAY,		"NO" },
275     { CTRY_OMAN,		"OM" },
276     { CTRY_PAKISTAN,		"PK" },
277     { CTRY_PANAMA,		"PA" },
278     { CTRY_PERU,		"PE" },
279     { CTRY_PHILIPPINES,		"PH" },
280     { CTRY_POLAND,		"PL" },
281     { CTRY_PORTUGAL,		"PT" },
282     { CTRY_PUERTO_RICO,		"PR" },
283     { CTRY_QATAR,		"QA" },
284     { CTRY_ROMANIA,		"RO" },
285     { CTRY_RUSSIA,		"RU" },
286     { CTRY_SAUDI_ARABIA,	"SA" },
287     { CTRY_SINGAPORE,		"SG" },
288     { CTRY_SLOVAKIA,		"SK" },
289     { CTRY_SLOVENIA,		"SI" },
290     { CTRY_SOUTH_AFRICA,	"ZA" },
291     { CTRY_SPAIN,		"ES" },
292     { CTRY_SWEDEN,		"SE" },
293     { CTRY_SWITZERLAND,		"CH" },
294     { CTRY_SYRIA,		"SY" },
295     { CTRY_TAIWAN,		"TW" },
296     { CTRY_THAILAND,		"TH" },
297     { CTRY_TRINIDAD_Y_TOBAGO,	"TT" },
298     { CTRY_TUNISIA,		"TN" },
299     { CTRY_TURKEY,		"TR" },
300     { CTRY_UKRAINE,		"UA" },
301     { CTRY_UAE,			"AE" },
302     { CTRY_UNITED_KINGDOM,	"GB" },
303     { CTRY_UNITED_STATES,	"US" },
304     { CTRY_URUGUAY,		"UY" },
305     { CTRY_UZBEKISTAN,		"UZ" },
306     { CTRY_VENEZUELA,		"VE" },
307     { CTRY_VIET_NAM,		"VN" },
308     { CTRY_YEMEN,		"YE" },
309     { CTRY_ZIMBABWE,		"ZW" }
310 };
311 
312 const char *
313 ieee80211_cctoiso(enum ISOCountryCode cc)
314 {
315 #define	N(a)	(sizeof(a) / sizeof(a[0]))
316 	int i;
317 
318 	for (i = 0; i < N(country_strings); i++) {
319 		if (country_strings[i].iso_code == cc)
320 			return country_strings[i].iso_name;
321 	}
322 	return NULL;
323 #undef N
324 }
325 
326 int
327 ieee80211_isotocc(const char iso[2])
328 {
329 #define	N(a)	(sizeof(a) / sizeof(a[0]))
330 	int i;
331 
332 	for (i = 0; i < N(country_strings); i++) {
333 		if (country_strings[i].iso_name[0] == iso[0] &&
334 		    country_strings[i].iso_name[1] == iso[1])
335 			return country_strings[i].iso_code;
336 	}
337 	return -1;
338 #undef N
339 }
340