static u_int getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect) { #define HAL_MODE_11A_ALL \ (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) u_int modesMask; /* get modes that HW is capable of */ modesMask = ath_hal_getWirelessModes(ah); modesMask &= modeSelect; /* optimize work below if no 11a channels */ if (isChanBitMaskZero(rd5GHz->chan11a) && (modesMask & HAL_MODE_11A_ALL)) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: disallow all 11a\n", __func__); modesMask &= ~HAL_MODE_11A_ALL; } return (modesMask); #undef HAL_MODE_11A_ALL }
/* * Construct the channel list for the specified regulatory config. */ static HAL_STATUS getchannels(struct ath_hal *ah, struct ieee80211_channel chans[], u_int maxchans, int *nchans, u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn, HAL_BOOL enableExtendedChannels, COUNTRY_CODE_TO_ENUM_RD **pcountry, REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz) { #define CHANNEL_HALF_BW 10 #define CHANNEL_QUARTER_BW 5 #define HAL_MODE_11A_ALL \ (HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \ HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE) REG_DOMAIN *rd5GHz, *rd2GHz; u_int modesAvail; const struct cmode *cm; struct ieee80211_channel *ic; int next, b; HAL_STATUS status; HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n", __func__, cc, regDmn, modeSelect, enableExtendedChannels ? " ecm" : ""); status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz); if (status != HAL_OK) return status; /* get modes that HW is capable of */ modesAvail = ath_hal_getWirelessModes(ah); /* optimize work below if no 11a channels */ if (isChanBitMaskZero(rd5GHz->chan11a) && (modesAvail & HAL_MODE_11A_ALL)) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: disallow all 11a\n", __func__); modesAvail &= ~HAL_MODE_11A_ALL; } next = 0; ic = &chans[0]; for (cm = modes; cm < &modes[N(modes)]; cm++) { uint16_t c, c_hi, c_lo; uint64_t *channelBM = AH_NULL; REG_DMN_FREQ_BAND *fband = AH_NULL,*freqs; int low_adj, hi_adj, channelSep, lastc; uint32_t rdflags; uint64_t dfsMask; uint64_t pscan; if ((cm->mode & modeSelect) == 0) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: skip mode 0x%x flags 0x%x\n", __func__, cm->mode, cm->flags); continue; } if ((cm->mode & modesAvail) == 0) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", __func__, modesAvail, cm->mode, cm->flags); continue; } if (!ath_hal_getChannelEdges(ah, cm->flags, &c_lo, &c_hi)) { /* channel not supported by hardware, skip it */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: channels 0x%x not supported by hardware\n", __func__,cm->flags); continue; } switch (cm->mode) { case HAL_MODE_TURBO: case HAL_MODE_11A_TURBO: rdflags = rd5GHz->flags; dfsMask = rd5GHz->dfsMask; pscan = rd5GHz->pscan; if (cm->mode == HAL_MODE_TURBO) channelBM = rd5GHz->chan11a_turbo; else channelBM = rd5GHz->chan11a_dyn_turbo; freqs = ®Dmn5GhzTurboFreq[0]; break; case HAL_MODE_11G_TURBO: rdflags = rd2GHz->flags; dfsMask = rd2GHz->dfsMask; pscan = rd2GHz->pscan; channelBM = rd2GHz->chan11g_turbo; freqs = ®Dmn2Ghz11gTurboFreq[0]; break; case HAL_MODE_11A: case HAL_MODE_11A_HALF_RATE: case HAL_MODE_11A_QUARTER_RATE: case HAL_MODE_11NA_HT20: case HAL_MODE_11NA_HT40PLUS: case HAL_MODE_11NA_HT40MINUS: rdflags = rd5GHz->flags; dfsMask = rd5GHz->dfsMask; pscan = rd5GHz->pscan; if (cm->mode == HAL_MODE_11A_HALF_RATE) channelBM = rd5GHz->chan11a_half; else if (cm->mode == HAL_MODE_11A_QUARTER_RATE) channelBM = rd5GHz->chan11a_quarter; else channelBM = rd5GHz->chan11a; freqs = ®Dmn5GhzFreq[0]; break; case HAL_MODE_11B: case HAL_MODE_11G: case HAL_MODE_11G_HALF_RATE: case HAL_MODE_11G_QUARTER_RATE: case HAL_MODE_11NG_HT20: case HAL_MODE_11NG_HT40PLUS: case HAL_MODE_11NG_HT40MINUS: rdflags = rd2GHz->flags; dfsMask = rd2GHz->dfsMask; pscan = rd2GHz->pscan; if (cm->mode == HAL_MODE_11G_HALF_RATE) channelBM = rd2GHz->chan11g_half; else if (cm->mode == HAL_MODE_11G_QUARTER_RATE) channelBM = rd2GHz->chan11g_quarter; else if (cm->mode == HAL_MODE_11B) channelBM = rd2GHz->chan11b; else channelBM = rd2GHz->chan11g; if (cm->mode == HAL_MODE_11B) freqs = ®Dmn2GhzFreq[0]; else freqs = ®Dmn2Ghz11gFreq[0]; break; default: HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: Unkonwn HAL mode 0x%x\n", __func__, cm->mode); continue; } if (isChanBitMaskZero(channelBM)) continue; /* * Setup special handling for HT40 channels; e.g. * 5G HT40 channels require 40Mhz channel separation. */ hi_adj = (cm->mode == HAL_MODE_11NA_HT40PLUS || cm->mode == HAL_MODE_11NG_HT40PLUS) ? -20 : 0; low_adj = (cm->mode == HAL_MODE_11NA_HT40MINUS || cm->mode == HAL_MODE_11NG_HT40MINUS) ? 20 : 0; channelSep = (cm->mode == HAL_MODE_11NA_HT40PLUS || cm->mode == HAL_MODE_11NA_HT40MINUS) ? 40 : 0; for (b = 0; b < 64*BMLEN; b++) { if (!IS_BIT_SET(b, channelBM)) continue; fband = &freqs[b]; lastc = 0; for (c = fband->lowChannel + low_adj; c <= fband->highChannel + hi_adj; c += fband->channelSep) { if (!(c_lo <= c && c <= c_hi)) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: c %u out of range [%u..%u]\n", __func__, c, c_lo, c_hi); continue; } if (next >= maxchans){ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: too many channels for channel table\n", __func__); goto done; } if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip ecm channel\n"); continue; } #if 0 if ((fband->useDfs & dfsMask) && (cm->flags & IEEE80211_CHAN_HT40)) { /* NB: DFS and HT40 don't mix */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip HT40 chan, DFS required\n"); continue; } #endif /* * Make sure that channel separation * meets the requirement. */ if (lastc && channelSep && (c-lastc) < channelSep) continue; lastc = c; OS_MEMZERO(ic, sizeof(*ic)); ic->ic_freq = c; ic->ic_flags = cm->flags; ic->ic_maxregpower = fband->powerDfs; ath_hal_getpowerlimits(ah, ic); ic->ic_maxantgain = fband->antennaMax; if (fband->usePassScan & pscan) ic->ic_flags |= IEEE80211_CHAN_PASSIVE; if (fband->useDfs & dfsMask) ic->ic_flags |= IEEE80211_CHAN_DFS; if (IEEE80211_IS_CHAN_5GHZ(ic) && (rdflags & DISALLOW_ADHOC_11A)) ic->ic_flags |= IEEE80211_CHAN_NOADHOC; if (IEEE80211_IS_CHAN_TURBO(ic) && (rdflags & DISALLOW_ADHOC_11A_TURB)) ic->ic_flags |= IEEE80211_CHAN_NOADHOC; if (rdflags & NO_HOSTAP) ic->ic_flags |= IEEE80211_CHAN_NOHOSTAP; if (rdflags & LIMIT_FRAME_4MS) ic->ic_flags |= IEEE80211_CHAN_4MSXMIT; if (rdflags & NEED_NFC) ic->ic_flags |= CHANNEL_NFCREQUIRED; ic++, next++; } } } done: *nchans = next; /* NB: pcountry set above by getregstate */ if (prd2GHz != AH_NULL) *prd2GHz = rd2GHz; if (prd5GHz != AH_NULL) *prd5GHz = rd5GHz; return HAL_OK; #undef HAL_MODE_11A_ALL #undef CHANNEL_HALF_BW #undef CHANNEL_QUARTER_BW }
static void add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[], u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd, HAL_BOOL enableExtendedChannels) { uint64_t *channelBM; uint16_t freq_lo, freq_hi; int b, error, low_adj, hi_adj, channelSep; if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) { /* channel not supported by hardware, skip it */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: channels 0x%x not supported by hardware\n", __func__, cm->flags); return; } channelBM = getchannelBM(cm->mode, rd); if (isChanBitMaskZero(channelBM)) return; /* * Setup special handling for HT40 channels; e.g. * 5G HT40 channels require 40Mhz channel separation. */ adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep); for (b = 0; b < 64*BMLEN; b++) { REG_DMN_FREQ_BAND *fband; uint16_t bfreq_lo, bfreq_hi; int step; if (!IS_BIT_SET(b, channelBM)) continue; fband = &cm->freqs[b]; if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip ecm channels\n"); continue; } #if 0 if ((fband->useDfs & rd->dfsMask) && (cm->flags & IEEE80211_CHAN_HT40)) { /* NB: DFS and HT40 don't mix */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip HT40 chan, DFS required\n"); continue; } #endif /* * XXX TODO: handle REG_EXT_FCC_CH_144. * * Figure out which instances/uses cause us to not * be allowed to use channel 144 (pri or sec overlap.) */ bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo); bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi); if (fband->channelSep >= channelSep) step = fband->channelSep; else step = roundup(channelSep, fband->channelSep); error = add_chanlist_band(ah, chans, maxchans, nchans, bfreq_lo, bfreq_hi, step, cm->flags, fband, rd); if (error != 0) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: too many channels for channel table\n", __func__); return; } } }
static void add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[], u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd, HAL_BOOL enableExtendedChannels) { uint64_t *channelBM; uint16_t freq_lo, freq_hi; int b, error, low_adj, hi_adj, channelSep; if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) { /* channel not supported by hardware, skip it */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: channels 0x%x not supported by hardware\n", __func__, cm->flags); return; } channelBM = getchannelBM(cm->mode, rd); if (isChanBitMaskZero(channelBM)) return; /* * Setup special handling for HT40 channels; e.g. * 5G HT40 channels require 40Mhz channel separation. */ adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep); for (b = 0; b < 64*BMLEN; b++) { REG_DMN_FREQ_BAND *fband; uint16_t bfreq_lo, bfreq_hi; int step; if (!IS_BIT_SET(b, channelBM)) continue; fband = &cm->freqs[b]; if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip ecm channels\n"); continue; } #if 0 if ((fband->useDfs & rd->dfsMask) && (cm->flags & IEEE80211_CHAN_HT40)) { /* NB: DFS and HT40 don't mix */ HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "skip HT40 chan, DFS required\n"); continue; } #endif /* * XXX TODO: handle REG_EXT_FCC_CH_144. * * Figure out which instances/uses cause us to not * be allowed to use channel 144 (pri or sec overlap.) */ bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo); bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi); /* * Don't start the 5GHz channel list at 5120MHz. * * Unfortunately (sigh) the HT40 channel creation * logic will create HT40U channels at 5120, 5160, 5200. * This means that 36 (5180) isn't considered as a * HT40 channel, and everything goes messed up from there. */ if ((cm->flags & IEEE80211_CHAN_5GHZ) && (cm->flags & IEEE80211_CHAN_HT40U)) { if (bfreq_lo < 5180) bfreq_lo = 5180; } /* * Same with HT40D - need to start at 5200 or the low * channels are all wrong again. */ if ((cm->flags & IEEE80211_CHAN_5GHZ) && (cm->flags & IEEE80211_CHAN_HT40D)) { if (bfreq_lo < 5200) bfreq_lo = 5200; } if (fband->channelSep >= channelSep) step = fband->channelSep; else step = roundup(channelSep, fband->channelSep); HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: freq_lo=%d, freq_hi=%d, low_adj=%d, hi_adj=%d, " "bandlo=%d, bandhi=%d, bfreqlo=%d, bfreqhi=%d, step=%d, " "flags=0x%08x\n", __func__, (int) freq_lo, (int) freq_hi, (int) low_adj, (int) hi_adj, (int) fband->lowChannel, (int) fband->highChannel, (int) bfreq_lo, (int) bfreq_hi, step, (int) cm->flags); error = add_chanlist_band(ah, chans, maxchans, nchans, bfreq_lo, bfreq_hi, step, cm->flags, fband, rd); if (error != 0) { HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: too many channels for channel table\n", __func__); return; } } }
/* * Return the mask of available modes based on the hardware * capabilities and the specified country code and reg domain. */ u_int32_t regdmn_getwmodesnreg(u_int32_t modesAvail, const COUNTRY_CODE_TO_ENUM_RD *country, const REG_DOMAIN *rd5GHz) { /* Check country regulations for allowed modes */ if ((modesAvail & (REGDMN_MODE_11A_TURBO|REGDMN_MODE_TURBO)) && (!country->allow11aTurbo)) modesAvail &= ~(REGDMN_MODE_11A_TURBO | REGDMN_MODE_TURBO); if ((modesAvail & REGDMN_MODE_11G_TURBO) && (!country->allow11gTurbo)) modesAvail &= ~REGDMN_MODE_11G_TURBO; if ((modesAvail & REGDMN_MODE_11G) && (!country->allow11g)) modesAvail &= ~REGDMN_MODE_11G; if ((modesAvail & REGDMN_MODE_11A) && (isChanBitMaskZero(rd5GHz->chan11a))) modesAvail &= ~REGDMN_MODE_11A; if ((modesAvail & REGDMN_MODE_11NG_HT20) && (!country->allow11ng20)) modesAvail &= ~REGDMN_MODE_11NG_HT20; if ((modesAvail & REGDMN_MODE_11NA_HT20) && (!country->allow11na20)) modesAvail &= ~REGDMN_MODE_11NA_HT20; if ((modesAvail & REGDMN_MODE_11NG_HT40PLUS) && (!country->allow11ng40)) modesAvail &= ~REGDMN_MODE_11NG_HT40PLUS; if ((modesAvail & REGDMN_MODE_11NG_HT40MINUS) && (!country->allow11ng40)) modesAvail &= ~REGDMN_MODE_11NG_HT40MINUS; if ((modesAvail & REGDMN_MODE_11NA_HT40PLUS) && (!country->allow11na40)) modesAvail &= ~REGDMN_MODE_11NA_HT40PLUS; if ((modesAvail & REGDMN_MODE_11NA_HT40MINUS) && (!country->allow11na40)) modesAvail &= ~REGDMN_MODE_11NA_HT40MINUS; if ((modesAvail & REGDMN_MODE_11AC_VHT20) && (!country->allow11na20)) modesAvail &= ~REGDMN_MODE_11AC_VHT20; if ((modesAvail & REGDMN_MODE_11AC_VHT40PLUS) && (!country->allow11na40)) modesAvail &= ~REGDMN_MODE_11AC_VHT40PLUS; if ((modesAvail & REGDMN_MODE_11AC_VHT40MINUS) && (!country->allow11na40)) modesAvail &= ~REGDMN_MODE_11AC_VHT40MINUS; if ((modesAvail & REGDMN_MODE_11AC_VHT80) && (!country->allow11na80)) modesAvail &= ~REGDMN_MODE_11AC_VHT80; if ((modesAvail & REGDMN_MODE_11AC_VHT20_2G) && (!country->allow11ng20)) modesAvail &= ~REGDMN_MODE_11AC_VHT20_2G; return modesAvail; }