1*b4c3e9b5SBjoern A. Zeeb /*
2*b4c3e9b5SBjoern A. Zeeb * Copyright (c) 2010 Broadcom Corporation
3*b4c3e9b5SBjoern A. Zeeb *
4*b4c3e9b5SBjoern A. Zeeb * Permission to use, copy, modify, and/or distribute this software for any
5*b4c3e9b5SBjoern A. Zeeb * purpose with or without fee is hereby granted, provided that the above
6*b4c3e9b5SBjoern A. Zeeb * copyright notice and this permission notice appear in all copies.
7*b4c3e9b5SBjoern A. Zeeb *
8*b4c3e9b5SBjoern A. Zeeb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*b4c3e9b5SBjoern A. Zeeb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*b4c3e9b5SBjoern A. Zeeb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11*b4c3e9b5SBjoern A. Zeeb * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*b4c3e9b5SBjoern A. Zeeb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13*b4c3e9b5SBjoern A. Zeeb * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14*b4c3e9b5SBjoern A. Zeeb * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*b4c3e9b5SBjoern A. Zeeb */
16*b4c3e9b5SBjoern A. Zeeb
17*b4c3e9b5SBjoern A. Zeeb #include <linux/types.h>
18*b4c3e9b5SBjoern A. Zeeb #include <net/cfg80211.h>
19*b4c3e9b5SBjoern A. Zeeb #include <net/mac80211.h>
20*b4c3e9b5SBjoern A. Zeeb #include <net/regulatory.h>
21*b4c3e9b5SBjoern A. Zeeb
22*b4c3e9b5SBjoern A. Zeeb #include <defs.h>
23*b4c3e9b5SBjoern A. Zeeb #include "pub.h"
24*b4c3e9b5SBjoern A. Zeeb #include "phy/phy_hal.h"
25*b4c3e9b5SBjoern A. Zeeb #include "main.h"
26*b4c3e9b5SBjoern A. Zeeb #include "stf.h"
27*b4c3e9b5SBjoern A. Zeeb #include "channel.h"
28*b4c3e9b5SBjoern A. Zeeb #include "mac80211_if.h"
29*b4c3e9b5SBjoern A. Zeeb #include "debug.h"
30*b4c3e9b5SBjoern A. Zeeb
31*b4c3e9b5SBjoern A. Zeeb /* QDB() macro takes a dB value and converts to a quarter dB value */
32*b4c3e9b5SBjoern A. Zeeb #define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR)
33*b4c3e9b5SBjoern A. Zeeb
34*b4c3e9b5SBjoern A. Zeeb #define LOCALE_MIMO_IDX_bn 0
35*b4c3e9b5SBjoern A. Zeeb #define LOCALE_MIMO_IDX_11n 0
36*b4c3e9b5SBjoern A. Zeeb
37*b4c3e9b5SBjoern A. Zeeb /* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */
38*b4c3e9b5SBjoern A. Zeeb #define BRCMS_MAXPWR_MIMO_TBL_SIZE 14
39*b4c3e9b5SBjoern A. Zeeb
40*b4c3e9b5SBjoern A. Zeeb /* maxpwr mapping to 5GHz band channels:
41*b4c3e9b5SBjoern A. Zeeb * maxpwr[0] - channels [34-48]
42*b4c3e9b5SBjoern A. Zeeb * maxpwr[1] - channels [52-60]
43*b4c3e9b5SBjoern A. Zeeb * maxpwr[2] - channels [62-64]
44*b4c3e9b5SBjoern A. Zeeb * maxpwr[3] - channels [100-140]
45*b4c3e9b5SBjoern A. Zeeb * maxpwr[4] - channels [149-165]
46*b4c3e9b5SBjoern A. Zeeb */
47*b4c3e9b5SBjoern A. Zeeb #define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */
48*b4c3e9b5SBjoern A. Zeeb
49*b4c3e9b5SBjoern A. Zeeb #define LC(id) LOCALE_MIMO_IDX_ ## id
50*b4c3e9b5SBjoern A. Zeeb
51*b4c3e9b5SBjoern A. Zeeb #define LOCALES(mimo2, mimo5) \
52*b4c3e9b5SBjoern A. Zeeb {LC(mimo2), LC(mimo5)}
53*b4c3e9b5SBjoern A. Zeeb
54*b4c3e9b5SBjoern A. Zeeb /* macro to get 5 GHz channel group index for tx power */
55*b4c3e9b5SBjoern A. Zeeb #define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \
56*b4c3e9b5SBjoern A. Zeeb (((c) < 62) ? 1 : \
57*b4c3e9b5SBjoern A. Zeeb (((c) < 100) ? 2 : \
58*b4c3e9b5SBjoern A. Zeeb (((c) < 149) ? 3 : 4))))
59*b4c3e9b5SBjoern A. Zeeb
60*b4c3e9b5SBjoern A. Zeeb #define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0)
61*b4c3e9b5SBjoern A. Zeeb #define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \
62*b4c3e9b5SBjoern A. Zeeb NL80211_RRF_NO_IR)
63*b4c3e9b5SBjoern A. Zeeb
64*b4c3e9b5SBjoern A. Zeeb #define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \
65*b4c3e9b5SBjoern A. Zeeb NL80211_RRF_NO_IR)
66*b4c3e9b5SBjoern A. Zeeb #define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \
67*b4c3e9b5SBjoern A. Zeeb NL80211_RRF_DFS | \
68*b4c3e9b5SBjoern A. Zeeb NL80211_RRF_NO_IR)
69*b4c3e9b5SBjoern A. Zeeb #define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \
70*b4c3e9b5SBjoern A. Zeeb NL80211_RRF_DFS | \
71*b4c3e9b5SBjoern A. Zeeb NL80211_RRF_NO_IR)
72*b4c3e9b5SBjoern A. Zeeb #define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \
73*b4c3e9b5SBjoern A. Zeeb NL80211_RRF_NO_IR)
74*b4c3e9b5SBjoern A. Zeeb
75*b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_regdomain brcms_regdom_x2 = {
76*b4c3e9b5SBjoern A. Zeeb .n_reg_rules = 6,
77*b4c3e9b5SBjoern A. Zeeb .alpha2 = "X2",
78*b4c3e9b5SBjoern A. Zeeb .reg_rules = {
79*b4c3e9b5SBjoern A. Zeeb BRCM_2GHZ_2412_2462,
80*b4c3e9b5SBjoern A. Zeeb BRCM_2GHZ_2467_2472,
81*b4c3e9b5SBjoern A. Zeeb BRCM_5GHZ_5180_5240,
82*b4c3e9b5SBjoern A. Zeeb BRCM_5GHZ_5260_5320,
83*b4c3e9b5SBjoern A. Zeeb BRCM_5GHZ_5500_5700,
84*b4c3e9b5SBjoern A. Zeeb BRCM_5GHZ_5745_5825,
85*b4c3e9b5SBjoern A. Zeeb }
86*b4c3e9b5SBjoern A. Zeeb };
87*b4c3e9b5SBjoern A. Zeeb
88*b4c3e9b5SBjoern A. Zeeb /* locale per-channel tx power limits for MIMO frames
89*b4c3e9b5SBjoern A. Zeeb * maxpwr arrays are index by channel for 2.4 GHz limits, and
90*b4c3e9b5SBjoern A. Zeeb * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel)
91*b4c3e9b5SBjoern A. Zeeb */
92*b4c3e9b5SBjoern A. Zeeb struct locale_mimo_info {
93*b4c3e9b5SBjoern A. Zeeb /* tx 20 MHz power limits, qdBm units */
94*b4c3e9b5SBjoern A. Zeeb s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE];
95*b4c3e9b5SBjoern A. Zeeb /* tx 40 MHz power limits, qdBm units */
96*b4c3e9b5SBjoern A. Zeeb s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE];
97*b4c3e9b5SBjoern A. Zeeb };
98*b4c3e9b5SBjoern A. Zeeb
99*b4c3e9b5SBjoern A. Zeeb /* Country names and abbreviations with locale defined from ISO 3166 */
100*b4c3e9b5SBjoern A. Zeeb struct country_info {
101*b4c3e9b5SBjoern A. Zeeb const u8 locale_mimo_2G; /* 2.4G mimo info */
102*b4c3e9b5SBjoern A. Zeeb const u8 locale_mimo_5G; /* 5G mimo info */
103*b4c3e9b5SBjoern A. Zeeb };
104*b4c3e9b5SBjoern A. Zeeb
105*b4c3e9b5SBjoern A. Zeeb struct brcms_regd {
106*b4c3e9b5SBjoern A. Zeeb struct country_info country;
107*b4c3e9b5SBjoern A. Zeeb const struct ieee80211_regdomain *regdomain;
108*b4c3e9b5SBjoern A. Zeeb };
109*b4c3e9b5SBjoern A. Zeeb
110*b4c3e9b5SBjoern A. Zeeb struct brcms_cm_info {
111*b4c3e9b5SBjoern A. Zeeb struct brcms_pub *pub;
112*b4c3e9b5SBjoern A. Zeeb struct brcms_c_info *wlc;
113*b4c3e9b5SBjoern A. Zeeb const struct brcms_regd *world_regd;
114*b4c3e9b5SBjoern A. Zeeb };
115*b4c3e9b5SBjoern A. Zeeb
116*b4c3e9b5SBjoern A. Zeeb /*
117*b4c3e9b5SBjoern A. Zeeb * MIMO Locale Definitions - 2.4 GHz
118*b4c3e9b5SBjoern A. Zeeb */
119*b4c3e9b5SBjoern A. Zeeb static const struct locale_mimo_info locale_bn = {
120*b4c3e9b5SBjoern A. Zeeb {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
121*b4c3e9b5SBjoern A. Zeeb QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
122*b4c3e9b5SBjoern A. Zeeb QDB(13), QDB(13), QDB(13)},
123*b4c3e9b5SBjoern A. Zeeb {0, 0, QDB(13), QDB(13), QDB(13),
124*b4c3e9b5SBjoern A. Zeeb QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
125*b4c3e9b5SBjoern A. Zeeb QDB(13), 0, 0},
126*b4c3e9b5SBjoern A. Zeeb };
127*b4c3e9b5SBjoern A. Zeeb
128*b4c3e9b5SBjoern A. Zeeb static const struct locale_mimo_info *g_mimo_2g_table[] = {
129*b4c3e9b5SBjoern A. Zeeb &locale_bn
130*b4c3e9b5SBjoern A. Zeeb };
131*b4c3e9b5SBjoern A. Zeeb
132*b4c3e9b5SBjoern A. Zeeb /*
133*b4c3e9b5SBjoern A. Zeeb * MIMO Locale Definitions - 5 GHz
134*b4c3e9b5SBjoern A. Zeeb */
135*b4c3e9b5SBjoern A. Zeeb static const struct locale_mimo_info locale_11n = {
136*b4c3e9b5SBjoern A. Zeeb { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)},
137*b4c3e9b5SBjoern A. Zeeb {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)},
138*b4c3e9b5SBjoern A. Zeeb };
139*b4c3e9b5SBjoern A. Zeeb
140*b4c3e9b5SBjoern A. Zeeb static const struct locale_mimo_info *g_mimo_5g_table[] = {
141*b4c3e9b5SBjoern A. Zeeb &locale_11n
142*b4c3e9b5SBjoern A. Zeeb };
143*b4c3e9b5SBjoern A. Zeeb
144*b4c3e9b5SBjoern A. Zeeb static const struct brcms_regd cntry_locales[] = {
145*b4c3e9b5SBjoern A. Zeeb /* Worldwide RoW 2, must always be at index 0 */
146*b4c3e9b5SBjoern A. Zeeb {
147*b4c3e9b5SBjoern A. Zeeb .country = LOCALES(bn, 11n),
148*b4c3e9b5SBjoern A. Zeeb .regdomain = &brcms_regdom_x2,
149*b4c3e9b5SBjoern A. Zeeb },
150*b4c3e9b5SBjoern A. Zeeb };
151*b4c3e9b5SBjoern A. Zeeb
brcms_c_get_mimo_2g(u8 locale_idx)152*b4c3e9b5SBjoern A. Zeeb static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx)
153*b4c3e9b5SBjoern A. Zeeb {
154*b4c3e9b5SBjoern A. Zeeb if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table))
155*b4c3e9b5SBjoern A. Zeeb return NULL;
156*b4c3e9b5SBjoern A. Zeeb
157*b4c3e9b5SBjoern A. Zeeb return g_mimo_2g_table[locale_idx];
158*b4c3e9b5SBjoern A. Zeeb }
159*b4c3e9b5SBjoern A. Zeeb
brcms_c_get_mimo_5g(u8 locale_idx)160*b4c3e9b5SBjoern A. Zeeb static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx)
161*b4c3e9b5SBjoern A. Zeeb {
162*b4c3e9b5SBjoern A. Zeeb if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table))
163*b4c3e9b5SBjoern A. Zeeb return NULL;
164*b4c3e9b5SBjoern A. Zeeb
165*b4c3e9b5SBjoern A. Zeeb return g_mimo_5g_table[locale_idx];
166*b4c3e9b5SBjoern A. Zeeb }
167*b4c3e9b5SBjoern A. Zeeb
168*b4c3e9b5SBjoern A. Zeeb /*
169*b4c3e9b5SBjoern A. Zeeb * Indicates whether the country provided is valid to pass
170*b4c3e9b5SBjoern A. Zeeb * to cfg80211 or not.
171*b4c3e9b5SBjoern A. Zeeb *
172*b4c3e9b5SBjoern A. Zeeb * returns true if valid; false if not.
173*b4c3e9b5SBjoern A. Zeeb */
brcms_c_country_valid(const char * ccode)174*b4c3e9b5SBjoern A. Zeeb static bool brcms_c_country_valid(const char *ccode)
175*b4c3e9b5SBjoern A. Zeeb {
176*b4c3e9b5SBjoern A. Zeeb /*
177*b4c3e9b5SBjoern A. Zeeb * only allow ascii alpha uppercase for the first 2
178*b4c3e9b5SBjoern A. Zeeb * chars.
179*b4c3e9b5SBjoern A. Zeeb */
180*b4c3e9b5SBjoern A. Zeeb if (!((ccode[0] & 0x80) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A &&
181*b4c3e9b5SBjoern A. Zeeb (ccode[1] & 0x80) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A))
182*b4c3e9b5SBjoern A. Zeeb return false;
183*b4c3e9b5SBjoern A. Zeeb
184*b4c3e9b5SBjoern A. Zeeb /*
185*b4c3e9b5SBjoern A. Zeeb * do not match ISO 3166-1 user assigned country codes
186*b4c3e9b5SBjoern A. Zeeb * that may be in the driver table
187*b4c3e9b5SBjoern A. Zeeb */
188*b4c3e9b5SBjoern A. Zeeb if (!strcmp("AA", ccode) || /* AA */
189*b4c3e9b5SBjoern A. Zeeb !strcmp("ZZ", ccode) || /* ZZ */
190*b4c3e9b5SBjoern A. Zeeb ccode[0] == 'X' || /* XA - XZ */
191*b4c3e9b5SBjoern A. Zeeb (ccode[0] == 'Q' && /* QM - QZ */
192*b4c3e9b5SBjoern A. Zeeb (ccode[1] >= 'M' && ccode[1] <= 'Z')))
193*b4c3e9b5SBjoern A. Zeeb return false;
194*b4c3e9b5SBjoern A. Zeeb
195*b4c3e9b5SBjoern A. Zeeb if (!strcmp("NA", ccode))
196*b4c3e9b5SBjoern A. Zeeb return false;
197*b4c3e9b5SBjoern A. Zeeb
198*b4c3e9b5SBjoern A. Zeeb return true;
199*b4c3e9b5SBjoern A. Zeeb }
200*b4c3e9b5SBjoern A. Zeeb
brcms_world_regd(const char * regdom,int len)201*b4c3e9b5SBjoern A. Zeeb static const struct brcms_regd *brcms_world_regd(const char *regdom, int len)
202*b4c3e9b5SBjoern A. Zeeb {
203*b4c3e9b5SBjoern A. Zeeb const struct brcms_regd *regd = NULL;
204*b4c3e9b5SBjoern A. Zeeb int i;
205*b4c3e9b5SBjoern A. Zeeb
206*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) {
207*b4c3e9b5SBjoern A. Zeeb if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) {
208*b4c3e9b5SBjoern A. Zeeb regd = &cntry_locales[i];
209*b4c3e9b5SBjoern A. Zeeb break;
210*b4c3e9b5SBjoern A. Zeeb }
211*b4c3e9b5SBjoern A. Zeeb }
212*b4c3e9b5SBjoern A. Zeeb
213*b4c3e9b5SBjoern A. Zeeb return regd;
214*b4c3e9b5SBjoern A. Zeeb }
215*b4c3e9b5SBjoern A. Zeeb
brcms_default_world_regd(void)216*b4c3e9b5SBjoern A. Zeeb static const struct brcms_regd *brcms_default_world_regd(void)
217*b4c3e9b5SBjoern A. Zeeb {
218*b4c3e9b5SBjoern A. Zeeb return &cntry_locales[0];
219*b4c3e9b5SBjoern A. Zeeb }
220*b4c3e9b5SBjoern A. Zeeb
221*b4c3e9b5SBjoern A. Zeeb /* JP, J1 - J10 are Japan ccodes */
brcms_c_japan_ccode(const char * ccode)222*b4c3e9b5SBjoern A. Zeeb static bool brcms_c_japan_ccode(const char *ccode)
223*b4c3e9b5SBjoern A. Zeeb {
224*b4c3e9b5SBjoern A. Zeeb return (ccode[0] == 'J' &&
225*b4c3e9b5SBjoern A. Zeeb (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
226*b4c3e9b5SBjoern A. Zeeb }
227*b4c3e9b5SBjoern A. Zeeb
228*b4c3e9b5SBjoern A. Zeeb static void
brcms_c_channel_min_txpower_limits_with_local_constraint(struct brcms_cm_info * wlc_cm,struct txpwr_limits * txpwr,u8 local_constraint_qdbm)229*b4c3e9b5SBjoern A. Zeeb brcms_c_channel_min_txpower_limits_with_local_constraint(
230*b4c3e9b5SBjoern A. Zeeb struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr,
231*b4c3e9b5SBjoern A. Zeeb u8 local_constraint_qdbm)
232*b4c3e9b5SBjoern A. Zeeb {
233*b4c3e9b5SBjoern A. Zeeb int j;
234*b4c3e9b5SBjoern A. Zeeb
235*b4c3e9b5SBjoern A. Zeeb /* CCK Rates */
236*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < WL_TX_POWER_CCK_NUM; j++)
237*b4c3e9b5SBjoern A. Zeeb txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm);
238*b4c3e9b5SBjoern A. Zeeb
239*b4c3e9b5SBjoern A. Zeeb /* 20 MHz Legacy OFDM SISO */
240*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++)
241*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm);
242*b4c3e9b5SBjoern A. Zeeb
243*b4c3e9b5SBjoern A. Zeeb /* 20 MHz Legacy OFDM CDD */
244*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
245*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_cdd[j] =
246*b4c3e9b5SBjoern A. Zeeb min(txpwr->ofdm_cdd[j], local_constraint_qdbm);
247*b4c3e9b5SBjoern A. Zeeb
248*b4c3e9b5SBjoern A. Zeeb /* 40 MHz Legacy OFDM SISO */
249*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
250*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_siso[j] =
251*b4c3e9b5SBjoern A. Zeeb min(txpwr->ofdm_40_siso[j], local_constraint_qdbm);
252*b4c3e9b5SBjoern A. Zeeb
253*b4c3e9b5SBjoern A. Zeeb /* 40 MHz Legacy OFDM CDD */
254*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++)
255*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_cdd[j] =
256*b4c3e9b5SBjoern A. Zeeb min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm);
257*b4c3e9b5SBjoern A. Zeeb
258*b4c3e9b5SBjoern A. Zeeb /* 20MHz MCS 0-7 SISO */
259*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
260*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_siso[j] =
261*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_20_siso[j], local_constraint_qdbm);
262*b4c3e9b5SBjoern A. Zeeb
263*b4c3e9b5SBjoern A. Zeeb /* 20MHz MCS 0-7 CDD */
264*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
265*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_cdd[j] =
266*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_20_cdd[j], local_constraint_qdbm);
267*b4c3e9b5SBjoern A. Zeeb
268*b4c3e9b5SBjoern A. Zeeb /* 20MHz MCS 0-7 STBC */
269*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
270*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_stbc[j] =
271*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_20_stbc[j], local_constraint_qdbm);
272*b4c3e9b5SBjoern A. Zeeb
273*b4c3e9b5SBjoern A. Zeeb /* 20MHz MCS 8-15 MIMO */
274*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
275*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_mimo[j] =
276*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_20_mimo[j], local_constraint_qdbm);
277*b4c3e9b5SBjoern A. Zeeb
278*b4c3e9b5SBjoern A. Zeeb /* 40MHz MCS 0-7 SISO */
279*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
280*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_siso[j] =
281*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_40_siso[j], local_constraint_qdbm);
282*b4c3e9b5SBjoern A. Zeeb
283*b4c3e9b5SBjoern A. Zeeb /* 40MHz MCS 0-7 CDD */
284*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
285*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_cdd[j] =
286*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_40_cdd[j], local_constraint_qdbm);
287*b4c3e9b5SBjoern A. Zeeb
288*b4c3e9b5SBjoern A. Zeeb /* 40MHz MCS 0-7 STBC */
289*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++)
290*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_stbc[j] =
291*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_40_stbc[j], local_constraint_qdbm);
292*b4c3e9b5SBjoern A. Zeeb
293*b4c3e9b5SBjoern A. Zeeb /* 40MHz MCS 8-15 MIMO */
294*b4c3e9b5SBjoern A. Zeeb for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
295*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_mimo[j] =
296*b4c3e9b5SBjoern A. Zeeb min(txpwr->mcs_40_mimo[j], local_constraint_qdbm);
297*b4c3e9b5SBjoern A. Zeeb
298*b4c3e9b5SBjoern A. Zeeb /* 40MHz MCS 32 */
299*b4c3e9b5SBjoern A. Zeeb txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm);
300*b4c3e9b5SBjoern A. Zeeb
301*b4c3e9b5SBjoern A. Zeeb }
302*b4c3e9b5SBjoern A. Zeeb
303*b4c3e9b5SBjoern A. Zeeb /*
304*b4c3e9b5SBjoern A. Zeeb * set the driver's current country and regulatory information
305*b4c3e9b5SBjoern A. Zeeb * using a country code as the source. Look up built in country
306*b4c3e9b5SBjoern A. Zeeb * information found with the country code.
307*b4c3e9b5SBjoern A. Zeeb */
308*b4c3e9b5SBjoern A. Zeeb static void
brcms_c_set_country(struct brcms_cm_info * wlc_cm,const struct brcms_regd * regd)309*b4c3e9b5SBjoern A. Zeeb brcms_c_set_country(struct brcms_cm_info *wlc_cm,
310*b4c3e9b5SBjoern A. Zeeb const struct brcms_regd *regd)
311*b4c3e9b5SBjoern A. Zeeb {
312*b4c3e9b5SBjoern A. Zeeb struct brcms_c_info *wlc = wlc_cm->wlc;
313*b4c3e9b5SBjoern A. Zeeb
314*b4c3e9b5SBjoern A. Zeeb if ((wlc->pub->_n_enab & SUPPORT_11N) !=
315*b4c3e9b5SBjoern A. Zeeb wlc->protection->nmode_user)
316*b4c3e9b5SBjoern A. Zeeb brcms_c_set_nmode(wlc);
317*b4c3e9b5SBjoern A. Zeeb
318*b4c3e9b5SBjoern A. Zeeb brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
319*b4c3e9b5SBjoern A. Zeeb brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
320*b4c3e9b5SBjoern A. Zeeb
321*b4c3e9b5SBjoern A. Zeeb brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
322*b4c3e9b5SBjoern A. Zeeb
323*b4c3e9b5SBjoern A. Zeeb return;
324*b4c3e9b5SBjoern A. Zeeb }
325*b4c3e9b5SBjoern A. Zeeb
brcms_c_channel_mgr_attach(struct brcms_c_info * wlc)326*b4c3e9b5SBjoern A. Zeeb struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc)
327*b4c3e9b5SBjoern A. Zeeb {
328*b4c3e9b5SBjoern A. Zeeb struct brcms_cm_info *wlc_cm;
329*b4c3e9b5SBjoern A. Zeeb struct brcms_pub *pub = wlc->pub;
330*b4c3e9b5SBjoern A. Zeeb struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom;
331*b4c3e9b5SBjoern A. Zeeb const char *ccode = sprom->alpha2;
332*b4c3e9b5SBjoern A. Zeeb int ccode_len = sizeof(sprom->alpha2);
333*b4c3e9b5SBjoern A. Zeeb
334*b4c3e9b5SBjoern A. Zeeb wlc_cm = kzalloc(sizeof(*wlc_cm), GFP_ATOMIC);
335*b4c3e9b5SBjoern A. Zeeb if (wlc_cm == NULL)
336*b4c3e9b5SBjoern A. Zeeb return NULL;
337*b4c3e9b5SBjoern A. Zeeb wlc_cm->pub = pub;
338*b4c3e9b5SBjoern A. Zeeb wlc_cm->wlc = wlc;
339*b4c3e9b5SBjoern A. Zeeb wlc->cmi = wlc_cm;
340*b4c3e9b5SBjoern A. Zeeb
341*b4c3e9b5SBjoern A. Zeeb /* store the country code for passing up as a regulatory hint */
342*b4c3e9b5SBjoern A. Zeeb wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len);
343*b4c3e9b5SBjoern A. Zeeb if (brcms_c_country_valid(ccode))
344*b4c3e9b5SBjoern A. Zeeb memcpy(wlc->pub->srom_ccode, ccode, ccode_len);
345*b4c3e9b5SBjoern A. Zeeb
346*b4c3e9b5SBjoern A. Zeeb /*
347*b4c3e9b5SBjoern A. Zeeb * If no custom world domain is found in the SROM, use the
348*b4c3e9b5SBjoern A. Zeeb * default "X2" domain.
349*b4c3e9b5SBjoern A. Zeeb */
350*b4c3e9b5SBjoern A. Zeeb if (!wlc_cm->world_regd) {
351*b4c3e9b5SBjoern A. Zeeb wlc_cm->world_regd = brcms_default_world_regd();
352*b4c3e9b5SBjoern A. Zeeb ccode = wlc_cm->world_regd->regdomain->alpha2;
353*b4c3e9b5SBjoern A. Zeeb ccode_len = BRCM_CNTRY_BUF_SZ - 1;
354*b4c3e9b5SBjoern A. Zeeb }
355*b4c3e9b5SBjoern A. Zeeb
356*b4c3e9b5SBjoern A. Zeeb /* save default country for exiting 11d regulatory mode */
357*b4c3e9b5SBjoern A. Zeeb memcpy(wlc->country_default, ccode, ccode_len);
358*b4c3e9b5SBjoern A. Zeeb
359*b4c3e9b5SBjoern A. Zeeb /* initialize autocountry_default to driver default */
360*b4c3e9b5SBjoern A. Zeeb memcpy(wlc->autocountry_default, ccode, ccode_len);
361*b4c3e9b5SBjoern A. Zeeb
362*b4c3e9b5SBjoern A. Zeeb brcms_c_set_country(wlc_cm, wlc_cm->world_regd);
363*b4c3e9b5SBjoern A. Zeeb
364*b4c3e9b5SBjoern A. Zeeb return wlc_cm;
365*b4c3e9b5SBjoern A. Zeeb }
366*b4c3e9b5SBjoern A. Zeeb
brcms_c_channel_mgr_detach(struct brcms_cm_info * wlc_cm)367*b4c3e9b5SBjoern A. Zeeb void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm)
368*b4c3e9b5SBjoern A. Zeeb {
369*b4c3e9b5SBjoern A. Zeeb kfree(wlc_cm);
370*b4c3e9b5SBjoern A. Zeeb }
371*b4c3e9b5SBjoern A. Zeeb
372*b4c3e9b5SBjoern A. Zeeb void
brcms_c_channel_set_chanspec(struct brcms_cm_info * wlc_cm,u16 chanspec,u8 local_constraint_qdbm)373*b4c3e9b5SBjoern A. Zeeb brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
374*b4c3e9b5SBjoern A. Zeeb u8 local_constraint_qdbm)
375*b4c3e9b5SBjoern A. Zeeb {
376*b4c3e9b5SBjoern A. Zeeb struct brcms_c_info *wlc = wlc_cm->wlc;
377*b4c3e9b5SBjoern A. Zeeb struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan;
378*b4c3e9b5SBjoern A. Zeeb struct txpwr_limits txpwr;
379*b4c3e9b5SBjoern A. Zeeb
380*b4c3e9b5SBjoern A. Zeeb brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
381*b4c3e9b5SBjoern A. Zeeb
382*b4c3e9b5SBjoern A. Zeeb brcms_c_channel_min_txpower_limits_with_local_constraint(
383*b4c3e9b5SBjoern A. Zeeb wlc_cm, &txpwr, local_constraint_qdbm
384*b4c3e9b5SBjoern A. Zeeb );
385*b4c3e9b5SBjoern A. Zeeb
386*b4c3e9b5SBjoern A. Zeeb /* set or restore gmode as required by regulatory */
387*b4c3e9b5SBjoern A. Zeeb if (ch->flags & IEEE80211_CHAN_NO_OFDM)
388*b4c3e9b5SBjoern A. Zeeb brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
389*b4c3e9b5SBjoern A. Zeeb else
390*b4c3e9b5SBjoern A. Zeeb brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
391*b4c3e9b5SBjoern A. Zeeb
392*b4c3e9b5SBjoern A. Zeeb brcms_b_set_chanspec(wlc->hw, chanspec,
393*b4c3e9b5SBjoern A. Zeeb !!(ch->flags & IEEE80211_CHAN_NO_IR),
394*b4c3e9b5SBjoern A. Zeeb &txpwr);
395*b4c3e9b5SBjoern A. Zeeb }
396*b4c3e9b5SBjoern A. Zeeb
397*b4c3e9b5SBjoern A. Zeeb void
brcms_c_channel_reg_limits(struct brcms_cm_info * wlc_cm,u16 chanspec,struct txpwr_limits * txpwr)398*b4c3e9b5SBjoern A. Zeeb brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec,
399*b4c3e9b5SBjoern A. Zeeb struct txpwr_limits *txpwr)
400*b4c3e9b5SBjoern A. Zeeb {
401*b4c3e9b5SBjoern A. Zeeb struct brcms_c_info *wlc = wlc_cm->wlc;
402*b4c3e9b5SBjoern A. Zeeb struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan;
403*b4c3e9b5SBjoern A. Zeeb uint i;
404*b4c3e9b5SBjoern A. Zeeb uint chan;
405*b4c3e9b5SBjoern A. Zeeb int maxpwr;
406*b4c3e9b5SBjoern A. Zeeb int delta;
407*b4c3e9b5SBjoern A. Zeeb const struct country_info *country;
408*b4c3e9b5SBjoern A. Zeeb struct brcms_band *band;
409*b4c3e9b5SBjoern A. Zeeb int conducted_max = BRCMS_TXPWR_MAX;
410*b4c3e9b5SBjoern A. Zeeb const struct locale_mimo_info *li_mimo;
411*b4c3e9b5SBjoern A. Zeeb int maxpwr20, maxpwr40;
412*b4c3e9b5SBjoern A. Zeeb int maxpwr_idx;
413*b4c3e9b5SBjoern A. Zeeb uint j;
414*b4c3e9b5SBjoern A. Zeeb
415*b4c3e9b5SBjoern A. Zeeb memset(txpwr, 0, sizeof(struct txpwr_limits));
416*b4c3e9b5SBjoern A. Zeeb
417*b4c3e9b5SBjoern A. Zeeb if (WARN_ON(!ch))
418*b4c3e9b5SBjoern A. Zeeb return;
419*b4c3e9b5SBjoern A. Zeeb
420*b4c3e9b5SBjoern A. Zeeb country = &wlc_cm->world_regd->country;
421*b4c3e9b5SBjoern A. Zeeb
422*b4c3e9b5SBjoern A. Zeeb chan = CHSPEC_CHANNEL(chanspec);
423*b4c3e9b5SBjoern A. Zeeb band = wlc->bandstate[chspec_bandunit(chanspec)];
424*b4c3e9b5SBjoern A. Zeeb li_mimo = (band->bandtype == BRCM_BAND_5G) ?
425*b4c3e9b5SBjoern A. Zeeb brcms_c_get_mimo_5g(country->locale_mimo_5G) :
426*b4c3e9b5SBjoern A. Zeeb brcms_c_get_mimo_2g(country->locale_mimo_2G);
427*b4c3e9b5SBjoern A. Zeeb
428*b4c3e9b5SBjoern A. Zeeb delta = band->antgain;
429*b4c3e9b5SBjoern A. Zeeb
430*b4c3e9b5SBjoern A. Zeeb if (band->bandtype == BRCM_BAND_2G)
431*b4c3e9b5SBjoern A. Zeeb conducted_max = QDB(22);
432*b4c3e9b5SBjoern A. Zeeb
433*b4c3e9b5SBjoern A. Zeeb maxpwr = QDB(ch->max_power) - delta;
434*b4c3e9b5SBjoern A. Zeeb maxpwr = max(maxpwr, 0);
435*b4c3e9b5SBjoern A. Zeeb maxpwr = min(maxpwr, conducted_max);
436*b4c3e9b5SBjoern A. Zeeb
437*b4c3e9b5SBjoern A. Zeeb /* CCK txpwr limits for 2.4G band */
438*b4c3e9b5SBjoern A. Zeeb if (band->bandtype == BRCM_BAND_2G) {
439*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_CCK; i++)
440*b4c3e9b5SBjoern A. Zeeb txpwr->cck[i] = (u8) maxpwr;
441*b4c3e9b5SBjoern A. Zeeb }
442*b4c3e9b5SBjoern A. Zeeb
443*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
444*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm[i] = (u8) maxpwr;
445*b4c3e9b5SBjoern A. Zeeb
446*b4c3e9b5SBjoern A. Zeeb /*
447*b4c3e9b5SBjoern A. Zeeb * OFDM 40 MHz SISO has the same power as the corresponding
448*b4c3e9b5SBjoern A. Zeeb * MCS0-7 rate unless overridden by the locale specific code.
449*b4c3e9b5SBjoern A. Zeeb * We set this value to 0 as a flag (presumably 0 dBm isn't
450*b4c3e9b5SBjoern A. Zeeb * a possibility) and then copy the MCS0-7 value to the 40 MHz
451*b4c3e9b5SBjoern A. Zeeb * value if it wasn't explicitly set.
452*b4c3e9b5SBjoern A. Zeeb */
453*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_siso[i] = 0;
454*b4c3e9b5SBjoern A. Zeeb
455*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_cdd[i] = (u8) maxpwr;
456*b4c3e9b5SBjoern A. Zeeb
457*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_cdd[i] = 0;
458*b4c3e9b5SBjoern A. Zeeb }
459*b4c3e9b5SBjoern A. Zeeb
460*b4c3e9b5SBjoern A. Zeeb delta = 0;
461*b4c3e9b5SBjoern A. Zeeb if (band->antgain > QDB(6))
462*b4c3e9b5SBjoern A. Zeeb delta = band->antgain - QDB(6); /* Excess over 6 dB */
463*b4c3e9b5SBjoern A. Zeeb
464*b4c3e9b5SBjoern A. Zeeb if (band->bandtype == BRCM_BAND_2G)
465*b4c3e9b5SBjoern A. Zeeb maxpwr_idx = (chan - 1);
466*b4c3e9b5SBjoern A. Zeeb else
467*b4c3e9b5SBjoern A. Zeeb maxpwr_idx = CHANNEL_POWER_IDX_5G(chan);
468*b4c3e9b5SBjoern A. Zeeb
469*b4c3e9b5SBjoern A. Zeeb maxpwr20 = li_mimo->maxpwr20[maxpwr_idx];
470*b4c3e9b5SBjoern A. Zeeb maxpwr40 = li_mimo->maxpwr40[maxpwr_idx];
471*b4c3e9b5SBjoern A. Zeeb
472*b4c3e9b5SBjoern A. Zeeb maxpwr20 = maxpwr20 - delta;
473*b4c3e9b5SBjoern A. Zeeb maxpwr20 = max(maxpwr20, 0);
474*b4c3e9b5SBjoern A. Zeeb maxpwr40 = maxpwr40 - delta;
475*b4c3e9b5SBjoern A. Zeeb maxpwr40 = max(maxpwr40, 0);
476*b4c3e9b5SBjoern A. Zeeb
477*b4c3e9b5SBjoern A. Zeeb /* Fill in the MCS 0-7 (SISO) rates */
478*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
479*b4c3e9b5SBjoern A. Zeeb
480*b4c3e9b5SBjoern A. Zeeb /*
481*b4c3e9b5SBjoern A. Zeeb * 20 MHz has the same power as the corresponding OFDM rate
482*b4c3e9b5SBjoern A. Zeeb * unless overridden by the locale specific code.
483*b4c3e9b5SBjoern A. Zeeb */
484*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
485*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_siso[i] = 0;
486*b4c3e9b5SBjoern A. Zeeb }
487*b4c3e9b5SBjoern A. Zeeb
488*b4c3e9b5SBjoern A. Zeeb /* Fill in the MCS 0-7 CDD rates */
489*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
490*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_cdd[i] = (u8) maxpwr20;
491*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_cdd[i] = (u8) maxpwr40;
492*b4c3e9b5SBjoern A. Zeeb }
493*b4c3e9b5SBjoern A. Zeeb
494*b4c3e9b5SBjoern A. Zeeb /*
495*b4c3e9b5SBjoern A. Zeeb * These locales have SISO expressed in the
496*b4c3e9b5SBjoern A. Zeeb * table and override CDD later
497*b4c3e9b5SBjoern A. Zeeb */
498*b4c3e9b5SBjoern A. Zeeb if (li_mimo == &locale_bn) {
499*b4c3e9b5SBjoern A. Zeeb maxpwr20 = QDB(16);
500*b4c3e9b5SBjoern A. Zeeb maxpwr40 = 0;
501*b4c3e9b5SBjoern A. Zeeb
502*b4c3e9b5SBjoern A. Zeeb if (chan >= 3 && chan <= 11)
503*b4c3e9b5SBjoern A. Zeeb maxpwr40 = QDB(16);
504*b4c3e9b5SBjoern A. Zeeb
505*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
506*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_siso[i] = (u8) maxpwr20;
507*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_siso[i] = (u8) maxpwr40;
508*b4c3e9b5SBjoern A. Zeeb }
509*b4c3e9b5SBjoern A. Zeeb }
510*b4c3e9b5SBjoern A. Zeeb
511*b4c3e9b5SBjoern A. Zeeb /* Fill in the MCS 0-7 STBC rates */
512*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
513*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_stbc[i] = 0;
514*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_stbc[i] = 0;
515*b4c3e9b5SBjoern A. Zeeb }
516*b4c3e9b5SBjoern A. Zeeb
517*b4c3e9b5SBjoern A. Zeeb /* Fill in the MCS 8-15 SDM rates */
518*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) {
519*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_mimo[i] = (u8) maxpwr20;
520*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_mimo[i] = (u8) maxpwr40;
521*b4c3e9b5SBjoern A. Zeeb }
522*b4c3e9b5SBjoern A. Zeeb
523*b4c3e9b5SBjoern A. Zeeb /* Fill in MCS32 */
524*b4c3e9b5SBjoern A. Zeeb txpwr->mcs32 = (u8) maxpwr40;
525*b4c3e9b5SBjoern A. Zeeb
526*b4c3e9b5SBjoern A. Zeeb for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
527*b4c3e9b5SBjoern A. Zeeb if (txpwr->ofdm_40_cdd[i] == 0)
528*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
529*b4c3e9b5SBjoern A. Zeeb if (i == 0) {
530*b4c3e9b5SBjoern A. Zeeb i = i + 1;
531*b4c3e9b5SBjoern A. Zeeb if (txpwr->ofdm_40_cdd[i] == 0)
532*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
533*b4c3e9b5SBjoern A. Zeeb }
534*b4c3e9b5SBjoern A. Zeeb }
535*b4c3e9b5SBjoern A. Zeeb
536*b4c3e9b5SBjoern A. Zeeb /*
537*b4c3e9b5SBjoern A. Zeeb * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO
538*b4c3e9b5SBjoern A. Zeeb * value if it wasn't provided explicitly.
539*b4c3e9b5SBjoern A. Zeeb */
540*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
541*b4c3e9b5SBjoern A. Zeeb if (txpwr->mcs_40_siso[i] == 0)
542*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i];
543*b4c3e9b5SBjoern A. Zeeb }
544*b4c3e9b5SBjoern A. Zeeb
545*b4c3e9b5SBjoern A. Zeeb for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
546*b4c3e9b5SBjoern A. Zeeb if (txpwr->ofdm_40_siso[i] == 0)
547*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
548*b4c3e9b5SBjoern A. Zeeb if (i == 0) {
549*b4c3e9b5SBjoern A. Zeeb i = i + 1;
550*b4c3e9b5SBjoern A. Zeeb if (txpwr->ofdm_40_siso[i] == 0)
551*b4c3e9b5SBjoern A. Zeeb txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
552*b4c3e9b5SBjoern A. Zeeb }
553*b4c3e9b5SBjoern A. Zeeb }
554*b4c3e9b5SBjoern A. Zeeb
555*b4c3e9b5SBjoern A. Zeeb /*
556*b4c3e9b5SBjoern A. Zeeb * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding
557*b4c3e9b5SBjoern A. Zeeb * STBC values if they weren't provided explicitly.
558*b4c3e9b5SBjoern A. Zeeb */
559*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
560*b4c3e9b5SBjoern A. Zeeb if (txpwr->mcs_20_stbc[i] == 0)
561*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i];
562*b4c3e9b5SBjoern A. Zeeb
563*b4c3e9b5SBjoern A. Zeeb if (txpwr->mcs_40_stbc[i] == 0)
564*b4c3e9b5SBjoern A. Zeeb txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i];
565*b4c3e9b5SBjoern A. Zeeb }
566*b4c3e9b5SBjoern A. Zeeb
567*b4c3e9b5SBjoern A. Zeeb return;
568*b4c3e9b5SBjoern A. Zeeb }
569*b4c3e9b5SBjoern A. Zeeb
570*b4c3e9b5SBjoern A. Zeeb /*
571*b4c3e9b5SBjoern A. Zeeb * Verify the chanspec is using a legal set of parameters, i.e. that the
572*b4c3e9b5SBjoern A. Zeeb * chanspec specified a band, bw, ctl_sb and channel and that the
573*b4c3e9b5SBjoern A. Zeeb * combination could be legal given any set of circumstances.
574*b4c3e9b5SBjoern A. Zeeb * RETURNS: true is the chanspec is malformed, false if it looks good.
575*b4c3e9b5SBjoern A. Zeeb */
brcms_c_chspec_malformed(u16 chanspec)576*b4c3e9b5SBjoern A. Zeeb static bool brcms_c_chspec_malformed(u16 chanspec)
577*b4c3e9b5SBjoern A. Zeeb {
578*b4c3e9b5SBjoern A. Zeeb /* must be 2G or 5G band */
579*b4c3e9b5SBjoern A. Zeeb if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec))
580*b4c3e9b5SBjoern A. Zeeb return true;
581*b4c3e9b5SBjoern A. Zeeb /* must be 20 or 40 bandwidth */
582*b4c3e9b5SBjoern A. Zeeb if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec))
583*b4c3e9b5SBjoern A. Zeeb return true;
584*b4c3e9b5SBjoern A. Zeeb
585*b4c3e9b5SBjoern A. Zeeb /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */
586*b4c3e9b5SBjoern A. Zeeb if (CHSPEC_IS20(chanspec)) {
587*b4c3e9b5SBjoern A. Zeeb if (!CHSPEC_SB_NONE(chanspec))
588*b4c3e9b5SBjoern A. Zeeb return true;
589*b4c3e9b5SBjoern A. Zeeb } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) {
590*b4c3e9b5SBjoern A. Zeeb return true;
591*b4c3e9b5SBjoern A. Zeeb }
592*b4c3e9b5SBjoern A. Zeeb
593*b4c3e9b5SBjoern A. Zeeb return false;
594*b4c3e9b5SBjoern A. Zeeb }
595*b4c3e9b5SBjoern A. Zeeb
596*b4c3e9b5SBjoern A. Zeeb /*
597*b4c3e9b5SBjoern A. Zeeb * Validate the chanspec for this locale, for 40MHZ we need to also
598*b4c3e9b5SBjoern A. Zeeb * check that the sidebands are valid 20MZH channels in this locale
599*b4c3e9b5SBjoern A. Zeeb * and they are also a legal HT combination
600*b4c3e9b5SBjoern A. Zeeb */
601*b4c3e9b5SBjoern A. Zeeb static bool
brcms_c_valid_chanspec_ext(struct brcms_cm_info * wlc_cm,u16 chspec)602*b4c3e9b5SBjoern A. Zeeb brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec)
603*b4c3e9b5SBjoern A. Zeeb {
604*b4c3e9b5SBjoern A. Zeeb struct brcms_c_info *wlc = wlc_cm->wlc;
605*b4c3e9b5SBjoern A. Zeeb u8 channel = CHSPEC_CHANNEL(chspec);
606*b4c3e9b5SBjoern A. Zeeb
607*b4c3e9b5SBjoern A. Zeeb /* check the chanspec */
608*b4c3e9b5SBjoern A. Zeeb if (brcms_c_chspec_malformed(chspec)) {
609*b4c3e9b5SBjoern A. Zeeb brcms_err(wlc->hw->d11core, "wl%d: malformed chanspec 0x%x\n",
610*b4c3e9b5SBjoern A. Zeeb wlc->pub->unit, chspec);
611*b4c3e9b5SBjoern A. Zeeb return false;
612*b4c3e9b5SBjoern A. Zeeb }
613*b4c3e9b5SBjoern A. Zeeb
614*b4c3e9b5SBjoern A. Zeeb if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) !=
615*b4c3e9b5SBjoern A. Zeeb chspec_bandunit(chspec))
616*b4c3e9b5SBjoern A. Zeeb return false;
617*b4c3e9b5SBjoern A. Zeeb
618*b4c3e9b5SBjoern A. Zeeb return true;
619*b4c3e9b5SBjoern A. Zeeb }
620*b4c3e9b5SBjoern A. Zeeb
brcms_c_valid_chanspec_db(struct brcms_cm_info * wlc_cm,u16 chspec)621*b4c3e9b5SBjoern A. Zeeb bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec)
622*b4c3e9b5SBjoern A. Zeeb {
623*b4c3e9b5SBjoern A. Zeeb return brcms_c_valid_chanspec_ext(wlc_cm, chspec);
624*b4c3e9b5SBjoern A. Zeeb }
625*b4c3e9b5SBjoern A. Zeeb
brcms_is_radar_freq(u16 center_freq)626*b4c3e9b5SBjoern A. Zeeb static bool brcms_is_radar_freq(u16 center_freq)
627*b4c3e9b5SBjoern A. Zeeb {
628*b4c3e9b5SBjoern A. Zeeb return center_freq >= 5260 && center_freq <= 5700;
629*b4c3e9b5SBjoern A. Zeeb }
630*b4c3e9b5SBjoern A. Zeeb
brcms_reg_apply_radar_flags(struct wiphy * wiphy)631*b4c3e9b5SBjoern A. Zeeb static void brcms_reg_apply_radar_flags(struct wiphy *wiphy)
632*b4c3e9b5SBjoern A. Zeeb {
633*b4c3e9b5SBjoern A. Zeeb struct ieee80211_supported_band *sband;
634*b4c3e9b5SBjoern A. Zeeb struct ieee80211_channel *ch;
635*b4c3e9b5SBjoern A. Zeeb int i;
636*b4c3e9b5SBjoern A. Zeeb
637*b4c3e9b5SBjoern A. Zeeb sband = wiphy->bands[NL80211_BAND_5GHZ];
638*b4c3e9b5SBjoern A. Zeeb if (!sband)
639*b4c3e9b5SBjoern A. Zeeb return;
640*b4c3e9b5SBjoern A. Zeeb
641*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < sband->n_channels; i++) {
642*b4c3e9b5SBjoern A. Zeeb ch = &sband->channels[i];
643*b4c3e9b5SBjoern A. Zeeb
644*b4c3e9b5SBjoern A. Zeeb if (!brcms_is_radar_freq(ch->center_freq))
645*b4c3e9b5SBjoern A. Zeeb continue;
646*b4c3e9b5SBjoern A. Zeeb
647*b4c3e9b5SBjoern A. Zeeb /*
648*b4c3e9b5SBjoern A. Zeeb * All channels in this range should be passive and have
649*b4c3e9b5SBjoern A. Zeeb * DFS enabled.
650*b4c3e9b5SBjoern A. Zeeb */
651*b4c3e9b5SBjoern A. Zeeb if (!(ch->flags & IEEE80211_CHAN_DISABLED))
652*b4c3e9b5SBjoern A. Zeeb ch->flags |= IEEE80211_CHAN_RADAR |
653*b4c3e9b5SBjoern A. Zeeb IEEE80211_CHAN_NO_IR;
654*b4c3e9b5SBjoern A. Zeeb }
655*b4c3e9b5SBjoern A. Zeeb }
656*b4c3e9b5SBjoern A. Zeeb
657*b4c3e9b5SBjoern A. Zeeb static void
brcms_reg_apply_beaconing_flags(struct wiphy * wiphy,enum nl80211_reg_initiator initiator)658*b4c3e9b5SBjoern A. Zeeb brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
659*b4c3e9b5SBjoern A. Zeeb enum nl80211_reg_initiator initiator)
660*b4c3e9b5SBjoern A. Zeeb {
661*b4c3e9b5SBjoern A. Zeeb struct ieee80211_supported_band *sband;
662*b4c3e9b5SBjoern A. Zeeb struct ieee80211_channel *ch;
663*b4c3e9b5SBjoern A. Zeeb const struct ieee80211_reg_rule *rule;
664*b4c3e9b5SBjoern A. Zeeb int band, i;
665*b4c3e9b5SBjoern A. Zeeb
666*b4c3e9b5SBjoern A. Zeeb for (band = 0; band < NUM_NL80211_BANDS; band++) {
667*b4c3e9b5SBjoern A. Zeeb sband = wiphy->bands[band];
668*b4c3e9b5SBjoern A. Zeeb if (!sband)
669*b4c3e9b5SBjoern A. Zeeb continue;
670*b4c3e9b5SBjoern A. Zeeb
671*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < sband->n_channels; i++) {
672*b4c3e9b5SBjoern A. Zeeb ch = &sband->channels[i];
673*b4c3e9b5SBjoern A. Zeeb
674*b4c3e9b5SBjoern A. Zeeb if (ch->flags &
675*b4c3e9b5SBjoern A. Zeeb (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR))
676*b4c3e9b5SBjoern A. Zeeb continue;
677*b4c3e9b5SBjoern A. Zeeb
678*b4c3e9b5SBjoern A. Zeeb if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
679*b4c3e9b5SBjoern A. Zeeb rule = freq_reg_info(wiphy,
680*b4c3e9b5SBjoern A. Zeeb MHZ_TO_KHZ(ch->center_freq));
681*b4c3e9b5SBjoern A. Zeeb if (IS_ERR(rule))
682*b4c3e9b5SBjoern A. Zeeb continue;
683*b4c3e9b5SBjoern A. Zeeb
684*b4c3e9b5SBjoern A. Zeeb if (!(rule->flags & NL80211_RRF_NO_IR))
685*b4c3e9b5SBjoern A. Zeeb ch->flags &= ~IEEE80211_CHAN_NO_IR;
686*b4c3e9b5SBjoern A. Zeeb } else if (ch->beacon_found) {
687*b4c3e9b5SBjoern A. Zeeb ch->flags &= ~IEEE80211_CHAN_NO_IR;
688*b4c3e9b5SBjoern A. Zeeb }
689*b4c3e9b5SBjoern A. Zeeb }
690*b4c3e9b5SBjoern A. Zeeb }
691*b4c3e9b5SBjoern A. Zeeb }
692*b4c3e9b5SBjoern A. Zeeb
brcms_reg_notifier(struct wiphy * wiphy,struct regulatory_request * request)693*b4c3e9b5SBjoern A. Zeeb static void brcms_reg_notifier(struct wiphy *wiphy,
694*b4c3e9b5SBjoern A. Zeeb struct regulatory_request *request)
695*b4c3e9b5SBjoern A. Zeeb {
696*b4c3e9b5SBjoern A. Zeeb struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
697*b4c3e9b5SBjoern A. Zeeb struct brcms_info *wl = hw->priv;
698*b4c3e9b5SBjoern A. Zeeb struct brcms_c_info *wlc = wl->wlc;
699*b4c3e9b5SBjoern A. Zeeb struct ieee80211_supported_band *sband;
700*b4c3e9b5SBjoern A. Zeeb struct ieee80211_channel *ch;
701*b4c3e9b5SBjoern A. Zeeb int band, i;
702*b4c3e9b5SBjoern A. Zeeb bool ch_found = false;
703*b4c3e9b5SBjoern A. Zeeb
704*b4c3e9b5SBjoern A. Zeeb brcms_reg_apply_radar_flags(wiphy);
705*b4c3e9b5SBjoern A. Zeeb
706*b4c3e9b5SBjoern A. Zeeb if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
707*b4c3e9b5SBjoern A. Zeeb brcms_reg_apply_beaconing_flags(wiphy, request->initiator);
708*b4c3e9b5SBjoern A. Zeeb
709*b4c3e9b5SBjoern A. Zeeb /* Disable radio if all channels disallowed by regulatory */
710*b4c3e9b5SBjoern A. Zeeb for (band = 0; !ch_found && band < NUM_NL80211_BANDS; band++) {
711*b4c3e9b5SBjoern A. Zeeb sband = wiphy->bands[band];
712*b4c3e9b5SBjoern A. Zeeb if (!sband)
713*b4c3e9b5SBjoern A. Zeeb continue;
714*b4c3e9b5SBjoern A. Zeeb
715*b4c3e9b5SBjoern A. Zeeb for (i = 0; !ch_found && i < sband->n_channels; i++) {
716*b4c3e9b5SBjoern A. Zeeb ch = &sband->channels[i];
717*b4c3e9b5SBjoern A. Zeeb
718*b4c3e9b5SBjoern A. Zeeb if (!(ch->flags & IEEE80211_CHAN_DISABLED))
719*b4c3e9b5SBjoern A. Zeeb ch_found = true;
720*b4c3e9b5SBjoern A. Zeeb }
721*b4c3e9b5SBjoern A. Zeeb }
722*b4c3e9b5SBjoern A. Zeeb
723*b4c3e9b5SBjoern A. Zeeb if (ch_found) {
724*b4c3e9b5SBjoern A. Zeeb mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
725*b4c3e9b5SBjoern A. Zeeb } else {
726*b4c3e9b5SBjoern A. Zeeb mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
727*b4c3e9b5SBjoern A. Zeeb brcms_err(wlc->hw->d11core,
728*b4c3e9b5SBjoern A. Zeeb "wl%d: %s: no valid channel for \"%s\"\n",
729*b4c3e9b5SBjoern A. Zeeb wlc->pub->unit, __func__, request->alpha2);
730*b4c3e9b5SBjoern A. Zeeb }
731*b4c3e9b5SBjoern A. Zeeb
732*b4c3e9b5SBjoern A. Zeeb if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G)
733*b4c3e9b5SBjoern A. Zeeb wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
734*b4c3e9b5SBjoern A. Zeeb brcms_c_japan_ccode(request->alpha2));
735*b4c3e9b5SBjoern A. Zeeb }
736*b4c3e9b5SBjoern A. Zeeb
brcms_c_regd_init(struct brcms_c_info * wlc)737*b4c3e9b5SBjoern A. Zeeb void brcms_c_regd_init(struct brcms_c_info *wlc)
738*b4c3e9b5SBjoern A. Zeeb {
739*b4c3e9b5SBjoern A. Zeeb struct wiphy *wiphy = wlc->wiphy;
740*b4c3e9b5SBjoern A. Zeeb const struct brcms_regd *regd = wlc->cmi->world_regd;
741*b4c3e9b5SBjoern A. Zeeb struct ieee80211_supported_band *sband;
742*b4c3e9b5SBjoern A. Zeeb struct ieee80211_channel *ch;
743*b4c3e9b5SBjoern A. Zeeb struct brcms_chanvec sup_chan;
744*b4c3e9b5SBjoern A. Zeeb struct brcms_band *band;
745*b4c3e9b5SBjoern A. Zeeb int band_idx, i;
746*b4c3e9b5SBjoern A. Zeeb
747*b4c3e9b5SBjoern A. Zeeb /* Disable any channels not supported by the phy */
748*b4c3e9b5SBjoern A. Zeeb for (band_idx = 0; band_idx < wlc->pub->_nbands; band_idx++) {
749*b4c3e9b5SBjoern A. Zeeb band = wlc->bandstate[band_idx];
750*b4c3e9b5SBjoern A. Zeeb
751*b4c3e9b5SBjoern A. Zeeb wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
752*b4c3e9b5SBjoern A. Zeeb &sup_chan);
753*b4c3e9b5SBjoern A. Zeeb
754*b4c3e9b5SBjoern A. Zeeb if (band_idx == BAND_2G_INDEX)
755*b4c3e9b5SBjoern A. Zeeb sband = wiphy->bands[NL80211_BAND_2GHZ];
756*b4c3e9b5SBjoern A. Zeeb else
757*b4c3e9b5SBjoern A. Zeeb sband = wiphy->bands[NL80211_BAND_5GHZ];
758*b4c3e9b5SBjoern A. Zeeb
759*b4c3e9b5SBjoern A. Zeeb for (i = 0; i < sband->n_channels; i++) {
760*b4c3e9b5SBjoern A. Zeeb ch = &sband->channels[i];
761*b4c3e9b5SBjoern A. Zeeb if (!isset(sup_chan.vec, ch->hw_value))
762*b4c3e9b5SBjoern A. Zeeb ch->flags |= IEEE80211_CHAN_DISABLED;
763*b4c3e9b5SBjoern A. Zeeb }
764*b4c3e9b5SBjoern A. Zeeb }
765*b4c3e9b5SBjoern A. Zeeb
766*b4c3e9b5SBjoern A. Zeeb wlc->wiphy->reg_notifier = brcms_reg_notifier;
767*b4c3e9b5SBjoern A. Zeeb wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
768*b4c3e9b5SBjoern A. Zeeb REGULATORY_STRICT_REG;
769*b4c3e9b5SBjoern A. Zeeb wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain);
770*b4c3e9b5SBjoern A. Zeeb brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER);
771*b4c3e9b5SBjoern A. Zeeb }
772