18318d78aSJohannes Berg /* 28318d78aSJohannes Berg * Copyright 2002-2005, Instant802 Networks, Inc. 38318d78aSJohannes Berg * Copyright 2005-2006, Devicescape Software, Inc. 48318d78aSJohannes Berg * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 5b2e1b302SLuis R. Rodriguez * Copyright 2008 Luis R. Rodriguez <lrodriguz@atheros.com> 68318d78aSJohannes Berg * 78318d78aSJohannes Berg * This program is free software; you can redistribute it and/or modify 88318d78aSJohannes Berg * it under the terms of the GNU General Public License version 2 as 98318d78aSJohannes Berg * published by the Free Software Foundation. 108318d78aSJohannes Berg */ 118318d78aSJohannes Berg 12b2e1b302SLuis R. Rodriguez /** 13b2e1b302SLuis R. Rodriguez * DOC: Wireless regulatory infrastructure 148318d78aSJohannes Berg * 158318d78aSJohannes Berg * The usual implementation is for a driver to read a device EEPROM to 168318d78aSJohannes Berg * determine which regulatory domain it should be operating under, then 178318d78aSJohannes Berg * looking up the allowable channels in a driver-local table and finally 188318d78aSJohannes Berg * registering those channels in the wiphy structure. 198318d78aSJohannes Berg * 20b2e1b302SLuis R. Rodriguez * Another set of compliance enforcement is for drivers to use their 21b2e1b302SLuis R. Rodriguez * own compliance limits which can be stored on the EEPROM. The host 22b2e1b302SLuis R. Rodriguez * driver or firmware may ensure these are used. 23b2e1b302SLuis R. Rodriguez * 24b2e1b302SLuis R. Rodriguez * In addition to all this we provide an extra layer of regulatory 25b2e1b302SLuis R. Rodriguez * conformance. For drivers which do not have any regulatory 26b2e1b302SLuis R. Rodriguez * information CRDA provides the complete regulatory solution. 27b2e1b302SLuis R. Rodriguez * For others it provides a community effort on further restrictions 28b2e1b302SLuis R. Rodriguez * to enhance compliance. 29b2e1b302SLuis R. Rodriguez * 30b2e1b302SLuis R. Rodriguez * Note: When number of rules --> infinity we will not be able to 31b2e1b302SLuis R. Rodriguez * index on alpha2 any more, instead we'll probably have to 32b2e1b302SLuis R. Rodriguez * rely on some SHA1 checksum of the regdomain for example. 33b2e1b302SLuis R. Rodriguez * 348318d78aSJohannes Berg */ 358318d78aSJohannes Berg #include <linux/kernel.h> 36b2e1b302SLuis R. Rodriguez #include <linux/list.h> 37b2e1b302SLuis R. Rodriguez #include <linux/random.h> 38b2e1b302SLuis R. Rodriguez #include <linux/nl80211.h> 39b2e1b302SLuis R. Rodriguez #include <linux/platform_device.h> 408318d78aSJohannes Berg #include <net/wireless.h> 41b2e1b302SLuis R. Rodriguez #include <net/cfg80211.h> 428318d78aSJohannes Berg #include "core.h" 43b2e1b302SLuis R. Rodriguez #include "reg.h" 448318d78aSJohannes Berg 45be3d4810SJohannes Berg /* 46be3d4810SJohannes Berg * wiphy is set if this request's initiator is 47be3d4810SJohannes Berg * REGDOM_SET_BY_COUNTRY_IE or _DRIVER 48be3d4810SJohannes Berg */ 49734366deSJohannes Berg struct regulatory_request { 50734366deSJohannes Berg struct wiphy *wiphy; 51734366deSJohannes Berg enum reg_set_by initiator; 52734366deSJohannes Berg char alpha2[2]; 53734366deSJohannes Berg }; 54734366deSJohannes Berg 55f6037d09SJohannes Berg static struct regulatory_request *last_request; 56734366deSJohannes Berg 57b2e1b302SLuis R. Rodriguez /* To trigger userspace events */ 58b2e1b302SLuis R. Rodriguez static struct platform_device *reg_pdev; 598318d78aSJohannes Berg 60b2e1b302SLuis R. Rodriguez /* Keep the ordering from large to small */ 61b2e1b302SLuis R. Rodriguez static u32 supported_bandwidths[] = { 62b2e1b302SLuis R. Rodriguez MHZ_TO_KHZ(40), 63b2e1b302SLuis R. Rodriguez MHZ_TO_KHZ(20), 648318d78aSJohannes Berg }; 658318d78aSJohannes Berg 66734366deSJohannes Berg /* Central wireless core regulatory domains, we only need two, 67734366deSJohannes Berg * the current one and a world regulatory domain in case we have no 68734366deSJohannes Berg * information to give us an alpha2 */ 69a3d2eaf0SJohannes Berg static const struct ieee80211_regdomain *cfg80211_regdomain; 70734366deSJohannes Berg 71734366deSJohannes Berg /* We keep a static world regulatory domain in case of the absence of CRDA */ 72734366deSJohannes Berg static const struct ieee80211_regdomain world_regdom = { 73734366deSJohannes Berg .n_reg_rules = 1, 74734366deSJohannes Berg .alpha2 = "00", 75734366deSJohannes Berg .reg_rules = { 76734366deSJohannes Berg REG_RULE(2412-10, 2462+10, 40, 6, 20, 77734366deSJohannes Berg NL80211_RRF_PASSIVE_SCAN | 78734366deSJohannes Berg NL80211_RRF_NO_IBSS), 79734366deSJohannes Berg } 80734366deSJohannes Berg }; 81734366deSJohannes Berg 82a3d2eaf0SJohannes Berg static const struct ieee80211_regdomain *cfg80211_world_regdom = 83a3d2eaf0SJohannes Berg &world_regdom; 84734366deSJohannes Berg 85734366deSJohannes Berg #ifdef CONFIG_WIRELESS_OLD_REGULATORY 86734366deSJohannes Berg static char *ieee80211_regdom = "US"; 87734366deSJohannes Berg module_param(ieee80211_regdom, charp, 0444); 88734366deSJohannes Berg MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); 89734366deSJohannes Berg 90734366deSJohannes Berg /* We assume 40 MHz bandwidth for the old regulatory work. 91734366deSJohannes Berg * We make emphasis we are using the exact same frequencies 92734366deSJohannes Berg * as before */ 93734366deSJohannes Berg 94734366deSJohannes Berg static const struct ieee80211_regdomain us_regdom = { 95734366deSJohannes Berg .n_reg_rules = 6, 96734366deSJohannes Berg .alpha2 = "US", 97734366deSJohannes Berg .reg_rules = { 98734366deSJohannes Berg /* IEEE 802.11b/g, channels 1..11 */ 99734366deSJohannes Berg REG_RULE(2412-10, 2462+10, 40, 6, 27, 0), 100734366deSJohannes Berg /* IEEE 802.11a, channel 36 */ 101734366deSJohannes Berg REG_RULE(5180-10, 5180+10, 40, 6, 23, 0), 102734366deSJohannes Berg /* IEEE 802.11a, channel 40 */ 103734366deSJohannes Berg REG_RULE(5200-10, 5200+10, 40, 6, 23, 0), 104734366deSJohannes Berg /* IEEE 802.11a, channel 44 */ 105734366deSJohannes Berg REG_RULE(5220-10, 5220+10, 40, 6, 23, 0), 106734366deSJohannes Berg /* IEEE 802.11a, channels 48..64 */ 107734366deSJohannes Berg REG_RULE(5240-10, 5320+10, 40, 6, 23, 0), 108734366deSJohannes Berg /* IEEE 802.11a, channels 149..165, outdoor */ 109734366deSJohannes Berg REG_RULE(5745-10, 5825+10, 40, 6, 30, 0), 110734366deSJohannes Berg } 111734366deSJohannes Berg }; 112734366deSJohannes Berg 113734366deSJohannes Berg static const struct ieee80211_regdomain jp_regdom = { 114734366deSJohannes Berg .n_reg_rules = 3, 115734366deSJohannes Berg .alpha2 = "JP", 116734366deSJohannes Berg .reg_rules = { 117734366deSJohannes Berg /* IEEE 802.11b/g, channels 1..14 */ 118734366deSJohannes Berg REG_RULE(2412-10, 2484+10, 40, 6, 20, 0), 119734366deSJohannes Berg /* IEEE 802.11a, channels 34..48 */ 120734366deSJohannes Berg REG_RULE(5170-10, 5240+10, 40, 6, 20, 121734366deSJohannes Berg NL80211_RRF_PASSIVE_SCAN), 122734366deSJohannes Berg /* IEEE 802.11a, channels 52..64 */ 123734366deSJohannes Berg REG_RULE(5260-10, 5320+10, 40, 6, 20, 124734366deSJohannes Berg NL80211_RRF_NO_IBSS | 125734366deSJohannes Berg NL80211_RRF_DFS), 126734366deSJohannes Berg } 127734366deSJohannes Berg }; 128734366deSJohannes Berg 129734366deSJohannes Berg static const struct ieee80211_regdomain eu_regdom = { 130734366deSJohannes Berg .n_reg_rules = 6, 131734366deSJohannes Berg /* This alpha2 is bogus, we leave it here just for stupid 132734366deSJohannes Berg * backward compatibility */ 133734366deSJohannes Berg .alpha2 = "EU", 134734366deSJohannes Berg .reg_rules = { 135734366deSJohannes Berg /* IEEE 802.11b/g, channels 1..13 */ 136734366deSJohannes Berg REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), 137734366deSJohannes Berg /* IEEE 802.11a, channel 36 */ 138734366deSJohannes Berg REG_RULE(5180-10, 5180+10, 40, 6, 23, 139734366deSJohannes Berg NL80211_RRF_PASSIVE_SCAN), 140734366deSJohannes Berg /* IEEE 802.11a, channel 40 */ 141734366deSJohannes Berg REG_RULE(5200-10, 5200+10, 40, 6, 23, 142734366deSJohannes Berg NL80211_RRF_PASSIVE_SCAN), 143734366deSJohannes Berg /* IEEE 802.11a, channel 44 */ 144734366deSJohannes Berg REG_RULE(5220-10, 5220+10, 40, 6, 23, 145734366deSJohannes Berg NL80211_RRF_PASSIVE_SCAN), 146734366deSJohannes Berg /* IEEE 802.11a, channels 48..64 */ 147734366deSJohannes Berg REG_RULE(5240-10, 5320+10, 40, 6, 20, 148734366deSJohannes Berg NL80211_RRF_NO_IBSS | 149734366deSJohannes Berg NL80211_RRF_DFS), 150734366deSJohannes Berg /* IEEE 802.11a, channels 100..140 */ 151734366deSJohannes Berg REG_RULE(5500-10, 5700+10, 40, 6, 30, 152734366deSJohannes Berg NL80211_RRF_NO_IBSS | 153734366deSJohannes Berg NL80211_RRF_DFS), 154734366deSJohannes Berg } 155734366deSJohannes Berg }; 156734366deSJohannes Berg 157734366deSJohannes Berg static const struct ieee80211_regdomain *static_regdom(char *alpha2) 158734366deSJohannes Berg { 159734366deSJohannes Berg if (alpha2[0] == 'U' && alpha2[1] == 'S') 160734366deSJohannes Berg return &us_regdom; 161734366deSJohannes Berg if (alpha2[0] == 'J' && alpha2[1] == 'P') 162734366deSJohannes Berg return &jp_regdom; 163734366deSJohannes Berg if (alpha2[0] == 'E' && alpha2[1] == 'U') 164734366deSJohannes Berg return &eu_regdom; 165734366deSJohannes Berg /* Default, as per the old rules */ 166734366deSJohannes Berg return &us_regdom; 167734366deSJohannes Berg } 168734366deSJohannes Berg 169a3d2eaf0SJohannes Berg static bool is_old_static_regdom(const struct ieee80211_regdomain *rd) 170734366deSJohannes Berg { 171734366deSJohannes Berg if (rd == &us_regdom || rd == &jp_regdom || rd == &eu_regdom) 172734366deSJohannes Berg return true; 173734366deSJohannes Berg return false; 174734366deSJohannes Berg } 175734366deSJohannes Berg #else 176942b25cfSJohannes Berg static inline bool is_old_static_regdom(const struct ieee80211_regdomain *rd) 177942b25cfSJohannes Berg { 178942b25cfSJohannes Berg return false; 179942b25cfSJohannes Berg } 180942b25cfSJohannes Berg #endif 181942b25cfSJohannes Berg 182734366deSJohannes Berg static void reset_regdomains(void) 183734366deSJohannes Berg { 184942b25cfSJohannes Berg /* avoid freeing static information or freeing something twice */ 185942b25cfSJohannes Berg if (cfg80211_regdomain == cfg80211_world_regdom) 186942b25cfSJohannes Berg cfg80211_regdomain = NULL; 187942b25cfSJohannes Berg if (cfg80211_world_regdom == &world_regdom) 188942b25cfSJohannes Berg cfg80211_world_regdom = NULL; 189942b25cfSJohannes Berg if (cfg80211_regdomain == &world_regdom) 190942b25cfSJohannes Berg cfg80211_regdomain = NULL; 191942b25cfSJohannes Berg if (is_old_static_regdom(cfg80211_regdomain)) 192942b25cfSJohannes Berg cfg80211_regdomain = NULL; 193942b25cfSJohannes Berg 194734366deSJohannes Berg kfree(cfg80211_regdomain); 195734366deSJohannes Berg kfree(cfg80211_world_regdom); 196734366deSJohannes Berg 197a3d2eaf0SJohannes Berg cfg80211_world_regdom = &world_regdom; 198734366deSJohannes Berg cfg80211_regdomain = NULL; 199734366deSJohannes Berg } 200734366deSJohannes Berg 201734366deSJohannes Berg /* Dynamic world regulatory domain requested by the wireless 202734366deSJohannes Berg * core upon initialization */ 203a3d2eaf0SJohannes Berg static void update_world_regdomain(const struct ieee80211_regdomain *rd) 204734366deSJohannes Berg { 205f6037d09SJohannes Berg BUG_ON(!last_request); 206734366deSJohannes Berg 207734366deSJohannes Berg reset_regdomains(); 208734366deSJohannes Berg 209734366deSJohannes Berg cfg80211_world_regdom = rd; 210734366deSJohannes Berg cfg80211_regdomain = rd; 211734366deSJohannes Berg } 212734366deSJohannes Berg 213a3d2eaf0SJohannes Berg bool is_world_regdom(const char *alpha2) 214b2e1b302SLuis R. Rodriguez { 215b2e1b302SLuis R. Rodriguez if (!alpha2) 216b2e1b302SLuis R. Rodriguez return false; 217b2e1b302SLuis R. Rodriguez if (alpha2[0] == '0' && alpha2[1] == '0') 218b2e1b302SLuis R. Rodriguez return true; 219b2e1b302SLuis R. Rodriguez return false; 220b2e1b302SLuis R. Rodriguez } 221b2e1b302SLuis R. Rodriguez 222a3d2eaf0SJohannes Berg static bool is_alpha2_set(const char *alpha2) 223b2e1b302SLuis R. Rodriguez { 224b2e1b302SLuis R. Rodriguez if (!alpha2) 225b2e1b302SLuis R. Rodriguez return false; 226b2e1b302SLuis R. Rodriguez if (alpha2[0] != 0 && alpha2[1] != 0) 227b2e1b302SLuis R. Rodriguez return true; 228b2e1b302SLuis R. Rodriguez return false; 229b2e1b302SLuis R. Rodriguez } 230b2e1b302SLuis R. Rodriguez 231b2e1b302SLuis R. Rodriguez static bool is_alpha_upper(char letter) 232b2e1b302SLuis R. Rodriguez { 233b2e1b302SLuis R. Rodriguez /* ASCII A - Z */ 234b2e1b302SLuis R. Rodriguez if (letter >= 65 && letter <= 90) 235b2e1b302SLuis R. Rodriguez return true; 236b2e1b302SLuis R. Rodriguez return false; 237b2e1b302SLuis R. Rodriguez } 238b2e1b302SLuis R. Rodriguez 239a3d2eaf0SJohannes Berg static bool is_unknown_alpha2(const char *alpha2) 240b2e1b302SLuis R. Rodriguez { 241b2e1b302SLuis R. Rodriguez if (!alpha2) 242b2e1b302SLuis R. Rodriguez return false; 243b2e1b302SLuis R. Rodriguez /* Special case where regulatory domain was built by driver 244b2e1b302SLuis R. Rodriguez * but a specific alpha2 cannot be determined */ 245b2e1b302SLuis R. Rodriguez if (alpha2[0] == '9' && alpha2[1] == '9') 246b2e1b302SLuis R. Rodriguez return true; 247b2e1b302SLuis R. Rodriguez return false; 248b2e1b302SLuis R. Rodriguez } 249b2e1b302SLuis R. Rodriguez 250a3d2eaf0SJohannes Berg static bool is_an_alpha2(const char *alpha2) 251b2e1b302SLuis R. Rodriguez { 252b2e1b302SLuis R. Rodriguez if (!alpha2) 253b2e1b302SLuis R. Rodriguez return false; 254b2e1b302SLuis R. Rodriguez if (is_alpha_upper(alpha2[0]) && is_alpha_upper(alpha2[1])) 255b2e1b302SLuis R. Rodriguez return true; 256b2e1b302SLuis R. Rodriguez return false; 257b2e1b302SLuis R. Rodriguez } 258b2e1b302SLuis R. Rodriguez 259a3d2eaf0SJohannes Berg static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) 260b2e1b302SLuis R. Rodriguez { 261b2e1b302SLuis R. Rodriguez if (!alpha2_x || !alpha2_y) 262b2e1b302SLuis R. Rodriguez return false; 263b2e1b302SLuis R. Rodriguez if (alpha2_x[0] == alpha2_y[0] && 264b2e1b302SLuis R. Rodriguez alpha2_x[1] == alpha2_y[1]) 265b2e1b302SLuis R. Rodriguez return true; 266b2e1b302SLuis R. Rodriguez return false; 267b2e1b302SLuis R. Rodriguez } 268b2e1b302SLuis R. Rodriguez 269a3d2eaf0SJohannes Berg static bool regdom_changed(const char *alpha2) 270b2e1b302SLuis R. Rodriguez { 271b2e1b302SLuis R. Rodriguez if (!cfg80211_regdomain) 272b2e1b302SLuis R. Rodriguez return true; 273b2e1b302SLuis R. Rodriguez if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) 274b2e1b302SLuis R. Rodriguez return false; 275b2e1b302SLuis R. Rodriguez return true; 276b2e1b302SLuis R. Rodriguez } 277b2e1b302SLuis R. Rodriguez 278b2e1b302SLuis R. Rodriguez /* This lets us keep regulatory code which is updated on a regulatory 279b2e1b302SLuis R. Rodriguez * basis in userspace. */ 280b2e1b302SLuis R. Rodriguez static int call_crda(const char *alpha2) 281b2e1b302SLuis R. Rodriguez { 282b2e1b302SLuis R. Rodriguez char country_env[9 + 2] = "COUNTRY="; 283b2e1b302SLuis R. Rodriguez char *envp[] = { 284b2e1b302SLuis R. Rodriguez country_env, 285b2e1b302SLuis R. Rodriguez NULL 2868318d78aSJohannes Berg }; 2878318d78aSJohannes Berg 288b2e1b302SLuis R. Rodriguez if (!is_world_regdom((char *) alpha2)) 289b2e1b302SLuis R. Rodriguez printk(KERN_INFO "cfg80211: Calling CRDA for country: %c%c\n", 290b2e1b302SLuis R. Rodriguez alpha2[0], alpha2[1]); 291b2e1b302SLuis R. Rodriguez else 292b2e1b302SLuis R. Rodriguez printk(KERN_INFO "cfg80211: Calling CRDA to update world " 293b2e1b302SLuis R. Rodriguez "regulatory domain\n"); 2948318d78aSJohannes Berg 295b2e1b302SLuis R. Rodriguez country_env[8] = alpha2[0]; 296b2e1b302SLuis R. Rodriguez country_env[9] = alpha2[1]; 2978318d78aSJohannes Berg 298b2e1b302SLuis R. Rodriguez return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); 299b2e1b302SLuis R. Rodriguez } 300b2e1b302SLuis R. Rodriguez 301b2e1b302SLuis R. Rodriguez /* Used by nl80211 before kmalloc'ing our regulatory domain */ 302a3d2eaf0SJohannes Berg bool reg_is_valid_request(const char *alpha2) 303b2e1b302SLuis R. Rodriguez { 304f6037d09SJohannes Berg if (!last_request) 305f6037d09SJohannes Berg return false; 306f6037d09SJohannes Berg 307f6037d09SJohannes Berg return alpha2_equal(last_request->alpha2, alpha2); 308b2e1b302SLuis R. Rodriguez } 309b2e1b302SLuis R. Rodriguez 310b2e1b302SLuis R. Rodriguez /* Sanity check on a regulatory rule */ 311a3d2eaf0SJohannes Berg static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) 312b2e1b302SLuis R. Rodriguez { 313a3d2eaf0SJohannes Berg const struct ieee80211_freq_range *freq_range = &rule->freq_range; 314b2e1b302SLuis R. Rodriguez u32 freq_diff; 315b2e1b302SLuis R. Rodriguez 316b2e1b302SLuis R. Rodriguez if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0) 317b2e1b302SLuis R. Rodriguez return false; 318b2e1b302SLuis R. Rodriguez 319b2e1b302SLuis R. Rodriguez if (freq_range->start_freq_khz > freq_range->end_freq_khz) 320b2e1b302SLuis R. Rodriguez return false; 321b2e1b302SLuis R. Rodriguez 322b2e1b302SLuis R. Rodriguez freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; 323b2e1b302SLuis R. Rodriguez 324d71aaf60SLuis R. Rodriguez if (freq_diff <= 0 || freq_range->max_bandwidth_khz > freq_diff) 325b2e1b302SLuis R. Rodriguez return false; 326b2e1b302SLuis R. Rodriguez 327b2e1b302SLuis R. Rodriguez return true; 328b2e1b302SLuis R. Rodriguez } 329b2e1b302SLuis R. Rodriguez 330a3d2eaf0SJohannes Berg static bool is_valid_rd(const struct ieee80211_regdomain *rd) 331b2e1b302SLuis R. Rodriguez { 332a3d2eaf0SJohannes Berg const struct ieee80211_reg_rule *reg_rule = NULL; 333b2e1b302SLuis R. Rodriguez unsigned int i; 334b2e1b302SLuis R. Rodriguez 335b2e1b302SLuis R. Rodriguez if (!rd->n_reg_rules) 336b2e1b302SLuis R. Rodriguez return false; 337b2e1b302SLuis R. Rodriguez 338b2e1b302SLuis R. Rodriguez for (i = 0; i < rd->n_reg_rules; i++) { 339b2e1b302SLuis R. Rodriguez reg_rule = &rd->reg_rules[i]; 340b2e1b302SLuis R. Rodriguez if (!is_valid_reg_rule(reg_rule)) 341b2e1b302SLuis R. Rodriguez return false; 342b2e1b302SLuis R. Rodriguez } 343b2e1b302SLuis R. Rodriguez 344b2e1b302SLuis R. Rodriguez return true; 345b2e1b302SLuis R. Rodriguez } 346b2e1b302SLuis R. Rodriguez 347b2e1b302SLuis R. Rodriguez /* Returns value in KHz */ 348b2e1b302SLuis R. Rodriguez static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, 349b2e1b302SLuis R. Rodriguez u32 freq) 350b2e1b302SLuis R. Rodriguez { 351b2e1b302SLuis R. Rodriguez unsigned int i; 352b2e1b302SLuis R. Rodriguez for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) { 353b2e1b302SLuis R. Rodriguez u32 start_freq_khz = freq - supported_bandwidths[i]/2; 354b2e1b302SLuis R. Rodriguez u32 end_freq_khz = freq + supported_bandwidths[i]/2; 355b2e1b302SLuis R. Rodriguez if (start_freq_khz >= freq_range->start_freq_khz && 356b2e1b302SLuis R. Rodriguez end_freq_khz <= freq_range->end_freq_khz) 357b2e1b302SLuis R. Rodriguez return supported_bandwidths[i]; 358b2e1b302SLuis R. Rodriguez } 359b2e1b302SLuis R. Rodriguez return 0; 360b2e1b302SLuis R. Rodriguez } 361b2e1b302SLuis R. Rodriguez 362b2e1b302SLuis R. Rodriguez /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may 363b2e1b302SLuis R. Rodriguez * want to just have the channel structure use these */ 364b2e1b302SLuis R. Rodriguez static u32 map_regdom_flags(u32 rd_flags) 365b2e1b302SLuis R. Rodriguez { 366b2e1b302SLuis R. Rodriguez u32 channel_flags = 0; 367b2e1b302SLuis R. Rodriguez if (rd_flags & NL80211_RRF_PASSIVE_SCAN) 368b2e1b302SLuis R. Rodriguez channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; 369b2e1b302SLuis R. Rodriguez if (rd_flags & NL80211_RRF_NO_IBSS) 370b2e1b302SLuis R. Rodriguez channel_flags |= IEEE80211_CHAN_NO_IBSS; 371b2e1b302SLuis R. Rodriguez if (rd_flags & NL80211_RRF_DFS) 372b2e1b302SLuis R. Rodriguez channel_flags |= IEEE80211_CHAN_RADAR; 373b2e1b302SLuis R. Rodriguez return channel_flags; 374b2e1b302SLuis R. Rodriguez } 375b2e1b302SLuis R. Rodriguez 376b2e1b302SLuis R. Rodriguez /** 377b2e1b302SLuis R. Rodriguez * freq_reg_info - get regulatory information for the given frequency 378b2e1b302SLuis R. Rodriguez * @center_freq: Frequency in KHz for which we want regulatory information for 379b2e1b302SLuis R. Rodriguez * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one 380b2e1b302SLuis R. Rodriguez * you can set this to 0. If this frequency is allowed we then set 381b2e1b302SLuis R. Rodriguez * this value to the maximum allowed bandwidth. 382b2e1b302SLuis R. Rodriguez * @reg_rule: the regulatory rule which we have for this frequency 383b2e1b302SLuis R. Rodriguez * 384b2e1b302SLuis R. Rodriguez * Use this function to get the regulatory rule for a specific frequency. 3858318d78aSJohannes Berg */ 386b2e1b302SLuis R. Rodriguez static int freq_reg_info(u32 center_freq, u32 *bandwidth, 387b2e1b302SLuis R. Rodriguez const struct ieee80211_reg_rule **reg_rule) 3888318d78aSJohannes Berg { 3898318d78aSJohannes Berg int i; 390b2e1b302SLuis R. Rodriguez u32 max_bandwidth = 0; 3918318d78aSJohannes Berg 392b2e1b302SLuis R. Rodriguez if (!cfg80211_regdomain) 393b2e1b302SLuis R. Rodriguez return -EINVAL; 394b2e1b302SLuis R. Rodriguez 395b2e1b302SLuis R. Rodriguez for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { 396b2e1b302SLuis R. Rodriguez const struct ieee80211_reg_rule *rr; 397b2e1b302SLuis R. Rodriguez const struct ieee80211_freq_range *fr = NULL; 398b2e1b302SLuis R. Rodriguez const struct ieee80211_power_rule *pr = NULL; 399b2e1b302SLuis R. Rodriguez 400b2e1b302SLuis R. Rodriguez rr = &cfg80211_regdomain->reg_rules[i]; 401b2e1b302SLuis R. Rodriguez fr = &rr->freq_range; 402b2e1b302SLuis R. Rodriguez pr = &rr->power_rule; 403b2e1b302SLuis R. Rodriguez max_bandwidth = freq_max_bandwidth(fr, center_freq); 404b2e1b302SLuis R. Rodriguez if (max_bandwidth && *bandwidth <= max_bandwidth) { 405b2e1b302SLuis R. Rodriguez *reg_rule = rr; 406b2e1b302SLuis R. Rodriguez *bandwidth = max_bandwidth; 4078318d78aSJohannes Berg break; 4088318d78aSJohannes Berg } 4098318d78aSJohannes Berg } 4108318d78aSJohannes Berg 411b2e1b302SLuis R. Rodriguez return !max_bandwidth; 412b2e1b302SLuis R. Rodriguez } 413b2e1b302SLuis R. Rodriguez 414b2e1b302SLuis R. Rodriguez static void handle_channel(struct ieee80211_channel *chan) 415b2e1b302SLuis R. Rodriguez { 416b2e1b302SLuis R. Rodriguez int r; 417b2e1b302SLuis R. Rodriguez u32 flags = chan->orig_flags; 418b2e1b302SLuis R. Rodriguez u32 max_bandwidth = 0; 419b2e1b302SLuis R. Rodriguez const struct ieee80211_reg_rule *reg_rule = NULL; 420b2e1b302SLuis R. Rodriguez const struct ieee80211_power_rule *power_rule = NULL; 421b2e1b302SLuis R. Rodriguez 422b2e1b302SLuis R. Rodriguez r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq), 423b2e1b302SLuis R. Rodriguez &max_bandwidth, ®_rule); 424b2e1b302SLuis R. Rodriguez 425b2e1b302SLuis R. Rodriguez if (r) { 4268318d78aSJohannes Berg flags |= IEEE80211_CHAN_DISABLED; 4278318d78aSJohannes Berg chan->flags = flags; 4288318d78aSJohannes Berg return; 4298318d78aSJohannes Berg } 4308318d78aSJohannes Berg 431b2e1b302SLuis R. Rodriguez power_rule = ®_rule->power_rule; 432b2e1b302SLuis R. Rodriguez 433b2e1b302SLuis R. Rodriguez chan->flags = flags | map_regdom_flags(reg_rule->flags); 4348318d78aSJohannes Berg chan->max_antenna_gain = min(chan->orig_mag, 435b2e1b302SLuis R. Rodriguez (int) MBI_TO_DBI(power_rule->max_antenna_gain)); 436b2e1b302SLuis R. Rodriguez chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); 437253898c4SJohn W. Linville if (chan->orig_mpwr) 438b2e1b302SLuis R. Rodriguez chan->max_power = min(chan->orig_mpwr, 439b2e1b302SLuis R. Rodriguez (int) MBM_TO_DBM(power_rule->max_eirp)); 440253898c4SJohn W. Linville else 441b2e1b302SLuis R. Rodriguez chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); 4428318d78aSJohannes Berg } 4438318d78aSJohannes Berg 444b2e1b302SLuis R. Rodriguez static void handle_band(struct ieee80211_supported_band *sband) 4458318d78aSJohannes Berg { 4468318d78aSJohannes Berg int i; 4478318d78aSJohannes Berg 4488318d78aSJohannes Berg for (i = 0; i < sband->n_channels; i++) 449b2e1b302SLuis R. Rodriguez handle_channel(&sband->channels[i]); 4508318d78aSJohannes Berg } 4518318d78aSJohannes Berg 452b2e1b302SLuis R. Rodriguez static void update_all_wiphy_regulatory(enum reg_set_by setby) 453b2e1b302SLuis R. Rodriguez { 454b2e1b302SLuis R. Rodriguez struct cfg80211_registered_device *drv; 455b2e1b302SLuis R. Rodriguez 456b2e1b302SLuis R. Rodriguez list_for_each_entry(drv, &cfg80211_drv_list, list) 457b2e1b302SLuis R. Rodriguez wiphy_update_regulatory(&drv->wiphy, setby); 458b2e1b302SLuis R. Rodriguez } 459b2e1b302SLuis R. Rodriguez 460b2e1b302SLuis R. Rodriguez void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) 4618318d78aSJohannes Berg { 4628318d78aSJohannes Berg enum ieee80211_band band; 463b2e1b302SLuis R. Rodriguez for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 4648318d78aSJohannes Berg if (wiphy->bands[band]) 465b2e1b302SLuis R. Rodriguez handle_band(wiphy->bands[band]); 466b2e1b302SLuis R. Rodriguez if (wiphy->reg_notifier) 467b2e1b302SLuis R. Rodriguez wiphy->reg_notifier(wiphy, setby); 468b2e1b302SLuis R. Rodriguez } 469b2e1b302SLuis R. Rodriguez } 470b2e1b302SLuis R. Rodriguez 47184fa4f43SJohannes Berg /* This has the logic which determines when a new request 47284fa4f43SJohannes Berg * should be ignored. */ 47384fa4f43SJohannes Berg static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, 47484fa4f43SJohannes Berg const char *alpha2) 47584fa4f43SJohannes Berg { 47684fa4f43SJohannes Berg /* All initial requests are respected */ 47784fa4f43SJohannes Berg if (!last_request) 47884fa4f43SJohannes Berg return 0; 47984fa4f43SJohannes Berg 48084fa4f43SJohannes Berg switch (set_by) { 48184fa4f43SJohannes Berg case REGDOM_SET_BY_INIT: 48284fa4f43SJohannes Berg return -EINVAL; 48384fa4f43SJohannes Berg case REGDOM_SET_BY_CORE: 48484fa4f43SJohannes Berg /* 48584fa4f43SJohannes Berg * Always respect new wireless core hints, should only happen 48684fa4f43SJohannes Berg * when updating the world regulatory domain at init. 48784fa4f43SJohannes Berg */ 48884fa4f43SJohannes Berg return 0; 48984fa4f43SJohannes Berg case REGDOM_SET_BY_COUNTRY_IE: 49084fa4f43SJohannes Berg if (unlikely(!is_an_alpha2(alpha2))) 49184fa4f43SJohannes Berg return -EINVAL; 49284fa4f43SJohannes Berg if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { 49384fa4f43SJohannes Berg if (last_request->wiphy != wiphy) { 49484fa4f43SJohannes Berg /* 49584fa4f43SJohannes Berg * Two cards with two APs claiming different 49684fa4f43SJohannes Berg * different Country IE alpha2s. We could 49784fa4f43SJohannes Berg * intersect them, but that seems unlikely 49884fa4f43SJohannes Berg * to be correct. Reject second one for now. 49984fa4f43SJohannes Berg */ 50084fa4f43SJohannes Berg if (!alpha2_equal(alpha2, 50184fa4f43SJohannes Berg cfg80211_regdomain->alpha2)) 50284fa4f43SJohannes Berg return -EOPNOTSUPP; 50384fa4f43SJohannes Berg return -EALREADY; 50484fa4f43SJohannes Berg } 50584fa4f43SJohannes Berg /* Two consecutive Country IE hints on the same wiphy */ 50684fa4f43SJohannes Berg if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) 50784fa4f43SJohannes Berg return 0; 50884fa4f43SJohannes Berg return -EALREADY; 50984fa4f43SJohannes Berg } 51084fa4f43SJohannes Berg /* 51184fa4f43SJohannes Berg * Ignore Country IE hints for now, need to think about 51284fa4f43SJohannes Berg * what we need to do to support multi-domain operation. 51384fa4f43SJohannes Berg */ 51484fa4f43SJohannes Berg return -EOPNOTSUPP; 51584fa4f43SJohannes Berg case REGDOM_SET_BY_DRIVER: 51684fa4f43SJohannes Berg if (last_request->initiator == REGDOM_SET_BY_DRIVER) 51784fa4f43SJohannes Berg return -EALREADY; 51884fa4f43SJohannes Berg return 0; 51984fa4f43SJohannes Berg case REGDOM_SET_BY_USER: 52084fa4f43SJohannes Berg /* 52184fa4f43SJohannes Berg * If the user wants to override the AP's hint, we may 52284fa4f43SJohannes Berg * need to follow both and use the intersection. For now, 52384fa4f43SJohannes Berg * reject any such attempt (but we don't support country 52484fa4f43SJohannes Berg * IEs right now anyway.) 52584fa4f43SJohannes Berg */ 52684fa4f43SJohannes Berg if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) 52784fa4f43SJohannes Berg return -EOPNOTSUPP; 52884fa4f43SJohannes Berg return 0; 52984fa4f43SJohannes Berg } 53084fa4f43SJohannes Berg 53184fa4f43SJohannes Berg return -EINVAL; 53284fa4f43SJohannes Berg } 53384fa4f43SJohannes Berg 534b2e1b302SLuis R. Rodriguez /* Caller must hold &cfg80211_drv_mutex */ 535b2e1b302SLuis R. Rodriguez int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, 536be3d4810SJohannes Berg const char *alpha2) 537b2e1b302SLuis R. Rodriguez { 538b2e1b302SLuis R. Rodriguez struct regulatory_request *request; 539b2e1b302SLuis R. Rodriguez int r = 0; 540b2e1b302SLuis R. Rodriguez 541be3d4810SJohannes Berg r = ignore_request(wiphy, set_by, alpha2); 542b2e1b302SLuis R. Rodriguez if (r) 543b2e1b302SLuis R. Rodriguez return r; 544b2e1b302SLuis R. Rodriguez 545b2e1b302SLuis R. Rodriguez switch (set_by) { 546b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_CORE: 547b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_COUNTRY_IE: 548b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_DRIVER: 549b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_USER: 550b2e1b302SLuis R. Rodriguez request = kzalloc(sizeof(struct regulatory_request), 551b2e1b302SLuis R. Rodriguez GFP_KERNEL); 552b2e1b302SLuis R. Rodriguez if (!request) 553b2e1b302SLuis R. Rodriguez return -ENOMEM; 554b2e1b302SLuis R. Rodriguez 555be3d4810SJohannes Berg request->alpha2[0] = alpha2[0]; 556be3d4810SJohannes Berg request->alpha2[1] = alpha2[1]; 557b2e1b302SLuis R. Rodriguez request->initiator = set_by; 558b2e1b302SLuis R. Rodriguez request->wiphy = wiphy; 559b2e1b302SLuis R. Rodriguez 560f6037d09SJohannes Berg kfree(last_request); 561f6037d09SJohannes Berg last_request = request; 562b2e1b302SLuis R. Rodriguez r = call_crda(alpha2); 563b2e1b302SLuis R. Rodriguez #ifndef CONFIG_WIRELESS_OLD_REGULATORY 564b2e1b302SLuis R. Rodriguez if (r) 565b2e1b302SLuis R. Rodriguez printk(KERN_ERR "cfg80211: Failed calling CRDA\n"); 566b2e1b302SLuis R. Rodriguez #endif 567b2e1b302SLuis R. Rodriguez break; 568b2e1b302SLuis R. Rodriguez default: 569b2e1b302SLuis R. Rodriguez r = -ENOTSUPP; 570b2e1b302SLuis R. Rodriguez break; 571b2e1b302SLuis R. Rodriguez } 572b2e1b302SLuis R. Rodriguez 573b2e1b302SLuis R. Rodriguez return r; 574b2e1b302SLuis R. Rodriguez } 575b2e1b302SLuis R. Rodriguez 576be3d4810SJohannes Berg void regulatory_hint(struct wiphy *wiphy, const char *alpha2) 577b2e1b302SLuis R. Rodriguez { 578be3d4810SJohannes Berg BUG_ON(!alpha2); 579b2e1b302SLuis R. Rodriguez 580b2e1b302SLuis R. Rodriguez mutex_lock(&cfg80211_drv_mutex); 581be3d4810SJohannes Berg __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2); 582b2e1b302SLuis R. Rodriguez mutex_unlock(&cfg80211_drv_mutex); 583b2e1b302SLuis R. Rodriguez } 584b2e1b302SLuis R. Rodriguez EXPORT_SYMBOL(regulatory_hint); 585b2e1b302SLuis R. Rodriguez 586b2e1b302SLuis R. Rodriguez 587a3d2eaf0SJohannes Berg static void print_rd_rules(const struct ieee80211_regdomain *rd) 588b2e1b302SLuis R. Rodriguez { 589b2e1b302SLuis R. Rodriguez unsigned int i; 590a3d2eaf0SJohannes Berg const struct ieee80211_reg_rule *reg_rule = NULL; 591a3d2eaf0SJohannes Berg const struct ieee80211_freq_range *freq_range = NULL; 592a3d2eaf0SJohannes Berg const struct ieee80211_power_rule *power_rule = NULL; 593b2e1b302SLuis R. Rodriguez 594b2e1b302SLuis R. Rodriguez printk(KERN_INFO "\t(start_freq - end_freq @ bandwidth), " 595b2e1b302SLuis R. Rodriguez "(max_antenna_gain, max_eirp)\n"); 596b2e1b302SLuis R. Rodriguez 597b2e1b302SLuis R. Rodriguez for (i = 0; i < rd->n_reg_rules; i++) { 598b2e1b302SLuis R. Rodriguez reg_rule = &rd->reg_rules[i]; 599b2e1b302SLuis R. Rodriguez freq_range = ®_rule->freq_range; 600b2e1b302SLuis R. Rodriguez power_rule = ®_rule->power_rule; 601b2e1b302SLuis R. Rodriguez 602b2e1b302SLuis R. Rodriguez /* There may not be documentation for max antenna gain 603b2e1b302SLuis R. Rodriguez * in certain regions */ 604b2e1b302SLuis R. Rodriguez if (power_rule->max_antenna_gain) 605b2e1b302SLuis R. Rodriguez printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), " 606b2e1b302SLuis R. Rodriguez "(%d mBi, %d mBm)\n", 607b2e1b302SLuis R. Rodriguez freq_range->start_freq_khz, 608b2e1b302SLuis R. Rodriguez freq_range->end_freq_khz, 609b2e1b302SLuis R. Rodriguez freq_range->max_bandwidth_khz, 610b2e1b302SLuis R. Rodriguez power_rule->max_antenna_gain, 611b2e1b302SLuis R. Rodriguez power_rule->max_eirp); 612b2e1b302SLuis R. Rodriguez else 613b2e1b302SLuis R. Rodriguez printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), " 614b2e1b302SLuis R. Rodriguez "(N/A, %d mBm)\n", 615b2e1b302SLuis R. Rodriguez freq_range->start_freq_khz, 616b2e1b302SLuis R. Rodriguez freq_range->end_freq_khz, 617b2e1b302SLuis R. Rodriguez freq_range->max_bandwidth_khz, 618b2e1b302SLuis R. Rodriguez power_rule->max_eirp); 619b2e1b302SLuis R. Rodriguez } 620b2e1b302SLuis R. Rodriguez } 621b2e1b302SLuis R. Rodriguez 622a3d2eaf0SJohannes Berg static void print_regdomain(const struct ieee80211_regdomain *rd) 623b2e1b302SLuis R. Rodriguez { 624b2e1b302SLuis R. Rodriguez 625b2e1b302SLuis R. Rodriguez if (is_world_regdom(rd->alpha2)) 626b2e1b302SLuis R. Rodriguez printk(KERN_INFO "cfg80211: World regulatory " 627b2e1b302SLuis R. Rodriguez "domain updated:\n"); 628b2e1b302SLuis R. Rodriguez else { 629b2e1b302SLuis R. Rodriguez if (is_unknown_alpha2(rd->alpha2)) 630b2e1b302SLuis R. Rodriguez printk(KERN_INFO "cfg80211: Regulatory domain " 631b2e1b302SLuis R. Rodriguez "changed to driver built-in settings " 632b2e1b302SLuis R. Rodriguez "(unknown country)\n"); 633b2e1b302SLuis R. Rodriguez else 634b2e1b302SLuis R. Rodriguez printk(KERN_INFO "cfg80211: Regulatory domain " 635b2e1b302SLuis R. Rodriguez "changed to country: %c%c\n", 636b2e1b302SLuis R. Rodriguez rd->alpha2[0], rd->alpha2[1]); 637b2e1b302SLuis R. Rodriguez } 638b2e1b302SLuis R. Rodriguez print_rd_rules(rd); 639b2e1b302SLuis R. Rodriguez } 640b2e1b302SLuis R. Rodriguez 6412df78167SJohannes Berg static void print_regdomain_info(const struct ieee80211_regdomain *rd) 642b2e1b302SLuis R. Rodriguez { 643b2e1b302SLuis R. Rodriguez printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", 644b2e1b302SLuis R. Rodriguez rd->alpha2[0], rd->alpha2[1]); 645b2e1b302SLuis R. Rodriguez print_rd_rules(rd); 646b2e1b302SLuis R. Rodriguez } 647b2e1b302SLuis R. Rodriguez 648d2372b31SJohannes Berg /* Takes ownership of rd only if it doesn't fail */ 649a3d2eaf0SJohannes Berg static int __set_regdom(const struct ieee80211_regdomain *rd) 650b2e1b302SLuis R. Rodriguez { 651b2e1b302SLuis R. Rodriguez /* Some basic sanity checks first */ 652b2e1b302SLuis R. Rodriguez 653b2e1b302SLuis R. Rodriguez if (is_world_regdom(rd->alpha2)) { 654f6037d09SJohannes Berg if (WARN_ON(!reg_is_valid_request(rd->alpha2))) 655b2e1b302SLuis R. Rodriguez return -EINVAL; 656b2e1b302SLuis R. Rodriguez update_world_regdomain(rd); 657b2e1b302SLuis R. Rodriguez return 0; 658b2e1b302SLuis R. Rodriguez } 659b2e1b302SLuis R. Rodriguez 660b2e1b302SLuis R. Rodriguez if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && 661b2e1b302SLuis R. Rodriguez !is_unknown_alpha2(rd->alpha2)) 662b2e1b302SLuis R. Rodriguez return -EINVAL; 663b2e1b302SLuis R. Rodriguez 664f6037d09SJohannes Berg if (!last_request) 665b2e1b302SLuis R. Rodriguez return -EINVAL; 666b2e1b302SLuis R. Rodriguez 667942b25cfSJohannes Berg /* allow overriding the static definitions if CRDA is present */ 668b2e1b302SLuis R. Rodriguez if (!is_old_static_regdom(cfg80211_regdomain) && 669b2e1b302SLuis R. Rodriguez !regdom_changed(rd->alpha2)) 670b2e1b302SLuis R. Rodriguez return -EINVAL; 671b2e1b302SLuis R. Rodriguez 672b2e1b302SLuis R. Rodriguez /* Now lets set the regulatory domain, update all driver channels 673b2e1b302SLuis R. Rodriguez * and finally inform them of what we have done, in case they want 674b2e1b302SLuis R. Rodriguez * to review or adjust their own settings based on their own 675b2e1b302SLuis R. Rodriguez * internal EEPROM data */ 676b2e1b302SLuis R. Rodriguez 677f6037d09SJohannes Berg if (WARN_ON(!reg_is_valid_request(rd->alpha2))) 678b2e1b302SLuis R. Rodriguez return -EINVAL; 679b2e1b302SLuis R. Rodriguez 680b2e1b302SLuis R. Rodriguez reset_regdomains(); 681b2e1b302SLuis R. Rodriguez 682b2e1b302SLuis R. Rodriguez /* Country IE parsing coming soon */ 683f6037d09SJohannes Berg switch (last_request->initiator) { 684b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_CORE: 685b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_DRIVER: 686b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_USER: 687b2e1b302SLuis R. Rodriguez if (!is_valid_rd(rd)) { 688b2e1b302SLuis R. Rodriguez printk(KERN_ERR "cfg80211: Invalid " 689b2e1b302SLuis R. Rodriguez "regulatory domain detected:\n"); 690b2e1b302SLuis R. Rodriguez print_regdomain_info(rd); 691b2e1b302SLuis R. Rodriguez return -EINVAL; 692b2e1b302SLuis R. Rodriguez } 693b2e1b302SLuis R. Rodriguez break; 694b2e1b302SLuis R. Rodriguez case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */ 695b2e1b302SLuis R. Rodriguez WARN_ON(1); 696b2e1b302SLuis R. Rodriguez default: 697b2e1b302SLuis R. Rodriguez return -EOPNOTSUPP; 698b2e1b302SLuis R. Rodriguez } 699b2e1b302SLuis R. Rodriguez 700b2e1b302SLuis R. Rodriguez /* Tada! */ 701b2e1b302SLuis R. Rodriguez cfg80211_regdomain = rd; 702b2e1b302SLuis R. Rodriguez 703b2e1b302SLuis R. Rodriguez return 0; 704b2e1b302SLuis R. Rodriguez } 705b2e1b302SLuis R. Rodriguez 706b2e1b302SLuis R. Rodriguez 707b2e1b302SLuis R. Rodriguez /* Use this call to set the current regulatory domain. Conflicts with 708b2e1b302SLuis R. Rodriguez * multiple drivers can be ironed out later. Caller must've already 709d2372b31SJohannes Berg * kmalloc'd the rd structure. Caller must hold cfg80211_drv_mutex */ 710a3d2eaf0SJohannes Berg int set_regdom(const struct ieee80211_regdomain *rd) 711b2e1b302SLuis R. Rodriguez { 712b2e1b302SLuis R. Rodriguez int r; 713b2e1b302SLuis R. Rodriguez 714b2e1b302SLuis R. Rodriguez /* Note that this doesn't update the wiphys, this is done below */ 715b2e1b302SLuis R. Rodriguez r = __set_regdom(rd); 716d2372b31SJohannes Berg if (r) { 717d2372b31SJohannes Berg kfree(rd); 718b2e1b302SLuis R. Rodriguez return r; 719d2372b31SJohannes Berg } 720b2e1b302SLuis R. Rodriguez 721b2e1b302SLuis R. Rodriguez /* This would make this whole thing pointless */ 722b2e1b302SLuis R. Rodriguez BUG_ON(rd != cfg80211_regdomain); 723b2e1b302SLuis R. Rodriguez 724b2e1b302SLuis R. Rodriguez /* update all wiphys now with the new established regulatory domain */ 725f6037d09SJohannes Berg update_all_wiphy_regulatory(last_request->initiator); 726b2e1b302SLuis R. Rodriguez 727b2e1b302SLuis R. Rodriguez print_regdomain(rd); 728b2e1b302SLuis R. Rodriguez 729b2e1b302SLuis R. Rodriguez return r; 730b2e1b302SLuis R. Rodriguez } 731b2e1b302SLuis R. Rodriguez 732b2e1b302SLuis R. Rodriguez int regulatory_init(void) 733b2e1b302SLuis R. Rodriguez { 734734366deSJohannes Berg int err; 735734366deSJohannes Berg 736b2e1b302SLuis R. Rodriguez reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); 737b2e1b302SLuis R. Rodriguez if (IS_ERR(reg_pdev)) 738b2e1b302SLuis R. Rodriguez return PTR_ERR(reg_pdev); 739734366deSJohannes Berg 740734366deSJohannes Berg #ifdef CONFIG_WIRELESS_OLD_REGULATORY 741a3d2eaf0SJohannes Berg cfg80211_regdomain = static_regdom(ieee80211_regdom); 742734366deSJohannes Berg 743942b25cfSJohannes Berg printk(KERN_INFO "cfg80211: Using static regulatory domain info\n"); 744734366deSJohannes Berg print_regdomain_info(cfg80211_regdomain); 745734366deSJohannes Berg /* The old code still requests for a new regdomain and if 746734366deSJohannes Berg * you have CRDA you get it updated, otherwise you get 747734366deSJohannes Berg * stuck with the static values. We ignore "EU" code as 748734366deSJohannes Berg * that is not a valid ISO / IEC 3166 alpha2 */ 749ac9440a4SJohannes Berg if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') 750734366deSJohannes Berg err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, 751be3d4810SJohannes Berg ieee80211_regdom); 752734366deSJohannes Berg #else 753a3d2eaf0SJohannes Berg cfg80211_regdomain = cfg80211_world_regdom; 754734366deSJohannes Berg 755be3d4810SJohannes Berg err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00"); 756734366deSJohannes Berg if (err) 757734366deSJohannes Berg printk(KERN_ERR "cfg80211: calling CRDA failed - " 758734366deSJohannes Berg "unable to update world regulatory domain, " 759734366deSJohannes Berg "using static definition\n"); 760734366deSJohannes Berg #endif 761734366deSJohannes Berg 762b2e1b302SLuis R. Rodriguez return 0; 763b2e1b302SLuis R. Rodriguez } 764b2e1b302SLuis R. Rodriguez 765b2e1b302SLuis R. Rodriguez void regulatory_exit(void) 766b2e1b302SLuis R. Rodriguez { 767b2e1b302SLuis R. Rodriguez mutex_lock(&cfg80211_drv_mutex); 768734366deSJohannes Berg 769b2e1b302SLuis R. Rodriguez reset_regdomains(); 770734366deSJohannes Berg 771f6037d09SJohannes Berg kfree(last_request); 772f6037d09SJohannes Berg 773b2e1b302SLuis R. Rodriguez platform_device_unregister(reg_pdev); 774734366deSJohannes Berg 775b2e1b302SLuis R. Rodriguez mutex_unlock(&cfg80211_drv_mutex); 7768318d78aSJohannes Berg } 777