u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan) { u32 ctl = NO_CTL; struct ath9k_channel *ichan; if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) { if (IS_CHAN_B(chan)) ctl = SD_NO_CTL | CTL_11B; else if (IS_CHAN_G(chan)) ctl = SD_NO_CTL | CTL_11G; else ctl = SD_NO_CTL | CTL_11A; } else { ichan = ath9k_regd_check_channel(ah, chan); if (ichan != NULL) { /* FIXME */ if (IS_CHAN_A(ichan)) ctl = ichan->conformanceTestLimit[0]; else if (IS_CHAN_B(ichan)) ctl = ichan->conformanceTestLimit[1]; else if (IS_CHAN_G(ichan)) ctl = ichan->conformanceTestLimit[2]; if (IS_CHAN_G(chan) && (ctl & 0xf) == CTL_11B) ctl = (ctl & ~0xf) | CTL_11G; } } return ctl; }
void ath9k_regd_get_current_country(struct ath_hal *ah, struct ath9k_country_entry *ctry) { u16 rd = ath9k_regd_get_eepromRD(ah); ctry->isMultidomain = false; if (rd == CTRY_DEFAULT) ctry->isMultidomain = true; else if (!(rd & COUNTRY_ERD_FLAG)) ctry->isMultidomain = isWwrSKU(ah); ctry->countryCode = ah->ah_countryCode; ctry->regDmnEnum = ah->ah_currentRD; ctry->regDmn5G = ah->ah_currentRD5G; ctry->regDmn2G = ah->ah_currentRD2G; ctry->iso[0] = ah->ah_iso[0]; ctry->iso[1] = ah->ah_iso[1]; ctry->iso[2] = ah->ah_iso[2]; }
/* * Return the test group for the specific channel based on * the current regulatory setup. */ u_int ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c) { u_int ctl; if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz || (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah))) ctl = SD_NO_CTL; else if (IEEE80211_IS_CHAN_2GHZ(c)) ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit; else ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit; if (IEEE80211_IS_CHAN_B(c)) return ctl | CTL_11B; if (IEEE80211_IS_CHAN_G(c)) return ctl | CTL_11G; if (IEEE80211_IS_CHAN_108G(c)) return ctl | CTL_108G; if (IEEE80211_IS_CHAN_TURBO(c)) return ctl | CTL_TURBO; if (IEEE80211_IS_CHAN_A(c)) return ctl | CTL_11A; return ctl; }
bool ath9k_regd_init_channels(struct ath_hal *ah, u32 maxchans, u32 *nchans, u8 *regclassids, u32 maxregids, u32 *nregids, u16 cc, bool enableOutdoor, bool enableExtendedChannels) { u16 maxChan = 7000; struct country_code_to_enum_rd *country = NULL; struct regDomain rd5GHz, rd2GHz; const struct cmode *cm; struct ath9k_channel *ichans = &ah->ah_channels[0]; int next = 0, b; u8 ctl; int regdmn; u16 chanSep; unsigned long *modes_avail; DECLARE_BITMAP(modes_allowed, ATH9K_MODE_MAX); DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: cc %u %s %s\n", __func__, cc, enableOutdoor ? "Enable outdoor" : "", enableExtendedChannels ? "Enable ecm" : ""); if (!ath9k_regd_is_ccode_valid(ah, cc)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: invalid country code %d\n", __func__, cc); return false; } if (!ath9k_regd_is_eeprom_valid(ah)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: invalid EEPROM contents\n", __func__); return false; } ah->ah_countryCode = ath9k_regd_get_default_country(ah); if (ah->ah_countryCode == CTRY_DEFAULT) { ah->ah_countryCode = cc & COUNTRY_CODE_MASK; if ((ah->ah_countryCode == CTRY_DEFAULT) && (ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) { ah->ah_countryCode = CTRY_UNITED_STATES; } } #ifdef AH_SUPPORT_11D if (ah->ah_countryCode == CTRY_DEFAULT) { regdmn = ath9k_regd_get_eepromRD(ah); country = NULL; } else { #endif country = ath9k_regd_find_country(ah->ah_countryCode); if (country == NULL) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "Country is NULL!!!!, cc= %d\n", ah->ah_countryCode); return false; } else { regdmn = country->regDmnEnum; #ifdef AH_SUPPORT_11D if (((ath9k_regd_get_eepromRD(ah) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) && (cc == CTRY_UNITED_STATES)) { if (!isWwrSKU_NoMidband(ah) && ath9k_regd_is_fcc_midband_supported(ah)) regdmn = FCC3_FCCA; else regdmn = FCC1_FCCA; } #endif } #ifdef AH_SUPPORT_11D } #endif if (!ath9k_regd_get_wmode_regdomain(ah, regdmn, ~CHANNEL_2GHZ, &rd5GHz)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: couldn't find unitary " "5GHz reg domain for country %u\n", __func__, ah->ah_countryCode); return false; } if (!ath9k_regd_get_wmode_regdomain(ah, regdmn, CHANNEL_2GHZ, &rd2GHz)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: couldn't find unitary 2GHz " "reg domain for country %u\n", __func__, ah->ah_countryCode); return false; } if (!isWwrSKU(ah) && ((rd5GHz.regDmnEnum == FCC1) || (rd5GHz.regDmnEnum == FCC2))) { if (ath9k_regd_is_fcc_midband_supported(ah)) { if (!ath9k_regd_get_wmode_regdomain(ah, FCC3_FCCA, ~CHANNEL_2GHZ, &rd5GHz)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: couldn't find unitary 5GHz " "reg domain for country %u\n", __func__, ah->ah_countryCode); return false; } } } if (country == NULL) { modes_avail = ah->ah_caps.wireless_modes; } else { ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz, modes_allowed); modes_avail = modes_allowed; if (!enableOutdoor) maxChan = country->outdoorChanStart; } next = 0; if (maxchans > ARRAY_SIZE(ah->ah_channels)) maxchans = ARRAY_SIZE(ah->ah_channels); for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) { u16 c, c_hi, c_lo; u64 *channelBM = NULL; struct regDomain *rd = NULL; struct RegDmnFreqBand *fband = NULL, *freqs; int8_t low_adj = 0, hi_adj = 0; if (!test_bit(cm->mode, modes_avail)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: !avail mode %d flags 0x%x\n", __func__, cm->mode, cm->flags); continue; } if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: channels 0x%x not supported " "by hardware\n", __func__, cm->flags); continue; } switch (cm->mode) { case ATH9K_MODE_11A: case ATH9K_MODE_11NA_HT20: case ATH9K_MODE_11NA_HT40PLUS: case ATH9K_MODE_11NA_HT40MINUS: rd = &rd5GHz; channelBM = rd->chan11a; freqs = ®Dmn5GhzFreq[0]; ctl = rd->conformanceTestLimit; break; case ATH9K_MODE_11B: rd = &rd2GHz; channelBM = rd->chan11b; freqs = ®Dmn2GhzFreq[0]; ctl = rd->conformanceTestLimit | CTL_11B; break; case ATH9K_MODE_11G: case ATH9K_MODE_11NG_HT20: case ATH9K_MODE_11NG_HT40PLUS: case ATH9K_MODE_11NG_HT40MINUS: rd = &rd2GHz; channelBM = rd->chan11g; freqs = ®Dmn2Ghz11gFreq[0]; ctl = rd->conformanceTestLimit | CTL_11G; break; default: DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: Unknown HAL mode 0x%x\n", __func__, cm->mode); continue; } if (ath9k_regd_is_chan_bm_zero(channelBM)) continue; if ((cm->mode == ATH9K_MODE_11NA_HT40PLUS) || (cm->mode == ATH9K_MODE_11NG_HT40PLUS)) { hi_adj = -20; } if ((cm->mode == ATH9K_MODE_11NA_HT40MINUS) || (cm->mode == ATH9K_MODE_11NG_HT40MINUS)) { low_adj = 20; } /* XXX: Add a helper here instead */ for (b = 0; b < 64 * BMLEN; b++) { if (ath9k_regd_is_bit_set(b, channelBM)) { fband = &freqs[b]; if (rd5GHz.regDmnEnum == MKK1 || rd5GHz.regDmnEnum == MKK2) { if (ath9k_regd_japan_check(ah, b, &rd5GHz)) continue; } ath9k_regd_add_reg_classid(regclassids, maxregids, nregids, fband-> regClassId); if (IS_HT40_MODE(cm->mode) && (rd == &rd5GHz)) { chanSep = 40; if (fband->lowChannel == 5280) low_adj += 20; if (fband->lowChannel == 5170) continue; } else chanSep = fband->channelSep; for (c = fband->lowChannel + low_adj; ((c <= (fband->highChannel + hi_adj)) && (c >= (fband->lowChannel + low_adj))); c += chanSep) { if (next >= maxchans) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: too many channels " "for channel table\n", __func__); goto done; } if (ath9k_regd_add_channel(ah, c, c_lo, c_hi, maxChan, ctl, next, rd5GHz, fband, rd, cm, ichans, enableExtendedChannels)) next++; } if (IS_HT40_MODE(cm->mode) && (fband->lowChannel == 5280)) { low_adj -= 20; } } } } done: if (next != 0) { int i; if (next > ARRAY_SIZE(ah->ah_channels)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: too many channels %u; truncating to %u\n", __func__, next, (int) ARRAY_SIZE(ah->ah_channels)); next = ARRAY_SIZE(ah->ah_channels); } #ifdef ATH_NF_PER_CHAN ath9k_regd_init_rf_buffer(ichans, next); #endif ath9k_regd_sort(ichans, next, sizeof(struct ath9k_channel), ath9k_regd_chansort); ah->ah_nchan = next; DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "Channel list:\n"); for (i = 0; i < next; i++) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "chan: %d flags: 0x%x\n", ah->ah_channels[i].channel, ah->ah_channels[i].channelFlags); } } *nchans = next; ah->ah_countryCode = ah->ah_countryCode; ah->ah_currentRDInUse = regdmn; ah->ah_currentRD5G = rd5GHz.regDmnEnum; ah->ah_currentRD2G = rd2GHz.regDmnEnum; if (country == NULL) { ah->ah_iso[0] = 0; ah->ah_iso[1] = 0; } else { ah->ah_iso[0] = country->isoName[0]; ah->ah_iso[1] = country->isoName[1]; } return next != 0; }