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