blob: bb82a8c008a42a120a295cf4759c8818c054468a [file] [log] [blame]
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001/*
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
Brett Rudley33279892010-10-01 18:03:27 -070017#include <linux/kernel.h>
18#include <linux/string.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070019#include <bcmdefs.h>
20#include <wlc_cfg.h>
21#include <osl.h>
Brett Rudleyc6ac24e2010-10-26 11:55:23 -070022#include <linux/module.h>
23#include <linux/pci.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070024#include <bcmutils.h>
25#include <siutils.h>
Brett Rudleya52ba662010-10-26 09:17:06 -070026#include <sbhndpio.h>
27#include <sbhnddma.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070028#include <wlioctl.h>
29#include <wlc_pub.h>
30#include <wlc_key.h>
Brett Rudley69ec3032010-11-05 19:20:16 -070031#include <wlc_event.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070032#include <wlc_mac80211.h>
33#include <wlc_bmac.h>
34#include <wlc_stf.h>
35#include <wlc_channel.h>
Brett Rudley69ec3032010-11-05 19:20:16 -070036#include <wl_dbg.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070037
38typedef struct wlc_cm_band {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -070039 u8 locale_flags; /* locale_info_t flags */
Henry Ptasinskia9533e72010-09-08 21:04:42 -070040 chanvec_t valid_channels; /* List of valid channels in the country */
41 const chanvec_t *restricted_channels; /* List of restricted use channels */
42 const chanvec_t *radar_channels; /* List of radar sensitive channels */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -070043 u8 PAD[8];
Henry Ptasinskia9533e72010-09-08 21:04:42 -070044} wlc_cm_band_t;
45
46struct wlc_cm_info {
47 wlc_pub_t *pub;
48 wlc_info_t *wlc;
49 char srom_ccode[WLC_CNTRY_BUF_SZ]; /* Country Code in SROM */
50 uint srom_regrev; /* Regulatory Rev for the SROM ccode */
51 const country_info_t *country; /* current country def */
52 char ccode[WLC_CNTRY_BUF_SZ]; /* current internal Country Code */
53 uint regrev; /* current Regulatory Revision */
54 char country_abbrev[WLC_CNTRY_BUF_SZ]; /* current advertised ccode */
55 wlc_cm_band_t bandstate[MAXBANDS]; /* per-band state (one per phy/radio) */
56 /* quiet channels currently for radar sensitivity or 11h support */
57 chanvec_t quiet_channels; /* channels on which we cannot transmit */
58};
59
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040060static int wlc_channels_init(wlc_cm_info_t *wlc_cm,
61 const country_info_t *country);
62static void wlc_set_country_common(wlc_cm_info_t *wlc_cm,
Henry Ptasinskia9533e72010-09-08 21:04:42 -070063 const char *country_abbrev,
64 const char *ccode, uint regrev,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040065 const country_info_t *country);
66static int wlc_country_aggregate_map(wlc_cm_info_t *wlc_cm, const char *ccode,
67 char *mapped_ccode, uint *mapped_regrev);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070068static const country_info_t *wlc_country_lookup_direct(const char *ccode,
69 uint regrev);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040070static const country_info_t *wlc_countrycode_map(wlc_cm_info_t *wlc_cm,
Henry Ptasinskia9533e72010-09-08 21:04:42 -070071 const char *ccode,
72 char *mapped_ccode,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040073 uint *mapped_regrev);
74static void wlc_channels_commit(wlc_cm_info_t *wlc_cm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070075static bool wlc_japan_ccode(const char *ccode);
76static void wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm_info_t *
77 wlc_cm,
78 struct
79 txpwr_limits
80 *txpwr,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -070081 u8
Henry Ptasinskia9533e72010-09-08 21:04:42 -070082 local_constraint_qdbm);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040083void wlc_locale_add_channels(chanvec_t *target, const chanvec_t *channels);
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -070084static const locale_mimo_info_t *wlc_get_mimo_2g(u8 locale_idx);
85static const locale_mimo_info_t *wlc_get_mimo_5g(u8 locale_idx);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070086
87/* QDB() macro takes a dB value and converts to a quarter dB value */
88#ifdef QDB
89#undef QDB
90#endif
91#define QDB(n) ((n) * WLC_TXPWR_DB_FACTOR)
92
93/* Regulatory Matrix Spreadsheet (CLM) MIMO v3.7.9 */
94
95/*
96 * Some common channel sets
97 */
98
99/* No channels */
100static const chanvec_t chanvec_none = {
101 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00}
105};
106
107/* All 2.4 GHz HW channels */
108const chanvec_t chanvec_all_2G = {
109 {0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00}
113};
114
115/* All 5 GHz HW channels */
116const chanvec_t chanvec_all_5G = {
117 {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x11, 0x11,
118 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11,
119 0x11, 0x11, 0x20, 0x22, 0x22, 0x00, 0x00, 0x11,
120 0x11, 0x11, 0x11, 0x01}
121};
122
123/*
124 * Radar channel sets
125 */
126
127/* No radar */
128#define radar_set_none chanvec_none
129
130static const chanvec_t radar_set1 = { /* Channels 52 - 64, 100 - 140 */
131 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, /* 52 - 60 */
132 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, /* 64, 100 - 124 */
133 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128 - 140 */
134 0x00, 0x00, 0x00, 0x00}
135};
136
137/*
138 * Restricted channel sets
139 */
140
141#define restricted_set_none chanvec_none
142
143/* Channels 34, 38, 42, 46 */
144static const chanvec_t restricted_set_japan_legacy = {
145 {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00, 0x00}
149};
150
151/* Channels 12, 13 */
152static const chanvec_t restricted_set_2g_short = {
153 {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156 0x00, 0x00, 0x00, 0x00}
157};
158
159/* Channel 165 */
160static const chanvec_t restricted_chan_165 = {
161 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00}
165};
166
167/* Channels 36 - 48 & 149 - 165 */
168static const chanvec_t restricted_low_hi = {
169 {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x20, 0x22, 0x22, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x00}
173};
174
175/* Channels 12 - 14 */
176static const chanvec_t restricted_set_12_13_14 = {
177 {0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00}
181};
182
183#define LOCALE_CHAN_01_11 (1<<0)
184#define LOCALE_CHAN_12_13 (1<<1)
185#define LOCALE_CHAN_14 (1<<2)
186#define LOCALE_SET_5G_LOW_JP1 (1<<3) /* 34-48, step 2 */
187#define LOCALE_SET_5G_LOW_JP2 (1<<4) /* 34-46, step 4 */
188#define LOCALE_SET_5G_LOW1 (1<<5) /* 36-48, step 4 */
189#define LOCALE_SET_5G_LOW2 (1<<6) /* 52 */
190#define LOCALE_SET_5G_LOW3 (1<<7) /* 56-64, step 4 */
191#define LOCALE_SET_5G_MID1 (1<<8) /* 100-116, step 4 */
192#define LOCALE_SET_5G_MID2 (1<<9) /* 120-124, step 4 */
193#define LOCALE_SET_5G_MID3 (1<<10) /* 128 */
194#define LOCALE_SET_5G_HIGH1 (1<<11) /* 132-140, step 4 */
195#define LOCALE_SET_5G_HIGH2 (1<<12) /* 149-161, step 4 */
196#define LOCALE_SET_5G_HIGH3 (1<<13) /* 165 */
197#define LOCALE_CHAN_52_140_ALL (1<<14)
198#define LOCALE_SET_5G_HIGH4 (1<<15) /* 184-216 */
199
Jason Cooper0d706ef2010-09-14 09:45:39 -0400200#define LOCALE_CHAN_36_64 (LOCALE_SET_5G_LOW1 | LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
201#define LOCALE_CHAN_52_64 (LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
202#define LOCALE_CHAN_100_124 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700203#define LOCALE_CHAN_100_140 \
Jason Cooper0d706ef2010-09-14 09:45:39 -0400204 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2 | LOCALE_SET_5G_MID3 | LOCALE_SET_5G_HIGH1)
205#define LOCALE_CHAN_149_165 (LOCALE_SET_5G_HIGH2 | LOCALE_SET_5G_HIGH3)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700206#define LOCALE_CHAN_184_216 LOCALE_SET_5G_HIGH4
207
208#define LOCALE_CHAN_01_14 (LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13 | LOCALE_CHAN_14)
209
210#define LOCALE_RADAR_SET_NONE 0
211#define LOCALE_RADAR_SET_1 1
212
213#define LOCALE_RESTRICTED_NONE 0
214#define LOCALE_RESTRICTED_SET_2G_SHORT 1
215#define LOCALE_RESTRICTED_CHAN_165 2
216#define LOCALE_CHAN_ALL_5G 3
217#define LOCALE_RESTRICTED_JAPAN_LEGACY 4
218#define LOCALE_RESTRICTED_11D_2G 5
219#define LOCALE_RESTRICTED_11D_5G 6
220#define LOCALE_RESTRICTED_LOW_HI 7
221#define LOCALE_RESTRICTED_12_13_14 8
222
223/* global memory to provide working buffer for expanded locale */
224
225static const chanvec_t *g_table_radar_set[] = {
226 &chanvec_none,
227 &radar_set1
228};
229
230static const chanvec_t *g_table_restricted_chan[] = {
231 &chanvec_none, /* restricted_set_none */
232 &restricted_set_2g_short,
233 &restricted_chan_165,
234 &chanvec_all_5G,
235 &restricted_set_japan_legacy,
236 &chanvec_all_2G, /* restricted_set_11d_2G */
237 &chanvec_all_5G, /* restricted_set_11d_5G */
238 &restricted_low_hi,
239 &restricted_set_12_13_14
240};
241
242static const chanvec_t locale_2g_01_11 = {
243 {0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x00, 0x00}
247};
248
249static const chanvec_t locale_2g_12_13 = {
250 {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
253 0x00, 0x00, 0x00, 0x00}
254};
255
256static const chanvec_t locale_2g_14 = {
257 {0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
258 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
259 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260 0x00, 0x00, 0x00, 0x00}
261};
262
263static const chanvec_t locale_5g_LOW_JP1 = {
264 {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x01, 0x00,
265 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
266 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
267 0x00, 0x00, 0x00, 0x00}
268};
269
270static const chanvec_t locale_5g_LOW_JP2 = {
271 {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
272 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
273 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x00, 0x00, 0x00}
275};
276
277static const chanvec_t locale_5g_LOW1 = {
278 {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281 0x00, 0x00, 0x00, 0x00}
282};
283
284static const chanvec_t locale_5g_LOW2 = {
285 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
286 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288 0x00, 0x00, 0x00, 0x00}
289};
290
291static const chanvec_t locale_5g_LOW3 = {
292 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
293 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
295 0x00, 0x00, 0x00, 0x00}
296};
297
298static const chanvec_t locale_5g_MID1 = {
299 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
300 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x00,
301 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x00, 0x00}
303};
304
305static const chanvec_t locale_5g_MID2 = {
306 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309 0x00, 0x00, 0x00, 0x00}
310};
311
312static const chanvec_t locale_5g_MID3 = {
313 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
315 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316 0x00, 0x00, 0x00, 0x00}
317};
318
319static const chanvec_t locale_5g_HIGH1 = {
320 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323 0x00, 0x00, 0x00, 0x00}
324};
325
326static const chanvec_t locale_5g_HIGH2 = {
327 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x00, 0x20, 0x22, 0x02, 0x00, 0x00, 0x00,
330 0x00, 0x00, 0x00, 0x00}
331};
332
333static const chanvec_t locale_5g_HIGH3 = {
334 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
337 0x00, 0x00, 0x00, 0x00}
338};
339
340static const chanvec_t locale_5g_52_140_ALL = {
341 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,
342 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
343 0x11, 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
344 0x00, 0x00, 0x00, 0x00}
345};
346
347static const chanvec_t locale_5g_HIGH4 = {
348 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
351 0x11, 0x11, 0x11, 0x11}
352};
353
354static const chanvec_t *g_table_locale_base[] = {
355 &locale_2g_01_11,
356 &locale_2g_12_13,
357 &locale_2g_14,
358 &locale_5g_LOW_JP1,
359 &locale_5g_LOW_JP2,
360 &locale_5g_LOW1,
361 &locale_5g_LOW2,
362 &locale_5g_LOW3,
363 &locale_5g_MID1,
364 &locale_5g_MID2,
365 &locale_5g_MID3,
366 &locale_5g_HIGH1,
367 &locale_5g_HIGH2,
368 &locale_5g_HIGH3,
369 &locale_5g_52_140_ALL,
370 &locale_5g_HIGH4
371};
372
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400373void wlc_locale_add_channels(chanvec_t *target, const chanvec_t *channels)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700374{
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700375 u8 i;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700376 for (i = 0; i < sizeof(chanvec_t); i++) {
377 target->vec[i] |= channels->vec[i];
378 }
379}
380
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400381void wlc_locale_get_channels(const locale_info_t *locale, chanvec_t *channels)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700382{
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700383 u8 i;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700384
Brett Rudley9249ede2010-11-30 20:09:49 -0800385 memset(channels, 0, sizeof(chanvec_t));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700386
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700387 for (i = 0; i < ARRAY_SIZE(g_table_locale_base); i++) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700388 if (locale->valid_channels & (1 << i)) {
389 wlc_locale_add_channels(channels,
390 g_table_locale_base[i]);
391 }
392 }
393}
394
395/*
396 * Locale Definitions - 2.4 GHz
397 */
398static const locale_info_t locale_i = { /* locale i. channel 1 - 13 */
399 LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13,
400 LOCALE_RADAR_SET_NONE,
401 LOCALE_RESTRICTED_SET_2G_SHORT,
402 {QDB(19), QDB(19), QDB(19),
403 QDB(19), QDB(19), QDB(19)},
404 {20, 20, 20, 0},
405 WLC_EIRP
406};
407
408/*
409 * Locale Definitions - 5 GHz
410 */
411static const locale_info_t locale_11 = {
412 /* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */
413 LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165,
414 LOCALE_RADAR_SET_1,
415 LOCALE_RESTRICTED_NONE,
416 {QDB(21), QDB(21), QDB(21), QDB(21), QDB(21)},
417 {23, 23, 23, 30, 30},
418 WLC_EIRP | WLC_DFS_EU
419};
420
421#define LOCALE_2G_IDX_i 0
422static const locale_info_t *g_locale_2g_table[] = {
423 &locale_i
424};
425
426#define LOCALE_5G_IDX_11 0
427static const locale_info_t *g_locale_5g_table[] = {
428 &locale_11
429};
430
431/*
432 * MIMO Locale Definitions - 2.4 GHz
433 */
434static const locale_mimo_info_t locale_bn = {
435 {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
436 QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
437 QDB(13), QDB(13), QDB(13)},
438 {0, 0, QDB(13), QDB(13), QDB(13),
439 QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
440 QDB(13), 0, 0},
441 0
442};
443
444/* locale mimo 2g indexes */
445#define LOCALE_MIMO_IDX_bn 0
446
447static const locale_mimo_info_t *g_mimo_2g_table[] = {
448 &locale_bn
449};
450
451/*
452 * MIMO Locale Definitions - 5 GHz
453 */
454static const locale_mimo_info_t locale_11n = {
455 { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)},
456 {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)},
457 0
458};
459
460#define LOCALE_MIMO_IDX_11n 0
461static const locale_mimo_info_t *g_mimo_5g_table[] = {
462 &locale_11n
463};
464
465#ifdef LC
466#undef LC
467#endif
468#define LC(id) LOCALE_MIMO_IDX_ ## id
469
470#ifdef LC_2G
471#undef LC_2G
472#endif
473#define LC_2G(id) LOCALE_2G_IDX_ ## id
474
475#ifdef LC_5G
476#undef LC_5G
477#endif
478#define LC_5G(id) LOCALE_5G_IDX_ ## id
479
480#define LOCALES(band2, band5, mimo2, mimo5) {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)}
481
482static const struct {
483 char abbrev[WLC_CNTRY_BUF_SZ]; /* country abbreviation */
484 country_info_t country;
485} cntry_locales[] = {
486 {
487 "X2", LOCALES(i, 11, bn, 11n)}, /* Worldwide RoW 2 */
488};
489
490#ifdef SUPPORT_40MHZ
491/* 20MHz channel info for 40MHz pairing support */
492struct chan20_info {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700493 u8 sb;
494 u8 adj_sbs;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700495};
496
497/* indicates adjacent channels that are allowed for a 40 Mhz channel and
498 * those that permitted by the HT
499 */
500struct chan20_info chan20_info[] = {
501 /* 11b/11g */
502/* 0 */ {1, (CH_UPPER_SB | CH_EWA_VALID)},
503/* 1 */ {2, (CH_UPPER_SB | CH_EWA_VALID)},
504/* 2 */ {3, (CH_UPPER_SB | CH_EWA_VALID)},
505/* 3 */ {4, (CH_UPPER_SB | CH_EWA_VALID)},
506/* 4 */ {5, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
507/* 5 */ {6, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
508/* 6 */ {7, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
509/* 7 */ {8, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
510/* 8 */ {9, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
511/* 9 */ {10, (CH_LOWER_SB | CH_EWA_VALID)},
512/* 10 */ {11, (CH_LOWER_SB | CH_EWA_VALID)},
513/* 11 */ {12, (CH_LOWER_SB)},
514/* 12 */ {13, (CH_LOWER_SB)},
515/* 13 */ {14, (CH_LOWER_SB)},
516
517/* 11a japan high */
518/* 14 */ {34, (CH_UPPER_SB)},
519/* 15 */ {38, (CH_LOWER_SB)},
520/* 16 */ {42, (CH_LOWER_SB)},
521/* 17 */ {46, (CH_LOWER_SB)},
522
523/* 11a usa low */
524/* 18 */ {36, (CH_UPPER_SB | CH_EWA_VALID)},
525/* 19 */ {40, (CH_LOWER_SB | CH_EWA_VALID)},
526/* 20 */ {44, (CH_UPPER_SB | CH_EWA_VALID)},
527/* 21 */ {48, (CH_LOWER_SB | CH_EWA_VALID)},
528/* 22 */ {52, (CH_UPPER_SB | CH_EWA_VALID)},
529/* 23 */ {56, (CH_LOWER_SB | CH_EWA_VALID)},
530/* 24 */ {60, (CH_UPPER_SB | CH_EWA_VALID)},
531/* 25 */ {64, (CH_LOWER_SB | CH_EWA_VALID)},
532
533/* 11a Europe */
534/* 26 */ {100, (CH_UPPER_SB | CH_EWA_VALID)},
535/* 27 */ {104, (CH_LOWER_SB | CH_EWA_VALID)},
536/* 28 */ {108, (CH_UPPER_SB | CH_EWA_VALID)},
537/* 29 */ {112, (CH_LOWER_SB | CH_EWA_VALID)},
538/* 30 */ {116, (CH_UPPER_SB | CH_EWA_VALID)},
539/* 31 */ {120, (CH_LOWER_SB | CH_EWA_VALID)},
540/* 32 */ {124, (CH_UPPER_SB | CH_EWA_VALID)},
541/* 33 */ {128, (CH_LOWER_SB | CH_EWA_VALID)},
542/* 34 */ {132, (CH_UPPER_SB | CH_EWA_VALID)},
543/* 35 */ {136, (CH_LOWER_SB | CH_EWA_VALID)},
544/* 36 */ {140, (CH_LOWER_SB)},
545
546/* 11a usa high, ref5 only */
547/* The 0x80 bit in pdiv means these are REF5, other entries are REF20 */
548/* 37 */ {149, (CH_UPPER_SB | CH_EWA_VALID)},
549/* 38 */ {153, (CH_LOWER_SB | CH_EWA_VALID)},
550/* 39 */ {157, (CH_UPPER_SB | CH_EWA_VALID)},
551/* 40 */ {161, (CH_LOWER_SB | CH_EWA_VALID)},
552/* 41 */ {165, (CH_LOWER_SB)},
553
554/* 11a japan */
555/* 42 */ {184, (CH_UPPER_SB)},
556/* 43 */ {188, (CH_LOWER_SB)},
557/* 44 */ {192, (CH_UPPER_SB)},
558/* 45 */ {196, (CH_LOWER_SB)},
559/* 46 */ {200, (CH_UPPER_SB)},
560/* 47 */ {204, (CH_LOWER_SB)},
561/* 48 */ {208, (CH_UPPER_SB)},
562/* 49 */ {212, (CH_LOWER_SB)},
563/* 50 */ {216, (CH_LOWER_SB)}
564};
565#endif /* SUPPORT_40MHZ */
566
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700567const locale_info_t *wlc_get_locale_2g(u8 locale_idx)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700568{
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700569 if (locale_idx >= ARRAY_SIZE(g_locale_2g_table)) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700570 WL_ERROR(("%s: locale 2g index size out of range %d\n",
571 __func__, locale_idx));
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700572 ASSERT(locale_idx < ARRAY_SIZE(g_locale_2g_table));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700573 return NULL;
574 }
575 return g_locale_2g_table[locale_idx];
576}
577
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700578const locale_info_t *wlc_get_locale_5g(u8 locale_idx)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700579{
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700580 if (locale_idx >= ARRAY_SIZE(g_locale_5g_table)) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700581 WL_ERROR(("%s: locale 5g index size out of range %d\n",
582 __func__, locale_idx));
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700583 ASSERT(locale_idx < ARRAY_SIZE(g_locale_5g_table));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700584 return NULL;
585 }
586 return g_locale_5g_table[locale_idx];
587}
588
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700589const locale_mimo_info_t *wlc_get_mimo_2g(u8 locale_idx)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700590{
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700591 if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700592 WL_ERROR(("%s: mimo 2g index size out of range %d\n", __func__,
593 locale_idx));
594 return NULL;
595 }
596 return g_mimo_2g_table[locale_idx];
597}
598
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700599const locale_mimo_info_t *wlc_get_mimo_5g(u8 locale_idx)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700600{
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700601 if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700602 WL_ERROR(("%s: mimo 5g index size out of range %d\n", __func__,
603 locale_idx));
604 return NULL;
605 }
606 return g_mimo_5g_table[locale_idx];
607}
608
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -0700609wlc_cm_info_t *wlc_channel_mgr_attach(wlc_info_t *wlc)
Jason Coopera2627bc2010-09-14 09:45:31 -0400610{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700611 wlc_cm_info_t *wlc_cm;
612 char country_abbrev[WLC_CNTRY_BUF_SZ];
613 const country_info_t *country;
614 wlc_pub_t *pub = wlc->pub;
615 char *ccode;
616
617 WL_TRACE(("wl%d: wlc_channel_mgr_attach\n", wlc->pub->unit));
618
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +0200619 wlc_cm = kzalloc(sizeof(wlc_cm_info_t), GFP_ATOMIC);
Jason Cooperca8c1e52010-09-14 09:45:33 -0400620 if (wlc_cm == NULL) {
mike.rapoport@gmail.com97e17d02010-10-13 00:09:09 +0200621 WL_ERROR(("wl%d: %s: out of memory", pub->unit, __func__));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700622 return NULL;
623 }
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700624 wlc_cm->pub = pub;
625 wlc_cm->wlc = wlc;
626 wlc->cmi = wlc_cm;
627
628 /* store the country code for passing up as a regulatory hint */
629 ccode = getvar(wlc->pub->vars, "ccode");
630 if (ccode) {
631 strncpy(wlc->pub->srom_ccode, ccode, WLC_CNTRY_BUF_SZ - 1);
632 WL_NONE(("%s: SROM country code is %c%c\n", __func__,
633 wlc->pub->srom_ccode[0], wlc->pub->srom_ccode[1]));
634 }
635
636 /* internal country information which must match regulatory constraints in firmware */
Brett Rudley9249ede2010-11-30 20:09:49 -0800637 memset(country_abbrev, 0, WLC_CNTRY_BUF_SZ);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700638 strncpy(country_abbrev, "X2", sizeof(country_abbrev) - 1);
639 country = wlc_country_lookup(wlc, country_abbrev);
640
641 ASSERT(country != NULL);
642
643 /* save default country for exiting 11d regulatory mode */
644 strncpy(wlc->country_default, country_abbrev, WLC_CNTRY_BUF_SZ - 1);
645
646 /* initialize autocountry_default to driver default */
647 strncpy(wlc->autocountry_default, "X2", WLC_CNTRY_BUF_SZ - 1);
648
649 wlc_set_countrycode(wlc_cm, country_abbrev);
650
651 return wlc_cm;
652}
653
Greg Kroah-Hartman0d2f0722010-10-08 14:28:21 -0700654void wlc_channel_mgr_detach(wlc_cm_info_t *wlc_cm)
Jason Coopera2627bc2010-09-14 09:45:31 -0400655{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700656 if (wlc_cm)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200657 kfree(wlc_cm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700658}
659
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400660const char *wlc_channel_country_abbrev(wlc_cm_info_t *wlc_cm)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700661{
662 return wlc_cm->country_abbrev;
663}
664
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700665u8 wlc_channel_locale_flags(wlc_cm_info_t *wlc_cm)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700666{
667 wlc_info_t *wlc = wlc_cm->wlc;
668
669 return wlc_cm->bandstate[wlc->band->bandunit].locale_flags;
670}
671
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -0700672u8 wlc_channel_locale_flags_in_band(wlc_cm_info_t *wlc_cm, uint bandunit)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700673{
674 return wlc_cm->bandstate[bandunit].locale_flags;
675}
676
677/* return chanvec for a given country code and band */
678bool
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400679wlc_channel_get_chanvec(struct wlc_info *wlc, const char *country_abbrev,
680 int bandtype, chanvec_t *channels)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700681{
682 const country_info_t *country;
683 const locale_info_t *locale = NULL;
684
685 country = wlc_country_lookup(wlc, country_abbrev);
686 if (country == NULL)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700687 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700688
689 if (bandtype == WLC_BAND_2G)
690 locale = wlc_get_locale_2g(country->locale_2G);
691 else if (bandtype == WLC_BAND_5G)
692 locale = wlc_get_locale_5g(country->locale_5G);
693 if (locale == NULL)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700694 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700695
696 wlc_locale_get_channels(locale, channels);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700697 return true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700698}
699
700/* set the driver's current country and regulatory information using a country code
701 * as the source. Lookup built in country information found with the country code.
702 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400703int wlc_set_countrycode(wlc_cm_info_t *wlc_cm, const char *ccode)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700704{
705 char country_abbrev[WLC_CNTRY_BUF_SZ];
706 strncpy(country_abbrev, ccode, WLC_CNTRY_BUF_SZ);
707 return wlc_set_countrycode_rev(wlc_cm, country_abbrev, ccode, -1);
708}
709
710int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400711wlc_set_countrycode_rev(wlc_cm_info_t *wlc_cm,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700712 const char *country_abbrev,
713 const char *ccode, int regrev)
714{
715 const country_info_t *country;
716 char mapped_ccode[WLC_CNTRY_BUF_SZ];
717 uint mapped_regrev;
718
719 WL_NONE(("%s: (country_abbrev \"%s\", ccode \"%s\", regrev %d) SPROM \"%s\"/%u\n", __func__, country_abbrev, ccode, regrev, wlc_cm->srom_ccode, wlc_cm->srom_regrev));
720
721 /* if regrev is -1, lookup the mapped country code,
722 * otherwise use the ccode and regrev directly
723 */
724 if (regrev == -1) {
725 /* map the country code to a built-in country code, regrev, and country_info */
726 country =
727 wlc_countrycode_map(wlc_cm, ccode, mapped_ccode,
728 &mapped_regrev);
729 } else {
730 /* find the matching built-in country definition */
731 ASSERT(0);
732 country = wlc_country_lookup_direct(ccode, regrev);
733 strncpy(mapped_ccode, ccode, WLC_CNTRY_BUF_SZ);
734 mapped_regrev = regrev;
735 }
736
737 if (country == NULL)
738 return BCME_BADARG;
739
740 /* set the driver state for the country */
741 wlc_set_country_common(wlc_cm, country_abbrev, mapped_ccode,
742 mapped_regrev, country);
743
744 return 0;
745}
746
747/* set the driver's current country and regulatory information using a country code
748 * as the source. Look up built in country information found with the country code.
749 */
750static void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400751wlc_set_country_common(wlc_cm_info_t *wlc_cm,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700752 const char *country_abbrev,
753 const char *ccode, uint regrev,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400754 const country_info_t *country)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700755{
756 const locale_mimo_info_t *li_mimo;
757 const locale_info_t *locale;
758 wlc_info_t *wlc = wlc_cm->wlc;
759 char prev_country_abbrev[WLC_CNTRY_BUF_SZ];
760
761 ASSERT(country != NULL);
762
763 /* save current country state */
764 wlc_cm->country = country;
765
Brett Rudley9249ede2010-11-30 20:09:49 -0800766 memset(&prev_country_abbrev, 0, WLC_CNTRY_BUF_SZ);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700767 strncpy(prev_country_abbrev, wlc_cm->country_abbrev,
768 WLC_CNTRY_BUF_SZ - 1);
769
770 strncpy(wlc_cm->country_abbrev, country_abbrev, WLC_CNTRY_BUF_SZ - 1);
771 strncpy(wlc_cm->ccode, ccode, WLC_CNTRY_BUF_SZ - 1);
772 wlc_cm->regrev = regrev;
773
774 /* disable/restore nmode based on country regulations */
775 li_mimo = wlc_get_mimo_2g(country->locale_mimo_2G);
776 if (li_mimo && (li_mimo->flags & WLC_NO_MIMO)) {
777 wlc_set_nmode(wlc, OFF);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700778 wlc->stf->no_cddstbc = true;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700779 } else {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700780 wlc->stf->no_cddstbc = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700781 if (N_ENAB(wlc->pub) != wlc->protection->nmode_user)
782 wlc_set_nmode(wlc, wlc->protection->nmode_user);
783 }
784
785 wlc_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
786 wlc_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
787 /* set or restore gmode as required by regulatory */
788 locale = wlc_get_locale_2g(country->locale_2G);
789 if (locale && (locale->flags & WLC_NO_OFDM)) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700790 wlc_set_gmode(wlc, GMODE_LEGACY_B, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700791 } else {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700792 wlc_set_gmode(wlc, wlc->protection->gmode_user, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700793 }
794
795 wlc_channels_init(wlc_cm, country);
796
797 return;
798}
799
800/* Lookup a country info structure from a null terminated country code
801 * The lookup is case sensitive.
802 */
803const country_info_t *wlc_country_lookup(struct wlc_info *wlc,
804 const char *ccode)
805{
806 const country_info_t *country;
807 char mapped_ccode[WLC_CNTRY_BUF_SZ];
808 uint mapped_regrev;
809
810 /* map the country code to a built-in country code, regrev, and country_info struct */
811 country =
812 wlc_countrycode_map(wlc->cmi, ccode, mapped_ccode, &mapped_regrev);
813
814 return country;
815}
816
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400817static const country_info_t *wlc_countrycode_map(wlc_cm_info_t *wlc_cm,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700818 const char *ccode,
819 char *mapped_ccode,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400820 uint *mapped_regrev)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700821{
822 wlc_info_t *wlc = wlc_cm->wlc;
823 const country_info_t *country;
824 uint srom_regrev = wlc_cm->srom_regrev;
825 const char *srom_ccode = wlc_cm->srom_ccode;
826 int mapped;
827
828 /* check for currently supported ccode size */
829 if (strlen(ccode) > (WLC_CNTRY_BUF_SZ - 1)) {
830 WL_ERROR(("wl%d: %s: ccode \"%s\" too long for match\n",
831 wlc->pub->unit, __func__, ccode));
832 return NULL;
833 }
834
835 /* default mapping is the given ccode and regrev 0 */
836 strncpy(mapped_ccode, ccode, WLC_CNTRY_BUF_SZ);
837 *mapped_regrev = 0;
838
839 /* If the desired country code matches the srom country code,
840 * then the mapped country is the srom regulatory rev.
841 * Otherwise look for an aggregate mapping.
842 */
843 if (!strcmp(srom_ccode, ccode)) {
844 *mapped_regrev = srom_regrev;
845 mapped = 0;
846 WL_ERROR(("srom_code == ccode %s\n", __func__));
847 ASSERT(0);
848 } else {
849 mapped =
850 wlc_country_aggregate_map(wlc_cm, ccode, mapped_ccode,
851 mapped_regrev);
852 }
853
854 /* find the matching built-in country definition */
855 country = wlc_country_lookup_direct(mapped_ccode, *mapped_regrev);
856
857 /* if there is not an exact rev match, default to rev zero */
858 if (country == NULL && *mapped_regrev != 0) {
859 *mapped_regrev = 0;
860 ASSERT(0);
861 country =
862 wlc_country_lookup_direct(mapped_ccode, *mapped_regrev);
863 }
864
865 return country;
866}
867
868static int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400869wlc_country_aggregate_map(wlc_cm_info_t *wlc_cm, const char *ccode,
870 char *mapped_ccode, uint *mapped_regrev)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700871{
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700872 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700873}
874
875/* Lookup a country info structure from a null terminated country
876 * abbreviation and regrev directly with no translation.
877 */
878static const country_info_t *wlc_country_lookup_direct(const char *ccode,
879 uint regrev)
880{
881 uint size, i;
882
883 /* Should just return 0 for single locale driver. */
884 /* Keep it this way in case we add more locales. (for now anyway) */
885
886 /* all other country def arrays are for regrev == 0, so if regrev is non-zero, fail */
887 if (regrev > 0)
888 return NULL;
889
890 /* find matched table entry from country code */
Greg Kroah-Hartman8d3d6a62010-10-08 11:47:11 -0700891 size = ARRAY_SIZE(cntry_locales);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700892 for (i = 0; i < size; i++) {
893 if (strcmp(ccode, cntry_locales[i].abbrev) == 0) {
894 return &cntry_locales[i].country;
895 }
896 }
897
898 WL_ERROR(("%s: Returning NULL\n", __func__));
899 ASSERT(0);
900 return NULL;
901}
902
903static int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400904wlc_channels_init(wlc_cm_info_t *wlc_cm, const country_info_t *country)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700905{
906 wlc_info_t *wlc = wlc_cm->wlc;
907 uint i, j;
908 wlcband_t *band;
909 const locale_info_t *li;
910 chanvec_t sup_chan;
911 const locale_mimo_info_t *li_mimo;
912
913 band = wlc->band;
914 for (i = 0; i < NBANDS(wlc);
915 i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
916
917 li = BAND_5G(band->bandtype) ?
918 wlc_get_locale_5g(country->locale_5G) :
919 wlc_get_locale_2g(country->locale_2G);
920 ASSERT(li);
921 wlc_cm->bandstate[band->bandunit].locale_flags = li->flags;
922 li_mimo = BAND_5G(band->bandtype) ?
923 wlc_get_mimo_5g(country->locale_mimo_5G) :
924 wlc_get_mimo_2g(country->locale_mimo_2G);
925 ASSERT(li_mimo);
926
927 /* merge the mimo non-mimo locale flags */
928 wlc_cm->bandstate[band->bandunit].locale_flags |=
929 li_mimo->flags;
930
931 wlc_cm->bandstate[band->bandunit].restricted_channels =
932 g_table_restricted_chan[li->restricted_channels];
933 wlc_cm->bandstate[band->bandunit].radar_channels =
934 g_table_radar_set[li->radar_channels];
935
936 /* set the channel availability,
937 * masking out the channels that may not be supported on this phy
938 */
939 wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
940 &sup_chan);
941 wlc_locale_get_channels(li,
942 &wlc_cm->bandstate[band->bandunit].
943 valid_channels);
944 for (j = 0; j < sizeof(chanvec_t); j++)
945 wlc_cm->bandstate[band->bandunit].valid_channels.
946 vec[j] &= sup_chan.vec[j];
947 }
948
949 wlc_quiet_channels_reset(wlc_cm);
950 wlc_channels_commit(wlc_cm);
951
Jason Cooper90ea2292010-09-14 09:45:32 -0400952 return 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700953}
954
955/* Update the radio state (enable/disable) and tx power targets
956 * based on a new set of channel/regulatory information
957 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400958static void wlc_channels_commit(wlc_cm_info_t *wlc_cm)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700959{
960 wlc_info_t *wlc = wlc_cm->wlc;
961 uint chan;
962 struct txpwr_limits txpwr;
963
964 /* search for the existence of any valid channel */
965 for (chan = 0; chan < MAXCHANNEL; chan++) {
966 if (VALID_CHANNEL20_DB(wlc, chan)) {
967 break;
968 }
969 }
970 if (chan == MAXCHANNEL)
971 chan = INVCHANNEL;
972
973 /* based on the channel search above, set or clear WL_RADIO_COUNTRY_DISABLE */
974 if (chan == INVCHANNEL) {
975 /* country/locale with no valid channels, set the radio disable bit */
976 mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
977 WL_ERROR(("wl%d: %s: no valid channel for \"%s\" nbands %d bandlocked %d\n", wlc->pub->unit, __func__, wlc_cm->country_abbrev, NBANDS(wlc), wlc->bandlocked));
978 } else
Jason Coopere5c45362010-09-14 09:45:35 -0400979 if (mboolisset(wlc->pub->radio_disabled,
980 WL_RADIO_COUNTRY_DISABLE)) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700981 /* country/locale with valid channel, clear the radio disable bit */
982 mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
983 }
984
985 /* Now that the country abbreviation is set, if the radio supports 2G, then
986 * set channel 14 restrictions based on the new locale.
987 */
988 if (NBANDS(wlc) > 1 || BAND_2G(wlc->band->bandtype)) {
989 wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700990 wlc_japan(wlc) ? true :
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700991 false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700992 }
993
994 if (wlc->pub->up && chan != INVCHANNEL) {
995 wlc_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr);
996 wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm,
997 &txpwr,
998 WLC_TXPWR_MAX);
999 wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec);
1000 }
1001}
1002
1003/* reset the quiet channels vector to the union of the restricted and radar channel sets */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001004void wlc_quiet_channels_reset(wlc_cm_info_t *wlc_cm)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001005{
1006 wlc_info_t *wlc = wlc_cm->wlc;
1007 uint i, j;
1008 wlcband_t *band;
1009 const chanvec_t *chanvec;
1010
Brett Rudley9249ede2010-11-30 20:09:49 -08001011 memset(&wlc_cm->quiet_channels, 0, sizeof(chanvec_t));
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001012
1013 band = wlc->band;
1014 for (i = 0; i < NBANDS(wlc);
1015 i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
1016
1017 /* initialize quiet channels for restricted channels */
1018 chanvec = wlc_cm->bandstate[band->bandunit].restricted_channels;
1019 for (j = 0; j < sizeof(chanvec_t); j++)
1020 wlc_cm->quiet_channels.vec[j] |= chanvec->vec[j];
1021
1022 }
1023}
1024
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001025bool wlc_quiet_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001026{
Jason Cooper90ea2292010-09-14 09:45:32 -04001027 return N_ENAB(wlc_cm->wlc->pub) && CHSPEC_IS40(chspec) ?
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001028 (isset
1029 (wlc_cm->quiet_channels.vec,
1030 LOWER_20_SB(CHSPEC_CHANNEL(chspec)))
1031 || isset(wlc_cm->quiet_channels.vec,
1032 UPPER_20_SB(CHSPEC_CHANNEL(chspec)))) : isset(wlc_cm->
1033 quiet_channels.
1034 vec,
1035 CHSPEC_CHANNEL
Jason Cooper90ea2292010-09-14 09:45:32 -04001036 (chspec));
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001037}
1038
1039/* Is the channel valid for the current locale? (but don't consider channels not
1040 * available due to bandlocking)
1041 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001042bool wlc_valid_channel20_db(wlc_cm_info_t *wlc_cm, uint val)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001043{
1044 wlc_info_t *wlc = wlc_cm->wlc;
1045
Jason Cooper90ea2292010-09-14 09:45:32 -04001046 return VALID_CHANNEL20(wlc, val) ||
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001047 (!wlc->bandlocked
Jason Cooper90ea2292010-09-14 09:45:32 -04001048 && VALID_CHANNEL20_IN_BAND(wlc, OTHERBANDUNIT(wlc), val));
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001049}
1050
1051/* Is the channel valid for the current locale and specified band? */
1052bool
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001053wlc_valid_channel20_in_band(wlc_cm_info_t *wlc_cm, uint bandunit, uint val)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001054{
1055 return ((val < MAXCHANNEL)
1056 && isset(wlc_cm->bandstate[bandunit].valid_channels.vec, val));
1057}
1058
1059/* Is the channel valid for the current locale and current band? */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001060bool wlc_valid_channel20(wlc_cm_info_t *wlc_cm, uint val)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001061{
1062 wlc_info_t *wlc = wlc_cm->wlc;
1063
1064 return ((val < MAXCHANNEL) &&
1065 isset(wlc_cm->bandstate[wlc->band->bandunit].valid_channels.vec,
1066 val));
1067}
1068
1069/* Is the 40 MHz allowed for the current locale and specified band? */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001070bool wlc_valid_40chanspec_in_band(wlc_cm_info_t *wlc_cm, uint bandunit)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001071{
1072 wlc_info_t *wlc = wlc_cm->wlc;
1073
1074 return (((wlc_cm->bandstate[bandunit].
1075 locale_flags & (WLC_NO_MIMO | WLC_NO_40MHZ)) == 0)
1076 && wlc->bandstate[bandunit]->mimo_cap_40);
1077}
1078
1079static void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001080wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm_info_t *wlc_cm,
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001081 struct txpwr_limits *txpwr,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001082 u8
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001083 local_constraint_qdbm)
1084{
1085 int j;
1086
1087 /* CCK Rates */
1088 for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001089 txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001090 }
1091
1092 /* 20 MHz Legacy OFDM SISO */
1093 for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001094 txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001095 }
1096
1097 /* 20 MHz Legacy OFDM CDD */
1098 for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
1099 txpwr->ofdm_cdd[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001100 min(txpwr->ofdm_cdd[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001101 }
1102
1103 /* 40 MHz Legacy OFDM SISO */
1104 for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
1105 txpwr->ofdm_40_siso[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001106 min(txpwr->ofdm_40_siso[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001107 }
1108
1109 /* 40 MHz Legacy OFDM CDD */
1110 for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
1111 txpwr->ofdm_40_cdd[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001112 min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001113 }
1114
1115 /* 20MHz MCS 0-7 SISO */
1116 for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
1117 txpwr->mcs_20_siso[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001118 min(txpwr->mcs_20_siso[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001119 }
1120
1121 /* 20MHz MCS 0-7 CDD */
1122 for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
1123 txpwr->mcs_20_cdd[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001124 min(txpwr->mcs_20_cdd[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001125 }
1126
1127 /* 20MHz MCS 0-7 STBC */
1128 for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
1129 txpwr->mcs_20_stbc[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001130 min(txpwr->mcs_20_stbc[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001131 }
1132
1133 /* 20MHz MCS 8-15 MIMO */
1134 for (j = 0; j < WLC_NUM_RATES_MCS_2_STREAM; j++)
1135 txpwr->mcs_20_mimo[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001136 min(txpwr->mcs_20_mimo[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001137
1138 /* 40MHz MCS 0-7 SISO */
1139 for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
1140 txpwr->mcs_40_siso[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001141 min(txpwr->mcs_40_siso[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001142 }
1143
1144 /* 40MHz MCS 0-7 CDD */
1145 for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
1146 txpwr->mcs_40_cdd[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001147 min(txpwr->mcs_40_cdd[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001148 }
1149
1150 /* 40MHz MCS 0-7 STBC */
1151 for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
1152 txpwr->mcs_40_stbc[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001153 min(txpwr->mcs_40_stbc[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001154 }
1155
1156 /* 40MHz MCS 8-15 MIMO */
1157 for (j = 0; j < WLC_NUM_RATES_MCS_2_STREAM; j++)
1158 txpwr->mcs_40_mimo[j] =
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001159 min(txpwr->mcs_40_mimo[j], local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001160
1161 /* 40MHz MCS 32 */
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001162 txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001163
1164}
1165
1166void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001167wlc_channel_set_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001168 u8 local_constraint_qdbm)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001169{
1170 wlc_info_t *wlc = wlc_cm->wlc;
1171 struct txpwr_limits txpwr;
1172
1173 wlc_channel_reg_limits(wlc_cm, chanspec, &txpwr);
1174
1175 wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm, &txpwr,
1176 local_constraint_qdbm);
1177
1178 wlc_bmac_set_chanspec(wlc->hw, chanspec,
1179 (wlc_quiet_chanspec(wlc_cm, chanspec) != 0),
1180 &txpwr);
1181}
1182
1183int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001184wlc_channel_set_txpower_limit(wlc_cm_info_t *wlc_cm,
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001185 u8 local_constraint_qdbm)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001186{
1187 wlc_info_t *wlc = wlc_cm->wlc;
1188 struct txpwr_limits txpwr;
1189
1190 wlc_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr);
1191
1192 wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm, &txpwr,
1193 local_constraint_qdbm);
1194
1195 wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec);
1196
1197 return 0;
1198}
1199
1200#ifdef POWER_DBG
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001201static void wlc_phy_txpower_limits_dump(txpwr_limits_t *txpwr)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001202{
1203 int i;
1204 char fraction[4][4] = { " ", ".25", ".5 ", ".75" };
1205
1206 printf("CCK ");
1207 for (i = 0; i < WLC_NUM_RATES_CCK; i++) {
1208 printf(" %2d%s", txpwr->cck[i] / WLC_TXPWR_DB_FACTOR,
1209 fraction[txpwr->cck[i] % WLC_TXPWR_DB_FACTOR]);
1210 }
1211 printf("\n");
1212
1213 printf("20 MHz OFDM SISO ");
1214 for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
1215 printf(" %2d%s", txpwr->ofdm[i] / WLC_TXPWR_DB_FACTOR,
1216 fraction[txpwr->ofdm[i] % WLC_TXPWR_DB_FACTOR]);
1217 }
1218 printf("\n");
1219
1220 printf("20 MHz OFDM CDD ");
1221 for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
1222 printf(" %2d%s", txpwr->ofdm_cdd[i] / WLC_TXPWR_DB_FACTOR,
1223 fraction[txpwr->ofdm_cdd[i] % WLC_TXPWR_DB_FACTOR]);
1224 }
1225 printf("\n");
1226
1227 printf("40 MHz OFDM SISO ");
1228 for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
1229 printf(" %2d%s", txpwr->ofdm_40_siso[i] / WLC_TXPWR_DB_FACTOR,
1230 fraction[txpwr->ofdm_40_siso[i] % WLC_TXPWR_DB_FACTOR]);
1231 }
1232 printf("\n");
1233
1234 printf("40 MHz OFDM CDD ");
1235 for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
1236 printf(" %2d%s", txpwr->ofdm_40_cdd[i] / WLC_TXPWR_DB_FACTOR,
1237 fraction[txpwr->ofdm_40_cdd[i] % WLC_TXPWR_DB_FACTOR]);
1238 }
1239 printf("\n");
1240
1241 printf("20 MHz MCS0-7 SISO ");
1242 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1243 printf(" %2d%s", txpwr->mcs_20_siso[i] / WLC_TXPWR_DB_FACTOR,
1244 fraction[txpwr->mcs_20_siso[i] % WLC_TXPWR_DB_FACTOR]);
1245 }
1246 printf("\n");
1247
1248 printf("20 MHz MCS0-7 CDD ");
1249 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1250 printf(" %2d%s", txpwr->mcs_20_cdd[i] / WLC_TXPWR_DB_FACTOR,
1251 fraction[txpwr->mcs_20_cdd[i] % WLC_TXPWR_DB_FACTOR]);
1252 }
1253 printf("\n");
1254
1255 printf("20 MHz MCS0-7 STBC ");
1256 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1257 printf(" %2d%s", txpwr->mcs_20_stbc[i] / WLC_TXPWR_DB_FACTOR,
1258 fraction[txpwr->mcs_20_stbc[i] % WLC_TXPWR_DB_FACTOR]);
1259 }
1260 printf("\n");
1261
1262 printf("20 MHz MCS8-15 SDM ");
1263 for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
1264 printf(" %2d%s", txpwr->mcs_20_mimo[i] / WLC_TXPWR_DB_FACTOR,
1265 fraction[txpwr->mcs_20_mimo[i] % WLC_TXPWR_DB_FACTOR]);
1266 }
1267 printf("\n");
1268
1269 printf("40 MHz MCS0-7 SISO ");
1270 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1271 printf(" %2d%s", txpwr->mcs_40_siso[i] / WLC_TXPWR_DB_FACTOR,
1272 fraction[txpwr->mcs_40_siso[i] % WLC_TXPWR_DB_FACTOR]);
1273 }
1274 printf("\n");
1275
1276 printf("40 MHz MCS0-7 CDD ");
1277 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1278 printf(" %2d%s", txpwr->mcs_40_cdd[i] / WLC_TXPWR_DB_FACTOR,
1279 fraction[txpwr->mcs_40_cdd[i] % WLC_TXPWR_DB_FACTOR]);
1280 }
1281 printf("\n");
1282
1283 printf("40 MHz MCS0-7 STBC ");
1284 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1285 printf(" %2d%s", txpwr->mcs_40_stbc[i] / WLC_TXPWR_DB_FACTOR,
1286 fraction[txpwr->mcs_40_stbc[i] % WLC_TXPWR_DB_FACTOR]);
1287 }
1288 printf("\n");
1289
1290 printf("40 MHz MCS8-15 SDM ");
1291 for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
1292 printf(" %2d%s", txpwr->mcs_40_mimo[i] / WLC_TXPWR_DB_FACTOR,
1293 fraction[txpwr->mcs_40_mimo[i] % WLC_TXPWR_DB_FACTOR]);
1294 }
1295 printf("\n");
1296
1297 printf("MCS32 %2d%s\n",
1298 txpwr->mcs32 / WLC_TXPWR_DB_FACTOR,
1299 fraction[txpwr->mcs32 % WLC_TXPWR_DB_FACTOR]);
1300}
1301#endif /* POWER_DBG */
1302
1303void
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001304wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
1305 txpwr_limits_t *txpwr)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001306{
1307 wlc_info_t *wlc = wlc_cm->wlc;
1308 uint i;
1309 uint chan;
1310 int maxpwr;
1311 int delta;
1312 const country_info_t *country;
1313 wlcband_t *band;
1314 const locale_info_t *li;
1315 int conducted_max;
1316 int conducted_ofdm_max;
1317 const locale_mimo_info_t *li_mimo;
1318 int maxpwr20, maxpwr40;
1319 int maxpwr_idx;
1320 uint j;
1321
Brett Rudley9249ede2010-11-30 20:09:49 -08001322 memset(txpwr, 0, sizeof(txpwr_limits_t));
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001323
1324 if (!wlc_valid_chanspec_db(wlc_cm, chanspec)) {
1325 country = wlc_country_lookup(wlc, wlc->autocountry_default);
1326 if (country == NULL)
1327 return;
1328 } else {
1329 country = wlc_cm->country;
1330 }
1331
1332 chan = CHSPEC_CHANNEL(chanspec);
1333 band = wlc->bandstate[CHSPEC_WLCBANDUNIT(chanspec)];
1334 li = BAND_5G(band->bandtype) ?
1335 wlc_get_locale_5g(country->locale_5G) :
1336 wlc_get_locale_2g(country->locale_2G);
1337
1338 li_mimo = BAND_5G(band->bandtype) ?
1339 wlc_get_mimo_5g(country->locale_mimo_5G) :
1340 wlc_get_mimo_2g(country->locale_mimo_2G);
1341
1342 if (li->flags & WLC_EIRP) {
1343 delta = band->antgain;
1344 } else {
1345 delta = 0;
1346 if (band->antgain > QDB(6))
1347 delta = band->antgain - QDB(6); /* Excess over 6 dB */
1348 }
1349
1350 if (li == &locale_i) {
1351 conducted_max = QDB(22);
1352 conducted_ofdm_max = QDB(22);
1353 }
1354
1355 /* CCK txpwr limits for 2.4G band */
1356 if (BAND_2G(band->bandtype)) {
1357 maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_CCK(chan)];
1358
1359 maxpwr = maxpwr - delta;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -07001360 maxpwr = max(maxpwr, 0);
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001361 maxpwr = min(maxpwr, conducted_max);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001362
1363 for (i = 0; i < WLC_NUM_RATES_CCK; i++)
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001364 txpwr->cck[i] = (u8) maxpwr;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001365 }
1366
1367 /* OFDM txpwr limits for 2.4G or 5G bands */
1368 if (BAND_2G(band->bandtype)) {
1369 maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_OFDM(chan)];
1370
1371 } else {
1372 maxpwr = li->maxpwr[CHANNEL_POWER_IDX_5G(chan)];
1373 }
1374
1375 maxpwr = maxpwr - delta;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -07001376 maxpwr = max(maxpwr, 0);
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001377 maxpwr = min(maxpwr, conducted_ofdm_max);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001378
1379 /* Keep OFDM lmit below CCK limit */
1380 if (BAND_2G(band->bandtype))
Greg Kroah-Hartmana300ce92010-10-08 12:28:02 -07001381 maxpwr = min_t(int, maxpwr, txpwr->cck[0]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001382
1383 for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001384 txpwr->ofdm[i] = (u8) maxpwr;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001385 }
1386
1387 for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
1388 /* OFDM 40 MHz SISO has the same power as the corresponding MCS0-7 rate unless
1389 * overriden by the locale specific code. We set this value to 0 as a
1390 * flag (presumably 0 dBm isn't a possibility) and then copy the MCS0-7 value
1391 * to the 40 MHz value if it wasn't explicitly set.
1392 */
1393 txpwr->ofdm_40_siso[i] = 0;
1394
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001395 txpwr->ofdm_cdd[i] = (u8) maxpwr;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001396
1397 txpwr->ofdm_40_cdd[i] = 0;
1398 }
1399
1400 /* MIMO/HT specific limits */
1401 if (li_mimo->flags & WLC_EIRP) {
1402 delta = band->antgain;
1403 } else {
1404 delta = 0;
1405 if (band->antgain > QDB(6))
1406 delta = band->antgain - QDB(6); /* Excess over 6 dB */
1407 }
1408
1409 if (BAND_2G(band->bandtype))
1410 maxpwr_idx = (chan - 1);
1411 else
1412 maxpwr_idx = CHANNEL_POWER_IDX_5G(chan);
1413
1414 maxpwr20 = li_mimo->maxpwr20[maxpwr_idx];
1415 maxpwr40 = li_mimo->maxpwr40[maxpwr_idx];
1416
1417 maxpwr20 = maxpwr20 - delta;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -07001418 maxpwr20 = max(maxpwr20, 0);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001419 maxpwr40 = maxpwr40 - delta;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -07001420 maxpwr40 = max(maxpwr40, 0);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001421
1422 /* Fill in the MCS 0-7 (SISO) rates */
1423 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1424
1425 /* 20 MHz has the same power as the corresponding OFDM rate unless
1426 * overriden by the locale specific code.
1427 */
1428 txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
1429 txpwr->mcs_40_siso[i] = 0;
1430 }
1431
1432 /* Fill in the MCS 0-7 CDD rates */
1433 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001434 txpwr->mcs_20_cdd[i] = (u8) maxpwr20;
1435 txpwr->mcs_40_cdd[i] = (u8) maxpwr40;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001436 }
1437
1438 /* These locales have SISO expressed in the table and override CDD later */
1439 if (li_mimo == &locale_bn) {
1440 if (li_mimo == &locale_bn) {
1441 maxpwr20 = QDB(16);
1442 maxpwr40 = 0;
1443
1444 if (chan >= 3 && chan <= 11) {
1445 maxpwr40 = QDB(16);
1446 }
1447 }
1448
1449 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001450 txpwr->mcs_20_siso[i] = (u8) maxpwr20;
1451 txpwr->mcs_40_siso[i] = (u8) maxpwr40;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001452 }
1453 }
1454
1455 /* Fill in the MCS 0-7 STBC rates */
1456 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1457 txpwr->mcs_20_stbc[i] = 0;
1458 txpwr->mcs_40_stbc[i] = 0;
1459 }
1460
1461 /* Fill in the MCS 8-15 SDM rates */
1462 for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001463 txpwr->mcs_20_mimo[i] = (u8) maxpwr20;
1464 txpwr->mcs_40_mimo[i] = (u8) maxpwr40;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001465 }
1466
1467 /* Fill in MCS32 */
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001468 txpwr->mcs32 = (u8) maxpwr40;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001469
1470 for (i = 0, j = 0; i < WLC_NUM_RATES_OFDM; i++, j++) {
1471 if (txpwr->ofdm_40_cdd[i] == 0)
1472 txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
1473 if (i == 0) {
1474 i = i + 1;
1475 if (txpwr->ofdm_40_cdd[i] == 0)
1476 txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
1477 }
1478 }
1479
1480 /* Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO value if it wasn't
Jason Cooper92dfc7d2010-09-14 09:45:50 -04001481 * provided explicitly.
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001482 */
1483
1484 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1485 if (txpwr->mcs_40_siso[i] == 0)
1486 txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i];
1487 }
1488
1489 for (i = 0, j = 0; i < WLC_NUM_RATES_OFDM; i++, j++) {
1490 if (txpwr->ofdm_40_siso[i] == 0)
1491 txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
1492 if (i == 0) {
1493 i = i + 1;
1494 if (txpwr->ofdm_40_siso[i] == 0)
1495 txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
1496 }
1497 }
1498
1499 /* Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding STBC values if they weren't
1500 * provided explicitly.
1501 */
1502 for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
1503 if (txpwr->mcs_20_stbc[i] == 0)
1504 txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i];
1505
1506 if (txpwr->mcs_40_stbc[i] == 0)
1507 txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i];
1508 }
1509
1510#ifdef POWER_DBG
1511 wlc_phy_txpower_limits_dump(txpwr);
1512#endif
1513 return;
1514}
1515
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001516/* Returns true if currently set country is Japan or variant */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001517bool wlc_japan(struct wlc_info *wlc)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001518{
1519 return wlc_japan_ccode(wlc->cmi->country_abbrev);
1520}
1521
1522/* JP, J1 - J10 are Japan ccodes */
1523static bool wlc_japan_ccode(const char *ccode)
1524{
1525 return (ccode[0] == 'J' &&
1526 (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
1527}
1528
1529/*
1530 * Validate the chanspec for this locale, for 40MHZ we need to also check that the sidebands
1531 * are valid 20MZH channels in this locale and they are also a legal HT combination
1532 */
1533static bool
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001534wlc_valid_chanspec_ext(wlc_cm_info_t *wlc_cm, chanspec_t chspec, bool dualband)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001535{
1536 wlc_info_t *wlc = wlc_cm->wlc;
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001537 u8 channel = CHSPEC_CHANNEL(chspec);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001538
1539 /* check the chanspec */
1540 if (wf_chspec_malformed(chspec)) {
1541 WL_ERROR(("wl%d: malformed chanspec 0x%x\n", wlc->pub->unit,
1542 chspec));
1543 ASSERT(0);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001544 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001545 }
1546
1547 if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) !=
1548 CHSPEC_WLCBANDUNIT(chspec))
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001549 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001550
1551 /* Check a 20Mhz channel */
1552 if (CHSPEC_IS20(chspec)) {
1553 if (dualband)
Jason Cooper90ea2292010-09-14 09:45:32 -04001554 return VALID_CHANNEL20_DB(wlc_cm->wlc, channel);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001555 else
Jason Cooper90ea2292010-09-14 09:45:32 -04001556 return VALID_CHANNEL20(wlc_cm->wlc, channel);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001557 }
1558#ifdef SUPPORT_40MHZ
1559 /* We know we are now checking a 40MHZ channel, so we should only be here
1560 * for NPHYS
1561 */
1562 if (WLCISNPHY(wlc->band) || WLCISSSLPNPHY(wlc->band)) {
Greg Kroah-Hartman41feb5e2010-10-05 10:09:00 -07001563 u8 upper_sideband = 0, idx;
1564 u8 num_ch20_entries =
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001565 sizeof(chan20_info) / sizeof(struct chan20_info);
1566
1567 if (!VALID_40CHANSPEC_IN_BAND(wlc, CHSPEC_WLCBANDUNIT(chspec)))
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001568 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001569
1570 if (dualband) {
1571 if (!VALID_CHANNEL20_DB(wlc, LOWER_20_SB(channel)) ||
1572 !VALID_CHANNEL20_DB(wlc, UPPER_20_SB(channel)))
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001573 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001574 } else {
1575 if (!VALID_CHANNEL20(wlc, LOWER_20_SB(channel)) ||
1576 !VALID_CHANNEL20(wlc, UPPER_20_SB(channel)))
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001577 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001578 }
1579
1580 /* find the lower sideband info in the sideband array */
1581 for (idx = 0; idx < num_ch20_entries; idx++) {
1582 if (chan20_info[idx].sb == LOWER_20_SB(channel))
1583 upper_sideband = chan20_info[idx].adj_sbs;
1584 }
1585 /* check that the lower sideband allows an upper sideband */
1586 if ((upper_sideband & (CH_UPPER_SB | CH_EWA_VALID)) ==
1587 (CH_UPPER_SB | CH_EWA_VALID))
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001588 return true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001589 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001590 }
1591#endif /* 40 MHZ */
1592
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001593 return false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001594}
1595
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001596bool wlc_valid_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001597{
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001598 return wlc_valid_chanspec_ext(wlc_cm, chspec, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001599}
1600
Jason Cooper7cc4a4c2010-09-14 09:45:30 -04001601bool wlc_valid_chanspec_db(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001602{
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001603 return wlc_valid_chanspec_ext(wlc_cm, chspec, true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001604}