diff options
Diffstat (limited to 'drivers/net/wireless/ath9k/regd.c')
-rw-r--r-- | drivers/net/wireless/ath9k/regd.c | 1031 |
1 files changed, 1031 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c new file mode 100644 index 000000000000..7b0176e3eb40 --- /dev/null +++ b/drivers/net/wireless/ath9k/regd.c | |||
@@ -0,0 +1,1031 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Atheros Communications Inc. | ||
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 | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include "core.h" | ||
20 | #include "hw.h" | ||
21 | #include "regd.h" | ||
22 | #include "regd_common.h" | ||
23 | |||
24 | static int ath9k_regd_chansort(const void *a, const void *b) | ||
25 | { | ||
26 | const struct ath9k_channel *ca = a; | ||
27 | const struct ath9k_channel *cb = b; | ||
28 | |||
29 | return (ca->channel == cb->channel) ? | ||
30 | (ca->channelFlags & CHAN_FLAGS) - | ||
31 | (cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel; | ||
32 | } | ||
33 | |||
34 | static void | ||
35 | ath9k_regd_sort(void *a, u32 n, u32 size, ath_hal_cmp_t *cmp) | ||
36 | { | ||
37 | u8 *aa = a; | ||
38 | u8 *ai, *t; | ||
39 | |||
40 | for (ai = aa + size; --n >= 1; ai += size) | ||
41 | for (t = ai; t > aa; t -= size) { | ||
42 | u8 *u = t - size; | ||
43 | if (cmp(u, t) <= 0) | ||
44 | break; | ||
45 | swap(u, t, size); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah) | ||
50 | { | ||
51 | return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG; | ||
52 | } | ||
53 | |||
54 | static bool ath9k_regd_is_chan_bm_zero(u64 *bitmask) | ||
55 | { | ||
56 | int i; | ||
57 | |||
58 | for (i = 0; i < BMLEN; i++) { | ||
59 | if (bitmask[i] != 0) | ||
60 | return false; | ||
61 | } | ||
62 | return true; | ||
63 | } | ||
64 | |||
65 | static bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah) | ||
66 | { | ||
67 | u16 rd = ath9k_regd_get_eepromRD(ah); | ||
68 | int i; | ||
69 | |||
70 | if (rd & COUNTRY_ERD_FLAG) { | ||
71 | u16 cc = rd & ~COUNTRY_ERD_FLAG; | ||
72 | for (i = 0; i < ARRAY_SIZE(allCountries); i++) | ||
73 | if (allCountries[i].countryCode == cc) | ||
74 | return true; | ||
75 | } else { | ||
76 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) | ||
77 | if (regDomainPairs[i].regDmnEnum == rd) | ||
78 | return true; | ||
79 | } | ||
80 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
81 | "%s: invalid regulatory domain/country code 0x%x\n", | ||
82 | __func__, rd); | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah) | ||
87 | { | ||
88 | u32 regcap; | ||
89 | |||
90 | regcap = ah->ah_caps.halRegCap; | ||
91 | |||
92 | if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND) | ||
93 | return true; | ||
94 | else | ||
95 | return false; | ||
96 | } | ||
97 | |||
98 | static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah, | ||
99 | u16 cc) | ||
100 | { | ||
101 | u16 rd; | ||
102 | int i; | ||
103 | |||
104 | if (cc == CTRY_DEFAULT) | ||
105 | return true; | ||
106 | if (cc == CTRY_DEBUG) | ||
107 | return true; | ||
108 | |||
109 | rd = ath9k_regd_get_eepromRD(ah); | ||
110 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n", | ||
111 | __func__, rd); | ||
112 | |||
113 | if (rd & COUNTRY_ERD_FLAG) { | ||
114 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
115 | "%s: EEPROM setting is country code %u\n", | ||
116 | __func__, rd & ~COUNTRY_ERD_FLAG); | ||
117 | return cc == (rd & ~COUNTRY_ERD_FLAG); | ||
118 | } | ||
119 | |||
120 | for (i = 0; i < ARRAY_SIZE(allCountries); i++) { | ||
121 | if (cc == allCountries[i].countryCode) { | ||
122 | #ifdef AH_SUPPORT_11D | ||
123 | if ((rd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) | ||
124 | return true; | ||
125 | #endif | ||
126 | if (allCountries[i].regDmnEnum == rd || | ||
127 | rd == DEBUG_REG_DMN || rd == NO_ENUMRD) | ||
128 | return true; | ||
129 | } | ||
130 | } | ||
131 | return false; | ||
132 | } | ||
133 | |||
134 | static u32 | ||
135 | ath9k_regd_get_wmodes_nreg(struct ath_hal *ah, | ||
136 | struct country_code_to_enum_rd *country, | ||
137 | struct regDomain *rd5GHz) | ||
138 | { | ||
139 | u32 modesAvail; | ||
140 | |||
141 | modesAvail = ah->ah_caps.halWirelessModes; | ||
142 | |||
143 | if ((modesAvail & ATH9K_MODE_SEL_11G) && (!country->allow11g)) | ||
144 | modesAvail &= ~ATH9K_MODE_SEL_11G; | ||
145 | if ((modesAvail & ATH9K_MODE_SEL_11A) && | ||
146 | (ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a))) | ||
147 | modesAvail &= ~ATH9K_MODE_SEL_11A; | ||
148 | |||
149 | if ((modesAvail & ATH9K_MODE_SEL_11NG_HT20) | ||
150 | && (!country->allow11ng20)) | ||
151 | modesAvail &= ~ATH9K_MODE_SEL_11NG_HT20; | ||
152 | |||
153 | if ((modesAvail & ATH9K_MODE_SEL_11NA_HT20) | ||
154 | && (!country->allow11na20)) | ||
155 | modesAvail &= ~ATH9K_MODE_SEL_11NA_HT20; | ||
156 | |||
157 | if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40PLUS) && | ||
158 | (!country->allow11ng40)) | ||
159 | modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40PLUS; | ||
160 | |||
161 | if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40MINUS) && | ||
162 | (!country->allow11ng40)) | ||
163 | modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40MINUS; | ||
164 | |||
165 | if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40PLUS) && | ||
166 | (!country->allow11na40)) | ||
167 | modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40PLUS; | ||
168 | |||
169 | if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40MINUS) && | ||
170 | (!country->allow11na40)) | ||
171 | modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40MINUS; | ||
172 | |||
173 | return modesAvail; | ||
174 | } | ||
175 | |||
176 | bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah) | ||
177 | { | ||
178 | u16 rd; | ||
179 | |||
180 | rd = ath9k_regd_get_eepromRD(ah); | ||
181 | |||
182 | switch (rd) { | ||
183 | case FCC4_FCCA: | ||
184 | case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG): | ||
185 | return true; | ||
186 | case DEBUG_REG_DMN: | ||
187 | case NO_ENUMRD: | ||
188 | if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49) | ||
189 | return true; | ||
190 | break; | ||
191 | } | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | static struct country_code_to_enum_rd* | ||
196 | ath9k_regd_find_country(u16 countryCode) | ||
197 | { | ||
198 | int i; | ||
199 | |||
200 | for (i = 0; i < ARRAY_SIZE(allCountries); i++) { | ||
201 | if (allCountries[i].countryCode == countryCode) | ||
202 | return &allCountries[i]; | ||
203 | } | ||
204 | return NULL; | ||
205 | } | ||
206 | |||
207 | static u16 ath9k_regd_get_default_country(struct ath_hal *ah) | ||
208 | { | ||
209 | u16 rd; | ||
210 | int i; | ||
211 | |||
212 | rd = ath9k_regd_get_eepromRD(ah); | ||
213 | if (rd & COUNTRY_ERD_FLAG) { | ||
214 | struct country_code_to_enum_rd *country = NULL; | ||
215 | u16 cc = rd & ~COUNTRY_ERD_FLAG; | ||
216 | |||
217 | country = ath9k_regd_find_country(cc); | ||
218 | if (country != NULL) | ||
219 | return cc; | ||
220 | } | ||
221 | |||
222 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) | ||
223 | if (regDomainPairs[i].regDmnEnum == rd) { | ||
224 | if (regDomainPairs[i].singleCC != 0) | ||
225 | return regDomainPairs[i].singleCC; | ||
226 | else | ||
227 | i = ARRAY_SIZE(regDomainPairs); | ||
228 | } | ||
229 | return CTRY_DEFAULT; | ||
230 | } | ||
231 | |||
232 | static bool ath9k_regd_is_valid_reg_domain(int regDmn, | ||
233 | struct regDomain *rd) | ||
234 | { | ||
235 | int i; | ||
236 | |||
237 | for (i = 0; i < ARRAY_SIZE(regDomains); i++) { | ||
238 | if (regDomains[i].regDmnEnum == regDmn) { | ||
239 | if (rd != NULL) { | ||
240 | memcpy(rd, ®Domains[i], | ||
241 | sizeof(struct regDomain)); | ||
242 | } | ||
243 | return true; | ||
244 | } | ||
245 | } | ||
246 | return false; | ||
247 | } | ||
248 | |||
249 | static bool ath9k_regd_is_valid_reg_domainPair(int regDmnPair) | ||
250 | { | ||
251 | int i; | ||
252 | |||
253 | if (regDmnPair == NO_ENUMRD) | ||
254 | return false; | ||
255 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { | ||
256 | if (regDomainPairs[i].regDmnEnum == regDmnPair) | ||
257 | return true; | ||
258 | } | ||
259 | return false; | ||
260 | } | ||
261 | |||
262 | static bool | ||
263 | ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn, | ||
264 | u16 channelFlag, struct regDomain *rd) | ||
265 | { | ||
266 | int i, found; | ||
267 | u64 flags = NO_REQ; | ||
268 | struct reg_dmn_pair_mapping *regPair = NULL; | ||
269 | int regOrg; | ||
270 | |||
271 | regOrg = regDmn; | ||
272 | if (regDmn == CTRY_DEFAULT) { | ||
273 | u16 rdnum; | ||
274 | rdnum = ath9k_regd_get_eepromRD(ah); | ||
275 | |||
276 | if (!(rdnum & COUNTRY_ERD_FLAG)) { | ||
277 | if (ath9k_regd_is_valid_reg_domain(rdnum, NULL) || | ||
278 | ath9k_regd_is_valid_reg_domainPair(rdnum)) { | ||
279 | regDmn = rdnum; | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | |||
284 | if ((regDmn & MULTI_DOMAIN_MASK) == 0) { | ||
285 | for (i = 0, found = 0; | ||
286 | (i < ARRAY_SIZE(regDomainPairs)) && (!found); i++) { | ||
287 | if (regDomainPairs[i].regDmnEnum == regDmn) { | ||
288 | regPair = ®DomainPairs[i]; | ||
289 | found = 1; | ||
290 | } | ||
291 | } | ||
292 | if (!found) { | ||
293 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
294 | "%s: Failed to find reg domain pair %u\n", | ||
295 | __func__, regDmn); | ||
296 | return false; | ||
297 | } | ||
298 | if (!(channelFlag & CHANNEL_2GHZ)) { | ||
299 | regDmn = regPair->regDmn5GHz; | ||
300 | flags = regPair->flags5GHz; | ||
301 | } | ||
302 | if (channelFlag & CHANNEL_2GHZ) { | ||
303 | regDmn = regPair->regDmn2GHz; | ||
304 | flags = regPair->flags2GHz; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | found = ath9k_regd_is_valid_reg_domain(regDmn, rd); | ||
309 | if (!found) { | ||
310 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
311 | "%s: Failed to find unitary reg domain %u\n", | ||
312 | __func__, regDmn); | ||
313 | return false; | ||
314 | } else { | ||
315 | rd->pscan &= regPair->pscanMask; | ||
316 | if (((regOrg & MULTI_DOMAIN_MASK) == 0) && | ||
317 | (flags != NO_REQ)) { | ||
318 | rd->flags = flags; | ||
319 | } | ||
320 | |||
321 | rd->flags &= (channelFlag & CHANNEL_2GHZ) ? | ||
322 | REG_DOMAIN_2GHZ_MASK : REG_DOMAIN_5GHZ_MASK; | ||
323 | return true; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | static bool ath9k_regd_is_bit_set(int bit, u64 *bitmask) | ||
328 | { | ||
329 | int byteOffset, bitnum; | ||
330 | u64 val; | ||
331 | |||
332 | byteOffset = bit / 64; | ||
333 | bitnum = bit - byteOffset * 64; | ||
334 | val = ((u64) 1) << bitnum; | ||
335 | if (bitmask[byteOffset] & val) | ||
336 | return true; | ||
337 | else | ||
338 | return false; | ||
339 | } | ||
340 | |||
341 | static void | ||
342 | ath9k_regd_add_reg_classid(u8 *regclassids, u32 maxregids, | ||
343 | u32 *nregids, u8 regclassid) | ||
344 | { | ||
345 | int i; | ||
346 | |||
347 | if (regclassid == 0) | ||
348 | return; | ||
349 | |||
350 | for (i = 0; i < maxregids; i++) { | ||
351 | if (regclassids[i] == regclassid) | ||
352 | return; | ||
353 | if (regclassids[i] == 0) | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | if (i == maxregids) | ||
358 | return; | ||
359 | else { | ||
360 | regclassids[i] = regclassid; | ||
361 | *nregids += 1; | ||
362 | } | ||
363 | |||
364 | return; | ||
365 | } | ||
366 | |||
367 | static bool | ||
368 | ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal *ah, | ||
369 | enum reg_ext_bitmap bit) | ||
370 | { | ||
371 | return (ah->ah_currentRDExt & (1 << bit)) ? true : false; | ||
372 | } | ||
373 | |||
374 | #ifdef ATH_NF_PER_CHAN | ||
375 | |||
376 | static void ath9k_regd_init_rf_buffer(struct ath9k_channel *ichans, | ||
377 | int nchans) | ||
378 | { | ||
379 | int i, j, next; | ||
380 | |||
381 | for (next = 0; next < nchans; next++) { | ||
382 | for (i = 0; i < NUM_NF_READINGS; i++) { | ||
383 | ichans[next].nfCalHist[i].currIndex = 0; | ||
384 | ichans[next].nfCalHist[i].privNF = | ||
385 | AR_PHY_CCA_MAX_GOOD_VALUE; | ||
386 | ichans[next].nfCalHist[i].invalidNFcount = | ||
387 | AR_PHY_CCA_FILTERWINDOW_LENGTH; | ||
388 | for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { | ||
389 | ichans[next].nfCalHist[i].nfCalBuffer[j] = | ||
390 | AR_PHY_CCA_MAX_GOOD_VALUE; | ||
391 | } | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | #endif | ||
396 | |||
397 | static int ath9k_regd_is_chan_present(struct ath_hal *ah, | ||
398 | u16 c) | ||
399 | { | ||
400 | int i; | ||
401 | |||
402 | for (i = 0; i < 150; i++) { | ||
403 | if (!ah->ah_channels[i].channel) | ||
404 | return -1; | ||
405 | else if (ah->ah_channels[i].channel == c) | ||
406 | return i; | ||
407 | } | ||
408 | |||
409 | return -1; | ||
410 | } | ||
411 | |||
412 | static bool | ||
413 | ath9k_regd_add_channel(struct ath_hal *ah, | ||
414 | u16 c, | ||
415 | u16 c_lo, | ||
416 | u16 c_hi, | ||
417 | u16 maxChan, | ||
418 | u8 ctl, | ||
419 | int pos, | ||
420 | struct regDomain rd5GHz, | ||
421 | struct RegDmnFreqBand *fband, | ||
422 | struct regDomain *rd, | ||
423 | const struct cmode *cm, | ||
424 | struct ath9k_channel *ichans, | ||
425 | bool enableExtendedChannels) | ||
426 | { | ||
427 | struct ath9k_channel *chan; | ||
428 | int ret; | ||
429 | u32 channelFlags = 0; | ||
430 | u8 privFlags = 0; | ||
431 | |||
432 | if (!(c_lo <= c && c <= c_hi)) { | ||
433 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
434 | "%s: c %u out of range [%u..%u]\n", | ||
435 | __func__, c, c_lo, c_hi); | ||
436 | return false; | ||
437 | } | ||
438 | if ((fband->channelBW == CHANNEL_HALF_BW) && | ||
439 | !ah->ah_caps.halChanHalfRate) { | ||
440 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
441 | "%s: Skipping %u half rate channel\n", | ||
442 | __func__, c); | ||
443 | return false; | ||
444 | } | ||
445 | |||
446 | if ((fband->channelBW == CHANNEL_QUARTER_BW) && | ||
447 | !ah->ah_caps.halChanQuarterRate) { | ||
448 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
449 | "%s: Skipping %u quarter rate channel\n", | ||
450 | __func__, c); | ||
451 | return false; | ||
452 | } | ||
453 | |||
454 | if (((c + fband->channelSep) / 2) > (maxChan + HALF_MAXCHANBW)) { | ||
455 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
456 | "%s: c %u > maxChan %u\n", | ||
457 | __func__, c, maxChan); | ||
458 | return false; | ||
459 | } | ||
460 | |||
461 | if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) { | ||
462 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
463 | "Skipping ecm channel\n"); | ||
464 | return false; | ||
465 | } | ||
466 | |||
467 | if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == ATH9K_M_HOSTAP)) { | ||
468 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
469 | "Skipping HOSTAP channel\n"); | ||
470 | return false; | ||
471 | } | ||
472 | |||
473 | if (IS_HT40_MODE(cm->mode) && | ||
474 | !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_FCC_DFS_HT40)) && | ||
475 | (fband->useDfs) && | ||
476 | (rd->conformanceTestLimit != MKK)) { | ||
477 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
478 | "Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n"); | ||
479 | return false; | ||
480 | } | ||
481 | |||
482 | if (IS_HT40_MODE(cm->mode) && | ||
483 | !(ath9k_regd_get_eeprom_reg_ext_bits(ah, | ||
484 | REG_EXT_JAPAN_NONDFS_HT40)) && | ||
485 | !(fband->useDfs) && (rd->conformanceTestLimit == MKK)) { | ||
486 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
487 | "Skipping HT40 channel (en_jap_ht40 = 0)\n"); | ||
488 | return false; | ||
489 | } | ||
490 | |||
491 | if (IS_HT40_MODE(cm->mode) && | ||
492 | !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_JAPAN_DFS_HT40)) && | ||
493 | (fband->useDfs) && | ||
494 | (rd->conformanceTestLimit == MKK)) { | ||
495 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
496 | "Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n"); | ||
497 | return false; | ||
498 | } | ||
499 | |||
500 | /* Calculate channel flags */ | ||
501 | |||
502 | channelFlags = cm->flags; | ||
503 | |||
504 | switch (fband->channelBW) { | ||
505 | case CHANNEL_HALF_BW: | ||
506 | channelFlags |= CHANNEL_HALF; | ||
507 | break; | ||
508 | case CHANNEL_QUARTER_BW: | ||
509 | channelFlags |= CHANNEL_QUARTER; | ||
510 | break; | ||
511 | } | ||
512 | |||
513 | if (fband->usePassScan & rd->pscan) | ||
514 | channelFlags |= CHANNEL_PASSIVE; | ||
515 | else | ||
516 | channelFlags &= ~CHANNEL_PASSIVE; | ||
517 | if (fband->useDfs & rd->dfsMask) | ||
518 | privFlags = CHANNEL_DFS; | ||
519 | else | ||
520 | privFlags = 0; | ||
521 | if (rd->flags & LIMIT_FRAME_4MS) | ||
522 | privFlags |= CHANNEL_4MS_LIMIT; | ||
523 | if (privFlags & CHANNEL_DFS) | ||
524 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
525 | if (rd->flags & ADHOC_PER_11D) | ||
526 | privFlags |= CHANNEL_PER_11D_ADHOC; | ||
527 | |||
528 | if (channelFlags & CHANNEL_PASSIVE) { | ||
529 | if ((c < 2412) || (c > 2462)) { | ||
530 | if (rd5GHz.regDmnEnum == MKK1 || | ||
531 | rd5GHz.regDmnEnum == MKK2) { | ||
532 | u32 regcap = ah->ah_caps.halRegCap; | ||
533 | if (!(regcap & | ||
534 | (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | | ||
535 | AR_EEPROM_EEREGCAP_EN_KK_U2 | | ||
536 | AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) && | ||
537 | isUNII1OddChan(c)) { | ||
538 | channelFlags &= ~CHANNEL_PASSIVE; | ||
539 | } else { | ||
540 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
541 | } | ||
542 | } else { | ||
543 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
544 | } | ||
545 | } | ||
546 | } | ||
547 | |||
548 | if (cm->mode & (ATH9K_MODE_SEL_11A | | ||
549 | ATH9K_MODE_SEL_11NA_HT20 | | ||
550 | ATH9K_MODE_SEL_11NA_HT40PLUS | | ||
551 | ATH9K_MODE_SEL_11NA_HT40MINUS)) { | ||
552 | if (rd->flags & (ADHOC_NO_11A | DISALLOW_ADHOC_11A)) | ||
553 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
554 | } | ||
555 | |||
556 | /* Fill in channel details */ | ||
557 | |||
558 | ret = ath9k_regd_is_chan_present(ah, c); | ||
559 | if (ret == -1) { | ||
560 | chan = &ah->ah_channels[pos]; | ||
561 | chan->channel = c; | ||
562 | chan->maxRegTxPower = fband->powerDfs; | ||
563 | chan->antennaMax = fband->antennaMax; | ||
564 | chan->regDmnFlags = rd->flags; | ||
565 | chan->maxTxPower = AR5416_MAX_RATE_POWER; | ||
566 | chan->minTxPower = AR5416_MAX_RATE_POWER; | ||
567 | chan->channelFlags = channelFlags; | ||
568 | chan->privFlags = privFlags; | ||
569 | } else { | ||
570 | chan = &ah->ah_channels[ret]; | ||
571 | chan->channelFlags |= channelFlags; | ||
572 | chan->privFlags |= privFlags; | ||
573 | } | ||
574 | |||
575 | /* Set CTLs */ | ||
576 | |||
577 | if ((cm->flags & CHANNEL_ALL) == CHANNEL_A) | ||
578 | chan->conformanceTestLimit[0] = ctl; | ||
579 | else if ((cm->flags & CHANNEL_ALL) == CHANNEL_B) | ||
580 | chan->conformanceTestLimit[1] = ctl; | ||
581 | else if ((cm->flags & CHANNEL_ALL) == CHANNEL_G) | ||
582 | chan->conformanceTestLimit[2] = ctl; | ||
583 | |||
584 | return (ret == -1) ? true : false; | ||
585 | } | ||
586 | |||
587 | static bool ath9k_regd_japan_check(struct ath_hal *ah, | ||
588 | int b, | ||
589 | struct regDomain *rd5GHz) | ||
590 | { | ||
591 | bool skipband = false; | ||
592 | int i; | ||
593 | u32 regcap; | ||
594 | |||
595 | for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) { | ||
596 | if (j_bandcheck[i].freqbandbit == b) { | ||
597 | regcap = ah->ah_caps.halRegCap; | ||
598 | if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) { | ||
599 | skipband = true; | ||
600 | } else if ((regcap & AR_EEPROM_EEREGCAP_EN_KK_U2) || | ||
601 | (regcap & AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) { | ||
602 | rd5GHz->dfsMask |= DFS_MKK4; | ||
603 | rd5GHz->pscan |= PSCAN_MKK3; | ||
604 | } | ||
605 | break; | ||
606 | } | ||
607 | } | ||
608 | |||
609 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
610 | "%s: Skipping %d freq band\n", | ||
611 | __func__, j_bandcheck[i].freqbandbit); | ||
612 | |||
613 | return skipband; | ||
614 | } | ||
615 | |||
616 | bool | ||
617 | ath9k_regd_init_channels(struct ath_hal *ah, | ||
618 | u32 maxchans, | ||
619 | u32 *nchans, u8 *regclassids, | ||
620 | u32 maxregids, u32 *nregids, u16 cc, | ||
621 | u32 modeSelect, bool enableOutdoor, | ||
622 | bool enableExtendedChannels) | ||
623 | { | ||
624 | u32 modesAvail; | ||
625 | u16 maxChan = 7000; | ||
626 | struct country_code_to_enum_rd *country = NULL; | ||
627 | struct regDomain rd5GHz, rd2GHz; | ||
628 | const struct cmode *cm; | ||
629 | struct ath9k_channel *ichans = &ah->ah_channels[0]; | ||
630 | int next = 0, b; | ||
631 | u8 ctl; | ||
632 | int regdmn; | ||
633 | u16 chanSep; | ||
634 | |||
635 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: cc %u mode 0x%x%s%s\n", | ||
636 | __func__, cc, modeSelect, | ||
637 | enableOutdoor ? " Enable outdoor" : " ", | ||
638 | enableExtendedChannels ? " Enable ecm" : ""); | ||
639 | |||
640 | if (!ath9k_regd_is_ccode_valid(ah, cc)) { | ||
641 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
642 | "%s: invalid country code %d\n", __func__, cc); | ||
643 | return false; | ||
644 | } | ||
645 | |||
646 | if (!ath9k_regd_is_eeprom_valid(ah)) { | ||
647 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
648 | "%s: invalid EEPROM contents\n", __func__); | ||
649 | return false; | ||
650 | } | ||
651 | |||
652 | ah->ah_countryCode = ath9k_regd_get_default_country(ah); | ||
653 | |||
654 | if (ah->ah_countryCode == CTRY_DEFAULT) { | ||
655 | ah->ah_countryCode = cc & COUNTRY_CODE_MASK; | ||
656 | if ((ah->ah_countryCode == CTRY_DEFAULT) && | ||
657 | (ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) { | ||
658 | ah->ah_countryCode = CTRY_UNITED_STATES; | ||
659 | } | ||
660 | } | ||
661 | |||
662 | #ifdef AH_SUPPORT_11D | ||
663 | if (ah->ah_countryCode == CTRY_DEFAULT) { | ||
664 | regdmn = ath9k_regd_get_eepromRD(ah); | ||
665 | country = NULL; | ||
666 | } else { | ||
667 | #endif | ||
668 | country = ath9k_regd_find_country(ah->ah_countryCode); | ||
669 | if (country == NULL) { | ||
670 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
671 | "Country is NULL!!!!, cc= %d\n", | ||
672 | ah->ah_countryCode); | ||
673 | return false; | ||
674 | } else { | ||
675 | regdmn = country->regDmnEnum; | ||
676 | #ifdef AH_SUPPORT_11D | ||
677 | if (((ath9k_regd_get_eepromRD(ah) & | ||
678 | WORLD_SKU_MASK) == WORLD_SKU_PREFIX) && | ||
679 | (cc == CTRY_UNITED_STATES)) { | ||
680 | if (!isWwrSKU_NoMidband(ah) | ||
681 | && ath9k_regd_is_fcc_midband_supported(ah)) | ||
682 | regdmn = FCC3_FCCA; | ||
683 | else | ||
684 | regdmn = FCC1_FCCA; | ||
685 | } | ||
686 | #endif | ||
687 | } | ||
688 | #ifdef AH_SUPPORT_11D | ||
689 | } | ||
690 | #endif | ||
691 | if (!ath9k_regd_get_wmode_regdomain(ah, | ||
692 | regdmn, | ||
693 | ~CHANNEL_2GHZ, | ||
694 | &rd5GHz)) { | ||
695 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
696 | "%s: couldn't find unitary " | ||
697 | "5GHz reg domain for country %u\n", | ||
698 | __func__, ah->ah_countryCode); | ||
699 | return false; | ||
700 | } | ||
701 | if (!ath9k_regd_get_wmode_regdomain(ah, | ||
702 | regdmn, | ||
703 | CHANNEL_2GHZ, | ||
704 | &rd2GHz)) { | ||
705 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
706 | "%s: couldn't find unitary 2GHz " | ||
707 | "reg domain for country %u\n", | ||
708 | __func__, ah->ah_countryCode); | ||
709 | return false; | ||
710 | } | ||
711 | |||
712 | if (!isWwrSKU(ah) && ((rd5GHz.regDmnEnum == FCC1) || | ||
713 | (rd5GHz.regDmnEnum == FCC2))) { | ||
714 | if (ath9k_regd_is_fcc_midband_supported(ah)) { | ||
715 | if (!ath9k_regd_get_wmode_regdomain(ah, | ||
716 | FCC3_FCCA, | ||
717 | ~CHANNEL_2GHZ, | ||
718 | &rd5GHz)) { | ||
719 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
720 | "%s: couldn't find unitary 5GHz " | ||
721 | "reg domain for country %u\n", | ||
722 | __func__, ah->ah_countryCode); | ||
723 | return false; | ||
724 | } | ||
725 | } | ||
726 | } | ||
727 | |||
728 | if (country == NULL) { | ||
729 | modesAvail = ah->ah_caps.halWirelessModes; | ||
730 | } else { | ||
731 | modesAvail = ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz); | ||
732 | if (!enableOutdoor) | ||
733 | maxChan = country->outdoorChanStart; | ||
734 | } | ||
735 | |||
736 | next = 0; | ||
737 | |||
738 | if (maxchans > ARRAY_SIZE(ah->ah_channels)) | ||
739 | maxchans = ARRAY_SIZE(ah->ah_channels); | ||
740 | |||
741 | for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) { | ||
742 | u16 c, c_hi, c_lo; | ||
743 | u64 *channelBM = NULL; | ||
744 | struct regDomain *rd = NULL; | ||
745 | struct RegDmnFreqBand *fband = NULL, *freqs; | ||
746 | int8_t low_adj = 0, hi_adj = 0; | ||
747 | |||
748 | if ((cm->mode & modeSelect) == 0) { | ||
749 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
750 | "%s: skip mode 0x%x flags 0x%x\n", | ||
751 | __func__, cm->mode, cm->flags); | ||
752 | continue; | ||
753 | } | ||
754 | if ((cm->mode & modesAvail) == 0) { | ||
755 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
756 | "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", | ||
757 | __func__, modesAvail, cm->mode, | ||
758 | cm->flags); | ||
759 | continue; | ||
760 | } | ||
761 | if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) { | ||
762 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
763 | "%s: channels 0x%x not supported " | ||
764 | "by hardware\n", | ||
765 | __func__, cm->flags); | ||
766 | continue; | ||
767 | } | ||
768 | |||
769 | switch (cm->mode) { | ||
770 | case ATH9K_MODE_SEL_11A: | ||
771 | case ATH9K_MODE_SEL_11NA_HT20: | ||
772 | case ATH9K_MODE_SEL_11NA_HT40PLUS: | ||
773 | case ATH9K_MODE_SEL_11NA_HT40MINUS: | ||
774 | rd = &rd5GHz; | ||
775 | channelBM = rd->chan11a; | ||
776 | freqs = ®Dmn5GhzFreq[0]; | ||
777 | ctl = rd->conformanceTestLimit; | ||
778 | break; | ||
779 | case ATH9K_MODE_SEL_11B: | ||
780 | rd = &rd2GHz; | ||
781 | channelBM = rd->chan11b; | ||
782 | freqs = ®Dmn2GhzFreq[0]; | ||
783 | ctl = rd->conformanceTestLimit | CTL_11B; | ||
784 | break; | ||
785 | case ATH9K_MODE_SEL_11G: | ||
786 | case ATH9K_MODE_SEL_11NG_HT20: | ||
787 | case ATH9K_MODE_SEL_11NG_HT40PLUS: | ||
788 | case ATH9K_MODE_SEL_11NG_HT40MINUS: | ||
789 | rd = &rd2GHz; | ||
790 | channelBM = rd->chan11g; | ||
791 | freqs = ®Dmn2Ghz11gFreq[0]; | ||
792 | ctl = rd->conformanceTestLimit | CTL_11G; | ||
793 | break; | ||
794 | default: | ||
795 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
796 | "%s: Unknown HAL mode 0x%x\n", __func__, | ||
797 | cm->mode); | ||
798 | continue; | ||
799 | } | ||
800 | |||
801 | if (ath9k_regd_is_chan_bm_zero(channelBM)) | ||
802 | continue; | ||
803 | |||
804 | if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40PLUS) || | ||
805 | (cm->mode == ATH9K_MODE_SEL_11NG_HT40PLUS)) { | ||
806 | hi_adj = -20; | ||
807 | } | ||
808 | |||
809 | if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40MINUS) || | ||
810 | (cm->mode == ATH9K_MODE_SEL_11NG_HT40MINUS)) { | ||
811 | low_adj = 20; | ||
812 | } | ||
813 | |||
814 | /* XXX: Add a helper here instead */ | ||
815 | for (b = 0; b < 64 * BMLEN; b++) { | ||
816 | if (ath9k_regd_is_bit_set(b, channelBM)) { | ||
817 | fband = &freqs[b]; | ||
818 | if (rd5GHz.regDmnEnum == MKK1 | ||
819 | || rd5GHz.regDmnEnum == MKK2) { | ||
820 | if (ath9k_regd_japan_check(ah, | ||
821 | b, | ||
822 | &rd5GHz)) | ||
823 | continue; | ||
824 | } | ||
825 | |||
826 | ath9k_regd_add_reg_classid(regclassids, | ||
827 | maxregids, | ||
828 | nregids, | ||
829 | fband-> | ||
830 | regClassId); | ||
831 | |||
832 | if (IS_HT40_MODE(cm->mode) && (rd == &rd5GHz)) { | ||
833 | chanSep = 40; | ||
834 | if (fband->lowChannel == 5280) | ||
835 | low_adj += 20; | ||
836 | |||
837 | if (fband->lowChannel == 5170) | ||
838 | continue; | ||
839 | } else | ||
840 | chanSep = fband->channelSep; | ||
841 | |||
842 | for (c = fband->lowChannel + low_adj; | ||
843 | ((c <= (fband->highChannel + hi_adj)) && | ||
844 | (c >= (fband->lowChannel + low_adj))); | ||
845 | c += chanSep) { | ||
846 | if (next >= maxchans) { | ||
847 | DPRINTF(ah->ah_sc, | ||
848 | ATH_DBG_REGULATORY, | ||
849 | "%s: too many channels " | ||
850 | "for channel table\n", | ||
851 | __func__); | ||
852 | goto done; | ||
853 | } | ||
854 | if (ath9k_regd_add_channel(ah, | ||
855 | c, c_lo, c_hi, | ||
856 | maxChan, ctl, | ||
857 | next, | ||
858 | rd5GHz, | ||
859 | fband, rd, cm, | ||
860 | ichans, | ||
861 | enableExtendedChannels)) | ||
862 | next++; | ||
863 | } | ||
864 | if (IS_HT40_MODE(cm->mode) && | ||
865 | (fband->lowChannel == 5280)) { | ||
866 | low_adj -= 20; | ||
867 | } | ||
868 | } | ||
869 | } | ||
870 | } | ||
871 | done: | ||
872 | if (next != 0) { | ||
873 | int i; | ||
874 | |||
875 | if (next > ARRAY_SIZE(ah->ah_channels)) { | ||
876 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
877 | "%s: too many channels %u; truncating to %u\n", | ||
878 | __func__, next, | ||
879 | (int) ARRAY_SIZE(ah->ah_channels)); | ||
880 | next = ARRAY_SIZE(ah->ah_channels); | ||
881 | } | ||
882 | #ifdef ATH_NF_PER_CHAN | ||
883 | ath9k_regd_init_rf_buffer(ichans, next); | ||
884 | #endif | ||
885 | ath9k_regd_sort(ichans, next, | ||
886 | sizeof(struct ath9k_channel), | ||
887 | ath9k_regd_chansort); | ||
888 | |||
889 | ah->ah_nchan = next; | ||
890 | |||
891 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "Channel list:\n"); | ||
892 | for (i = 0; i < next; i++) { | ||
893 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
894 | "chan: %d flags: 0x%x\n", | ||
895 | ah->ah_channels[i].channel, | ||
896 | ah->ah_channels[i].channelFlags); | ||
897 | } | ||
898 | } | ||
899 | *nchans = next; | ||
900 | |||
901 | ah->ah_countryCode = ah->ah_countryCode; | ||
902 | |||
903 | ah->ah_currentRDInUse = regdmn; | ||
904 | ah->ah_currentRD5G = rd5GHz.regDmnEnum; | ||
905 | ah->ah_currentRD2G = rd2GHz.regDmnEnum; | ||
906 | if (country == NULL) { | ||
907 | ah->ah_iso[0] = 0; | ||
908 | ah->ah_iso[1] = 0; | ||
909 | } else { | ||
910 | ah->ah_iso[0] = country->isoName[0]; | ||
911 | ah->ah_iso[1] = country->isoName[1]; | ||
912 | } | ||
913 | |||
914 | return next != 0; | ||
915 | } | ||
916 | |||
917 | struct ath9k_channel* | ||
918 | ath9k_regd_check_channel(struct ath_hal *ah, | ||
919 | const struct ath9k_channel *c) | ||
920 | { | ||
921 | struct ath9k_channel *base, *cc; | ||
922 | |||
923 | int flags = c->channelFlags & CHAN_FLAGS; | ||
924 | int n, lim; | ||
925 | |||
926 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
927 | "%s: channel %u/0x%x (0x%x) requested\n", __func__, | ||
928 | c->channel, c->channelFlags, flags); | ||
929 | |||
930 | cc = ah->ah_curchan; | ||
931 | if (cc != NULL && cc->channel == c->channel && | ||
932 | (cc->channelFlags & CHAN_FLAGS) == flags) { | ||
933 | if ((cc->privFlags & CHANNEL_INTERFERENCE) && | ||
934 | (cc->privFlags & CHANNEL_DFS)) | ||
935 | return NULL; | ||
936 | else | ||
937 | return cc; | ||
938 | } | ||
939 | |||
940 | base = ah->ah_channels; | ||
941 | n = ah->ah_nchan; | ||
942 | |||
943 | for (lim = n; lim != 0; lim >>= 1) { | ||
944 | int d; | ||
945 | cc = &base[lim >> 1]; | ||
946 | d = c->channel - cc->channel; | ||
947 | if (d == 0) { | ||
948 | if ((cc->channelFlags & CHAN_FLAGS) == flags) { | ||
949 | if ((cc->privFlags & CHANNEL_INTERFERENCE) && | ||
950 | (cc->privFlags & CHANNEL_DFS)) | ||
951 | return NULL; | ||
952 | else | ||
953 | return cc; | ||
954 | } | ||
955 | d = flags - (cc->channelFlags & CHAN_FLAGS); | ||
956 | } | ||
957 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
958 | "%s: channel %u/0x%x d %d\n", __func__, | ||
959 | cc->channel, cc->channelFlags, d); | ||
960 | if (d > 0) { | ||
961 | base = cc + 1; | ||
962 | lim--; | ||
963 | } | ||
964 | } | ||
965 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: no match for %u/0x%x\n", | ||
966 | __func__, c->channel, c->channelFlags); | ||
967 | return NULL; | ||
968 | } | ||
969 | |||
970 | u32 | ||
971 | ath9k_regd_get_antenna_allowed(struct ath_hal *ah, | ||
972 | struct ath9k_channel *chan) | ||
973 | { | ||
974 | struct ath9k_channel *ichan = NULL; | ||
975 | |||
976 | ichan = ath9k_regd_check_channel(ah, chan); | ||
977 | if (!ichan) | ||
978 | return 0; | ||
979 | |||
980 | return ichan->antennaMax; | ||
981 | } | ||
982 | |||
983 | u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan) | ||
984 | { | ||
985 | u32 ctl = NO_CTL; | ||
986 | struct ath9k_channel *ichan; | ||
987 | |||
988 | if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) { | ||
989 | if (IS_CHAN_B(chan)) | ||
990 | ctl = SD_NO_CTL | CTL_11B; | ||
991 | else if (IS_CHAN_G(chan)) | ||
992 | ctl = SD_NO_CTL | CTL_11G; | ||
993 | else | ||
994 | ctl = SD_NO_CTL | CTL_11A; | ||
995 | } else { | ||
996 | ichan = ath9k_regd_check_channel(ah, chan); | ||
997 | if (ichan != NULL) { | ||
998 | /* FIXME */ | ||
999 | if (IS_CHAN_A(ichan)) | ||
1000 | ctl = ichan->conformanceTestLimit[0]; | ||
1001 | else if (IS_CHAN_B(ichan)) | ||
1002 | ctl = ichan->conformanceTestLimit[1]; | ||
1003 | else if (IS_CHAN_G(ichan)) | ||
1004 | ctl = ichan->conformanceTestLimit[2]; | ||
1005 | |||
1006 | if (IS_CHAN_G(chan) && (ctl & 0xf) == CTL_11B) | ||
1007 | ctl = (ctl & ~0xf) | CTL_11G; | ||
1008 | } | ||
1009 | } | ||
1010 | return ctl; | ||
1011 | } | ||
1012 | |||
1013 | void ath9k_regd_get_current_country(struct ath_hal *ah, | ||
1014 | struct ath9k_country_entry *ctry) | ||
1015 | { | ||
1016 | u16 rd = ath9k_regd_get_eepromRD(ah); | ||
1017 | |||
1018 | ctry->isMultidomain = false; | ||
1019 | if (rd == CTRY_DEFAULT) | ||
1020 | ctry->isMultidomain = true; | ||
1021 | else if (!(rd & COUNTRY_ERD_FLAG)) | ||
1022 | ctry->isMultidomain = isWwrSKU(ah); | ||
1023 | |||
1024 | ctry->countryCode = ah->ah_countryCode; | ||
1025 | ctry->regDmnEnum = ah->ah_currentRD; | ||
1026 | ctry->regDmn5G = ah->ah_currentRD5G; | ||
1027 | ctry->regDmn2G = ah->ah_currentRD2G; | ||
1028 | ctry->iso[0] = ah->ah_iso[0]; | ||
1029 | ctry->iso[1] = ah->ah_iso[1]; | ||
1030 | ctry->iso[2] = ah->ah_iso[2]; | ||
1031 | } | ||