コード例 #1
0
static void
dumpchannels(struct ath_hal *ah, int nc,
	const struct ieee80211_channel *chans, int16_t *txpow)
{
	int i;

	for (i = 0; i < nc; i++) {
		const struct ieee80211_channel *c = &chans[i];
		int type;

		if (showchannels)
			printf("%s%3d", sep,
			    ath_hal_mhz2ieee(ah, c->ic_freq, c->ic_flags));
		else
			printf("%s%u", sep, c->ic_freq);
		if (IEEE80211_IS_CHAN_HALF(c))
			type = 'H';
		else if (IEEE80211_IS_CHAN_QUARTER(c))
			type = 'Q';
		else if (IEEE80211_IS_CHAN_TURBO(c))
			type = 'T';
		else if (IEEE80211_IS_CHAN_HT(c))
			type = 'N';
		else if (IEEE80211_IS_CHAN_A(c))
			type = 'A';
		else if (IEEE80211_IS_CHAN_108G(c))
			type = 'T';
		else if (IEEE80211_IS_CHAN_G(c))
			type = 'G';
		else
			type = 'B';
		if (dopassive && IEEE80211_IS_CHAN_PASSIVE(c))
			type = tolower(type);
		if (isdfs && is4ms)
			printf("%c%c%c %d.%d", type,
			    IEEE80211_IS_CHAN_DFS(c) ? '*' : ' ',
			    IEEE80211_IS_CHAN_4MS(c) ? '4' : ' ',
			    txpow[i]/2, (txpow[i]%2)*5);
		else if (isdfs)
			printf("%c%c %d.%d", type,
			    IEEE80211_IS_CHAN_DFS(c) ? '*' : ' ',
			    txpow[i]/2, (txpow[i]%2)*5);
		else if (is4ms)
			printf("%c%c %d.%d", type,
			    IEEE80211_IS_CHAN_4MS(c) ? '4' : ' ',
			    txpow[i]/2, (txpow[i]%2)*5);
		else
			printf("%c %d.%d", type, txpow[i]/2, (txpow[i]%2)*5);
		if ((n++ % (showchannels ? 7 : 6)) == 0)
			sep = "\n";
		else
			sep = " ";
	}
}
コード例 #2
0
static void
ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan)
{
    u_int modesIndex, freqIndex;
    int regWrites = 0;

    /* Setup the indices for the next set of register array writes */
    /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */
    if (IEEE80211_IS_CHAN_2GHZ(chan)) {
        freqIndex = 2;
        if (IEEE80211_IS_CHAN_HT40(chan))
            modesIndex = 3;
        else if (IEEE80211_IS_CHAN_108G(chan))
            modesIndex = 5;
        else
            modesIndex = 4;
    } else {
        freqIndex = 1;
        if (IEEE80211_IS_CHAN_HT40(chan) ||
                IEEE80211_IS_CHAN_TURBO(chan))
            modesIndex = 2;
        else
            modesIndex = 1;
    }

    /* Set correct Baseband to analog shift setting to access analog chips. */
    OS_REG_WRITE(ah, AR_PHY(0), 0x00000007);
    OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC);

    /* XXX Merlin ini fixups */
    /* XXX Merlin 100us delay for shift registers */
    regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes,
                                  modesIndex, regWrites);
    if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
        regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_rxgain,
                                      modesIndex, regWrites);
        regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_txgain,
                                      modesIndex, regWrites);
    }
    /* XXX Merlin 100us delay for shift registers */
    regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common,
                                  1, regWrites);

    if (AR_SREV_MERLIN_20(ah) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) {
        /* 5GHz channels w/ Fast Clock use different modal values */
        regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_xmodes,
                                      modesIndex, regWrites);
    }
}
コード例 #3
0
ファイル: icm_utils.c プロジェクト: KHATEEBNSIT/AP
void icm_display_channel_flags(ICM_CHANNEL_T* pch)
{
    ICM_DEV_INFO_T* pdev = get_pdev();
    if (IEEE80211_IS_CHAN_FHSS(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tFHSS\n");
    }

    if (IEEE80211_IS_CHAN_11NA(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\t11na\n");
    } else if (IEEE80211_IS_CHAN_A(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\t11a\n");
    } else if (IEEE80211_IS_CHAN_11NG(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\t11ng\n");
    } else if (IEEE80211_IS_CHAN_G(pch) ||
        IEEE80211_IS_CHAN_PUREG(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\t11g\n");
    } else if (IEEE80211_IS_CHAN_B(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\t11b\n");
    }
    if (IEEE80211_IS_CHAN_TURBO(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tTurbo\n");
    }
    if(IEEE80211_IS_CHAN_11N_CTL_CAPABLE(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tControl capable\n");
    }
    if(IEEE80211_IS_CHAN_11N_CTL_U_CAPABLE(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tControl capable upper\n");
    }
    if(IEEE80211_IS_CHAN_11N_CTL_L_CAPABLE(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tControl capable lower\n");
    }

    if (IEEE80211_IS_CHAN_DFSFLAG(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tDFS\n");
    }

    if (IEEE80211_IS_CHAN_HALF(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tHalf\n");
    }

    if (IEEE80211_IS_CHAN_PASSIVE(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tPassive\n");
    }

    if (IEEE80211_IS_CHAN_QUARTER(pch)) {
        ICM_DPRINTF(pdev, ICM_PRCTRL_FLAG_NONE, ICM_DEBUG_LEVEL_DEFAULT, ICM_MODULE_ID_UTIL, "\tQuarter\n");
    }
}
コード例 #4
0
ファイル: ah_regdomain.c プロジェクト: looncraz/haiku
static void
setchannelflags(struct ieee80211_channel *c, REG_DMN_FREQ_BAND *fband,
    REG_DOMAIN *rd)
{
	if (fband->usePassScan & rd->pscan)
		c->ic_flags |= IEEE80211_CHAN_PASSIVE;
	if (fband->useDfs & rd->dfsMask)
		c->ic_flags |= IEEE80211_CHAN_DFS;
	if (IEEE80211_IS_CHAN_5GHZ(c) && (rd->flags & DISALLOW_ADHOC_11A))
		c->ic_flags |= IEEE80211_CHAN_NOADHOC;
	if (IEEE80211_IS_CHAN_TURBO(c) &&
	    (rd->flags & DISALLOW_ADHOC_11A_TURB))
		c->ic_flags |= IEEE80211_CHAN_NOADHOC;
	if (rd->flags & NO_HOSTAP)
		c->ic_flags |= IEEE80211_CHAN_NOHOSTAP;
	if (rd->flags & LIMIT_FRAME_4MS)
		c->ic_flags |= IEEE80211_CHAN_4MSXMIT;
	if (rd->flags & NEED_NFC)
		c->ic_flags |= CHANNEL_NFCREQUIRED;
}
コード例 #5
0
ファイル: ah_regdomain.c プロジェクト: ele7enxxh/dtrace-pf
/*
 * 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;
}
コード例 #6
0
/*
 * Sets the transmit power in the baseband for the given
 * operating channel and mode.
 */
static HAL_BOOL
setRateTable(struct ath_hal *ah, const struct ieee80211_channel *chan, 
		   int16_t tpcScaleReduction, int16_t powerLimit,
                   int16_t *pMinPower, int16_t *pMaxPower)
{
	u_int16_t ratesArray[16];
	u_int16_t *rpow = ratesArray;
	u_int16_t twiceMaxRDPower, twiceMaxEdgePower, twiceMaxEdgePowerCck;
	int8_t twiceAntennaGain, twiceAntennaReduction;
	TRGT_POWER_INFO targetPowerOfdm, targetPowerCck;
	RD_EDGES_POWER *rep;
	int16_t scaledPower;
	u_int8_t cfgCtl;

	twiceMaxRDPower = chan->ic_maxregpower * 2;
	*pMaxPower = -MAX_RATE_POWER;
	*pMinPower = MAX_RATE_POWER;

	/* Get conformance test limit maximum for this channel */
	cfgCtl = ath_hal_getctl(ah, chan);
	rep = findEdgePower(ah, cfgCtl);
	if (rep != AH_NULL)
		twiceMaxEdgePower = ar5212GetMaxEdgePower(chan->ic_freq, rep);
	else
		twiceMaxEdgePower = MAX_RATE_POWER;

	if (IEEE80211_IS_CHAN_G(chan)) {
		/* Check for a CCK CTL for 11G CCK powers */
		cfgCtl = (cfgCtl & 0xFC) | 0x01;
		rep = findEdgePower(ah, cfgCtl);
		if (rep != AH_NULL)
			twiceMaxEdgePowerCck = ar5212GetMaxEdgePower(chan->ic_freq, rep);
		else
			twiceMaxEdgePowerCck = MAX_RATE_POWER;
	} else {
		/* Set the 11B cck edge power to the one found before */
		twiceMaxEdgePowerCck = twiceMaxEdgePower;
	}

	/* Get Antenna Gain reduction */
	if (IEEE80211_IS_CHAN_5GHZ(chan)) {
		twiceAntennaGain = antennaGainMax[0];
	} else {
		twiceAntennaGain = antennaGainMax[1];
	}
	twiceAntennaReduction =
		ath_hal_getantennareduction(ah, chan, twiceAntennaGain);

	if (IEEE80211_IS_CHAN_OFDM(chan)) {
		/* Get final OFDM target powers */
		if (IEEE80211_IS_CHAN_G(chan)) { 
			/* TODO - add Turbo 2.4 to this mode check */
			ar5212GetTargetPowers(ah, chan, trgtPwr_11g,
				numTargetPwr_11g, &targetPowerOfdm);
		} else {
			ar5212GetTargetPowers(ah, chan, trgtPwr_11a,
				numTargetPwr_11a, &targetPowerOfdm);
		}

		/* Get Maximum OFDM power */
		/* Minimum of target and edge powers */
		scaledPower = AH_MIN(twiceMaxEdgePower,
				twiceMaxRDPower - twiceAntennaReduction);

		/*
		 * If turbo is set, reduce power to keep power
		 * consumption under 2 Watts.  Note that we always do
		 * this unless specially configured.  Then we limit
		 * power only for non-AP operation.
		 */
		if (IEEE80211_IS_CHAN_TURBO(chan)
#ifdef AH_ENABLE_AP_SUPPORT
		    && AH_PRIVATE(ah)->ah_opmode != HAL_M_HOSTAP
#endif
		) {
			/*
			 * If turbo is set, reduce power to keep power
			 * consumption under 2 Watts
			 */
			if (eeversion >= AR_EEPROM_VER3_1)
				scaledPower = AH_MIN(scaledPower,
					turbo2WMaxPower5);
			/*
			 * EEPROM version 4.0 added an additional
			 * constraint on 2.4GHz channels.
			 */
			if (eeversion >= AR_EEPROM_VER4_0 &&
			    IEEE80211_IS_CHAN_2GHZ(chan))
				scaledPower = AH_MIN(scaledPower,
					turbo2WMaxPower2);
		}
		/* Reduce power by max regulatory domain allowed restrictions */
		scaledPower -= (tpcScaleReduction * 2);
		scaledPower = (scaledPower < 0) ? 0 : scaledPower;
		scaledPower = AH_MIN(scaledPower, powerLimit);

		scaledPower = AH_MIN(scaledPower, targetPowerOfdm.twicePwr6_24);

		/* Set OFDM rates 9, 12, 18, 24, 36, 48, 54, XR */
		rpow[0] = rpow[1] = rpow[2] = rpow[3] = rpow[4] = scaledPower;
		rpow[5] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr36);
		rpow[6] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr48);
		rpow[7] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr54);

#ifdef notyet
		if (eeversion >= AR_EEPROM_VER4_0) {
			/* Setup XR target power from EEPROM */
			rpow[15] = AH_MIN(scaledPower, IS_CHAN_2GHZ(chan) ?
				xrTargetPower2 : xrTargetPower5);
		} else {
			/* XR uses 6mb power */
			rpow[15] = rpow[0];
		}
#else
		rpow[15] = rpow[0];
#endif

		*pMinPower = rpow[7];
		*pMaxPower = rpow[0];

#if 0
		ahp->ah_ofdmTxPower = rpow[0];
#endif

		HALDEBUG(ah, HAL_DEBUG_ANY,
		    "%s: MaxRD: %d TurboMax: %d MaxCTL: %d "
		    "TPC_Reduction %d\n", __func__,
		    twiceMaxRDPower, turbo2WMaxPower5,
		    twiceMaxEdgePower, tpcScaleReduction * 2);
	}

	if (IEEE80211_IS_CHAN_CCK(chan)) {
		/* Get final CCK target powers */
		ar5212GetTargetPowers(ah, chan, trgtPwr_11b,
			numTargetPwr_11b, &targetPowerCck);

		/* Reduce power by max regulatory domain allowed restrictions */
		scaledPower = AH_MIN(twiceMaxEdgePowerCck,
			twiceMaxRDPower - twiceAntennaReduction);

		scaledPower -= (tpcScaleReduction * 2);
		scaledPower = (scaledPower < 0) ? 0 : scaledPower;
		scaledPower = AH_MIN(scaledPower, powerLimit);

		rpow[8] = (scaledPower < 1) ? 1 : scaledPower;

		/* Set CCK rates 2L, 2S, 5.5L, 5.5S, 11L, 11S */
		rpow[8]  = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24);
		rpow[9]  = AH_MIN(scaledPower, targetPowerCck.twicePwr36);
		rpow[10] = rpow[9];
		rpow[11] = AH_MIN(scaledPower, targetPowerCck.twicePwr48);
		rpow[12] = rpow[11];
		rpow[13] = AH_MIN(scaledPower, targetPowerCck.twicePwr54);
		rpow[14] = rpow[13];

		/* Set min/max power based off OFDM values or initialization */
		if (rpow[13] < *pMinPower)
		    *pMinPower = rpow[13];
		if (rpow[9] > *pMaxPower)
		    *pMaxPower = rpow[9];

	}
#if 0
	ahp->ah_tx6PowerInHalfDbm = *pMaxPower;
#endif
	return AH_TRUE;
}
コード例 #7
0
ファイル: ah_regdomain.c プロジェクト: ele7enxxh/dtrace-pf
/*
 * 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 = &regDmn5GhzTurboFreq[0];
			break;
		case HAL_MODE_11G_TURBO:
			rdflags = rd2GHz->flags;
			dfsMask = rd2GHz->dfsMask;
			pscan = rd2GHz->pscan;
			channelBM = rd2GHz->chan11g_turbo;
			freqs = &regDmn2Ghz11gTurboFreq[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 = &regDmn5GhzFreq[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 = &regDmn2GhzFreq[0];
			else
				freqs = &regDmn2Ghz11gFreq[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
}
コード例 #8
0
static void
ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan)
{
	u_int modesIndex, freqIndex;
	int regWrites = 0;
	int i;
	const HAL_INI_ARRAY *ia;

	/* Setup the indices for the next set of register array writes */
	/* XXX Ignore 11n dynamic mode on the AR5416 for the moment */
	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
		freqIndex = 2;
		if (IEEE80211_IS_CHAN_HT40(chan))
			modesIndex = 3;
		else if (IEEE80211_IS_CHAN_108G(chan))
			modesIndex = 5;
		else
			modesIndex = 4;
	} else {
		freqIndex = 1;
		if (IEEE80211_IS_CHAN_HT40(chan) ||
		    IEEE80211_IS_CHAN_TURBO(chan))
			modesIndex = 2;
		else
			modesIndex = 1;
	}

	/* Set correct Baseband to analog shift setting to access analog chips. */
	OS_REG_WRITE(ah, AR_PHY(0), 0x00000007);
	OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC);

	/*
	 * This is unwound because at the moment, there's a requirement
	 * for Merlin (and later, perhaps) to have a specific bit fixed
	 * in the AR_AN_TOP2 register before writing it.
	 */
	ia = &AH5212(ah)->ah_ini_modes;
#if 0
	regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes,
	    modesIndex, regWrites);
#endif
	HALASSERT(modesIndex < ia->cols);
	for (i = 0; i < ia->rows; i++) {
		uint32_t reg = HAL_INI_VAL(ia, i, 0);
		uint32_t val = HAL_INI_VAL(ia, i, modesIndex);

		if (reg == AR_AN_TOP2 && AH5416(ah)->ah_need_an_top2_fixup)
			val &= ~AR_AN_TOP2_PWDCLKIND;

		OS_REG_WRITE(ah, reg, val);

		/* Analog shift register delay seems needed for Merlin - PR kern/154220 */
		if (reg >= 0x7800 && reg < 0x7900)
			OS_DELAY(100);

		DMA_YIELD(regWrites);
	}

	if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
		regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_rxgain,
		    modesIndex, regWrites);
		regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_txgain,
		    modesIndex, regWrites);
	}
	/* XXX Merlin 100us delay for shift registers */
	regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common,
	    1, regWrites);

	if (AR_SREV_MERLIN_20(ah) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) {
		/* 5GHz channels w/ Fast Clock use different modal values */
		regWrites = ath_hal_ini_write(ah, &AH9280(ah)->ah_ini_xmodes,
		    modesIndex, regWrites);
	}
}
コード例 #9
0
static void
ar9287AniSetup(struct ath_hal *ah)
{
	/*
	 * These are the parameters from the AR5416 ANI code;
	 * they likely need quite a bit of adjustment for the
	 * AR9280.
	 */
        static const struct ar5212AniParams aniparams = {
                .maxNoiseImmunityLevel  = 4,    /* levels 0..4 */
                .totalSizeDesired       = { -55, -55, -55, -55, -62 },
                .coarseHigh             = { -14, -14, -14, -14, -12 },
                .coarseLow              = { -64, -64, -64, -64, -70 },
                .firpwr                 = { -78, -78, -78, -78, -80 },
                .maxSpurImmunityLevel   = 2,
                .cycPwrThr1             = { 2, 4, 6 },
                .maxFirstepLevel        = 2,    /* levels 0..2 */
                .firstep                = { 0, 4, 8 },
                .ofdmTrigHigh           = 500,
                .ofdmTrigLow            = 200,
                .cckTrigHigh            = 200,
                .cckTrigLow             = 100,
                .rssiThrHigh            = 40,
                .rssiThrLow             = 7,
                .period                 = 100,
        };
	/* NB: disable ANI noise immmunity for reliable RIFS rx */
	AH5416(ah)->ah_ani_function &= ~ HAL_ANI_NOISE_IMMUNITY_LEVEL;

        /* NB: ANI is not enabled yet */
        ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE);
}

/*
 * Attach for an AR9287 part.
 */
static struct ath_hal *
ar9287Attach(uint16_t devid, HAL_SOFTC sc,
	HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata,
	HAL_STATUS *status)
{
	struct ath_hal_9287 *ahp9287;
	struct ath_hal_5212 *ahp;
	struct ath_hal *ah;
	uint32_t val;
	HAL_STATUS ecode;
	HAL_BOOL rfStatus;
	int8_t pwr_table_offset;

	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
	    __func__, sc, (void*) st, (void*) sh);

	/* NB: memory is returned zero'd */
	ahp9287 = ath_hal_malloc(sizeof (struct ath_hal_9287));
	if (ahp9287 == AH_NULL) {
		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
		    "%s: cannot allocate memory for state block\n", __func__);
		*status = HAL_ENOMEM;
		return AH_NULL;
	}
	ahp = AH5212(ahp9287);
	ah = &ahp->ah_priv.h;

	ar5416InitState(AH5416(ah), devid, sc, st, sh, status);

	/* XXX override with 9280 specific state */
	/* override 5416 methods for our needs */
	AH5416(ah)->ah_initPLL = ar9280InitPLL;

	ah->ah_setAntennaSwitch		= ar9287SetAntennaSwitch;
	ah->ah_configPCIE		= ar9287ConfigPCIE;

	AH5416(ah)->ah_cal.iqCalData.calData = &ar9287_iq_cal;
	AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9287_adc_gain_cal;
	AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9287_adc_dc_cal;
	AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9287_adc_init_dc_cal;
	/* Better performance without ADC Gain Calibration */
	AH5416(ah)->ah_cal.suppCals = ADC_DC_CAL | IQ_MISMATCH_CAL;

	AH5416(ah)->ah_spurMitigate	= ar9280SpurMitigate;
	AH5416(ah)->ah_writeIni		= ar9287WriteIni;

	ah->ah_setTxPower		= ar9287SetTransmitPower;
	ah->ah_setBoardValues		= ar9287SetBoardValues;

	AH5416(ah)->ah_olcInit		= ar9287olcInit;
	AH5416(ah)->ah_olcTempCompensation = ar9287olcTemperatureCompensation;
	//AH5416(ah)->ah_setPowerCalTable	= ar9287SetPowerCalTable;
	AH5416(ah)->ah_cal_initcal	= ar9287InitCalHardware;
	AH5416(ah)->ah_cal_pacal	= ar9287PACal;

	/* XXX NF calibration */
	/* XXX Ini override? (IFS vars - since the kiwi mac clock is faster?) */
	/* XXX what else is kiwi-specific in the radio/calibration pathway? */

	AH5416(ah)->ah_rx_chainmask	= AR9287_DEFAULT_RXCHAINMASK;
	AH5416(ah)->ah_tx_chainmask	= AR9287_DEFAULT_TXCHAINMASK;

	if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) {
		/* reset chip */
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n",
		    __func__);
		ecode = HAL_EIO;
		goto bad;
	}

	if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) {
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n",
		    __func__);
		ecode = HAL_EIO;
		goto bad;
	}
	/* Read Revisions from Chips before taking out of reset */
	val = OS_REG_READ(ah, AR_SREV);
	HALDEBUG(ah, HAL_DEBUG_ATTACH,
	    "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n",
	    __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION),
	    MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION));
	/* NB: include chip type to differentiate from pre-Sowl versions */
	AH_PRIVATE(ah)->ah_macVersion =
	    (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S;
	AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION);
	AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0;

	/* Don't support Kiwi < 1.2; those are pre-release chips */
	if (! AR_SREV_KIWI_12_OR_LATER(ah)) {
		ath_hal_printf(ah, "[ath]: Kiwi < 1.2 is not supported\n");
		ecode = HAL_EIO;
		goto bad;
	}

	/* setup common ini data; rf backends handle remainder */
	HAL_INI_INIT(&ahp->ah_ini_modes, ar9287Modes_9287_1_1, 6);
	HAL_INI_INIT(&ahp->ah_ini_common, ar9287Common_9287_1_1, 2);

	/* If pcie_clock_req */
	HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes,
	    ar9287PciePhy_clkreq_always_on_L1_9287_1_1, 2);

	/* XXX WoW ini values */

	/* Else */
#if 0
	HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes,
	    ar9287PciePhy_clkreq_off_L1_9287_1_1, 2);
#endif

	/* Initialise Japan arrays */
	HAL_INI_INIT(&ahp9287->ah_ini_cckFirNormal,
	    ar9287Common_normal_cck_fir_coeff_9287_1_1, 2);
	HAL_INI_INIT(&ahp9287->ah_ini_cckFirJapan2484,
	    ar9287Common_japan_2484_cck_fir_coeff_9287_1_1, 2);

	ar5416AttachPCIE(ah);

	ecode = ath_hal_9287EepromAttach(ah);
	if (ecode != HAL_OK)
		goto bad;

	if (!ar5416ChipReset(ah, AH_NULL)) {	/* reset chip */
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__);
		ecode = HAL_EIO;
		goto bad;
	}

	AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID);

	if (!ar5212ChipTest(ah)) {
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n",
		    __func__);
		ecode = HAL_ESELFTEST;
		goto bad;
	}

	/*
	 * Set correct Baseband to analog shift
	 * setting to access analog chips.
	 */
	OS_REG_WRITE(ah, AR_PHY(0), 0x00000007);

	/* Read Radio Chip Rev Extract */
	AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah);
	switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) {
        case AR_RAD2133_SREV_MAJOR:	/* Sowl: 2G/3x3 */
	case AR_RAD5133_SREV_MAJOR:	/* Sowl: 2+5G/3x3 */
		break;
	default:
		if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) {
			AH_PRIVATE(ah)->ah_analog5GhzRev =
				AR_RAD5133_SREV_MAJOR;
			break;
		}
#ifdef AH_DEBUG
		HALDEBUG(ah, HAL_DEBUG_ANY,
		    "%s: 5G Radio Chip Rev 0x%02X is not supported by "
		    "this driver\n", __func__,
		    AH_PRIVATE(ah)->ah_analog5GhzRev);
		ecode = HAL_ENOTSUPP;
		goto bad;
#endif
	}
	rfStatus = ar9287RfAttach(ah, &ecode);
	if (!rfStatus) {
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n",
		    __func__, ecode);
		goto bad;
	}

	/*
	 * We only implement open-loop TX power control
	 * for the AR9287 in this codebase.
	 */
	if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
		ath_hal_printf(ah, "[ath] AR9287 w/ closed-loop TX power control"
		    " isn't supported.\n");
		ecode = HAL_ENOTSUPP;
		goto bad;
	}

        /*
         * Check whether the power table offset isn't the default.
         * This can occur with eeprom minor V21 or greater on Merlin.
         */
	(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
	if (pwr_table_offset != AR5416_PWR_TABLE_OFFSET_DB)
		ath_hal_printf(ah, "[ath]: default pwr offset: %d dBm != EEPROM pwr offset: %d dBm; curves will be adjusted.\n",
		    AR5416_PWR_TABLE_OFFSET_DB, (int) pwr_table_offset);

	/* setup rxgain table */
	HAL_INI_INIT(&ahp9287->ah_ini_rxgain, ar9287Modes_rx_gain_9287_1_1, 6);

	/* setup txgain table */
	HAL_INI_INIT(&ahp9287->ah_ini_txgain, ar9287Modes_tx_gain_9287_1_1, 6);

	/*
	 * Got everything we need now to setup the capabilities.
	 */
	if (!ar9287FillCapabilityInfo(ah)) {
		ecode = HAL_EEREAD;
		goto bad;
	}

	ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr);
	if (ecode != HAL_OK) {
		HALDEBUG(ah, HAL_DEBUG_ANY,
		    "%s: error getting mac address from EEPROM\n", __func__);
		goto bad;
        }
	/* XXX How about the serial number ? */
	/* Read Reg Domain */
	AH_PRIVATE(ah)->ah_currentRD =
	    ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL);
	AH_PRIVATE(ah)->ah_currentRDext = AR9287_RDEXT_DEFAULT;

	/*
	 * ah_miscMode is populated by ar5416FillCapabilityInfo()
	 * starting from griffin. Set here to make sure that
	 * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is
	 * placed into hardware.
	 */
	if (ahp->ah_miscMode != 0)
		OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode);

	ar9287AniSetup(ah);			/* Anti Noise Immunity */

	/* Setup noise floor min/max/nominal values */
	AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ;
	AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ;
	AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9287_2GHZ;
	AH5416(ah)->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9287_5GHZ;
	AH5416(ah)->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9287_5GHZ;
	AH5416(ah)->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9287_5GHZ;

	ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist);

	HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__);

	return ah;
bad:
	if (ah != AH_NULL)
		ah->ah_detach(ah);
	if (status)
		*status = ecode;
	return AH_NULL;
}

static void
ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
{
	if (AH_PRIVATE(ah)->ah_ispcie && !restore) {
		ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0);
		OS_DELAY(1000);
		OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
		OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT);	/* Yes, Kiwi uses the Kite PCIe PHY WA */
	}
}

static void
ar9287WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan)
{
	u_int modesIndex, freqIndex;
	int regWrites = 0;

	/* Setup the indices for the next set of register array writes */
	/* XXX Ignore 11n dynamic mode on the AR5416 for the moment */
	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
		freqIndex = 2;
		if (IEEE80211_IS_CHAN_HT40(chan))
			modesIndex = 3;
		else if (IEEE80211_IS_CHAN_108G(chan))
			modesIndex = 5;
		else
			modesIndex = 4;
	} else {
		freqIndex = 1;
		if (IEEE80211_IS_CHAN_HT40(chan) ||
		    IEEE80211_IS_CHAN_TURBO(chan))
			modesIndex = 2;
		else
			modesIndex = 1;
	}

	/* Set correct Baseband to analog shift setting to access analog chips. */
	OS_REG_WRITE(ah, AR_PHY(0), 0x00000007);
	OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC);

	regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, modesIndex, regWrites);
	regWrites = ath_hal_ini_write(ah, &AH9287(ah)->ah_ini_rxgain, modesIndex, regWrites);
	regWrites = ath_hal_ini_write(ah, &AH9287(ah)->ah_ini_txgain, modesIndex, regWrites);
	regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites);
}
コード例 #10
0
ファイル: ar5312_reset.c プロジェクト: 2asoft/freebsd
/*
 * Places the hardware into reset and then pulls it out of reset
 *
 * TODO: Only write the PLL if we're changing to or from CCK mode
 * 
 * WARNING: The order of the PLL and mode registers must be correct.
 */
HAL_BOOL
ar5312ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan)
{

	OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0);

	/*
	 * Reset the HW 
	 */
	if (!ar5312SetResetReg(ah, AR_RC_MAC | AR_RC_BB)) {
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5312SetResetReg failed\n",
		    __func__);
		return AH_FALSE;
	}

	/* Bring out of sleep mode (AGAIN) */
	if (!ar5312SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) {
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5312SetPowerMode failed\n",
		    __func__);
		return AH_FALSE;
	}

	/* Clear warm reset register */
	if (!ar5312SetResetReg(ah, 0)) {
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5312SetResetReg failed\n",
		    __func__);
		return AH_FALSE;
	}

	/*
	 * Perform warm reset before the mode/PLL/turbo registers
	 * are changed in order to deactivate the radio.  Mode changes
	 * with an active radio can result in corrupted shifts to the
	 * radio device.
	 */

	/*
	 * Set CCK and Turbo modes correctly.
	 */
	if (chan != AH_NULL) {		/* NB: can be null during attach */
		uint32_t rfMode, phyPLL = 0, curPhyPLL, turbo;

		if (IS_RAD5112_ANY(ah)) {
			rfMode = AR_PHY_MODE_AR5112;
			if (!IS_5315(ah)) {
				if (IEEE80211_IS_CHAN_CCK(chan)) {
					phyPLL = AR_PHY_PLL_CTL_44_5312;
				} else {
					if (IEEE80211_IS_CHAN_HALF(chan)) {
						phyPLL = AR_PHY_PLL_CTL_40_5312_HALF;
					} else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
						phyPLL = AR_PHY_PLL_CTL_40_5312_QUARTER;
					} else {
						phyPLL = AR_PHY_PLL_CTL_40_5312;
					}
				}
			} else {
				if (IEEE80211_IS_CHAN_CCK(chan))
					phyPLL = AR_PHY_PLL_CTL_44_5112;
				else
					phyPLL = AR_PHY_PLL_CTL_40_5112;
				if (IEEE80211_IS_CHAN_HALF(chan))
					phyPLL |= AR_PHY_PLL_CTL_HALF;
				else if (IEEE80211_IS_CHAN_QUARTER(chan))
					phyPLL |= AR_PHY_PLL_CTL_QUARTER;
			}
		} else {
			rfMode = AR_PHY_MODE_AR5111;
			if (IEEE80211_IS_CHAN_CCK(chan))
				phyPLL = AR_PHY_PLL_CTL_44;
			else
				phyPLL = AR_PHY_PLL_CTL_40;
			if (IEEE80211_IS_CHAN_HALF(chan))
				phyPLL = AR_PHY_PLL_CTL_HALF;
			else if (IEEE80211_IS_CHAN_QUARTER(chan))
				phyPLL = AR_PHY_PLL_CTL_QUARTER;
		}
		if (IEEE80211_IS_CHAN_G(chan))
			rfMode |= AR_PHY_MODE_DYNAMIC;
		else if (IEEE80211_IS_CHAN_OFDM(chan))
			rfMode |= AR_PHY_MODE_OFDM;
		else
			rfMode |= AR_PHY_MODE_CCK;
		if (IEEE80211_IS_CHAN_5GHZ(chan))
			rfMode |= AR_PHY_MODE_RF5GHZ;
		else
			rfMode |= AR_PHY_MODE_RF2GHZ;
		turbo = IEEE80211_IS_CHAN_TURBO(chan) ?
			(AR_PHY_FC_TURBO_MODE | AR_PHY_FC_TURBO_SHORT) : 0;
		curPhyPLL = OS_REG_READ(ah, AR_PHY_PLL_CTL);
		/*
		 * PLL, Mode, and Turbo values must be written in the correct
		 * order to ensure:
		 * - The PLL cannot be set to 44 unless the CCK or DYNAMIC
		 *   mode bit is set
		 * - Turbo cannot be set at the same time as CCK or DYNAMIC
		 */
		if (IEEE80211_IS_CHAN_CCK(chan)) {
			OS_REG_WRITE(ah, AR_PHY_TURBO, turbo);
			OS_REG_WRITE(ah, AR_PHY_MODE, rfMode);
			if (curPhyPLL != phyPLL) {
				OS_REG_WRITE(ah,  AR_PHY_PLL_CTL,  phyPLL);
				/* Wait for the PLL to settle */
				OS_DELAY(PLL_SETTLE_DELAY);
			}
		} else {
			if (curPhyPLL != phyPLL) {
				OS_REG_WRITE(ah,  AR_PHY_PLL_CTL,  phyPLL);
				/* Wait for the PLL to settle */
				OS_DELAY(PLL_SETTLE_DELAY);
			}
			OS_REG_WRITE(ah, AR_PHY_TURBO, turbo);
			OS_REG_WRITE(ah, AR_PHY_MODE, rfMode);
		}
	}
	return AH_TRUE;
}
コード例 #11
0
HAL_BOOL
ar5210ResetTxQueue(struct ath_hal *ah, u_int q)
{
	struct ath_hal_5210 *ahp = AH5210(ah);
	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
	HAL_TX_QUEUE_INFO *qi;
	uint32_t cwMin;

	if (q >= HAL_NUM_TX_QUEUES) {
		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
		    __func__, q);
		return AH_FALSE;
	}
	qi = &ahp->ah_txq[q];
	if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
		HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue %u\n",
		    __func__, q);
		return AH_FALSE;
	}

	/*
	 * Ignore any non-data queue(s).
	 */
	if (qi->tqi_type != HAL_TX_QUEUE_DATA)
		return AH_TRUE;

	/* Set turbo mode / base mode parameters on or off */
	if (IEEE80211_IS_CHAN_TURBO(chan)) {
		OS_REG_WRITE(ah, AR_SLOT_TIME, INIT_SLOT_TIME_TURBO);
		OS_REG_WRITE(ah, AR_TIME_OUT, INIT_ACK_CTS_TIMEOUT_TURBO);
		OS_REG_WRITE(ah, AR_USEC, INIT_TRANSMIT_LATENCY_TURBO);
		OS_REG_WRITE(ah, AR_IFS0, 
			((INIT_SIFS_TURBO + qi->tqi_aifs * INIT_SLOT_TIME_TURBO)
				<< AR_IFS0_DIFS_S)
			| INIT_SIFS_TURBO);
		OS_REG_WRITE(ah, AR_IFS1, INIT_PROTO_TIME_CNTRL_TURBO);
		OS_REG_WRITE(ah, AR_PHY(17),
			(OS_REG_READ(ah, AR_PHY(17)) & ~0x7F) | 0x38);
		OS_REG_WRITE(ah, AR_PHY_FRCTL,
			AR_PHY_SERVICE_ERR | AR_PHY_TXURN_ERR |
			AR_PHY_ILLLEN_ERR | AR_PHY_ILLRATE_ERR |
			AR_PHY_PARITY_ERR | AR_PHY_TIMING_ERR |
			0x2020 |
			AR_PHY_TURBO_MODE | AR_PHY_TURBO_SHORT);
	} else {
		OS_REG_WRITE(ah, AR_SLOT_TIME, INIT_SLOT_TIME);
		OS_REG_WRITE(ah, AR_TIME_OUT, INIT_ACK_CTS_TIMEOUT);
		OS_REG_WRITE(ah, AR_USEC, INIT_TRANSMIT_LATENCY);
		OS_REG_WRITE(ah, AR_IFS0, 
			((INIT_SIFS + qi->tqi_aifs * INIT_SLOT_TIME)
				<< AR_IFS0_DIFS_S)
			| INIT_SIFS);
		OS_REG_WRITE(ah, AR_IFS1, INIT_PROTO_TIME_CNTRL);
		OS_REG_WRITE(ah, AR_PHY(17),
			(OS_REG_READ(ah, AR_PHY(17)) & ~0x7F) | 0x1C);
		OS_REG_WRITE(ah, AR_PHY_FRCTL,
			AR_PHY_SERVICE_ERR | AR_PHY_TXURN_ERR |
			AR_PHY_ILLLEN_ERR | AR_PHY_ILLRATE_ERR |
			AR_PHY_PARITY_ERR | AR_PHY_TIMING_ERR | 0x1020);
	}

	if (qi->tqi_cwmin == HAL_TXQ_USEDEFAULT)
		cwMin = INIT_CWMIN;
	else
		cwMin = qi->tqi_cwmin;

	/* Set cwmin and retry limit values */
	OS_REG_WRITE(ah, AR_RETRY_LMT, 
		  (cwMin << AR_RETRY_LMT_CW_MIN_S)
		 | SM(INIT_SLG_RETRY, AR_RETRY_LMT_SLG_RETRY)
		 | SM(INIT_SSH_RETRY, AR_RETRY_LMT_SSH_RETRY)
		 | SM(qi->tqi_lgretry, AR_RETRY_LMT_LG_RETRY)
		 | SM(qi->tqi_shretry, AR_RETRY_LMT_SH_RETRY)
	);

	if (qi->tqi_qflags & HAL_TXQ_TXOKINT_ENABLE)
		ahp->ah_txOkInterruptMask |= 1 << q;
	else
		ahp->ah_txOkInterruptMask &= ~(1 << q);
	if (qi->tqi_qflags & HAL_TXQ_TXERRINT_ENABLE)
		ahp->ah_txErrInterruptMask |= 1 << q;
	else
		ahp->ah_txErrInterruptMask &= ~(1 << q);
	if (qi->tqi_qflags & HAL_TXQ_TXDESCINT_ENABLE)
		ahp->ah_txDescInterruptMask |= 1 << q;
	else
		ahp->ah_txDescInterruptMask &= ~(1 << q);
	if (qi->tqi_qflags & HAL_TXQ_TXEOLINT_ENABLE)
		ahp->ah_txEolInterruptMask |= 1 << q;
	else
		ahp->ah_txEolInterruptMask &= ~(1 << q);
	if (qi->tqi_qflags & HAL_TXQ_TXURNINT_ENABLE)
		ahp->ah_txUrnInterruptMask |= 1 << q;
	else
		ahp->ah_txUrnInterruptMask &= ~(1 << q);

	return AH_TRUE;
}
コード例 #12
0
ファイル: ieee80211_beacon.c プロジェクト: KHATEEBNSIT/AP
/*
 * Update the dynamic parts of a beacon frame based on the current state.
 */
int
ieee80211_beacon_update(struct ieee80211_node *ni,
	struct ieee80211_beacon_offsets *bo, struct sk_buff *skb, int mcast)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211com *ic = ni->ni_ic;
	int len_changed = 0;
	u_int16_t capinfo;

	IEEE80211_LOCK(ic);

	if ((ic->ic_flags & IEEE80211_F_DOTH) &&
	    (vap->iv_flags & IEEE80211_F_CHANSWITCH) &&
	    (vap->iv_chanchange_count == ic->ic_chanchange_tbtt)) {
		u_int8_t *frm;
		struct ieee80211_channel *c;

		vap->iv_chanchange_count = 0;

		IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
			"%s: reinit beacon\n", __func__);

		/* 
		 * NB: ic_bsschan is in the DSPARMS beacon IE, so must set this
		 *     prior to the beacon re-init, below.
		 */
		c = ieee80211_doth_findchan(vap, ic->ic_chanchange_chan);
		if (c == NULL) {
			IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
				"%s: find channel failure\n", __func__);
			IEEE80211_UNLOCK(ic);
			return 0;
		}
		ic->ic_bsschan = c;

		skb_pull(skb, sizeof(struct ieee80211_frame));
		skb_trim(skb, 0);
		frm = skb->data;
		skb_put(skb, ieee80211_beacon_init(ni, bo, frm) - frm); 
		skb_push(skb, sizeof(struct ieee80211_frame));

		vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
		ic->ic_flags &= ~IEEE80211_F_CHANSWITCH;

		/* NB: only for the first VAP to get here */
		if (ic->ic_curchan != c) {
			ic->ic_curchan = c;
			ic->ic_set_channel(ic);
		}

		len_changed = 1;
	}

	/* XXX faster to recalculate entirely or just changes? */
	if (vap->iv_opmode == IEEE80211_M_IBSS)
		capinfo = IEEE80211_CAPINFO_IBSS;
	else
		capinfo = IEEE80211_CAPINFO_ESS;
	if (vap->iv_flags & IEEE80211_F_PRIVACY)
		capinfo |= IEEE80211_CAPINFO_PRIVACY;
	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
	    IEEE80211_IS_CHAN_2GHZ(ic->ic_bsschan))
		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
	if (ic->ic_flags & IEEE80211_F_SHSLOT)
		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
	if (ic->ic_flags & IEEE80211_F_DOTH)
		capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;

	*bo->bo_caps = htole16(capinfo);

	if (vap->iv_flags & IEEE80211_F_WME) {
		struct ieee80211_wme_state *wme = &ic->ic_wme;

		/*
		 * Check for aggressive mode change.  When there is
		 * significant high priority traffic in the BSS
		 * throttle back BE traffic by using conservative
		 * parameters.  Otherwise BE uses aggressive params
		 * to optimize performance of legacy/non-QoS traffic.
		 */
		if (wme->wme_flags & WME_F_AGGRMODE) {
			if (wme->wme_hipri_traffic >
			    wme->wme_hipri_switch_thresh) {
				IEEE80211_NOTE(vap, IEEE80211_MSG_WME, ni,
					"%s: traffic %u, disable aggressive mode",
					__func__, wme->wme_hipri_traffic);
				wme->wme_flags &= ~WME_F_AGGRMODE;
				ieee80211_wme_updateparams_locked(vap);
				wme->wme_hipri_traffic =
					wme->wme_hipri_switch_hysteresis;
			} else
				wme->wme_hipri_traffic = 0;
		} else {
			if (wme->wme_hipri_traffic <=
			    wme->wme_hipri_switch_thresh) {
				IEEE80211_NOTE(vap, IEEE80211_MSG_WME, ni,
					"%s: traffic %u, enable aggressive mode",
					__func__, wme->wme_hipri_traffic);
				wme->wme_flags |= WME_F_AGGRMODE;
				ieee80211_wme_updateparams_locked(vap);
				wme->wme_hipri_traffic = 0;
			} else
				wme->wme_hipri_traffic =
					wme->wme_hipri_switch_hysteresis;
		}
		/* XXX multi-bss */
		if (vap->iv_flags & IEEE80211_F_WMEUPDATE) {
			(void) ieee80211_add_wme_param(bo->bo_wme, wme, IEEE80211_VAP_UAPSD_ENABLED(vap));
			vap->iv_flags &= ~IEEE80211_F_WMEUPDATE;
		}
	}

	if (vap->iv_opmode == IEEE80211_M_HOSTAP) {	/* NB: no IBSS support*/
		struct ieee80211_tim_ie *tie =
			(struct ieee80211_tim_ie *) bo->bo_tim;
		if (vap->iv_flags & IEEE80211_F_TIMUPDATE) {
			u_int timlen, timoff, i;
			/*
			 * ATIM/DTIM needs updating.  If it fits in the
			 * current space allocated then just copy in the
			 * new bits.  Otherwise we need to move any trailing
			 * data to make room.  Note that we know there is
			 * contiguous space because ieee80211_beacon_allocate
			 * ensures there is space in the mbuf to write a
			 * maximal-size virtual bitmap (based on ic_max_aid).
			 */
			/*
			 * Calculate the bitmap size and offset, copy any
			 * trailer out of the way, and then copy in the
			 * new bitmap and update the information element.
			 * Note that the tim bitmap must contain at least
			 * one byte and any offset must be even.
			 */
			if (vap->iv_ps_pending != 0) {
				timoff = 128;		/* impossibly large */
				for (i = 0; i < vap->iv_tim_len; i++)
					if (vap->iv_tim_bitmap[i]) {
						timoff = i &~ 1;
						break;
					}
				KASSERT(timoff != 128, ("tim bitmap empty!"));
				for (i = vap->iv_tim_len-1; i >= timoff; i--)
					if (vap->iv_tim_bitmap[i])
						break;
				timlen = 1 + (i - timoff);
			} else {
				timoff = 0;
				timlen = 1;
			}
			if (timlen != bo->bo_tim_len) {
				/* copy up/down trailer */
				int trailer_adjust =
					(tie->tim_bitmap+timlen) - (bo->bo_tim_trailer);
				memmove(tie->tim_bitmap+timlen, bo->bo_tim_trailer,
					bo->bo_tim_trailerlen);
				bo->bo_tim_trailer = tie->tim_bitmap+timlen;
				bo->bo_chanswitch += trailer_adjust;
				bo->bo_wme += trailer_adjust;
				bo->bo_erp += trailer_adjust;
				bo->bo_ath_caps += trailer_adjust;
				bo->bo_xr += trailer_adjust;
				if (timlen > bo->bo_tim_len)
					skb_put(skb, timlen - bo->bo_tim_len);
				else
					skb_trim(skb, skb->len - (bo->bo_tim_len - timlen));
				bo->bo_tim_len = timlen;

				/* update information element */
				tie->tim_len = 3 + timlen;
				tie->tim_bitctl = timoff;
				len_changed = 1;
			}
			memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff,
				bo->bo_tim_len);

			vap->iv_flags &= ~IEEE80211_F_TIMUPDATE;

			IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
				"%s: TIM updated, pending %u, off %u, len %u",
				__func__, vap->iv_ps_pending, timoff, timlen);
		}
		/* count down DTIM period */
		if (tie->tim_count == 0)
			tie->tim_count = tie->tim_period - 1;
		else
			tie->tim_count--;
		/* update state for buffered multicast frames on DTIM */
		if (mcast && (tie->tim_count == 0))
			tie->tim_bitctl |= 1;
		else
			tie->tim_bitctl &= ~1;

		if ((ic->ic_flags & IEEE80211_F_DOTH) &&
		    (ic->ic_flags & IEEE80211_F_CHANSWITCH)) {

			if (!vap->iv_chanchange_count) {
				vap->iv_flags |= IEEE80211_F_CHANSWITCH;

				/* copy out trailer to open up a slot */
				memmove(bo->bo_chanswitch + IEEE80211_CHANSWITCHANN_BYTES, 
					bo->bo_chanswitch, bo->bo_chanswitch_trailerlen);

				/* add ie in opened slot */
				bo->bo_chanswitch[0] = IEEE80211_ELEMID_CHANSWITCHANN;
				bo->bo_chanswitch[1] = 3; /* fixed length */
				bo->bo_chanswitch[2] = 1; /* stas get off for now */
				bo->bo_chanswitch[3] = ic->ic_chanchange_chan;
				bo->bo_chanswitch[4] = ic->ic_chanchange_tbtt;

				/* update the trailer lens */
				bo->bo_chanswitch_trailerlen += IEEE80211_CHANSWITCHANN_BYTES;
				bo->bo_tim_trailerlen += IEEE80211_CHANSWITCHANN_BYTES;
				bo->bo_wme += IEEE80211_CHANSWITCHANN_BYTES;
				bo->bo_erp += IEEE80211_CHANSWITCHANN_BYTES;
				bo->bo_ath_caps += IEEE80211_CHANSWITCHANN_BYTES;
				bo->bo_xr += IEEE80211_CHANSWITCHANN_BYTES;

				/* indicate new beacon length so other layers may manage memory */
				skb_put(skb, IEEE80211_CHANSWITCHANN_BYTES);
				len_changed = 1;
			}
			else
				bo->bo_chanswitch[4]--;
			vap->iv_chanchange_count++;
			IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
				"%s: CHANSWITCH IE, change in %d\n",
				__func__, bo->bo_chanswitch[4]);
		}
