diff options
author | Arend van Spriel <arend@broadcom.com> | 2011-10-05 07:19:03 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-10-11 15:55:30 -0400 |
commit | 5b435de0d786869c95d1962121af0d7df2542009 (patch) | |
tree | 9b7cfbc4aa9f1ec0e719e3a0c677bd9f4e56540d /drivers/net/wireless/brcm80211/brcmsmac/channel.c | |
parent | 5f68a2b0a890d086e40fc7b55f4a0c32c28bc0d2 (diff) |
net: wireless: add brcm80211 drivers
Add the brcm80211 tree to drivers/net/wireless, and disable the version that's
in drivers/staging. This version includes the sources currently in staging,
plus any changes that have been sent out for review.
Sources in staging will be deleted in a followup patch.
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmsmac/channel.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmsmac/channel.c | 1565 |
1 files changed, 1565 insertions, 0 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c new file mode 100644 index 000000000000..a1b415da6c3a --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c | |||
@@ -0,0 +1,1565 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Broadcom Corporation | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <net/mac80211.h> | ||
19 | |||
20 | #include <defs.h> | ||
21 | #include "pub.h" | ||
22 | #include "phy/phy_hal.h" | ||
23 | #include "main.h" | ||
24 | #include "stf.h" | ||
25 | #include "channel.h" | ||
26 | |||
27 | /* QDB() macro takes a dB value and converts to a quarter dB value */ | ||
28 | #define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) | ||
29 | |||
30 | #define LOCALE_CHAN_01_11 (1<<0) | ||
31 | #define LOCALE_CHAN_12_13 (1<<1) | ||
32 | #define LOCALE_CHAN_14 (1<<2) | ||
33 | #define LOCALE_SET_5G_LOW_JP1 (1<<3) /* 34-48, step 2 */ | ||
34 | #define LOCALE_SET_5G_LOW_JP2 (1<<4) /* 34-46, step 4 */ | ||
35 | #define LOCALE_SET_5G_LOW1 (1<<5) /* 36-48, step 4 */ | ||
36 | #define LOCALE_SET_5G_LOW2 (1<<6) /* 52 */ | ||
37 | #define LOCALE_SET_5G_LOW3 (1<<7) /* 56-64, step 4 */ | ||
38 | #define LOCALE_SET_5G_MID1 (1<<8) /* 100-116, step 4 */ | ||
39 | #define LOCALE_SET_5G_MID2 (1<<9) /* 120-124, step 4 */ | ||
40 | #define LOCALE_SET_5G_MID3 (1<<10) /* 128 */ | ||
41 | #define LOCALE_SET_5G_HIGH1 (1<<11) /* 132-140, step 4 */ | ||
42 | #define LOCALE_SET_5G_HIGH2 (1<<12) /* 149-161, step 4 */ | ||
43 | #define LOCALE_SET_5G_HIGH3 (1<<13) /* 165 */ | ||
44 | #define LOCALE_CHAN_52_140_ALL (1<<14) | ||
45 | #define LOCALE_SET_5G_HIGH4 (1<<15) /* 184-216 */ | ||
46 | |||
47 | #define LOCALE_CHAN_36_64 (LOCALE_SET_5G_LOW1 | \ | ||
48 | LOCALE_SET_5G_LOW2 | \ | ||
49 | LOCALE_SET_5G_LOW3) | ||
50 | #define LOCALE_CHAN_52_64 (LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3) | ||
51 | #define LOCALE_CHAN_100_124 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2) | ||
52 | #define LOCALE_CHAN_100_140 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2 | \ | ||
53 | LOCALE_SET_5G_MID3 | LOCALE_SET_5G_HIGH1) | ||
54 | #define LOCALE_CHAN_149_165 (LOCALE_SET_5G_HIGH2 | LOCALE_SET_5G_HIGH3) | ||
55 | #define LOCALE_CHAN_184_216 LOCALE_SET_5G_HIGH4 | ||
56 | |||
57 | #define LOCALE_CHAN_01_14 (LOCALE_CHAN_01_11 | \ | ||
58 | LOCALE_CHAN_12_13 | \ | ||
59 | LOCALE_CHAN_14) | ||
60 | |||
61 | #define LOCALE_RADAR_SET_NONE 0 | ||
62 | #define LOCALE_RADAR_SET_1 1 | ||
63 | |||
64 | #define LOCALE_RESTRICTED_NONE 0 | ||
65 | #define LOCALE_RESTRICTED_SET_2G_SHORT 1 | ||
66 | #define LOCALE_RESTRICTED_CHAN_165 2 | ||
67 | #define LOCALE_CHAN_ALL_5G 3 | ||
68 | #define LOCALE_RESTRICTED_JAPAN_LEGACY 4 | ||
69 | #define LOCALE_RESTRICTED_11D_2G 5 | ||
70 | #define LOCALE_RESTRICTED_11D_5G 6 | ||
71 | #define LOCALE_RESTRICTED_LOW_HI 7 | ||
72 | #define LOCALE_RESTRICTED_12_13_14 8 | ||
73 | |||
74 | #define LOCALE_2G_IDX_i 0 | ||
75 | #define LOCALE_5G_IDX_11 0 | ||
76 | #define LOCALE_MIMO_IDX_bn 0 | ||
77 | #define LOCALE_MIMO_IDX_11n 0 | ||
78 | |||
79 | /* max of BAND_5G_PWR_LVLS and 6 for 2.4 GHz */ | ||
80 | #define BRCMS_MAXPWR_TBL_SIZE 6 | ||
81 | /* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */ | ||
82 | #define BRCMS_MAXPWR_MIMO_TBL_SIZE 14 | ||
83 | |||
84 | /* power level in group of 2.4GHz band channels: | ||
85 | * maxpwr[0] - CCK channels [1] | ||
86 | * maxpwr[1] - CCK channels [2-10] | ||
87 | * maxpwr[2] - CCK channels [11-14] | ||
88 | * maxpwr[3] - OFDM channels [1] | ||
89 | * maxpwr[4] - OFDM channels [2-10] | ||
90 | * maxpwr[5] - OFDM channels [11-14] | ||
91 | */ | ||
92 | |||
93 | /* maxpwr mapping to 5GHz band channels: | ||
94 | * maxpwr[0] - channels [34-48] | ||
95 | * maxpwr[1] - channels [52-60] | ||
96 | * maxpwr[2] - channels [62-64] | ||
97 | * maxpwr[3] - channels [100-140] | ||
98 | * maxpwr[4] - channels [149-165] | ||
99 | */ | ||
100 | #define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */ | ||
101 | |||
102 | #define LC(id) LOCALE_MIMO_IDX_ ## id | ||
103 | |||
104 | #define LC_2G(id) LOCALE_2G_IDX_ ## id | ||
105 | |||
106 | #define LC_5G(id) LOCALE_5G_IDX_ ## id | ||
107 | |||
108 | #define LOCALES(band2, band5, mimo2, mimo5) \ | ||
109 | {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)} | ||
110 | |||
111 | /* macro to get 2.4 GHz channel group index for tx power */ | ||
112 | #define CHANNEL_POWER_IDX_2G_CCK(c) (((c) < 2) ? 0 : (((c) < 11) ? 1 : 2)) | ||
113 | #define CHANNEL_POWER_IDX_2G_OFDM(c) (((c) < 2) ? 3 : (((c) < 11) ? 4 : 5)) | ||
114 | |||
115 | /* macro to get 5 GHz channel group index for tx power */ | ||
116 | #define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \ | ||
117 | (((c) < 62) ? 1 : \ | ||
118 | (((c) < 100) ? 2 : \ | ||
119 | (((c) < 149) ? 3 : 4)))) | ||
120 | |||
121 | #define ISDFS_EU(fl) (((fl) & BRCMS_DFS_EU) == BRCMS_DFS_EU) | ||
122 | |||
123 | struct brcms_cm_band { | ||
124 | /* struct locale_info flags */ | ||
125 | u8 locale_flags; | ||
126 | /* List of valid channels in the country */ | ||
127 | struct brcms_chanvec valid_channels; | ||
128 | /* List of restricted use channels */ | ||
129 | const struct brcms_chanvec *restricted_channels; | ||
130 | /* List of radar sensitive channels */ | ||
131 | const struct brcms_chanvec *radar_channels; | ||
132 | u8 PAD[8]; | ||
133 | }; | ||
134 | |||
135 | /* locale per-channel tx power limits for MIMO frames | ||
136 | * maxpwr arrays are index by channel for 2.4 GHz limits, and | ||
137 | * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel) | ||
138 | */ | ||
139 | struct locale_mimo_info { | ||
140 | /* tx 20 MHz power limits, qdBm units */ | ||
141 | s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE]; | ||
142 | /* tx 40 MHz power limits, qdBm units */ | ||
143 | s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE]; | ||
144 | u8 flags; | ||
145 | }; | ||
146 | |||
147 | /* Country names and abbreviations with locale defined from ISO 3166 */ | ||
148 | struct country_info { | ||
149 | const u8 locale_2G; /* 2.4G band locale */ | ||
150 | const u8 locale_5G; /* 5G band locale */ | ||
151 | const u8 locale_mimo_2G; /* 2.4G mimo info */ | ||
152 | const u8 locale_mimo_5G; /* 5G mimo info */ | ||
153 | }; | ||
154 | |||
155 | struct brcms_cm_info { | ||
156 | struct brcms_pub *pub; | ||
157 | struct brcms_c_info *wlc; | ||
158 | char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */ | ||
159 | uint srom_regrev; /* Regulatory Rev for the SROM ccode */ | ||
160 | const struct country_info *country; /* current country def */ | ||
161 | char ccode[BRCM_CNTRY_BUF_SZ]; /* current internal Country Code */ | ||
162 | uint regrev; /* current Regulatory Revision */ | ||
163 | char country_abbrev[BRCM_CNTRY_BUF_SZ]; /* current advertised ccode */ | ||
164 | /* per-band state (one per phy/radio) */ | ||
165 | struct brcms_cm_band bandstate[MAXBANDS]; | ||
166 | /* quiet channels currently for radar sensitivity or 11h support */ | ||
167 | /* channels on which we cannot transmit */ | ||
168 | struct brcms_chanvec quiet_channels; | ||
169 | }; | ||
170 | |||
171 | /* locale channel and power info. */ | ||
172 | struct locale_info { | ||
173 | u32 valid_channels; | ||
174 | /* List of radar sensitive channels */ | ||
175 | u8 radar_channels; | ||
176 | /* List of channels used only if APs are detected */ | ||
177 | u8 restricted_channels; | ||
178 | /* Max tx pwr in qdBm for each sub-band */ | ||
179 | s8 maxpwr[BRCMS_MAXPWR_TBL_SIZE]; | ||
180 | /* Country IE advertised max tx pwr in dBm per sub-band */ | ||
181 | s8 pub_maxpwr[BAND_5G_PWR_LVLS]; | ||
182 | u8 flags; | ||
183 | }; | ||
184 | |||
185 | /* Regulatory Matrix Spreadsheet (CLM) MIMO v3.7.9 */ | ||
186 | |||
187 | /* | ||
188 | * Some common channel sets | ||
189 | */ | ||
190 | |||
191 | /* No channels */ | ||
192 | static const struct brcms_chanvec chanvec_none = { | ||
193 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
194 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
195 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
196 | 0x00, 0x00, 0x00, 0x00} | ||
197 | }; | ||
198 | |||
199 | /* All 2.4 GHz HW channels */ | ||
200 | static const struct brcms_chanvec chanvec_all_2G = { | ||
201 | {0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
202 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
203 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
204 | 0x00, 0x00, 0x00, 0x00} | ||
205 | }; | ||
206 | |||
207 | /* All 5 GHz HW channels */ | ||
208 | static const struct brcms_chanvec chanvec_all_5G = { | ||
209 | {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x11, 0x11, | ||
210 | 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, | ||
211 | 0x11, 0x11, 0x20, 0x22, 0x22, 0x00, 0x00, 0x11, | ||
212 | 0x11, 0x11, 0x11, 0x01} | ||
213 | }; | ||
214 | |||
215 | /* | ||
216 | * Radar channel sets | ||
217 | */ | ||
218 | |||
219 | /* Channels 52 - 64, 100 - 140 */ | ||
220 | static const struct brcms_chanvec radar_set1 = { | ||
221 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, /* 52 - 60 */ | ||
222 | 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, /* 64, 100 - 124 */ | ||
223 | 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128 - 140 */ | ||
224 | 0x00, 0x00, 0x00, 0x00} | ||
225 | }; | ||
226 | |||
227 | /* | ||
228 | * Restricted channel sets | ||
229 | */ | ||
230 | |||
231 | /* Channels 34, 38, 42, 46 */ | ||
232 | static const struct brcms_chanvec restricted_set_japan_legacy = { | ||
233 | {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, | ||
234 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
235 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
236 | 0x00, 0x00, 0x00, 0x00} | ||
237 | }; | ||
238 | |||
239 | /* Channels 12, 13 */ | ||
240 | static const struct brcms_chanvec restricted_set_2g_short = { | ||
241 | {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
242 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
243 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
244 | 0x00, 0x00, 0x00, 0x00} | ||
245 | }; | ||
246 | |||
247 | /* Channel 165 */ | ||
248 | static const struct brcms_chanvec restricted_chan_165 = { | ||
249 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
250 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
251 | 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, | ||
252 | 0x00, 0x00, 0x00, 0x00} | ||
253 | }; | ||
254 | |||
255 | /* Channels 36 - 48 & 149 - 165 */ | ||
256 | static const struct brcms_chanvec restricted_low_hi = { | ||
257 | {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00, | ||
258 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
259 | 0x00, 0x00, 0x20, 0x22, 0x22, 0x00, 0x00, 0x00, | ||
260 | 0x00, 0x00, 0x00, 0x00} | ||
261 | }; | ||
262 | |||
263 | /* Channels 12 - 14 */ | ||
264 | static const struct brcms_chanvec restricted_set_12_13_14 = { | ||
265 | {0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
266 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
267 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
268 | 0x00, 0x00, 0x00, 0x00} | ||
269 | }; | ||
270 | |||
271 | /* global memory to provide working buffer for expanded locale */ | ||
272 | |||
273 | static const struct brcms_chanvec *g_table_radar_set[] = { | ||
274 | &chanvec_none, | ||
275 | &radar_set1 | ||
276 | }; | ||
277 | |||
278 | static const struct brcms_chanvec *g_table_restricted_chan[] = { | ||
279 | &chanvec_none, /* restricted_set_none */ | ||
280 | &restricted_set_2g_short, | ||
281 | &restricted_chan_165, | ||
282 | &chanvec_all_5G, | ||
283 | &restricted_set_japan_legacy, | ||
284 | &chanvec_all_2G, /* restricted_set_11d_2G */ | ||
285 | &chanvec_all_5G, /* restricted_set_11d_5G */ | ||
286 | &restricted_low_hi, | ||
287 | &restricted_set_12_13_14 | ||
288 | }; | ||
289 | |||
290 | static const struct brcms_chanvec locale_2g_01_11 = { | ||
291 | {0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
292 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
293 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
294 | 0x00, 0x00, 0x00, 0x00} | ||
295 | }; | ||
296 | |||
297 | static const struct brcms_chanvec locale_2g_12_13 = { | ||
298 | {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
299 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
300 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
301 | 0x00, 0x00, 0x00, 0x00} | ||
302 | }; | ||
303 | |||
304 | static const struct brcms_chanvec locale_2g_14 = { | ||
305 | {0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
306 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
307 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
308 | 0x00, 0x00, 0x00, 0x00} | ||
309 | }; | ||
310 | |||
311 | static const struct brcms_chanvec locale_5g_LOW_JP1 = { | ||
312 | {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x01, 0x00, | ||
313 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
314 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
315 | 0x00, 0x00, 0x00, 0x00} | ||
316 | }; | ||
317 | |||
318 | static const struct brcms_chanvec locale_5g_LOW_JP2 = { | ||
319 | {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, | ||
320 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
321 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
322 | 0x00, 0x00, 0x00, 0x00} | ||
323 | }; | ||
324 | |||
325 | static const struct brcms_chanvec locale_5g_LOW1 = { | ||
326 | {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00, | ||
327 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
328 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
329 | 0x00, 0x00, 0x00, 0x00} | ||
330 | }; | ||
331 | |||
332 | static const struct brcms_chanvec locale_5g_LOW2 = { | ||
333 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, | ||
334 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
335 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
336 | 0x00, 0x00, 0x00, 0x00} | ||
337 | }; | ||
338 | |||
339 | static const struct brcms_chanvec locale_5g_LOW3 = { | ||
340 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, | ||
341 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
342 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
343 | 0x00, 0x00, 0x00, 0x00} | ||
344 | }; | ||
345 | |||
346 | static const struct brcms_chanvec locale_5g_MID1 = { | ||
347 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
348 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x00, | ||
349 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
350 | 0x00, 0x00, 0x00, 0x00} | ||
351 | }; | ||
352 | |||
353 | static const struct brcms_chanvec locale_5g_MID2 = { | ||
354 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
355 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, | ||
356 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
357 | 0x00, 0x00, 0x00, 0x00} | ||
358 | }; | ||
359 | |||
360 | static const struct brcms_chanvec locale_5g_MID3 = { | ||
361 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
362 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
363 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
364 | 0x00, 0x00, 0x00, 0x00} | ||
365 | }; | ||
366 | |||
367 | static const struct brcms_chanvec locale_5g_HIGH1 = { | ||
368 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
369 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
370 | 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
371 | 0x00, 0x00, 0x00, 0x00} | ||
372 | }; | ||
373 | |||
374 | static const struct brcms_chanvec locale_5g_HIGH2 = { | ||
375 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
376 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
377 | 0x00, 0x00, 0x20, 0x22, 0x02, 0x00, 0x00, 0x00, | ||
378 | 0x00, 0x00, 0x00, 0x00} | ||
379 | }; | ||
380 | |||
381 | static const struct brcms_chanvec locale_5g_HIGH3 = { | ||
382 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
383 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
384 | 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, | ||
385 | 0x00, 0x00, 0x00, 0x00} | ||
386 | }; | ||
387 | |||
388 | static const struct brcms_chanvec locale_5g_52_140_ALL = { | ||
389 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, | ||
390 | 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, | ||
391 | 0x11, 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, | ||
392 | 0x00, 0x00, 0x00, 0x00} | ||
393 | }; | ||
394 | |||
395 | static const struct brcms_chanvec locale_5g_HIGH4 = { | ||
396 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
397 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
398 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, | ||
399 | 0x11, 0x11, 0x11, 0x11} | ||
400 | }; | ||
401 | |||
402 | static const struct brcms_chanvec *g_table_locale_base[] = { | ||
403 | &locale_2g_01_11, | ||
404 | &locale_2g_12_13, | ||
405 | &locale_2g_14, | ||
406 | &locale_5g_LOW_JP1, | ||
407 | &locale_5g_LOW_JP2, | ||
408 | &locale_5g_LOW1, | ||
409 | &locale_5g_LOW2, | ||
410 | &locale_5g_LOW3, | ||
411 | &locale_5g_MID1, | ||
412 | &locale_5g_MID2, | ||
413 | &locale_5g_MID3, | ||
414 | &locale_5g_HIGH1, | ||
415 | &locale_5g_HIGH2, | ||
416 | &locale_5g_HIGH3, | ||
417 | &locale_5g_52_140_ALL, | ||
418 | &locale_5g_HIGH4 | ||
419 | }; | ||
420 | |||
421 | static void brcms_c_locale_add_channels(struct brcms_chanvec *target, | ||
422 | const struct brcms_chanvec *channels) | ||
423 | { | ||
424 | u8 i; | ||
425 | for (i = 0; i < sizeof(struct brcms_chanvec); i++) | ||
426 | target->vec[i] |= channels->vec[i]; | ||
427 | } | ||
428 | |||
429 | static void brcms_c_locale_get_channels(const struct locale_info *locale, | ||
430 | struct brcms_chanvec *channels) | ||
431 | { | ||
432 | u8 i; | ||
433 | |||
434 | memset(channels, 0, sizeof(struct brcms_chanvec)); | ||
435 | |||
436 | for (i = 0; i < ARRAY_SIZE(g_table_locale_base); i++) { | ||
437 | if (locale->valid_channels & (1 << i)) | ||
438 | brcms_c_locale_add_channels(channels, | ||
439 | g_table_locale_base[i]); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | /* | ||
444 | * Locale Definitions - 2.4 GHz | ||
445 | */ | ||
446 | static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */ | ||
447 | LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13, | ||
448 | LOCALE_RADAR_SET_NONE, | ||
449 | LOCALE_RESTRICTED_SET_2G_SHORT, | ||
450 | {QDB(19), QDB(19), QDB(19), | ||
451 | QDB(19), QDB(19), QDB(19)}, | ||
452 | {20, 20, 20, 0}, | ||
453 | BRCMS_EIRP | ||
454 | }; | ||
455 | |||
456 | /* | ||
457 | * Locale Definitions - 5 GHz | ||
458 | */ | ||
459 | static const struct locale_info locale_11 = { | ||
460 | /* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */ | ||
461 | LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165, | ||
462 | LOCALE_RADAR_SET_1, | ||
463 | LOCALE_RESTRICTED_NONE, | ||
464 | {QDB(21), QDB(21), QDB(21), QDB(21), QDB(21)}, | ||
465 | {23, 23, 23, 30, 30}, | ||
466 | BRCMS_EIRP | BRCMS_DFS_EU | ||
467 | }; | ||
468 | |||
469 | static const struct locale_info *g_locale_2g_table[] = { | ||
470 | &locale_i | ||
471 | }; | ||
472 | |||
473 | static const struct locale_info *g_locale_5g_table[] = { | ||
474 | &locale_11 | ||
475 | }; | ||
476 | |||
477 | /* | ||
478 | * MIMO Locale Definitions - 2.4 GHz | ||
479 | */ | ||
480 | static const struct locale_mimo_info locale_bn = { | ||
481 | {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), | ||
482 | QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), | ||
483 | QDB(13), QDB(13), QDB(13)}, | ||
484 | {0, 0, QDB(13), QDB(13), QDB(13), | ||
485 | QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), | ||
486 | QDB(13), 0, 0}, | ||
487 | 0 | ||
488 | }; | ||
489 | |||
490 | static const struct locale_mimo_info *g_mimo_2g_table[] = { | ||
491 | &locale_bn | ||
492 | }; | ||
493 | |||
494 | /* | ||
495 | * MIMO Locale Definitions - 5 GHz | ||
496 | */ | ||
497 | static const struct locale_mimo_info locale_11n = { | ||
498 | { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)}, | ||
499 | {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)}, | ||
500 | 0 | ||
501 | }; | ||
502 | |||
503 | static const struct locale_mimo_info *g_mimo_5g_table[] = { | ||
504 | &locale_11n | ||
505 | }; | ||
506 | |||
507 | static const struct { | ||
508 | char abbrev[BRCM_CNTRY_BUF_SZ]; /* country abbreviation */ | ||
509 | struct country_info country; | ||
510 | } cntry_locales[] = { | ||
511 | { | ||
512 | "X2", LOCALES(i, 11, bn, 11n)}, /* Worldwide RoW 2 */ | ||
513 | }; | ||
514 | |||
515 | #ifdef SUPPORT_40MHZ | ||
516 | /* 20MHz channel info for 40MHz pairing support */ | ||
517 | struct chan20_info { | ||
518 | u8 sb; | ||
519 | u8 adj_sbs; | ||
520 | }; | ||
521 | |||
522 | /* indicates adjacent channels that are allowed for a 40 Mhz channel and | ||
523 | * those that permitted by the HT | ||
524 | */ | ||
525 | struct chan20_info chan20_info[] = { | ||
526 | /* 11b/11g */ | ||
527 | /* 0 */ {1, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
528 | /* 1 */ {2, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
529 | /* 2 */ {3, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
530 | /* 3 */ {4, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
531 | /* 4 */ {5, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, | ||
532 | /* 5 */ {6, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, | ||
533 | /* 6 */ {7, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, | ||
534 | /* 7 */ {8, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, | ||
535 | /* 8 */ {9, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, | ||
536 | /* 9 */ {10, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
537 | /* 10 */ {11, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
538 | /* 11 */ {12, (CH_LOWER_SB)}, | ||
539 | /* 12 */ {13, (CH_LOWER_SB)}, | ||
540 | /* 13 */ {14, (CH_LOWER_SB)}, | ||
541 | |||
542 | /* 11a japan high */ | ||
543 | /* 14 */ {34, (CH_UPPER_SB)}, | ||
544 | /* 15 */ {38, (CH_LOWER_SB)}, | ||
545 | /* 16 */ {42, (CH_LOWER_SB)}, | ||
546 | /* 17 */ {46, (CH_LOWER_SB)}, | ||
547 | |||
548 | /* 11a usa low */ | ||
549 | /* 18 */ {36, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
550 | /* 19 */ {40, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
551 | /* 20 */ {44, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
552 | /* 21 */ {48, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
553 | /* 22 */ {52, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
554 | /* 23 */ {56, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
555 | /* 24 */ {60, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
556 | /* 25 */ {64, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
557 | |||
558 | /* 11a Europe */ | ||
559 | /* 26 */ {100, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
560 | /* 27 */ {104, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
561 | /* 28 */ {108, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
562 | /* 29 */ {112, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
563 | /* 30 */ {116, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
564 | /* 31 */ {120, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
565 | /* 32 */ {124, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
566 | /* 33 */ {128, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
567 | /* 34 */ {132, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
568 | /* 35 */ {136, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
569 | /* 36 */ {140, (CH_LOWER_SB)}, | ||
570 | |||
571 | /* 11a usa high, ref5 only */ | ||
572 | /* The 0x80 bit in pdiv means these are REF5, other entries are REF20 */ | ||
573 | /* 37 */ {149, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
574 | /* 38 */ {153, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
575 | /* 39 */ {157, (CH_UPPER_SB | CH_EWA_VALID)}, | ||
576 | /* 40 */ {161, (CH_LOWER_SB | CH_EWA_VALID)}, | ||
577 | /* 41 */ {165, (CH_LOWER_SB)}, | ||
578 | |||
579 | /* 11a japan */ | ||
580 | /* 42 */ {184, (CH_UPPER_SB)}, | ||
581 | /* 43 */ {188, (CH_LOWER_SB)}, | ||
582 | /* 44 */ {192, (CH_UPPER_SB)}, | ||
583 | /* 45 */ {196, (CH_LOWER_SB)}, | ||
584 | /* 46 */ {200, (CH_UPPER_SB)}, | ||
585 | /* 47 */ {204, (CH_LOWER_SB)}, | ||
586 | /* 48 */ {208, (CH_UPPER_SB)}, | ||
587 | /* 49 */ {212, (CH_LOWER_SB)}, | ||
588 | /* 50 */ {216, (CH_LOWER_SB)} | ||
589 | }; | ||
590 | #endif /* SUPPORT_40MHZ */ | ||
591 | |||
592 | static const struct locale_info *brcms_c_get_locale_2g(u8 locale_idx) | ||
593 | { | ||
594 | if (locale_idx >= ARRAY_SIZE(g_locale_2g_table)) | ||
595 | return NULL; /* error condition */ | ||
596 | |||
597 | return g_locale_2g_table[locale_idx]; | ||
598 | } | ||
599 | |||
600 | static const struct locale_info *brcms_c_get_locale_5g(u8 locale_idx) | ||
601 | { | ||
602 | if (locale_idx >= ARRAY_SIZE(g_locale_5g_table)) | ||
603 | return NULL; /* error condition */ | ||
604 | |||
605 | return g_locale_5g_table[locale_idx]; | ||
606 | } | ||
607 | |||
608 | static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx) | ||
609 | { | ||
610 | if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) | ||
611 | return NULL; | ||
612 | |||
613 | return g_mimo_2g_table[locale_idx]; | ||
614 | } | ||
615 | |||
616 | static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx) | ||
617 | { | ||
618 | if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) | ||
619 | return NULL; | ||
620 | |||
621 | return g_mimo_5g_table[locale_idx]; | ||
622 | } | ||
623 | |||
624 | static int | ||
625 | brcms_c_country_aggregate_map(struct brcms_cm_info *wlc_cm, const char *ccode, | ||
626 | char *mapped_ccode, uint *mapped_regrev) | ||
627 | { | ||
628 | return false; | ||
629 | } | ||
630 | |||
631 | /* Lookup a country info structure from a null terminated country | ||
632 | * abbreviation and regrev directly with no translation. | ||
633 | */ | ||
634 | static const struct country_info * | ||
635 | brcms_c_country_lookup_direct(const char *ccode, uint regrev) | ||
636 | { | ||
637 | uint size, i; | ||
638 | |||
639 | /* Should just return 0 for single locale driver. */ | ||
640 | /* Keep it this way in case we add more locales. (for now anyway) */ | ||
641 | |||
642 | /* | ||
643 | * all other country def arrays are for regrev == 0, so if | ||
644 | * regrev is non-zero, fail | ||
645 | */ | ||
646 | if (regrev > 0) | ||
647 | return NULL; | ||
648 | |||
649 | /* find matched table entry from country code */ | ||
650 | size = ARRAY_SIZE(cntry_locales); | ||
651 | for (i = 0; i < size; i++) { | ||
652 | if (strcmp(ccode, cntry_locales[i].abbrev) == 0) | ||
653 | return &cntry_locales[i].country; | ||
654 | } | ||
655 | return NULL; | ||
656 | } | ||
657 | |||
658 | static const struct country_info * | ||
659 | brcms_c_countrycode_map(struct brcms_cm_info *wlc_cm, const char *ccode, | ||
660 | char *mapped_ccode, uint *mapped_regrev) | ||
661 | { | ||
662 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
663 | const struct country_info *country; | ||
664 | uint srom_regrev = wlc_cm->srom_regrev; | ||
665 | const char *srom_ccode = wlc_cm->srom_ccode; | ||
666 | int mapped; | ||
667 | |||
668 | /* check for currently supported ccode size */ | ||
669 | if (strlen(ccode) > (BRCM_CNTRY_BUF_SZ - 1)) { | ||
670 | wiphy_err(wlc->wiphy, "wl%d: %s: ccode \"%s\" too long for " | ||
671 | "match\n", wlc->pub->unit, __func__, ccode); | ||
672 | return NULL; | ||
673 | } | ||
674 | |||
675 | /* default mapping is the given ccode and regrev 0 */ | ||
676 | strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ); | ||
677 | *mapped_regrev = 0; | ||
678 | |||
679 | /* If the desired country code matches the srom country code, | ||
680 | * then the mapped country is the srom regulatory rev. | ||
681 | * Otherwise look for an aggregate mapping. | ||
682 | */ | ||
683 | if (!strcmp(srom_ccode, ccode)) { | ||
684 | *mapped_regrev = srom_regrev; | ||
685 | mapped = 0; | ||
686 | wiphy_err(wlc->wiphy, "srom_code == ccode %s\n", __func__); | ||
687 | } else { | ||
688 | mapped = | ||
689 | brcms_c_country_aggregate_map(wlc_cm, ccode, mapped_ccode, | ||
690 | mapped_regrev); | ||
691 | } | ||
692 | |||
693 | /* find the matching built-in country definition */ | ||
694 | country = brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev); | ||
695 | |||
696 | /* if there is not an exact rev match, default to rev zero */ | ||
697 | if (country == NULL && *mapped_regrev != 0) { | ||
698 | *mapped_regrev = 0; | ||
699 | country = | ||
700 | brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev); | ||
701 | } | ||
702 | |||
703 | return country; | ||
704 | } | ||
705 | |||
706 | /* Lookup a country info structure from a null terminated country code | ||
707 | * The lookup is case sensitive. | ||
708 | */ | ||
709 | static const struct country_info * | ||
710 | brcms_c_country_lookup(struct brcms_c_info *wlc, const char *ccode) | ||
711 | { | ||
712 | const struct country_info *country; | ||
713 | char mapped_ccode[BRCM_CNTRY_BUF_SZ]; | ||
714 | uint mapped_regrev; | ||
715 | |||
716 | /* | ||
717 | * map the country code to a built-in country code, regrev, and | ||
718 | * country_info struct | ||
719 | */ | ||
720 | country = brcms_c_countrycode_map(wlc->cmi, ccode, mapped_ccode, | ||
721 | &mapped_regrev); | ||
722 | |||
723 | return country; | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * reset the quiet channels vector to the union | ||
728 | * of the restricted and radar channel sets | ||
729 | */ | ||
730 | static void brcms_c_quiet_channels_reset(struct brcms_cm_info *wlc_cm) | ||
731 | { | ||
732 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
733 | uint i, j; | ||
734 | struct brcms_band *band; | ||
735 | const struct brcms_chanvec *chanvec; | ||
736 | |||
737 | memset(&wlc_cm->quiet_channels, 0, sizeof(struct brcms_chanvec)); | ||
738 | |||
739 | band = wlc->band; | ||
740 | for (i = 0; i < wlc->pub->_nbands; | ||
741 | i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) { | ||
742 | |||
743 | /* initialize quiet channels for restricted channels */ | ||
744 | chanvec = wlc_cm->bandstate[band->bandunit].restricted_channels; | ||
745 | for (j = 0; j < sizeof(struct brcms_chanvec); j++) | ||
746 | wlc_cm->quiet_channels.vec[j] |= chanvec->vec[j]; | ||
747 | |||
748 | } | ||
749 | } | ||
750 | |||
751 | /* Is the channel valid for the current locale and current band? */ | ||
752 | static bool brcms_c_valid_channel20(struct brcms_cm_info *wlc_cm, uint val) | ||
753 | { | ||
754 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
755 | |||
756 | return ((val < MAXCHANNEL) && | ||
757 | isset(wlc_cm->bandstate[wlc->band->bandunit].valid_channels.vec, | ||
758 | val)); | ||
759 | } | ||
760 | |||
761 | /* Is the channel valid for the current locale and specified band? */ | ||
762 | static bool brcms_c_valid_channel20_in_band(struct brcms_cm_info *wlc_cm, | ||
763 | uint bandunit, uint val) | ||
764 | { | ||
765 | return ((val < MAXCHANNEL) | ||
766 | && isset(wlc_cm->bandstate[bandunit].valid_channels.vec, val)); | ||
767 | } | ||
768 | |||
769 | /* Is the channel valid for the current locale? (but don't consider channels not | ||
770 | * available due to bandlocking) | ||
771 | */ | ||
772 | static bool brcms_c_valid_channel20_db(struct brcms_cm_info *wlc_cm, uint val) | ||
773 | { | ||
774 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
775 | |||
776 | return brcms_c_valid_channel20(wlc->cmi, val) || | ||
777 | (!wlc->bandlocked | ||
778 | && brcms_c_valid_channel20_in_band(wlc->cmi, | ||
779 | OTHERBANDUNIT(wlc), val)); | ||
780 | } | ||
781 | |||
782 | /* JP, J1 - J10 are Japan ccodes */ | ||
783 | static bool brcms_c_japan_ccode(const char *ccode) | ||
784 | { | ||
785 | return (ccode[0] == 'J' && | ||
786 | (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9'))); | ||
787 | } | ||
788 | |||
789 | /* Returns true if currently set country is Japan or variant */ | ||
790 | static bool brcms_c_japan(struct brcms_c_info *wlc) | ||
791 | { | ||
792 | return brcms_c_japan_ccode(wlc->cmi->country_abbrev); | ||
793 | } | ||
794 | |||
795 | static void | ||
796 | brcms_c_channel_min_txpower_limits_with_local_constraint( | ||
797 | struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr, | ||
798 | u8 local_constraint_qdbm) | ||
799 | { | ||
800 | int j; | ||
801 | |||
802 | /* CCK Rates */ | ||
803 | for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) | ||
804 | txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm); | ||
805 | |||
806 | /* 20 MHz Legacy OFDM SISO */ | ||
807 | for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) | ||
808 | txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm); | ||
809 | |||
810 | /* 20 MHz Legacy OFDM CDD */ | ||
811 | for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) | ||
812 | txpwr->ofdm_cdd[j] = | ||
813 | min(txpwr->ofdm_cdd[j], local_constraint_qdbm); | ||
814 | |||
815 | /* 40 MHz Legacy OFDM SISO */ | ||
816 | for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) | ||
817 | txpwr->ofdm_40_siso[j] = | ||
818 | min(txpwr->ofdm_40_siso[j], local_constraint_qdbm); | ||
819 | |||
820 | /* 40 MHz Legacy OFDM CDD */ | ||
821 | for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) | ||
822 | txpwr->ofdm_40_cdd[j] = | ||
823 | min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm); | ||
824 | |||
825 | /* 20MHz MCS 0-7 SISO */ | ||
826 | for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) | ||
827 | txpwr->mcs_20_siso[j] = | ||
828 | min(txpwr->mcs_20_siso[j], local_constraint_qdbm); | ||
829 | |||
830 | /* 20MHz MCS 0-7 CDD */ | ||
831 | for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) | ||
832 | txpwr->mcs_20_cdd[j] = | ||
833 | min(txpwr->mcs_20_cdd[j], local_constraint_qdbm); | ||
834 | |||
835 | /* 20MHz MCS 0-7 STBC */ | ||
836 | for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) | ||
837 | txpwr->mcs_20_stbc[j] = | ||
838 | min(txpwr->mcs_20_stbc[j], local_constraint_qdbm); | ||
839 | |||
840 | /* 20MHz MCS 8-15 MIMO */ | ||
841 | for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) | ||
842 | txpwr->mcs_20_mimo[j] = | ||
843 | min(txpwr->mcs_20_mimo[j], local_constraint_qdbm); | ||
844 | |||
845 | /* 40MHz MCS 0-7 SISO */ | ||
846 | for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) | ||
847 | txpwr->mcs_40_siso[j] = | ||
848 | min(txpwr->mcs_40_siso[j], local_constraint_qdbm); | ||
849 | |||
850 | /* 40MHz MCS 0-7 CDD */ | ||
851 | for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) | ||
852 | txpwr->mcs_40_cdd[j] = | ||
853 | min(txpwr->mcs_40_cdd[j], local_constraint_qdbm); | ||
854 | |||
855 | /* 40MHz MCS 0-7 STBC */ | ||
856 | for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) | ||
857 | txpwr->mcs_40_stbc[j] = | ||
858 | min(txpwr->mcs_40_stbc[j], local_constraint_qdbm); | ||
859 | |||
860 | /* 40MHz MCS 8-15 MIMO */ | ||
861 | for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) | ||
862 | txpwr->mcs_40_mimo[j] = | ||
863 | min(txpwr->mcs_40_mimo[j], local_constraint_qdbm); | ||
864 | |||
865 | /* 40MHz MCS 32 */ | ||
866 | txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm); | ||
867 | |||
868 | } | ||
869 | |||
870 | /* Update the radio state (enable/disable) and tx power targets | ||
871 | * based on a new set of channel/regulatory information | ||
872 | */ | ||
873 | static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm) | ||
874 | { | ||
875 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
876 | uint chan; | ||
877 | struct txpwr_limits txpwr; | ||
878 | |||
879 | /* search for the existence of any valid channel */ | ||
880 | for (chan = 0; chan < MAXCHANNEL; chan++) { | ||
881 | if (brcms_c_valid_channel20_db(wlc->cmi, chan)) | ||
882 | break; | ||
883 | } | ||
884 | if (chan == MAXCHANNEL) | ||
885 | chan = INVCHANNEL; | ||
886 | |||
887 | /* | ||
888 | * based on the channel search above, set or | ||
889 | * clear WL_RADIO_COUNTRY_DISABLE. | ||
890 | */ | ||
891 | if (chan == INVCHANNEL) { | ||
892 | /* | ||
893 | * country/locale with no valid channels, set | ||
894 | * the radio disable bit | ||
895 | */ | ||
896 | mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); | ||
897 | wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\" " | ||
898 | "nbands %d bandlocked %d\n", wlc->pub->unit, | ||
899 | __func__, wlc_cm->country_abbrev, wlc->pub->_nbands, | ||
900 | wlc->bandlocked); | ||
901 | } else if (mboolisset(wlc->pub->radio_disabled, | ||
902 | WL_RADIO_COUNTRY_DISABLE)) { | ||
903 | /* | ||
904 | * country/locale with valid channel, clear | ||
905 | * the radio disable bit | ||
906 | */ | ||
907 | mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); | ||
908 | } | ||
909 | |||
910 | /* | ||
911 | * Now that the country abbreviation is set, if the radio supports 2G, | ||
912 | * then set channel 14 restrictions based on the new locale. | ||
913 | */ | ||
914 | if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) | ||
915 | wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, | ||
916 | brcms_c_japan(wlc) ? true : | ||
917 | false); | ||
918 | |||
919 | if (wlc->pub->up && chan != INVCHANNEL) { | ||
920 | brcms_c_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr); | ||
921 | brcms_c_channel_min_txpower_limits_with_local_constraint(wlc_cm, | ||
922 | &txpwr, BRCMS_TXPWR_MAX); | ||
923 | wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec); | ||
924 | } | ||
925 | } | ||
926 | |||
927 | static int | ||
928 | brcms_c_channels_init(struct brcms_cm_info *wlc_cm, | ||
929 | const struct country_info *country) | ||
930 | { | ||
931 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
932 | uint i, j; | ||
933 | struct brcms_band *band; | ||
934 | const struct locale_info *li; | ||
935 | struct brcms_chanvec sup_chan; | ||
936 | const struct locale_mimo_info *li_mimo; | ||
937 | |||
938 | band = wlc->band; | ||
939 | for (i = 0; i < wlc->pub->_nbands; | ||
940 | i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) { | ||
941 | |||
942 | li = (band->bandtype == BRCM_BAND_5G) ? | ||
943 | brcms_c_get_locale_5g(country->locale_5G) : | ||
944 | brcms_c_get_locale_2g(country->locale_2G); | ||
945 | wlc_cm->bandstate[band->bandunit].locale_flags = li->flags; | ||
946 | li_mimo = (band->bandtype == BRCM_BAND_5G) ? | ||
947 | brcms_c_get_mimo_5g(country->locale_mimo_5G) : | ||
948 | brcms_c_get_mimo_2g(country->locale_mimo_2G); | ||
949 | |||
950 | /* merge the mimo non-mimo locale flags */ | ||
951 | wlc_cm->bandstate[band->bandunit].locale_flags |= | ||
952 | li_mimo->flags; | ||
953 | |||
954 | wlc_cm->bandstate[band->bandunit].restricted_channels = | ||
955 | g_table_restricted_chan[li->restricted_channels]; | ||
956 | wlc_cm->bandstate[band->bandunit].radar_channels = | ||
957 | g_table_radar_set[li->radar_channels]; | ||
958 | |||
959 | /* | ||
960 | * set the channel availability, masking out the channels | ||
961 | * that may not be supported on this phy. | ||
962 | */ | ||
963 | wlc_phy_chanspec_band_validch(band->pi, band->bandtype, | ||
964 | &sup_chan); | ||
965 | brcms_c_locale_get_channels(li, | ||
966 | &wlc_cm->bandstate[band->bandunit]. | ||
967 | valid_channels); | ||
968 | for (j = 0; j < sizeof(struct brcms_chanvec); j++) | ||
969 | wlc_cm->bandstate[band->bandunit].valid_channels. | ||
970 | vec[j] &= sup_chan.vec[j]; | ||
971 | } | ||
972 | |||
973 | brcms_c_quiet_channels_reset(wlc_cm); | ||
974 | brcms_c_channels_commit(wlc_cm); | ||
975 | |||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * set the driver's current country and regulatory information | ||
981 | * using a country code as the source. Look up built in country | ||
982 | * information found with the country code. | ||
983 | */ | ||
984 | static void | ||
985 | brcms_c_set_country_common(struct brcms_cm_info *wlc_cm, | ||
986 | const char *country_abbrev, | ||
987 | const char *ccode, uint regrev, | ||
988 | const struct country_info *country) | ||
989 | { | ||
990 | const struct locale_info *locale; | ||
991 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
992 | char prev_country_abbrev[BRCM_CNTRY_BUF_SZ]; | ||
993 | |||
994 | /* save current country state */ | ||
995 | wlc_cm->country = country; | ||
996 | |||
997 | memset(&prev_country_abbrev, 0, BRCM_CNTRY_BUF_SZ); | ||
998 | strncpy(prev_country_abbrev, wlc_cm->country_abbrev, | ||
999 | BRCM_CNTRY_BUF_SZ - 1); | ||
1000 | |||
1001 | strncpy(wlc_cm->country_abbrev, country_abbrev, BRCM_CNTRY_BUF_SZ - 1); | ||
1002 | strncpy(wlc_cm->ccode, ccode, BRCM_CNTRY_BUF_SZ - 1); | ||
1003 | wlc_cm->regrev = regrev; | ||
1004 | |||
1005 | if ((wlc->pub->_n_enab & SUPPORT_11N) != | ||
1006 | wlc->protection->nmode_user) | ||
1007 | brcms_c_set_nmode(wlc); | ||
1008 | |||
1009 | brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); | ||
1010 | brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); | ||
1011 | /* set or restore gmode as required by regulatory */ | ||
1012 | locale = brcms_c_get_locale_2g(country->locale_2G); | ||
1013 | if (locale && (locale->flags & BRCMS_NO_OFDM)) | ||
1014 | brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false); | ||
1015 | else | ||
1016 | brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); | ||
1017 | |||
1018 | brcms_c_channels_init(wlc_cm, country); | ||
1019 | |||
1020 | return; | ||
1021 | } | ||
1022 | |||
1023 | static int | ||
1024 | brcms_c_set_countrycode_rev(struct brcms_cm_info *wlc_cm, | ||
1025 | const char *country_abbrev, | ||
1026 | const char *ccode, int regrev) | ||
1027 | { | ||
1028 | const struct country_info *country; | ||
1029 | char mapped_ccode[BRCM_CNTRY_BUF_SZ]; | ||
1030 | uint mapped_regrev; | ||
1031 | |||
1032 | /* if regrev is -1, lookup the mapped country code, | ||
1033 | * otherwise use the ccode and regrev directly | ||
1034 | */ | ||
1035 | if (regrev == -1) { | ||
1036 | /* | ||
1037 | * map the country code to a built-in country | ||
1038 | * code, regrev, and country_info | ||
1039 | */ | ||
1040 | country = | ||
1041 | brcms_c_countrycode_map(wlc_cm, ccode, mapped_ccode, | ||
1042 | &mapped_regrev); | ||
1043 | } else { | ||
1044 | /* find the matching built-in country definition */ | ||
1045 | country = brcms_c_country_lookup_direct(ccode, regrev); | ||
1046 | strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ); | ||
1047 | mapped_regrev = regrev; | ||
1048 | } | ||
1049 | |||
1050 | if (country == NULL) | ||
1051 | return -EINVAL; | ||
1052 | |||
1053 | /* set the driver state for the country */ | ||
1054 | brcms_c_set_country_common(wlc_cm, country_abbrev, mapped_ccode, | ||
1055 | mapped_regrev, country); | ||
1056 | |||
1057 | return 0; | ||
1058 | } | ||
1059 | |||
1060 | /* | ||
1061 | * set the driver's current country and regulatory information using | ||
1062 | * a country code as the source. Lookup built in country information | ||
1063 | * found with the country code. | ||
1064 | */ | ||
1065 | static int | ||
1066 | brcms_c_set_countrycode(struct brcms_cm_info *wlc_cm, const char *ccode) | ||
1067 | { | ||
1068 | char country_abbrev[BRCM_CNTRY_BUF_SZ]; | ||
1069 | strncpy(country_abbrev, ccode, BRCM_CNTRY_BUF_SZ); | ||
1070 | return brcms_c_set_countrycode_rev(wlc_cm, country_abbrev, ccode, -1); | ||
1071 | } | ||
1072 | |||
1073 | struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) | ||
1074 | { | ||
1075 | struct brcms_cm_info *wlc_cm; | ||
1076 | char country_abbrev[BRCM_CNTRY_BUF_SZ]; | ||
1077 | const struct country_info *country; | ||
1078 | struct brcms_pub *pub = wlc->pub; | ||
1079 | char *ccode; | ||
1080 | |||
1081 | BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); | ||
1082 | |||
1083 | wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC); | ||
1084 | if (wlc_cm == NULL) | ||
1085 | return NULL; | ||
1086 | wlc_cm->pub = pub; | ||
1087 | wlc_cm->wlc = wlc; | ||
1088 | wlc->cmi = wlc_cm; | ||
1089 | |||
1090 | /* store the country code for passing up as a regulatory hint */ | ||
1091 | ccode = getvar(wlc->hw->sih, BRCMS_SROM_CCODE); | ||
1092 | if (ccode) | ||
1093 | strncpy(wlc->pub->srom_ccode, ccode, BRCM_CNTRY_BUF_SZ - 1); | ||
1094 | |||
1095 | /* | ||
1096 | * internal country information which must match | ||
1097 | * regulatory constraints in firmware | ||
1098 | */ | ||
1099 | memset(country_abbrev, 0, BRCM_CNTRY_BUF_SZ); | ||
1100 | strncpy(country_abbrev, "X2", sizeof(country_abbrev) - 1); | ||
1101 | country = brcms_c_country_lookup(wlc, country_abbrev); | ||
1102 | |||
1103 | /* save default country for exiting 11d regulatory mode */ | ||
1104 | strncpy(wlc->country_default, country_abbrev, BRCM_CNTRY_BUF_SZ - 1); | ||
1105 | |||
1106 | /* initialize autocountry_default to driver default */ | ||
1107 | strncpy(wlc->autocountry_default, "X2", BRCM_CNTRY_BUF_SZ - 1); | ||
1108 | |||
1109 | brcms_c_set_countrycode(wlc_cm, country_abbrev); | ||
1110 | |||
1111 | return wlc_cm; | ||
1112 | } | ||
1113 | |||
1114 | void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm) | ||
1115 | { | ||
1116 | kfree(wlc_cm); | ||
1117 | } | ||
1118 | |||
1119 | u8 | ||
1120 | brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm, | ||
1121 | uint bandunit) | ||
1122 | { | ||
1123 | return wlc_cm->bandstate[bandunit].locale_flags; | ||
1124 | } | ||
1125 | |||
1126 | static bool | ||
1127 | brcms_c_quiet_chanspec(struct brcms_cm_info *wlc_cm, u16 chspec) | ||
1128 | { | ||
1129 | return (wlc_cm->wlc->pub->_n_enab & SUPPORT_11N) && | ||
1130 | CHSPEC_IS40(chspec) ? | ||
1131 | (isset(wlc_cm->quiet_channels.vec, | ||
1132 | lower_20_sb(CHSPEC_CHANNEL(chspec))) || | ||
1133 | isset(wlc_cm->quiet_channels.vec, | ||
1134 | upper_20_sb(CHSPEC_CHANNEL(chspec)))) : | ||
1135 | isset(wlc_cm->quiet_channels.vec, CHSPEC_CHANNEL(chspec)); | ||
1136 | } | ||
1137 | |||
1138 | void | ||
1139 | brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, | ||
1140 | u8 local_constraint_qdbm) | ||
1141 | { | ||
1142 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
1143 | struct txpwr_limits txpwr; | ||
1144 | |||
1145 | brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr); | ||
1146 | |||
1147 | brcms_c_channel_min_txpower_limits_with_local_constraint( | ||
1148 | wlc_cm, &txpwr, local_constraint_qdbm | ||
1149 | ); | ||
1150 | |||
1151 | brcms_b_set_chanspec(wlc->hw, chanspec, | ||
1152 | (brcms_c_quiet_chanspec(wlc_cm, chanspec) != 0), | ||
1153 | &txpwr); | ||
1154 | } | ||
1155 | |||
1156 | #ifdef POWER_DBG | ||
1157 | static void wlc_phy_txpower_limits_dump(struct txpwr_limits *txpwr) | ||
1158 | { | ||
1159 | int i; | ||
1160 | char buf[80]; | ||
1161 | char fraction[4][4] = { " ", ".25", ".5 ", ".75" }; | ||
1162 | |||
1163 | sprintf(buf, "CCK "); | ||
1164 | for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) | ||
1165 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1166 | txpwr->cck[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1167 | fraction[txpwr->cck[i] % BRCMS_TXPWR_DB_FACTOR]); | ||
1168 | printk(KERN_DEBUG "%s\n", buf); | ||
1169 | |||
1170 | sprintf(buf, "20 MHz OFDM SISO "); | ||
1171 | for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) | ||
1172 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1173 | txpwr->ofdm[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1174 | fraction[txpwr->ofdm[i] % BRCMS_TXPWR_DB_FACTOR]); | ||
1175 | printk(KERN_DEBUG "%s\n", buf); | ||
1176 | |||
1177 | sprintf(buf, "20 MHz OFDM CDD "); | ||
1178 | for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) | ||
1179 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1180 | txpwr->ofdm_cdd[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1181 | fraction[txpwr->ofdm_cdd[i] % BRCMS_TXPWR_DB_FACTOR]); | ||
1182 | printk(KERN_DEBUG "%s\n", buf); | ||
1183 | |||
1184 | sprintf(buf, "40 MHz OFDM SISO "); | ||
1185 | for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) | ||
1186 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1187 | txpwr->ofdm_40_siso[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1188 | fraction[txpwr->ofdm_40_siso[i] % | ||
1189 | BRCMS_TXPWR_DB_FACTOR]); | ||
1190 | printk(KERN_DEBUG "%s\n", buf); | ||
1191 | |||
1192 | sprintf(buf, "40 MHz OFDM CDD "); | ||
1193 | for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) | ||
1194 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1195 | txpwr->ofdm_40_cdd[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1196 | fraction[txpwr->ofdm_40_cdd[i] % | ||
1197 | BRCMS_TXPWR_DB_FACTOR]); | ||
1198 | printk(KERN_DEBUG "%s\n", buf); | ||
1199 | |||
1200 | sprintf(buf, "20 MHz MCS0-7 SISO "); | ||
1201 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) | ||
1202 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1203 | txpwr->mcs_20_siso[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1204 | fraction[txpwr->mcs_20_siso[i] % | ||
1205 | BRCMS_TXPWR_DB_FACTOR]); | ||
1206 | printk(KERN_DEBUG "%s\n", buf); | ||
1207 | |||
1208 | sprintf(buf, "20 MHz MCS0-7 CDD "); | ||
1209 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) | ||
1210 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1211 | txpwr->mcs_20_cdd[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1212 | fraction[txpwr->mcs_20_cdd[i] % | ||
1213 | BRCMS_TXPWR_DB_FACTOR]); | ||
1214 | printk(KERN_DEBUG "%s\n", buf); | ||
1215 | |||
1216 | sprintf(buf, "20 MHz MCS0-7 STBC "); | ||
1217 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) | ||
1218 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1219 | txpwr->mcs_20_stbc[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1220 | fraction[txpwr->mcs_20_stbc[i] % | ||
1221 | BRCMS_TXPWR_DB_FACTOR]); | ||
1222 | printk(KERN_DEBUG "%s\n", buf); | ||
1223 | |||
1224 | sprintf(buf, "20 MHz MCS8-15 SDM "); | ||
1225 | for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) | ||
1226 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1227 | txpwr->mcs_20_mimo[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1228 | fraction[txpwr->mcs_20_mimo[i] % | ||
1229 | BRCMS_TXPWR_DB_FACTOR]); | ||
1230 | printk(KERN_DEBUG "%s\n", buf); | ||
1231 | |||
1232 | sprintf(buf, "40 MHz MCS0-7 SISO "); | ||
1233 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) | ||
1234 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1235 | txpwr->mcs_40_siso[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1236 | fraction[txpwr->mcs_40_siso[i] % | ||
1237 | BRCMS_TXPWR_DB_FACTOR]); | ||
1238 | printk(KERN_DEBUG "%s\n", buf); | ||
1239 | |||
1240 | sprintf(buf, "40 MHz MCS0-7 CDD "); | ||
1241 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) | ||
1242 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1243 | txpwr->mcs_40_cdd[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1244 | fraction[txpwr->mcs_40_cdd[i] % | ||
1245 | BRCMS_TXPWR_DB_FACTOR]); | ||
1246 | printk(KERN_DEBUG "%s\n", buf); | ||
1247 | |||
1248 | sprintf(buf, "40 MHz MCS0-7 STBC "); | ||
1249 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) | ||
1250 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1251 | txpwr->mcs_40_stbc[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1252 | fraction[txpwr->mcs_40_stbc[i] % | ||
1253 | BRCMS_TXPWR_DB_FACTOR]); | ||
1254 | printk(KERN_DEBUG "%s\n", buf); | ||
1255 | |||
1256 | sprintf(buf, "40 MHz MCS8-15 SDM "); | ||
1257 | for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) | ||
1258 | sprintf(buf[strlen(buf)], " %2d%s", | ||
1259 | txpwr->mcs_40_mimo[i] / BRCMS_TXPWR_DB_FACTOR, | ||
1260 | fraction[txpwr->mcs_40_mimo[i] % | ||
1261 | BRCMS_TXPWR_DB_FACTOR]); | ||
1262 | } | ||
1263 | printk(KERN_DEBUG "%s\n", buf); | ||
1264 | |||
1265 | printk(KERN_DEBUG "MCS32 %2d%s\n", | ||
1266 | txpwr->mcs32 / BRCMS_TXPWR_DB_FACTOR, | ||
1267 | fraction[txpwr->mcs32 % BRCMS_TXPWR_DB_FACTOR]); | ||
1268 | } | ||
1269 | #endif /* POWER_DBG */ | ||
1270 | |||
1271 | void | ||
1272 | brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, | ||
1273 | struct txpwr_limits *txpwr) | ||
1274 | { | ||
1275 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
1276 | uint i; | ||
1277 | uint chan; | ||
1278 | int maxpwr; | ||
1279 | int delta; | ||
1280 | const struct country_info *country; | ||
1281 | struct brcms_band *band; | ||
1282 | const struct locale_info *li; | ||
1283 | int conducted_max = BRCMS_TXPWR_MAX; | ||
1284 | int conducted_ofdm_max = BRCMS_TXPWR_MAX; | ||
1285 | const struct locale_mimo_info *li_mimo; | ||
1286 | int maxpwr20, maxpwr40; | ||
1287 | int maxpwr_idx; | ||
1288 | uint j; | ||
1289 | |||
1290 | memset(txpwr, 0, sizeof(struct txpwr_limits)); | ||
1291 | |||
1292 | if (!brcms_c_valid_chanspec_db(wlc_cm, chanspec)) { | ||
1293 | country = brcms_c_country_lookup(wlc, wlc->autocountry_default); | ||
1294 | if (country == NULL) | ||
1295 | return; | ||
1296 | } else { | ||
1297 | country = wlc_cm->country; | ||
1298 | } | ||
1299 | |||
1300 | chan = CHSPEC_CHANNEL(chanspec); | ||
1301 | band = wlc->bandstate[chspec_bandunit(chanspec)]; | ||
1302 | li = (band->bandtype == BRCM_BAND_5G) ? | ||
1303 | brcms_c_get_locale_5g(country->locale_5G) : | ||
1304 | brcms_c_get_locale_2g(country->locale_2G); | ||
1305 | |||
1306 | li_mimo = (band->bandtype == BRCM_BAND_5G) ? | ||
1307 | brcms_c_get_mimo_5g(country->locale_mimo_5G) : | ||
1308 | brcms_c_get_mimo_2g(country->locale_mimo_2G); | ||
1309 | |||
1310 | if (li->flags & BRCMS_EIRP) { | ||
1311 | delta = band->antgain; | ||
1312 | } else { | ||
1313 | delta = 0; | ||
1314 | if (band->antgain > QDB(6)) | ||
1315 | delta = band->antgain - QDB(6); /* Excess over 6 dB */ | ||
1316 | } | ||
1317 | |||
1318 | if (li == &locale_i) { | ||
1319 | conducted_max = QDB(22); | ||
1320 | conducted_ofdm_max = QDB(22); | ||
1321 | } | ||
1322 | |||
1323 | /* CCK txpwr limits for 2.4G band */ | ||
1324 | if (band->bandtype == BRCM_BAND_2G) { | ||
1325 | maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_CCK(chan)]; | ||
1326 | |||
1327 | maxpwr = maxpwr - delta; | ||
1328 | maxpwr = max(maxpwr, 0); | ||
1329 | maxpwr = min(maxpwr, conducted_max); | ||
1330 | |||
1331 | for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) | ||
1332 | txpwr->cck[i] = (u8) maxpwr; | ||
1333 | } | ||
1334 | |||
1335 | /* OFDM txpwr limits for 2.4G or 5G bands */ | ||
1336 | if (band->bandtype == BRCM_BAND_2G) | ||
1337 | maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_OFDM(chan)]; | ||
1338 | else | ||
1339 | maxpwr = li->maxpwr[CHANNEL_POWER_IDX_5G(chan)]; | ||
1340 | |||
1341 | maxpwr = maxpwr - delta; | ||
1342 | maxpwr = max(maxpwr, 0); | ||
1343 | maxpwr = min(maxpwr, conducted_ofdm_max); | ||
1344 | |||
1345 | /* Keep OFDM lmit below CCK limit */ | ||
1346 | if (band->bandtype == BRCM_BAND_2G) | ||
1347 | maxpwr = min_t(int, maxpwr, txpwr->cck[0]); | ||
1348 | |||
1349 | for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) | ||
1350 | txpwr->ofdm[i] = (u8) maxpwr; | ||
1351 | |||
1352 | for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) { | ||
1353 | /* | ||
1354 | * OFDM 40 MHz SISO has the same power as the corresponding | ||
1355 | * MCS0-7 rate unless overriden by the locale specific code. | ||
1356 | * We set this value to 0 as a flag (presumably 0 dBm isn't | ||
1357 | * a possibility) and then copy the MCS0-7 value to the 40 MHz | ||
1358 | * value if it wasn't explicitly set. | ||
1359 | */ | ||
1360 | txpwr->ofdm_40_siso[i] = 0; | ||
1361 | |||
1362 | txpwr->ofdm_cdd[i] = (u8) maxpwr; | ||
1363 | |||
1364 | txpwr->ofdm_40_cdd[i] = 0; | ||
1365 | } | ||
1366 | |||
1367 | /* MIMO/HT specific limits */ | ||
1368 | if (li_mimo->flags & BRCMS_EIRP) { | ||
1369 | delta = band->antgain; | ||
1370 | } else { | ||
1371 | delta = 0; | ||
1372 | if (band->antgain > QDB(6)) | ||
1373 | delta = band->antgain - QDB(6); /* Excess over 6 dB */ | ||
1374 | } | ||
1375 | |||
1376 | if (band->bandtype == BRCM_BAND_2G) | ||
1377 | maxpwr_idx = (chan - 1); | ||
1378 | else | ||
1379 | maxpwr_idx = CHANNEL_POWER_IDX_5G(chan); | ||
1380 | |||
1381 | maxpwr20 = li_mimo->maxpwr20[maxpwr_idx]; | ||
1382 | maxpwr40 = li_mimo->maxpwr40[maxpwr_idx]; | ||
1383 | |||
1384 | maxpwr20 = maxpwr20 - delta; | ||
1385 | maxpwr20 = max(maxpwr20, 0); | ||
1386 | maxpwr40 = maxpwr40 - delta; | ||
1387 | maxpwr40 = max(maxpwr40, 0); | ||
1388 | |||
1389 | /* Fill in the MCS 0-7 (SISO) rates */ | ||
1390 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { | ||
1391 | |||
1392 | /* | ||
1393 | * 20 MHz has the same power as the corresponding OFDM rate | ||
1394 | * unless overriden by the locale specific code. | ||
1395 | */ | ||
1396 | txpwr->mcs_20_siso[i] = txpwr->ofdm[i]; | ||
1397 | txpwr->mcs_40_siso[i] = 0; | ||
1398 | } | ||
1399 | |||
1400 | /* Fill in the MCS 0-7 CDD rates */ | ||
1401 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { | ||
1402 | txpwr->mcs_20_cdd[i] = (u8) maxpwr20; | ||
1403 | txpwr->mcs_40_cdd[i] = (u8) maxpwr40; | ||
1404 | } | ||
1405 | |||
1406 | /* | ||
1407 | * These locales have SISO expressed in the | ||
1408 | * table and override CDD later | ||
1409 | */ | ||
1410 | if (li_mimo == &locale_bn) { | ||
1411 | if (li_mimo == &locale_bn) { | ||
1412 | maxpwr20 = QDB(16); | ||
1413 | maxpwr40 = 0; | ||
1414 | |||
1415 | if (chan >= 3 && chan <= 11) | ||
1416 | maxpwr40 = QDB(16); | ||
1417 | } | ||
1418 | |||
1419 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { | ||
1420 | txpwr->mcs_20_siso[i] = (u8) maxpwr20; | ||
1421 | txpwr->mcs_40_siso[i] = (u8) maxpwr40; | ||
1422 | } | ||
1423 | } | ||
1424 | |||
1425 | /* Fill in the MCS 0-7 STBC rates */ | ||
1426 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { | ||
1427 | txpwr->mcs_20_stbc[i] = 0; | ||
1428 | txpwr->mcs_40_stbc[i] = 0; | ||
1429 | } | ||
1430 | |||
1431 | /* Fill in the MCS 8-15 SDM rates */ | ||
1432 | for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) { | ||
1433 | txpwr->mcs_20_mimo[i] = (u8) maxpwr20; | ||
1434 | txpwr->mcs_40_mimo[i] = (u8) maxpwr40; | ||
1435 | } | ||
1436 | |||
1437 | /* Fill in MCS32 */ | ||
1438 | txpwr->mcs32 = (u8) maxpwr40; | ||
1439 | |||
1440 | for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { | ||
1441 | if (txpwr->ofdm_40_cdd[i] == 0) | ||
1442 | txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; | ||
1443 | if (i == 0) { | ||
1444 | i = i + 1; | ||
1445 | if (txpwr->ofdm_40_cdd[i] == 0) | ||
1446 | txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; | ||
1447 | } | ||
1448 | } | ||
1449 | |||
1450 | /* | ||
1451 | * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO | ||
1452 | * value if it wasn't provided explicitly. | ||
1453 | */ | ||
1454 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { | ||
1455 | if (txpwr->mcs_40_siso[i] == 0) | ||
1456 | txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i]; | ||
1457 | } | ||
1458 | |||
1459 | for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { | ||
1460 | if (txpwr->ofdm_40_siso[i] == 0) | ||
1461 | txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; | ||
1462 | if (i == 0) { | ||
1463 | i = i + 1; | ||
1464 | if (txpwr->ofdm_40_siso[i] == 0) | ||
1465 | txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; | ||
1466 | } | ||
1467 | } | ||
1468 | |||
1469 | /* | ||
1470 | * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding | ||
1471 | * STBC values if they weren't provided explicitly. | ||
1472 | */ | ||
1473 | for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { | ||
1474 | if (txpwr->mcs_20_stbc[i] == 0) | ||
1475 | txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i]; | ||
1476 | |||
1477 | if (txpwr->mcs_40_stbc[i] == 0) | ||
1478 | txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i]; | ||
1479 | } | ||
1480 | |||
1481 | #ifdef POWER_DBG | ||
1482 | wlc_phy_txpower_limits_dump(txpwr); | ||
1483 | #endif | ||
1484 | return; | ||
1485 | } | ||
1486 | |||
1487 | /* | ||
1488 | * Validate the chanspec for this locale, for 40MHZ we need to also | ||
1489 | * check that the sidebands are valid 20MZH channels in this locale | ||
1490 | * and they are also a legal HT combination | ||
1491 | */ | ||
1492 | static bool | ||
1493 | brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec, | ||
1494 | bool dualband) | ||
1495 | { | ||
1496 | struct brcms_c_info *wlc = wlc_cm->wlc; | ||
1497 | u8 channel = CHSPEC_CHANNEL(chspec); | ||
1498 | |||
1499 | /* check the chanspec */ | ||
1500 | if (brcmu_chspec_malformed(chspec)) { | ||
1501 | wiphy_err(wlc->wiphy, "wl%d: malformed chanspec 0x%x\n", | ||
1502 | wlc->pub->unit, chspec); | ||
1503 | return false; | ||
1504 | } | ||
1505 | |||
1506 | if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) != | ||
1507 | chspec_bandunit(chspec)) | ||
1508 | return false; | ||
1509 | |||
1510 | /* Check a 20Mhz channel */ | ||
1511 | if (CHSPEC_IS20(chspec)) { | ||
1512 | if (dualband) | ||
1513 | return brcms_c_valid_channel20_db(wlc_cm->wlc->cmi, | ||
1514 | channel); | ||
1515 | else | ||
1516 | return brcms_c_valid_channel20(wlc_cm->wlc->cmi, | ||
1517 | channel); | ||
1518 | } | ||
1519 | #ifdef SUPPORT_40MHZ | ||
1520 | /* | ||
1521 | * We know we are now checking a 40MHZ channel, so we should | ||
1522 | * only be here for NPHYS | ||
1523 | */ | ||
1524 | if (BRCMS_ISNPHY(wlc->band) || BRCMS_ISSSLPNPHY(wlc->band)) { | ||
1525 | u8 upper_sideband = 0, idx; | ||
1526 | u8 num_ch20_entries = | ||
1527 | sizeof(chan20_info) / sizeof(struct chan20_info); | ||
1528 | |||
1529 | if (!VALID_40CHANSPEC_IN_BAND(wlc, chspec_bandunit(chspec))) | ||
1530 | return false; | ||
1531 | |||
1532 | if (dualband) { | ||
1533 | if (!brcms_c_valid_channel20_db(wlc->cmi, | ||
1534 | lower_20_sb(channel)) || | ||
1535 | !brcms_c_valid_channel20_db(wlc->cmi, | ||
1536 | upper_20_sb(channel))) | ||
1537 | return false; | ||
1538 | } else { | ||
1539 | if (!brcms_c_valid_channel20(wlc->cmi, | ||
1540 | lower_20_sb(channel)) || | ||
1541 | !brcms_c_valid_channel20(wlc->cmi, | ||
1542 | upper_20_sb(channel))) | ||
1543 | return false; | ||
1544 | } | ||
1545 | |||
1546 | /* find the lower sideband info in the sideband array */ | ||
1547 | for (idx = 0; idx < num_ch20_entries; idx++) { | ||
1548 | if (chan20_info[idx].sb == lower_20_sb(channel)) | ||
1549 | upper_sideband = chan20_info[idx].adj_sbs; | ||
1550 | } | ||
1551 | /* check that the lower sideband allows an upper sideband */ | ||
1552 | if ((upper_sideband & (CH_UPPER_SB | CH_EWA_VALID)) == | ||
1553 | (CH_UPPER_SB | CH_EWA_VALID)) | ||
1554 | return true; | ||
1555 | return false; | ||
1556 | } | ||
1557 | #endif /* 40 MHZ */ | ||
1558 | |||
1559 | return false; | ||
1560 | } | ||
1561 | |||
1562 | bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec) | ||
1563 | { | ||
1564 | return brcms_c_valid_chanspec_ext(wlc_cm, chspec, true); | ||
1565 | } | ||