FreeBSD/sys/dev/iwn/if_iwn.c
/*
 * Set TX power for current channel (each rate has its own power settings).
 * This function takes into account the regulatory information from EEPROM,
 * the current temperature and the current voltage.
 */
Set TX power for current channel (each rate has its own power settings).
This function takes into account the regulatory information from EEPROM,
the current temperature and the current voltage.
Set TX power for current channel ( each rate has its own power settings ) . This function takes into account the regulatory information from EEPROM the current temperature and the current voltage .
static int
iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch,
    int async)
{
#define fdivround(a, b, n)	\
	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
#define interpolate(x, x1, y1, x2, y2, n)	\
	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
	static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 };
	struct iwn_ucode_info *uc = &sc->ucode_info;
	struct iwn4965_cmd_txpower cmd;
	struct iwn4965_eeprom_chan_samples *chans;
	const uint8_t *rf_gain, *dsp_gain;
	int32_t vdiff, tdiff;
	int i, c, grp, maxpwr;
	uint8_t chan;
	sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX];
	chan = sc->rxon->chan;
	DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n",
	    chan);
	memset(&cmd, 0, sizeof cmd);
	cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1;
	cmd.chan = chan;
	if (IEEE80211_IS_CHAN_5GHZ(ch)) {
		maxpwr   = sc->maxpwr5GHz;
		rf_gain  = iwn4965_rf_gain_5ghz;
		dsp_gain = iwn4965_dsp_gain_5ghz;
	} else {
		maxpwr   = sc->maxpwr2GHz;
		rf_gain  = iwn4965_rf_gain_2ghz;
		dsp_gain = iwn4965_dsp_gain_2ghz;
	}
	vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7;
	if (vdiff > 0)
		vdiff *= 2;
	if (abs(vdiff) > 2)
		vdiff = 0;
	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
	    "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n",
	    __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage);
	if (chan <= 20)		
		grp = 4;
	else if (chan <= 43)	
		grp = 0;
	else if (chan <= 70)	
		grp = 1;
	else if (chan <= 124)	
		grp = 2;
	else			
		grp = 3;
	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
	    "%s: chan %d, attenuation group=%d\n", __func__, chan, grp);
	for (i = 0; i < IWN_NBANDS; i++)
		if (sc->bands[i].lo != 0 &&
		    sc->bands[i].lo <= chan && chan <= sc->bands[i].hi)
			break;
	if (i == IWN_NBANDS)	
		return EINVAL;
	chans = sc->bands[i].chans;
	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
	    "%s: chan %d sub-band=%d\n", __func__, chan, i);
	for (c = 0; c < 2; c++) {
		uint8_t power, gain, temp;
		int maxchpwr, pwr, ridx, idx;
		power = interpolate(chan,
		    chans[0].num, chans[0].samples[c][1].power,
		    chans[1].num, chans[1].samples[c][1].power, 1);
		gain  = interpolate(chan,
		    chans[0].num, chans[0].samples[c][1].gain,
		    chans[1].num, chans[1].samples[c][1].gain, 1);
		temp  = interpolate(chan,
		    chans[0].num, chans[0].samples[c][1].temp,
		    chans[1].num, chans[1].samples[c][1].temp, 1);
		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
		    "%s: Tx chain %d: power=%d gain=%d temp=%d\n",
		    __func__, c, power, gain, temp);
		tdiff = ((sc->temp - temp) * 2) / tdiv[grp];
		DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
		    "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n",
		    __func__, tdiff, sc->temp, temp);
		for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
			maxchpwr = sc->maxpwr[chan] * 2;
			if ((ridx / 8) & 1)
				maxchpwr -= 6;	
			pwr = maxpwr;
			if ((ridx % 8) == 5)
				pwr -= 15;	
			else if ((ridx % 8) == 6)
				pwr -= 17;	
			else if ((ridx % 8) == 7)
				pwr -= 20;	
			else
				pwr -= 10;	
			if (pwr > maxchpwr)
				pwr = maxchpwr;
			idx = gain - (pwr - power) - tdiff - vdiff;
			if ((ridx / 8) & 1)	
				idx += (int32_t)le32toh(uc->atten[grp][c]);
			if (cmd.band == 0)
				idx += 9;	
			if (ridx == IWN_RIDX_MAX)
				idx += 5;	
			if (idx < 0)
				idx = 0;
			else if (idx > IWN4965_MAX_PWR_INDEX)
				idx = IWN4965_MAX_PWR_INDEX;
			DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
			    "%s: Tx chain %d, rate idx %d: power=%d\n",
			    __func__, c, ridx, idx);
			cmd.power[ridx].rf_gain[c] = rf_gain[idx];
			cmd.power[ridx].dsp_gain[c] = dsp_gain[idx];
		}
	}
	DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW,
	    "%s: set tx power for chan %d\n", __func__, chan);
	return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async);
