162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 1162306a36Sopenharmony_ci * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 1362306a36Sopenharmony_ci * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 1462306a36Sopenharmony_ci * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci#include <net/cfg80211.h> 1962306a36Sopenharmony_ci#include <net/mac80211.h> 2062306a36Sopenharmony_ci#include <net/regulatory.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <defs.h> 2362306a36Sopenharmony_ci#include "pub.h" 2462306a36Sopenharmony_ci#include "phy/phy_hal.h" 2562306a36Sopenharmony_ci#include "main.h" 2662306a36Sopenharmony_ci#include "stf.h" 2762306a36Sopenharmony_ci#include "channel.h" 2862306a36Sopenharmony_ci#include "mac80211_if.h" 2962306a36Sopenharmony_ci#include "debug.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* QDB() macro takes a dB value and converts to a quarter dB value */ 3262306a36Sopenharmony_ci#define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define LOCALE_MIMO_IDX_bn 0 3562306a36Sopenharmony_ci#define LOCALE_MIMO_IDX_11n 0 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */ 3862306a36Sopenharmony_ci#define BRCMS_MAXPWR_MIMO_TBL_SIZE 14 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* maxpwr mapping to 5GHz band channels: 4162306a36Sopenharmony_ci * maxpwr[0] - channels [34-48] 4262306a36Sopenharmony_ci * maxpwr[1] - channels [52-60] 4362306a36Sopenharmony_ci * maxpwr[2] - channels [62-64] 4462306a36Sopenharmony_ci * maxpwr[3] - channels [100-140] 4562306a36Sopenharmony_ci * maxpwr[4] - channels [149-165] 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci#define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define LC(id) LOCALE_MIMO_IDX_ ## id 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define LOCALES(mimo2, mimo5) \ 5262306a36Sopenharmony_ci {LC(mimo2), LC(mimo5)} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* macro to get 5 GHz channel group index for tx power */ 5562306a36Sopenharmony_ci#define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \ 5662306a36Sopenharmony_ci (((c) < 62) ? 1 : \ 5762306a36Sopenharmony_ci (((c) < 100) ? 2 : \ 5862306a36Sopenharmony_ci (((c) < 149) ? 3 : 4)))) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0) 6162306a36Sopenharmony_ci#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \ 6262306a36Sopenharmony_ci NL80211_RRF_NO_IR) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \ 6562306a36Sopenharmony_ci NL80211_RRF_NO_IR) 6662306a36Sopenharmony_ci#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \ 6762306a36Sopenharmony_ci NL80211_RRF_DFS | \ 6862306a36Sopenharmony_ci NL80211_RRF_NO_IR) 6962306a36Sopenharmony_ci#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \ 7062306a36Sopenharmony_ci NL80211_RRF_DFS | \ 7162306a36Sopenharmony_ci NL80211_RRF_NO_IR) 7262306a36Sopenharmony_ci#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \ 7362306a36Sopenharmony_ci NL80211_RRF_NO_IR) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const struct ieee80211_regdomain brcms_regdom_x2 = { 7662306a36Sopenharmony_ci .n_reg_rules = 6, 7762306a36Sopenharmony_ci .alpha2 = "X2", 7862306a36Sopenharmony_ci .reg_rules = { 7962306a36Sopenharmony_ci BRCM_2GHZ_2412_2462, 8062306a36Sopenharmony_ci BRCM_2GHZ_2467_2472, 8162306a36Sopenharmony_ci BRCM_5GHZ_5180_5240, 8262306a36Sopenharmony_ci BRCM_5GHZ_5260_5320, 8362306a36Sopenharmony_ci BRCM_5GHZ_5500_5700, 8462306a36Sopenharmony_ci BRCM_5GHZ_5745_5825, 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* locale per-channel tx power limits for MIMO frames 8962306a36Sopenharmony_ci * maxpwr arrays are index by channel for 2.4 GHz limits, and 9062306a36Sopenharmony_ci * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel) 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistruct locale_mimo_info { 9362306a36Sopenharmony_ci /* tx 20 MHz power limits, qdBm units */ 9462306a36Sopenharmony_ci s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE]; 9562306a36Sopenharmony_ci /* tx 40 MHz power limits, qdBm units */ 9662306a36Sopenharmony_ci s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE]; 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* Country names and abbreviations with locale defined from ISO 3166 */ 10062306a36Sopenharmony_cistruct country_info { 10162306a36Sopenharmony_ci const u8 locale_mimo_2G; /* 2.4G mimo info */ 10262306a36Sopenharmony_ci const u8 locale_mimo_5G; /* 5G mimo info */ 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct brcms_regd { 10662306a36Sopenharmony_ci struct country_info country; 10762306a36Sopenharmony_ci const struct ieee80211_regdomain *regdomain; 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct brcms_cm_info { 11162306a36Sopenharmony_ci struct brcms_pub *pub; 11262306a36Sopenharmony_ci struct brcms_c_info *wlc; 11362306a36Sopenharmony_ci const struct brcms_regd *world_regd; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * MIMO Locale Definitions - 2.4 GHz 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistatic const struct locale_mimo_info locale_bn = { 12062306a36Sopenharmony_ci {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), 12162306a36Sopenharmony_ci QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), 12262306a36Sopenharmony_ci QDB(13), QDB(13), QDB(13)}, 12362306a36Sopenharmony_ci {0, 0, QDB(13), QDB(13), QDB(13), 12462306a36Sopenharmony_ci QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), 12562306a36Sopenharmony_ci QDB(13), 0, 0}, 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic const struct locale_mimo_info *g_mimo_2g_table[] = { 12962306a36Sopenharmony_ci &locale_bn 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * MIMO Locale Definitions - 5 GHz 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic const struct locale_mimo_info locale_11n = { 13662306a36Sopenharmony_ci { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)}, 13762306a36Sopenharmony_ci {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)}, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct locale_mimo_info *g_mimo_5g_table[] = { 14162306a36Sopenharmony_ci &locale_11n 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const struct brcms_regd cntry_locales[] = { 14562306a36Sopenharmony_ci /* Worldwide RoW 2, must always be at index 0 */ 14662306a36Sopenharmony_ci { 14762306a36Sopenharmony_ci .country = LOCALES(bn, 11n), 14862306a36Sopenharmony_ci .regdomain = &brcms_regdom_x2, 14962306a36Sopenharmony_ci }, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) 15562306a36Sopenharmony_ci return NULL; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return g_mimo_2g_table[locale_idx]; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return g_mimo_5g_table[locale_idx]; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * Indicates whether the country provided is valid to pass 17062306a36Sopenharmony_ci * to cfg80211 or not. 17162306a36Sopenharmony_ci * 17262306a36Sopenharmony_ci * returns true if valid; false if not. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cistatic bool brcms_c_country_valid(const char *ccode) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * only allow ascii alpha uppercase for the first 2 17862306a36Sopenharmony_ci * chars. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci if (!((ccode[0] & 0x80) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A && 18162306a36Sopenharmony_ci (ccode[1] & 0x80) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A)) 18262306a36Sopenharmony_ci return false; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * do not match ISO 3166-1 user assigned country codes 18662306a36Sopenharmony_ci * that may be in the driver table 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci if (!strcmp("AA", ccode) || /* AA */ 18962306a36Sopenharmony_ci !strcmp("ZZ", ccode) || /* ZZ */ 19062306a36Sopenharmony_ci ccode[0] == 'X' || /* XA - XZ */ 19162306a36Sopenharmony_ci (ccode[0] == 'Q' && /* QM - QZ */ 19262306a36Sopenharmony_ci (ccode[1] >= 'M' && ccode[1] <= 'Z'))) 19362306a36Sopenharmony_ci return false; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (!strcmp("NA", ccode)) 19662306a36Sopenharmony_ci return false; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return true; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic const struct brcms_regd *brcms_world_regd(const char *regdom, int len) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci const struct brcms_regd *regd = NULL; 20462306a36Sopenharmony_ci int i; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) { 20762306a36Sopenharmony_ci if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) { 20862306a36Sopenharmony_ci regd = &cntry_locales[i]; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return regd; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const struct brcms_regd *brcms_default_world_regd(void) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci return &cntry_locales[0]; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* JP, J1 - J10 are Japan ccodes */ 22262306a36Sopenharmony_cistatic bool brcms_c_japan_ccode(const char *ccode) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci return (ccode[0] == 'J' && 22562306a36Sopenharmony_ci (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9'))); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void 22962306a36Sopenharmony_cibrcms_c_channel_min_txpower_limits_with_local_constraint( 23062306a36Sopenharmony_ci struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr, 23162306a36Sopenharmony_ci u8 local_constraint_qdbm) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci int j; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* CCK Rates */ 23662306a36Sopenharmony_ci for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) 23762306a36Sopenharmony_ci txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* 20 MHz Legacy OFDM SISO */ 24062306a36Sopenharmony_ci for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) 24162306a36Sopenharmony_ci txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 20 MHz Legacy OFDM CDD */ 24462306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) 24562306a36Sopenharmony_ci txpwr->ofdm_cdd[j] = 24662306a36Sopenharmony_ci min(txpwr->ofdm_cdd[j], local_constraint_qdbm); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 40 MHz Legacy OFDM SISO */ 24962306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) 25062306a36Sopenharmony_ci txpwr->ofdm_40_siso[j] = 25162306a36Sopenharmony_ci min(txpwr->ofdm_40_siso[j], local_constraint_qdbm); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* 40 MHz Legacy OFDM CDD */ 25462306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) 25562306a36Sopenharmony_ci txpwr->ofdm_40_cdd[j] = 25662306a36Sopenharmony_ci min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 20MHz MCS 0-7 SISO */ 25962306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) 26062306a36Sopenharmony_ci txpwr->mcs_20_siso[j] = 26162306a36Sopenharmony_ci min(txpwr->mcs_20_siso[j], local_constraint_qdbm); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* 20MHz MCS 0-7 CDD */ 26462306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) 26562306a36Sopenharmony_ci txpwr->mcs_20_cdd[j] = 26662306a36Sopenharmony_ci min(txpwr->mcs_20_cdd[j], local_constraint_qdbm); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 20MHz MCS 0-7 STBC */ 26962306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) 27062306a36Sopenharmony_ci txpwr->mcs_20_stbc[j] = 27162306a36Sopenharmony_ci min(txpwr->mcs_20_stbc[j], local_constraint_qdbm); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 20MHz MCS 8-15 MIMO */ 27462306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) 27562306a36Sopenharmony_ci txpwr->mcs_20_mimo[j] = 27662306a36Sopenharmony_ci min(txpwr->mcs_20_mimo[j], local_constraint_qdbm); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 40MHz MCS 0-7 SISO */ 27962306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) 28062306a36Sopenharmony_ci txpwr->mcs_40_siso[j] = 28162306a36Sopenharmony_ci min(txpwr->mcs_40_siso[j], local_constraint_qdbm); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* 40MHz MCS 0-7 CDD */ 28462306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) 28562306a36Sopenharmony_ci txpwr->mcs_40_cdd[j] = 28662306a36Sopenharmony_ci min(txpwr->mcs_40_cdd[j], local_constraint_qdbm); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* 40MHz MCS 0-7 STBC */ 28962306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) 29062306a36Sopenharmony_ci txpwr->mcs_40_stbc[j] = 29162306a36Sopenharmony_ci min(txpwr->mcs_40_stbc[j], local_constraint_qdbm); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* 40MHz MCS 8-15 MIMO */ 29462306a36Sopenharmony_ci for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) 29562306a36Sopenharmony_ci txpwr->mcs_40_mimo[j] = 29662306a36Sopenharmony_ci min(txpwr->mcs_40_mimo[j], local_constraint_qdbm); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* 40MHz MCS 32 */ 29962306a36Sopenharmony_ci txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* 30462306a36Sopenharmony_ci * set the driver's current country and regulatory information 30562306a36Sopenharmony_ci * using a country code as the source. Look up built in country 30662306a36Sopenharmony_ci * information found with the country code. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistatic void 30962306a36Sopenharmony_cibrcms_c_set_country(struct brcms_cm_info *wlc_cm, 31062306a36Sopenharmony_ci const struct brcms_regd *regd) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_cm->wlc; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if ((wlc->pub->_n_enab & SUPPORT_11N) != 31562306a36Sopenharmony_ci wlc->protection->nmode_user) 31662306a36Sopenharmony_ci brcms_c_set_nmode(wlc); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); 31962306a36Sopenharmony_ci brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistruct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct brcms_cm_info *wlc_cm; 32962306a36Sopenharmony_ci struct brcms_pub *pub = wlc->pub; 33062306a36Sopenharmony_ci struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; 33162306a36Sopenharmony_ci const char *ccode = sprom->alpha2; 33262306a36Sopenharmony_ci int ccode_len = sizeof(sprom->alpha2); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC); 33562306a36Sopenharmony_ci if (wlc_cm == NULL) 33662306a36Sopenharmony_ci return NULL; 33762306a36Sopenharmony_ci wlc_cm->pub = pub; 33862306a36Sopenharmony_ci wlc_cm->wlc = wlc; 33962306a36Sopenharmony_ci wlc->cmi = wlc_cm; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* store the country code for passing up as a regulatory hint */ 34262306a36Sopenharmony_ci wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len); 34362306a36Sopenharmony_ci if (brcms_c_country_valid(ccode)) 34462306a36Sopenharmony_ci strncpy(wlc->pub->srom_ccode, ccode, ccode_len); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci * If no custom world domain is found in the SROM, use the 34862306a36Sopenharmony_ci * default "X2" domain. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci if (!wlc_cm->world_regd) { 35162306a36Sopenharmony_ci wlc_cm->world_regd = brcms_default_world_regd(); 35262306a36Sopenharmony_ci ccode = wlc_cm->world_regd->regdomain->alpha2; 35362306a36Sopenharmony_ci ccode_len = BRCM_CNTRY_BUF_SZ - 1; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* save default country for exiting 11d regulatory mode */ 35762306a36Sopenharmony_ci strncpy(wlc->country_default, ccode, ccode_len); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* initialize autocountry_default to driver default */ 36062306a36Sopenharmony_ci strncpy(wlc->autocountry_default, ccode, ccode_len); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci brcms_c_set_country(wlc_cm, wlc_cm->world_regd); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return wlc_cm; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_civoid brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci kfree(wlc_cm); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_civoid 37362306a36Sopenharmony_cibrcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, 37462306a36Sopenharmony_ci u8 local_constraint_qdbm) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_cm->wlc; 37762306a36Sopenharmony_ci struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; 37862306a36Sopenharmony_ci struct txpwr_limits txpwr; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci brcms_c_channel_min_txpower_limits_with_local_constraint( 38362306a36Sopenharmony_ci wlc_cm, &txpwr, local_constraint_qdbm 38462306a36Sopenharmony_ci ); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* set or restore gmode as required by regulatory */ 38762306a36Sopenharmony_ci if (ch->flags & IEEE80211_CHAN_NO_OFDM) 38862306a36Sopenharmony_ci brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false); 38962306a36Sopenharmony_ci else 39062306a36Sopenharmony_ci brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci brcms_b_set_chanspec(wlc->hw, chanspec, 39362306a36Sopenharmony_ci !!(ch->flags & IEEE80211_CHAN_NO_IR), 39462306a36Sopenharmony_ci &txpwr); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_civoid 39862306a36Sopenharmony_cibrcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, 39962306a36Sopenharmony_ci struct txpwr_limits *txpwr) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_cm->wlc; 40262306a36Sopenharmony_ci struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; 40362306a36Sopenharmony_ci uint i; 40462306a36Sopenharmony_ci uint chan; 40562306a36Sopenharmony_ci int maxpwr; 40662306a36Sopenharmony_ci int delta; 40762306a36Sopenharmony_ci const struct country_info *country; 40862306a36Sopenharmony_ci struct brcms_band *band; 40962306a36Sopenharmony_ci int conducted_max = BRCMS_TXPWR_MAX; 41062306a36Sopenharmony_ci const struct locale_mimo_info *li_mimo; 41162306a36Sopenharmony_ci int maxpwr20, maxpwr40; 41262306a36Sopenharmony_ci int maxpwr_idx; 41362306a36Sopenharmony_ci uint j; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci memset(txpwr, 0, sizeof(struct txpwr_limits)); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (WARN_ON(!ch)) 41862306a36Sopenharmony_ci return; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci country = &wlc_cm->world_regd->country; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci chan = CHSPEC_CHANNEL(chanspec); 42362306a36Sopenharmony_ci band = wlc->bandstate[chspec_bandunit(chanspec)]; 42462306a36Sopenharmony_ci li_mimo = (band->bandtype == BRCM_BAND_5G) ? 42562306a36Sopenharmony_ci brcms_c_get_mimo_5g(country->locale_mimo_5G) : 42662306a36Sopenharmony_ci brcms_c_get_mimo_2g(country->locale_mimo_2G); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci delta = band->antgain; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (band->bandtype == BRCM_BAND_2G) 43162306a36Sopenharmony_ci conducted_max = QDB(22); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci maxpwr = QDB(ch->max_power) - delta; 43462306a36Sopenharmony_ci maxpwr = max(maxpwr, 0); 43562306a36Sopenharmony_ci maxpwr = min(maxpwr, conducted_max); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* CCK txpwr limits for 2.4G band */ 43862306a36Sopenharmony_ci if (band->bandtype == BRCM_BAND_2G) { 43962306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) 44062306a36Sopenharmony_ci txpwr->cck[i] = (u8) maxpwr; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) { 44462306a36Sopenharmony_ci txpwr->ofdm[i] = (u8) maxpwr; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* 44762306a36Sopenharmony_ci * OFDM 40 MHz SISO has the same power as the corresponding 44862306a36Sopenharmony_ci * MCS0-7 rate unless overriden by the locale specific code. 44962306a36Sopenharmony_ci * We set this value to 0 as a flag (presumably 0 dBm isn't 45062306a36Sopenharmony_ci * a possibility) and then copy the MCS0-7 value to the 40 MHz 45162306a36Sopenharmony_ci * value if it wasn't explicitly set. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci txpwr->ofdm_40_siso[i] = 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci txpwr->ofdm_cdd[i] = (u8) maxpwr; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci txpwr->ofdm_40_cdd[i] = 0; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci delta = 0; 46162306a36Sopenharmony_ci if (band->antgain > QDB(6)) 46262306a36Sopenharmony_ci delta = band->antgain - QDB(6); /* Excess over 6 dB */ 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (band->bandtype == BRCM_BAND_2G) 46562306a36Sopenharmony_ci maxpwr_idx = (chan - 1); 46662306a36Sopenharmony_ci else 46762306a36Sopenharmony_ci maxpwr_idx = CHANNEL_POWER_IDX_5G(chan); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci maxpwr20 = li_mimo->maxpwr20[maxpwr_idx]; 47062306a36Sopenharmony_ci maxpwr40 = li_mimo->maxpwr40[maxpwr_idx]; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci maxpwr20 = maxpwr20 - delta; 47362306a36Sopenharmony_ci maxpwr20 = max(maxpwr20, 0); 47462306a36Sopenharmony_ci maxpwr40 = maxpwr40 - delta; 47562306a36Sopenharmony_ci maxpwr40 = max(maxpwr40, 0); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* Fill in the MCS 0-7 (SISO) rates */ 47862306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * 20 MHz has the same power as the corresponding OFDM rate 48262306a36Sopenharmony_ci * unless overriden by the locale specific code. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci txpwr->mcs_20_siso[i] = txpwr->ofdm[i]; 48562306a36Sopenharmony_ci txpwr->mcs_40_siso[i] = 0; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Fill in the MCS 0-7 CDD rates */ 48962306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { 49062306a36Sopenharmony_ci txpwr->mcs_20_cdd[i] = (u8) maxpwr20; 49162306a36Sopenharmony_ci txpwr->mcs_40_cdd[i] = (u8) maxpwr40; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * These locales have SISO expressed in the 49662306a36Sopenharmony_ci * table and override CDD later 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci if (li_mimo == &locale_bn) { 49962306a36Sopenharmony_ci maxpwr20 = QDB(16); 50062306a36Sopenharmony_ci maxpwr40 = 0; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (chan >= 3 && chan <= 11) 50362306a36Sopenharmony_ci maxpwr40 = QDB(16); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { 50662306a36Sopenharmony_ci txpwr->mcs_20_siso[i] = (u8) maxpwr20; 50762306a36Sopenharmony_ci txpwr->mcs_40_siso[i] = (u8) maxpwr40; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Fill in the MCS 0-7 STBC rates */ 51262306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { 51362306a36Sopenharmony_ci txpwr->mcs_20_stbc[i] = 0; 51462306a36Sopenharmony_ci txpwr->mcs_40_stbc[i] = 0; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Fill in the MCS 8-15 SDM rates */ 51862306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) { 51962306a36Sopenharmony_ci txpwr->mcs_20_mimo[i] = (u8) maxpwr20; 52062306a36Sopenharmony_ci txpwr->mcs_40_mimo[i] = (u8) maxpwr40; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Fill in MCS32 */ 52462306a36Sopenharmony_ci txpwr->mcs32 = (u8) maxpwr40; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { 52762306a36Sopenharmony_ci if (txpwr->ofdm_40_cdd[i] == 0) 52862306a36Sopenharmony_ci txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; 52962306a36Sopenharmony_ci if (i == 0) { 53062306a36Sopenharmony_ci i = i + 1; 53162306a36Sopenharmony_ci if (txpwr->ofdm_40_cdd[i] == 0) 53262306a36Sopenharmony_ci txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* 53762306a36Sopenharmony_ci * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO 53862306a36Sopenharmony_ci * value if it wasn't provided explicitly. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { 54162306a36Sopenharmony_ci if (txpwr->mcs_40_siso[i] == 0) 54262306a36Sopenharmony_ci txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i]; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { 54662306a36Sopenharmony_ci if (txpwr->ofdm_40_siso[i] == 0) 54762306a36Sopenharmony_ci txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; 54862306a36Sopenharmony_ci if (i == 0) { 54962306a36Sopenharmony_ci i = i + 1; 55062306a36Sopenharmony_ci if (txpwr->ofdm_40_siso[i] == 0) 55162306a36Sopenharmony_ci txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding 55762306a36Sopenharmony_ci * STBC values if they weren't provided explicitly. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { 56062306a36Sopenharmony_ci if (txpwr->mcs_20_stbc[i] == 0) 56162306a36Sopenharmony_ci txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i]; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (txpwr->mcs_40_stbc[i] == 0) 56462306a36Sopenharmony_ci txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i]; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * Verify the chanspec is using a legal set of parameters, i.e. that the 57262306a36Sopenharmony_ci * chanspec specified a band, bw, ctl_sb and channel and that the 57362306a36Sopenharmony_ci * combination could be legal given any set of circumstances. 57462306a36Sopenharmony_ci * RETURNS: true is the chanspec is malformed, false if it looks good. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_cistatic bool brcms_c_chspec_malformed(u16 chanspec) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci /* must be 2G or 5G band */ 57962306a36Sopenharmony_ci if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) 58062306a36Sopenharmony_ci return true; 58162306a36Sopenharmony_ci /* must be 20 or 40 bandwidth */ 58262306a36Sopenharmony_ci if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) 58362306a36Sopenharmony_ci return true; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ 58662306a36Sopenharmony_ci if (CHSPEC_IS20(chanspec)) { 58762306a36Sopenharmony_ci if (!CHSPEC_SB_NONE(chanspec)) 58862306a36Sopenharmony_ci return true; 58962306a36Sopenharmony_ci } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) { 59062306a36Sopenharmony_ci return true; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return false; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci/* 59762306a36Sopenharmony_ci * Validate the chanspec for this locale, for 40MHZ we need to also 59862306a36Sopenharmony_ci * check that the sidebands are valid 20MZH channels in this locale 59962306a36Sopenharmony_ci * and they are also a legal HT combination 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_cistatic bool 60262306a36Sopenharmony_cibrcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_cm->wlc; 60562306a36Sopenharmony_ci u8 channel = CHSPEC_CHANNEL(chspec); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* check the chanspec */ 60862306a36Sopenharmony_ci if (brcms_c_chspec_malformed(chspec)) { 60962306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: malformed chanspec 0x%x\n", 61062306a36Sopenharmony_ci wlc->pub->unit, chspec); 61162306a36Sopenharmony_ci return false; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) != 61562306a36Sopenharmony_ci chspec_bandunit(chspec)) 61662306a36Sopenharmony_ci return false; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return true; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cibool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci return brcms_c_valid_chanspec_ext(wlc_cm, chspec); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic bool brcms_is_radar_freq(u16 center_freq) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci return center_freq >= 5260 && center_freq <= 5700; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void brcms_reg_apply_radar_flags(struct wiphy *wiphy) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 63462306a36Sopenharmony_ci struct ieee80211_channel *ch; 63562306a36Sopenharmony_ci int i; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci sband = wiphy->bands[NL80211_BAND_5GHZ]; 63862306a36Sopenharmony_ci if (!sband) 63962306a36Sopenharmony_ci return; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 64262306a36Sopenharmony_ci ch = &sband->channels[i]; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (!brcms_is_radar_freq(ch->center_freq)) 64562306a36Sopenharmony_ci continue; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * All channels in this range should be passive and have 64962306a36Sopenharmony_ci * DFS enabled. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci if (!(ch->flags & IEEE80211_CHAN_DISABLED)) 65262306a36Sopenharmony_ci ch->flags |= IEEE80211_CHAN_RADAR | 65362306a36Sopenharmony_ci IEEE80211_CHAN_NO_IR; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic void 65862306a36Sopenharmony_cibrcms_reg_apply_beaconing_flags(struct wiphy *wiphy, 65962306a36Sopenharmony_ci enum nl80211_reg_initiator initiator) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 66262306a36Sopenharmony_ci struct ieee80211_channel *ch; 66362306a36Sopenharmony_ci const struct ieee80211_reg_rule *rule; 66462306a36Sopenharmony_ci int band, i; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 66762306a36Sopenharmony_ci sband = wiphy->bands[band]; 66862306a36Sopenharmony_ci if (!sband) 66962306a36Sopenharmony_ci continue; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 67262306a36Sopenharmony_ci ch = &sband->channels[i]; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (ch->flags & 67562306a36Sopenharmony_ci (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) 67662306a36Sopenharmony_ci continue; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { 67962306a36Sopenharmony_ci rule = freq_reg_info(wiphy, 68062306a36Sopenharmony_ci MHZ_TO_KHZ(ch->center_freq)); 68162306a36Sopenharmony_ci if (IS_ERR(rule)) 68262306a36Sopenharmony_ci continue; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (!(rule->flags & NL80211_RRF_NO_IR)) 68562306a36Sopenharmony_ci ch->flags &= ~IEEE80211_CHAN_NO_IR; 68662306a36Sopenharmony_ci } else if (ch->beacon_found) { 68762306a36Sopenharmony_ci ch->flags &= ~IEEE80211_CHAN_NO_IR; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic void brcms_reg_notifier(struct wiphy *wiphy, 69462306a36Sopenharmony_ci struct regulatory_request *request) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); 69762306a36Sopenharmony_ci struct brcms_info *wl = hw->priv; 69862306a36Sopenharmony_ci struct brcms_c_info *wlc = wl->wlc; 69962306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 70062306a36Sopenharmony_ci struct ieee80211_channel *ch; 70162306a36Sopenharmony_ci int band, i; 70262306a36Sopenharmony_ci bool ch_found = false; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci brcms_reg_apply_radar_flags(wiphy); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) 70762306a36Sopenharmony_ci brcms_reg_apply_beaconing_flags(wiphy, request->initiator); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* Disable radio if all channels disallowed by regulatory */ 71062306a36Sopenharmony_ci for (band = 0; !ch_found && band < NUM_NL80211_BANDS; band++) { 71162306a36Sopenharmony_ci sband = wiphy->bands[band]; 71262306a36Sopenharmony_ci if (!sband) 71362306a36Sopenharmony_ci continue; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci for (i = 0; !ch_found && i < sband->n_channels; i++) { 71662306a36Sopenharmony_ci ch = &sband->channels[i]; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (!(ch->flags & IEEE80211_CHAN_DISABLED)) 71962306a36Sopenharmony_ci ch_found = true; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (ch_found) { 72462306a36Sopenharmony_ci mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); 72562306a36Sopenharmony_ci } else { 72662306a36Sopenharmony_ci mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); 72762306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 72862306a36Sopenharmony_ci "wl%d: %s: no valid channel for \"%s\"\n", 72962306a36Sopenharmony_ci wlc->pub->unit, __func__, request->alpha2); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) 73362306a36Sopenharmony_ci wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, 73462306a36Sopenharmony_ci brcms_c_japan_ccode(request->alpha2)); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_civoid brcms_c_regd_init(struct brcms_c_info *wlc) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct wiphy *wiphy = wlc->wiphy; 74062306a36Sopenharmony_ci const struct brcms_regd *regd = wlc->cmi->world_regd; 74162306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 74262306a36Sopenharmony_ci struct ieee80211_channel *ch; 74362306a36Sopenharmony_ci struct brcms_chanvec sup_chan; 74462306a36Sopenharmony_ci struct brcms_band *band; 74562306a36Sopenharmony_ci int band_idx, i; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Disable any channels not supported by the phy */ 74862306a36Sopenharmony_ci for (band_idx = 0; band_idx < wlc->pub->_nbands; band_idx++) { 74962306a36Sopenharmony_ci band = wlc->bandstate[band_idx]; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci wlc_phy_chanspec_band_validch(band->pi, band->bandtype, 75262306a36Sopenharmony_ci &sup_chan); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (band_idx == BAND_2G_INDEX) 75562306a36Sopenharmony_ci sband = wiphy->bands[NL80211_BAND_2GHZ]; 75662306a36Sopenharmony_ci else 75762306a36Sopenharmony_ci sband = wiphy->bands[NL80211_BAND_5GHZ]; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 76062306a36Sopenharmony_ci ch = &sband->channels[i]; 76162306a36Sopenharmony_ci if (!isset(sup_chan.vec, ch->hw_value)) 76262306a36Sopenharmony_ci ch->flags |= IEEE80211_CHAN_DISABLED; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci wlc->wiphy->reg_notifier = brcms_reg_notifier; 76762306a36Sopenharmony_ci wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | 76862306a36Sopenharmony_ci REGULATORY_STRICT_REG; 76962306a36Sopenharmony_ci wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain); 77062306a36Sopenharmony_ci brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER); 77162306a36Sopenharmony_ci} 772