📄 iwl-3945.c
字号:
{51, 118}, {51, 111}, {51, 104}, {51, 98}, {19, 116}, {19, 109}, {19, 102}, {19, 98}, {19, 93}, {171, 113}, {171, 107}, {171, 99}, {139, 120}, {139, 113}, {139, 107}, {139, 99}, {107, 120}, {107, 113}, {107, 107}, {107, 99}, {75, 120}, {75, 113}, {75, 107}, {75, 99}, {43, 120}, {43, 113}, {43, 107}, {43, 99}, {11, 120}, {11, 113}, {11, 107}, {11, 99}, {131, 107}, {131, 99}, {99, 120}, {99, 113}, {99, 107}, {99, 99}, {67, 120}, {67, 113}, {67, 107}, {67, 99}, {35, 120}, {35, 113}, {35, 107}, {35, 99}, {3, 120} } /* 5.x GHz, lowest power */};static inline u8 iwl_hw_reg_fix_power_index(int index){ if (index < 0) return 0; if (index >= IWL_MAX_GAIN_ENTRIES) return IWL_MAX_GAIN_ENTRIES - 1; return (u8) index;}/* Kick off thermal recalibration check every 60 seconds */#define REG_RECALIB_PERIOD (60)/** * iwl_hw_reg_set_scan_power - Set Tx power for scan probe requests * * Set (in our channel info database) the direct scan Tx power for 1 Mbit (CCK) * or 6 Mbit (OFDM) rates. */static void iwl_hw_reg_set_scan_power(struct iwl_priv *priv, u32 scan_tbl_index, s32 rate_index, const s8 *clip_pwrs, struct iwl_channel_info *ch_info, int band_index){ struct iwl_scan_power_info *scan_power_info; s8 power; u8 power_index; scan_power_info = &ch_info->scan_pwr_info[scan_tbl_index]; /* use this channel group's 6Mbit clipping/saturation pwr, * but cap at regulatory scan power restriction (set during init * based on eeprom channel data) for this channel. */ power = min(ch_info->scan_power, clip_pwrs[IWL_RATE_6M_INDEX_TABLE]); /* further limit to user's max power preference. * FIXME: Other spectrum management power limitations do not * seem to apply?? */ power = min(power, priv->user_txpower_limit); scan_power_info->requested_power = power; /* find difference between new scan *power* and current "normal" * Tx *power* for 6Mb. Use this difference (x2) to adjust the * current "normal" temperature-compensated Tx power *index* for * this rate (1Mb or 6Mb) to yield new temp-compensated scan power * *index*. */ power_index = ch_info->power_info[rate_index].power_table_index - (power - ch_info->power_info [IWL_RATE_6M_INDEX_TABLE].requested_power) * 2; /* store reference index that we use when adjusting *all* scan * powers. So we can accommodate user (all channel) or spectrum * management (single channel) power changes "between" temperature * feedback compensation procedures. * don't force fit this reference index into gain table; it may be a * negative number. This will help avoid errors when we're at * the lower bounds (highest gains, for warmest temperatures) * of the table. */ /* don't exceed table bounds for "real" setting */ power_index = iwl_hw_reg_fix_power_index(power_index); scan_power_info->power_table_index = power_index; scan_power_info->tpc.tx_gain = power_gain_table[band_index][power_index].tx_gain; scan_power_info->tpc.dsp_atten = power_gain_table[band_index][power_index].dsp_atten;}/** * iwl_hw_reg_send_txpower - fill in Tx Power command with gain settings * * Configures power settings for all rates for the current channel, * using values from channel info struct, and send to NIC */int iwl_hw_reg_send_txpower(struct iwl_priv *priv){ int rate_idx, i; const struct iwl_channel_info *ch_info = NULL; struct iwl_txpowertable_cmd txpower = { .channel = priv->active_rxon.channel, }; txpower.band = (priv->phymode == MODE_IEEE80211A) ? 0 : 1; ch_info = iwl_get_channel_info(priv, priv->phymode, le16_to_cpu(priv->active_rxon.channel)); if (!ch_info) { IWL_ERROR ("Failed to get channel info for channel %d [%d]\n", le16_to_cpu(priv->active_rxon.channel), priv->phymode); return -EINVAL; } if (!is_channel_valid(ch_info)) { IWL_DEBUG_POWER("Not calling TX_PWR_TABLE_CMD on " "non-Tx channel.\n"); return 0; } /* fill cmd with power settings for all rates for current channel */ /* Fill OFDM rate */ for (rate_idx = IWL_FIRST_OFDM_RATE, i = 0; rate_idx <= IWL_LAST_OFDM_RATE; rate_idx++, i++) { txpower.power[i].tpc = ch_info->power_info[i].tpc; txpower.power[i].rate = iwl_rates[rate_idx].plcp; IWL_DEBUG_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", le16_to_cpu(txpower.channel), txpower.band, txpower.power[i].tpc.tx_gain, txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); } /* Fill CCK rates */ for (rate_idx = IWL_FIRST_CCK_RATE; rate_idx <= IWL_LAST_CCK_RATE; rate_idx++, i++) { txpower.power[i].tpc = ch_info->power_info[i].tpc; txpower.power[i].rate = iwl_rates[rate_idx].plcp; IWL_DEBUG_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", le16_to_cpu(txpower.channel), txpower.band, txpower.power[i].tpc.tx_gain, txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); } return iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, sizeof(struct iwl_txpowertable_cmd), &txpower);}/** * iwl_hw_reg_set_new_power - Configures power tables at new levels * @ch_info: Channel to update. Uses power_info.requested_power. * * Replace requested_power and base_power_index ch_info fields for * one channel. * * Called if user or spectrum management changes power preferences. * Takes into account h/w and modulation limitations (clip power). * * This does *not* send anything to NIC, just sets up ch_info for one channel. * * NOTE: reg_compensate_for_temperature_dif() *must* be run after this to * properly fill out the scan powers, and actual h/w gain settings, * and send changes to NIC */static int iwl_hw_reg_set_new_power(struct iwl_priv *priv, struct iwl_channel_info *ch_info){ struct iwl_channel_power_info *power_info; int power_changed = 0; int i; const s8 *clip_pwrs; int power; /* Get this chnlgrp's rate-to-max/clip-powers table */ clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers; /* Get this channel's rate-to-current-power settings table */ power_info = ch_info->power_info; /* update OFDM Txpower settings */ for (i = IWL_RATE_6M_INDEX_TABLE; i <= IWL_RATE_54M_INDEX_TABLE; i++, ++power_info) { int delta_idx; /* limit new power to be no more than h/w capability */ power = min(ch_info->curr_txpow, clip_pwrs[i]); if (power == power_info->requested_power) continue; /* find difference between old and new requested powers, * update base (non-temp-compensated) power index */ delta_idx = (power - power_info->requested_power) * 2; power_info->base_power_index -= delta_idx; /* save new requested power value */ power_info->requested_power = power; power_changed = 1; } /* update CCK Txpower settings, based on OFDM 12M setting ... * ... all CCK power settings for a given channel are the *same*. */ if (power_changed) { power = ch_info->power_info[IWL_RATE_12M_INDEX_TABLE]. requested_power + IWL_CCK_FROM_OFDM_POWER_DIFF; /* do all CCK rates' iwl_channel_power_info structures */ for (i = IWL_RATE_1M_INDEX_TABLE; i <= IWL_RATE_11M_INDEX_TABLE; i++) { power_info->requested_power = power; power_info->base_power_index = ch_info->power_info[IWL_RATE_12M_INDEX_TABLE]. base_power_index + IWL_CCK_FROM_OFDM_INDEX_DIFF; ++power_info; } } return 0;}/** * iwl_hw_reg_get_ch_txpower_limit - returns new power limit for channel * * NOTE: Returned power limit may be less (but not more) than requested, * based strictly on regulatory (eeprom and spectrum mgt) limitations * (no consideration for h/w clipping limitations). */static int iwl_hw_reg_get_ch_txpower_limit(struct iwl_channel_info *ch_info){ s8 max_power;#if 0 /* if we're using TGd limits, use lower of TGd or EEPROM */ if (ch_info->tgd_data.max_power != 0) max_power = min(ch_info->tgd_data.max_power, ch_info->eeprom.max_power_avg); /* else just use EEPROM limits */ else#endif max_power = ch_info->eeprom.max_power_avg; return min(max_power, ch_info->max_power_avg);}/** * iwl_hw_reg_comp_txpower_temp - Compensate for temperature * * Compensate txpower settings of *all* channels for temperature. * This only accounts for the difference between current temperature * and the factory calibration temperatures, and bases the new settings * on the channel's base_power_index. * * If RxOn is "associated", this sends the new Txpower to NIC! */static int iwl_hw_reg_comp_txpower_temp(struct iwl_priv *priv){ struct iwl_channel_info *ch_info = NULL; int delta_index; const s8 *clip_pwrs; /* array of h/w max power levels for each rate */ u8 a_band; u8 rate_index; u8 scan_tbl_index; u8 i; int ref_temp; int temperature = priv->temperature; /* set up new Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0; i < priv->channel_count; i++) { ch_info = &priv->channel_info[i]; a_band = is_channel_a_band(ch_info); /* Get this chnlgrp's factory calibration temperature */ ref_temp = (s16)priv->eeprom.groups[ch_info->group_index]. temperature; /* get power index adjustment based on curr and factory * temps */ delta_index = iwl_hw_reg_adjust_power_by_temp(temperature, ref_temp); /* set tx power value for all rates, OFDM and CCK */ for (rate_index = 0; rate_index < IWL_RATE_COUNT; rate_index++) { int power_idx = ch_info->power_info[rate_index].base_power_index; /* temperature compensate */ power_idx += delta_index; /* stay within table range */ power_idx = iwl_hw_reg_fix_power_index(power_idx); ch_info->power_info[rate_index]. power_table_index = (u8) power_idx; ch_info->power_info[rate_index].tpc = power_gain_table[a_band][power_idx]; } /* Get this chnlgrp's rate-to-max/clip-powers table */ clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers; /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ for (scan_tbl_index = 0; scan_tbl_index < IWL_NUM_SCAN_RATES; scan_tbl_index++) { s32 actual_index = (scan_tbl_index == 0) ? IWL_RATE_1M_INDEX_TABLE : IWL_RATE_6M_INDEX_TABLE; iwl_hw_reg_set_scan_power(priv, scan_tbl_index, actual_index, clip_pwrs, ch_info, a_band); } } /* send Txpower command for current channel to ucode */ return iwl_hw_reg_send_txpower(priv);}int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power){ struct iwl_channel_info *ch_info; s8 max_power; u8 a_band; u8 i; if (priv->user_txpower_limit == power) { IWL_DEBUG_POWER("Requested Tx power same as current " "limit: %ddBm.\n", power); return 0; } IWL_DEBUG_POWER("Setting upper limit clamp to %ddBm.\n", power); priv->user_txpower_limit = power; /* set up new Tx powers for each and every channel, 2.4 and 5.x */ for (i = 0; i < priv->channel_count; i++) { ch_info = &priv->channel_info[i]; a_band = is_channel_a_band(ch_info); /* find minimum power of all user and regulatory constraints * (does not consider h/w clipping limitations) */ max_power = iwl_hw_reg_get_ch_txpower_limit(ch_info); max_power = min(power, max_power); if (max_power != ch_info->curr_txpow) { ch_info->curr_txpow = max_power; /* this considers the h/w clipping limitations */ iwl_hw_reg_set_new_power(priv, ch_info); } } /* update txpower settings for all channels, * send to NIC if associated. */ is_temp_calib_needed(priv); iwl_hw_reg_comp_txpower_temp(priv); return 0;}/* will add 3945 channel switch cmd handling later */int iwl_hw_channel_switch(struct iwl_priv *priv, u16 channel){ return 0;}/** * iwl3945_reg_txpower_periodic - called when time to check our temperature. * * -- reset periodic timer * -- see if temp has changed enough to warrant re-calibration ... if so: * -- correct coeffs for temp (can reset temp timer) * -- save this temp as "last", * -- send new set of gain settings to NIC * NOTE: This should continue working, even when we're not associated, * so we can keep our internal table of scan powers current. */void iwl3945_reg_txpower_periodic(struct iwl_priv *priv){ /* This will kick in the "brute force" * iwl_hw_reg_comp_txpower_temp() below */ if (!is_temp_calib_needed(priv)) goto reschedule; /* Set up a new set of temp-adjusted TxPowers, send to NIC. * This is based *only* on current temperature, * ignoring any previous power measurements */ iwl_hw_reg_comp_txpower_temp(priv); reschedule: queue_delayed_work(priv->workqueue, &priv->thermal_periodic, REG_RECALIB_PERIOD * HZ);}void iwl3945_bg_reg_txpower_periodic(struct work_struct *work){ struct iwl_priv *priv = container_of(work, struct iwl_priv, thermal_periodic.work); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; mutex_lock(&priv->mutex); iwl3945_reg_txpower_periodic(priv); mutex_unlock(&priv->mutex);}/** * iwl_hw_reg_get_ch_grp_index - find the channel-group index (0-4) * for the channel. * * This function is used when initializing channel-info structs. * * NOTE: These channel groups do *NOT* match the bands above! * These channel groups are based on factory-tested channels; * on A-band, EEPROM's "group frequency" entries represent the top * channel in each group 1-4. Group 5 All B/G channels are in group 0. */static u16 iwl_hw_reg_get_ch_grp_index(struct iwl_priv *priv, const struct iwl_channel_info *ch_info){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -