+static bool
+ath9k_regd_add_channel(struct ath_hal *ah,
+ u_int16_t c,
+ u_int16_t c_lo,
+ u_int16_t c_hi,
+ u_int16_t maxChan,
+ u_int8_t ctl,
+ int pos,
+ struct regDomain rd5GHz,
+ struct RegDmnFreqBand *fband,
+ struct regDomain *rd,
+ const struct cmode *cm,
+ struct hal_channel_internal *ichans,
+ bool enableExtendedChannels)
+{
+ struct hal_channel_internal icv;
+
+ if (!(c_lo <= c && c <= c_hi)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "%s: c %u out of range [%u..%u]\n",
+ __func__, c, c_lo, c_hi);
+ return false;
+ }
+ if ((fband->channelBW == CHANNEL_HALF_BW) &&
+ !ah->ah_caps.halChanHalfRate) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "%s: Skipping %u half rate channel\n",
+ __func__, c);
+ return false;
+ }
+
+ if ((fband->channelBW == CHANNEL_QUARTER_BW) &&
+ !ah->ah_caps.halChanQuarterRate) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "%s: Skipping %u quarter rate channel\n",
+ __func__, c);
+ return false;
+ }
+
+ if (((c + fband->channelSep) / 2) > (maxChan + HALF_MAXCHANBW)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "%s: c %u > maxChan %u\n",
+ __func__, c, maxChan);
+ return false;
+ }
+
+ if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "Skipping ecm channel\n");
+ return false;
+ }
+
+ if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == HAL_M_HOSTAP)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "Skipping HOSTAP channel\n");
+ return false;
+ }
+
+ if (IS_HT40_MODE(cm->mode) &&
+ !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_FCC_DFS_HT40)) &&
+ (fband->useDfs) &&
+ (rd->conformanceTestLimit != MKK)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n");
+ return false;
+ }
+
+ if (IS_HT40_MODE(cm->mode) &&
+ !(ath9k_regd_get_eeprom_reg_ext_bits(ah,
+ REG_EXT_JAPAN_NONDFS_HT40)) &&
+ !(fband->useDfs) && (rd->conformanceTestLimit == MKK)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "Skipping HT40 channel (en_jap_ht40 = 0)\n");
+ return false;
+ }
+
+ if (IS_HT40_MODE(cm->mode) &&
+ !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_JAPAN_DFS_HT40)) &&
+ (fband->useDfs) &&
+ (rd->conformanceTestLimit == MKK)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n");
+ return false;
+ }
+
+ memset(&icv, 0, sizeof(icv));
+ icv.channel = c;
+ icv.channelFlags = cm->flags;
+
+ switch (fband->channelBW) {
+ case CHANNEL_HALF_BW:
+ icv.channelFlags |= CHANNEL_HALF;
+ break;
+ case CHANNEL_QUARTER_BW:
+ icv.channelFlags |= CHANNEL_QUARTER;
+ break;
+ }
+
+ icv.maxRegTxPower = fband->powerDfs;
+ icv.antennaMax = fband->antennaMax;
+ icv.regDmnFlags = rd->flags;
+ icv.conformanceTestLimit = ctl;
+ if (fband->usePassScan & rd->pscan)
+ icv.channelFlags |= CHANNEL_PASSIVE;
+ else
+ icv.channelFlags &= ~CHANNEL_PASSIVE;
+ if (fband->useDfs & rd->dfsMask)
+ icv.privFlags = CHANNEL_DFS;
+ else
+ icv.privFlags = 0;
+ if (rd->flags & LIMIT_FRAME_4MS)
+ icv.privFlags |= CHANNEL_4MS_LIMIT;
+ if (icv.privFlags & CHANNEL_DFS)
+ icv.privFlags |= CHANNEL_DISALLOW_ADHOC;
+ if (icv.regDmnFlags & ADHOC_PER_11D)
+ icv.privFlags |= CHANNEL_PER_11D_ADHOC;
+
+ if (icv.channelFlags & CHANNEL_PASSIVE) {
+ if ((icv.channel < 2412) || (icv.channel > 2462)) {
+ if (rd5GHz.regDmnEnum == MKK1 ||
+ rd5GHz.regDmnEnum == MKK2) {
+ u_int32_t regcap = ah->ah_caps.halRegCap;
+ if (!(regcap &
+ (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN |
+ AR_EEPROM_EEREGCAP_EN_KK_U2 |
+ AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) &&
+ isUNII1OddChan(icv.channel)) {
+ icv.channelFlags &= ~CHANNEL_PASSIVE;
+ } else {
+ icv.privFlags |= CHANNEL_DISALLOW_ADHOC;
+ }
+ } else {
+ icv.privFlags |= CHANNEL_DISALLOW_ADHOC;
+ }
+ }
+ }
+
+ if (cm->mode & (ATH9K_MODE_SEL_11A |
+ ATH9K_MODE_SEL_11NA_HT20 |
+ ATH9K_MODE_SEL_11NA_HT40PLUS |
+ ATH9K_MODE_SEL_11NA_HT40MINUS)) {
+ if (icv.regDmnFlags & (ADHOC_NO_11A | DISALLOW_ADHOC_11A))
+ icv.privFlags |= CHANNEL_DISALLOW_ADHOC;
+ }
+
+ memcpy(&ichans[pos], &icv,
+ sizeof(struct hal_channel_internal));
+
+ return true;
+}
+
+static bool ath9k_regd_japan_check(struct ath_hal *ah,
+ int b,
+ struct regDomain *rd5GHz)
+{
+ bool skipband = false;
+ int i;
+ u_int32_t regcap;
+
+ for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) {
+ if (j_bandcheck[i].freqbandbit == b) {
+ regcap = ah->ah_caps.halRegCap;
+ if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) {
+ skipband = true;
+ } else if ((regcap & AR_EEPROM_EEREGCAP_EN_KK_U2) ||
+ (regcap & AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) {
+ rd5GHz->dfsMask |= DFS_MKK4;
+ rd5GHz->pscan |= PSCAN_MKK3;
+ }
+ break;
+ }
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "%s: Skipping %d freq band\n",
+ __func__, j_bandcheck[i].freqbandbit);
+
+ return skipband;
+}
+
+bool