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