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