#undef interpolate
#undef fdivround
}
static int iwn4965_set_txpower ( struct iwn_softc * sc , struct ieee80211_channel * ch , int async ) { #define fdivround ( a , b , n ) 	\
	((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) #define interpolate ( x , x1 , y1 , x2 , y2 , n ) 	\
	((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) static const int tdiv [ IWN_NATTEN_GROUPS ] = { 9 , 8 , 8 , 8 , 6 } ; struct iwn_ucode_info * uc = & sc -> ucode_info ; struct iwn4965_cmd_txpower cmd ; struct iwn4965_eeprom_chan_samples * chans ; const uint8_t * rf_gain , * dsp_gain ; int32_t vdiff , tdiff ; int i , c , grp , maxpwr ; uint8_t chan ; sc -> rxon = & sc -> rx_on [ IWN_RXON_BSS_CTX ] ; chan = sc -> rxon -> chan ; DPRINTF ( sc , IWN_DEBUG_RESET , " \n " , chan ) ; memset ( & cmd , 0 , sizeof cmd ) ; cmd . band = IEEE80211_IS_CHAN_5GHZ ( ch ) ? 0 : 1 ; cmd . chan = chan ; if ( IEEE80211_IS_CHAN_5GHZ ( ch ) ) { maxpwr = sc -> maxpwr5GHz ; rf_gain = iwn4965_rf_gain_5ghz ; dsp_gain = iwn4965_dsp_gain_5ghz ; } else { maxpwr = sc -> maxpwr2GHz ; rf_gain = iwn4965_rf_gain_2ghz ; dsp_gain = iwn4965_dsp_gain_2ghz ; } vdiff = ( ( int32_t ) le32toh ( uc -> volt ) - sc -> eeprom_voltage ) / 7 ; if ( vdiff > 0 ) vdiff *= 2 ; if ( abs ( vdiff ) > 2 ) vdiff = 0 ; DPRINTF ( sc , IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW , " \n " , __func__ , vdiff , le32toh ( uc -> volt ) , sc -> eeprom_voltage ) ; if ( chan <= 20 ) grp = 4 ; else if ( chan <= 43 ) grp = 0 ; else if ( chan <= 70 ) grp = 1 ; else if ( chan <= 124 ) grp = 2 ; else grp = 3 ; DPRINTF ( sc , IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW , " \n " , __func__ , chan , grp ) ; for ( i = 0 ; i < IWN_NBANDS ; i ++ ) if ( sc -> bands [ i ] . lo != 0 && sc -> bands [ i ] . lo <= chan && chan <= sc -> bands [ i ] . hi ) break ; if ( i == IWN_NBANDS ) return EINVAL ; chans = sc -> bands [ i ] . chans ; DPRINTF ( sc , IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW , " \n " , __func__ , chan , i ) ; for ( c = 0 ; c < 2 ; c ++ ) { uint8_t power , gain , temp ; int maxchpwr , pwr , ridx , idx ; power = interpolate ( chan , chans [ 0 ] . num , chans [ 0 ] . samples [ c ] [ 1 ] . power , chans [ 1 ] . num , chans [ 1 ] . samples [ c ] [ 1 ] . power , 1 ) ; gain = interpolate ( chan , chans [ 0 ] . num , chans [ 0 ] . samples [ c ] [ 1 ] . gain , chans [ 1 ] . num , chans [ 1 ] . samples [ c ] [ 1 ] . gain , 1 ) ; temp = interpolate ( chan , chans [ 0 ] . num , chans [ 0 ] . samples [ c ] [ 1 ] . temp , chans [ 1 ] . num , chans [ 1 ] . samples [ c ] [ 1 ] . temp , 1 ) ; DPRINTF ( sc , IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW , " \n " , __func__ , c , power , gain , temp ) ; tdiff = ( ( sc -> temp - temp ) * 2 ) / tdiv [ grp ] ; DPRINTF ( sc , IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW , " \n " , __func__ , tdiff , sc -> temp , temp ) ; for ( ridx = 0 ; ridx <= IWN_RIDX_MAX ; ridx ++ ) { maxchpwr = sc -> maxpwr [ chan ] * 2 ; if ( ( ridx / 8 ) & 1 ) maxchpwr -= 6 ; pwr = maxpwr ; if ( ( ridx % 8 ) == 5 ) pwr -= 15 ; else if ( ( ridx % 8 ) == 6 ) pwr -= 17 ; else if ( ( ridx % 8 ) == 7 ) pwr -= 20 ; else pwr -= 10 ; if ( pwr > maxchpwr ) pwr = maxchpwr ; idx = gain - ( pwr - power ) - tdiff - vdiff ; if ( ( ridx / 8 ) & 1 ) idx += ( int32_t ) le32toh ( uc -> atten [ grp ] [ c ] ) ; if ( cmd . band == 0 ) idx += 9 ; if ( ridx == IWN_RIDX_MAX ) idx += 5 ; if ( idx < 0 ) idx = 0 ; else if ( idx > IWN4965_MAX_PWR_INDEX ) idx = IWN4965_MAX_PWR_INDEX ; DPRINTF ( sc , IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW , " \n " , __func__ , c , ridx , idx ) ; cmd . power [ ridx ] . rf_gain [ c ] = rf_gain [ idx ] ; cmd . power [ ridx ] . dsp_gain [ c ] = dsp_gain [ idx ] ; } } DPRINTF ( sc , IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW , " \n " , __func__ , chan ) ; return iwn_cmd ( sc , IWN_CMD_TXPOWER , & cmd , sizeof cmd , async ) ; #undef  interpolate #undef  fdivround }
Set TX power for current channel (each rate has its own power settings).
Set TX power for current channel ( each rate has its own power settings ) .
/* Fixed-point arithmetic division using a n-bit fractional part. */ /* Linear interpolation. */ /* Retrieve current channel from last RXON. */ /* Compute voltage compensation. */ /* Get channel attenuation group. */ /* 1-20 */ /* 34-43 */ /* 44-70 */ /* 71-124 */ /* 125-200 */ /* Get channel sub-band. */ /* Can't happen in real-life. */ /* Compute temperature compensation. */ /* Convert dBm to half-dBm. */ /* MIMO 2T: -3dB */ /* Adjust TX power based on rate. */ /* OFDM48: -7.5dB */ /* OFDM54: -8.5dB */ /* OFDM60: -10dB */ /* Others: -5dB */ /* Do not exceed channel max TX power. */ /* MIMO */ /* 5GHz */ /* CCK */ /* Make sure idx stays in a valid range. */