#ifdef ATH_SUPERG_XR
		if (vap->iv_flags & IEEE80211_F_XRUPDATE) {
			if (vap->iv_xrvap)
				(void) ieee80211_add_xr_param(bo->bo_xr, vap);
			vap->iv_flags &= ~IEEE80211_F_XRUPDATE;
		}
#endif
		if (ic->ic_flags_ext & IEEE80211_FEXT_ERPUPDATE) {
			(void) ieee80211_add_erp(bo->bo_erp, ic);
			ic->ic_flags_ext &= ~IEEE80211_FEXT_ERPUPDATE;
		}
	}
	/* if it is a mode change beacon for dynamic turbo case */
	if (((ic->ic_ath_cap & IEEE80211_ATHC_BOOST) != 0) ^
	    IEEE80211_IS_CHAN_TURBO(ic->ic_curchan))
		ieee80211_add_athAdvCap(bo->bo_ath_caps, vap->iv_bss->ni_ath_flags,
			vap->iv_bss->ni_ath_defkeyindex);
	/* add APP_IE buffer if app updated it */
	if (vap->iv_flags_ext & IEEE80211_FEXT_APPIE_UPDATE) {
		/* adjust the buffer size if the size is changed */
		if (vap->app_ie[IEEE80211_APPIE_FRAME_BEACON].length != bo->bo_appie_buf_len) {
			int diff_len;
			diff_len = vap->app_ie[IEEE80211_APPIE_FRAME_BEACON].length - bo->bo_appie_buf_len;

			if (diff_len > 0)
				skb_put(skb, diff_len);
			else
				skb_trim(skb, skb->len + diff_len);

			bo->bo_appie_buf_len = vap->app_ie[IEEE80211_APPIE_FRAME_BEACON].length;
			/* update the trailer lens */
			bo->bo_chanswitch_trailerlen += diff_len;
			bo->bo_tim_trailerlen += diff_len;

			len_changed = 1;
		}
		memcpy(bo->bo_appie_buf,vap->app_ie[IEEE80211_APPIE_FRAME_BEACON].ie,
			vap->app_ie[IEEE80211_APPIE_FRAME_BEACON].length);

		vap->iv_flags_ext &= ~IEEE80211_FEXT_APPIE_UPDATE;
	}

	IEEE80211_UNLOCK(ic);

	return len_changed;
}
コード例 #13
0
/*
 * Build the country information element.
 */
void
ieee80211_build_countryie(struct ieee80211vap *vap)
{
    struct country_ie_triplet *pTriplet;
    struct ieee80211_channel *c;
    int i, j, chanflags, chancnt, isnewband;
    u_int8_t chanlist[IEEE80211_CHAN_MAX + 1];
    u_int8_t prevchan;
    struct ieee80211com *ic = vap->iv_ic;

    if (!ic->ic_country_iso[0] || !ic->ic_country_iso[1] || !ic->ic_country_iso[2]) {
        /* Default, no country is set */
        vap->iv_country_ie_data.country_len = 0;
        IEEE80211_DISABLE_COUNTRYIE(ic);
        return;
    }

    IEEE80211_ENABLE_COUNTRYIE(ic);

    /*
     * Construct the country IE:
     * 1. The country string come first.
     * 2. Then construct the channel triplets from lowest channel to highest channel.
     * 3. If we support the regulatory domain class (802.11J)
     *    then add the class triplets before the channel triplets of each band.
     */
    OS_MEMZERO(&vap->iv_country_ie_data, sizeof(vap->iv_country_ie_data));  
    vap->iv_country_ie_data.country_id = IEEE80211_ELEMID_COUNTRY;
    vap->iv_country_ie_data.country_len = 3;

    vap->iv_country_ie_data.country_str[0] = ic->ic_country_iso[0];
    vap->iv_country_ie_data.country_str[1] = ic->ic_country_iso[1];
    vap->iv_country_ie_data.country_str[2] = ic->ic_country_iso[2];

    pTriplet = (struct country_ie_triplet*)&vap->iv_country_ie_data.country_triplet;

    if(IEEE80211_IS_CHAN_2GHZ(vap->iv_bsschan))
        chanflags = IEEE80211_CHAN_2GHZ;
    else
        chanflags = IEEE80211_CHAN_5GHZ;

    vap->iv_country_ie_chanflags = chanflags;
    OS_MEMZERO(&chanlist[0], sizeof(chanlist));
    prevchan = 0;
    chancnt = 0;
    isnewband = 1;
    ieee80211_enumerate_channels(c, ic, i) {
        /* Does channel belong to current operation mode */
        if (!(c->ic_flags & chanflags))
            continue;

        /* Skip previously reported channels */
        for (j=0; j < chancnt; j++) {
            if (c->ic_ieee == chanlist[j])
                break;
        }
    
        if (j != chancnt) /* found a match */
            continue;

        chanlist[chancnt] = c->ic_ieee;
        chancnt++;

        /* Skip turbo channels */
        if (IEEE80211_IS_CHAN_TURBO(c))
            continue;

        /* Skip half/quarter rate channels */
        if (IEEE80211_IS_CHAN_HALF(c) || 
                IEEE80211_IS_CHAN_QUARTER(c))
            continue;

        if (isnewband) {
            isnewband = 0;
        } else if ((pTriplet->maxtxpwr == c->ic_maxregpower) && (c->ic_ieee == prevchan + 1)) {
            pTriplet->nchan ++;
            prevchan = c->ic_ieee;
            continue;
        } else {
            pTriplet ++;
        }

        prevchan = c->ic_ieee;
        pTriplet->schan = c->ic_ieee;
        pTriplet->nchan = 1; /* init as 1 channel */
        pTriplet->maxtxpwr = c->ic_maxregpower;
        vap->iv_country_ie_data.country_len += 3;
    }

    /* pad */
    if (vap->iv_country_ie_data.country_len & 1) {
        vap->iv_country_ie_data.country_triplet[vap->iv_country_ie_data.country_len] = 0;
        vap->iv_country_ie_data.country_len++;
    }
}