base = le32_to_cpu(il->card_alive.log_event_table_ptr); if (!il3945_hw_valid_rtc_data_addr(base)) {
IL_ERR("Invalid event log pointer 0x%08X\n", base); return;
}
disable_ptr = il_read_targ_mem(il, base + (4 * sizeof(u32)));
array_size = il_read_targ_mem(il, base + (5 * sizeof(u32)));
if (IL_EVT_DISABLE && array_size == IL_EVT_DISABLE_SIZE) {
D_INFO("Disabling selected uCode log events at 0x%x\n",
disable_ptr); for (i = 0; i < IL_EVT_DISABLE_SIZE; i++)
il_write_targ_mem(il, disable_ptr + (i * sizeof(u32)),
evt_disable[i]);
} else {
D_INFO("Selected uCode log events may be disabled\n");
D_INFO(" by writing \"1\"s into disable bitmap\n");
D_INFO(" in SRAM at 0x%x, size %d u32s\n", disable_ptr,
array_size);
}
}
staticint
il3945_hwrate_to_plcp_idx(u8 plcp)
{ int idx;
for (idx = 0; idx < RATE_COUNT_3945; idx++) if (il3945_rates[idx].plcp == plcp) return idx; return -1;
}
#ifdef CONFIG_IWLEGACY_DEBUG #define TX_STATUS_ENTRY(x) case TX_3945_STATUS_FAIL_ ## x: return#x
/* * get ieee prev rate from rate scale table. * for A and B mode we need to overright prev * value
*/ int
il3945_rs_next_rate(struct il_priv *il, int rate)
{ int next_rate = il3945_get_prev_ieee_rate(rate);
switch (il->band) { case NL80211_BAND_5GHZ: if (rate == RATE_12M_IDX)
next_rate = RATE_9M_IDX; elseif (rate == RATE_6M_IDX)
next_rate = RATE_6M_IDX; break; case NL80211_BAND_2GHZ: if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) &&
il_is_associated(il)) { if (rate == RATE_11M_IDX)
next_rate = RATE_5M_IDX;
} break;
default: break;
}
return next_rate;
}
/* * il3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd * * When FW advances 'R' idx, all entries between old and new 'R' idx * need to be reclaimed. As result, some free space forms. If there is * enough free space (> low mark), wake the stack that feeds us.
*/ staticvoid
il3945_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx)
{ struct il_tx_queue *txq = &il->txq[txq_id]; struct il_queue *q = &txq->q; struct sk_buff *skb;
if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) {
IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " "is out of range [0-%d] %d %d\n", txq_id, idx,
txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); return;
}
/* * Firmware will not transmit frame on passive channel, if it not yet * received some valid frame on that channel. When this error happen * we have to wait until firmware will unblock itself i.e. when we * note received beacon or other frame. We unblock queues in * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
*/ if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
il->iw_mode == NL80211_IFTYPE_STATION) {
il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
D_INFO("Stopped queues - RX waiting on passive channel\n");
}
txq->time_stamp = jiffies;
info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]);
ieee80211_tx_info_clear_status(info);
/* Fill the MRR chain with some info about on-chip retransmissions */
rate_idx = il3945_hwrate_to_plcp_idx(tx_resp->rate); if (info->band == NL80211_BAND_5GHZ)
rate_idx -= IL_FIRST_OFDM_RATE;
if (le32_to_cpu(*flag) & UCODE_STATS_CLEAR_MSK) { #ifdef CONFIG_IWLEGACY_DEBUGFS
memset(&il->_3945.accum_stats, 0, sizeof(struct il3945_notif_stats));
memset(&il->_3945.delta_stats, 0, sizeof(struct il3945_notif_stats));
memset(&il->_3945.max_delta, 0, sizeof(struct il3945_notif_stats)); #endif
D_RX("Statistics have been cleared\n");
}
il3945_hdl_stats(il, rxb);
}
/****************************************************************************** * * Misc. internal state and helper functions *
******************************************************************************/
/* This is necessary only for a number of stats, see the caller. */ staticint
il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header)
{ /* Filter incoming packets to determine if they are targeted toward
* this network, discarding packets coming from ourselves */ switch (il->iw_mode) { case NL80211_IFTYPE_ADHOC: /* Header: Dest. | Source | BSSID */ /* packets to our IBSS update information */ return ether_addr_equal_64bits(header->addr3, il->bssid); case NL80211_IFTYPE_STATION: /* Header: Dest. | AP{BSSID} | Source */ /* packets to our IBSS update information */ return ether_addr_equal_64bits(header->addr2, il->bssid); default: return 1;
}
}
/* We received data from the HW, so stop the watchdog */ if (unlikely(len + IL39_RX_FRAME_SIZE > fraglen)) {
D_DROP("Corruption detected!\n"); return;
}
/* We only process data packets if the interface is open */ if (unlikely(!il->is_open)) {
D_DROP("Dropping packet while interface is not open.\n"); return;
}
if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
D_INFO("Woke queues - frame received on passive channel\n");
}
skb = dev_alloc_skb(SMALL_PACKET_SIZE); if (!skb) {
IL_ERR("dev_alloc_skb failed\n"); return;
}
if (!il3945_mod_params.sw_crypto)
il_set_decrypted_flag(il, (struct ieee80211_hdr *)pkt,
le32_to_cpu(rx_end->status), stats);
/* If frame is small enough to fit into skb->head, copy it * and do not consume a full page
*/ if (len <= SMALL_PACKET_SIZE) {
skb_put_data(skb, rx_hdr->payload, len);
} else {
skb_add_rx_frag(skb, 0, rxb->page,
(void *)rx_hdr->payload - (void *)pkt, len,
fraglen);
il->alloc_rxb_page--;
rxb->page = NULL;
}
il_update_stats(il, false, fc, len);
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
/* We need to figure out how to get the sta->supp_rates while
* in this running context */
rate_mask = RATES_MASK_3945;
/* Set retry limit on DATA packets and Probe Responses */ if (ieee80211_is_probe_resp(fc))
data_retry_limit = 3; else
data_retry_limit = IL_DEFAULT_TX_RETRY;
tx_cmd->data_retry_limit = data_retry_limit; /* Set retry limit on RTS packets */
tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit);
/* * Start up 3945's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via il_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor
*/ staticint
il3945_apm_init(struct il_priv *il)
{ int ret = il_apm_init(il);
if (EEPROM_SKU_CAP_OP_MODE_MRC == eeprom->sku_cap) {
D_INFO("SKU OP mode is mrc\n");
il_set_bit(il, CSR_HW_IF_CONFIG_REG,
CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC);
} else
D_INFO("SKU OP mode is basic\n");
if ((eeprom->board_revision & 0xF0) == 0xD0) {
D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision);
il_set_bit(il, CSR_HW_IF_CONFIG_REG,
CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
} else {
D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision);
il_clear_bit(il, CSR_HW_IF_CONFIG_REG,
CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
}
if (eeprom->almgor_m_version <= 1) {
il_set_bit(il, CSR_HW_IF_CONFIG_REG,
CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
D_INFO("Card M type A version is 0x%X\n",
eeprom->almgor_m_version);
} else {
D_INFO("Card M type B version is 0x%X\n",
eeprom->almgor_m_version);
il_set_bit(il, CSR_HW_IF_CONFIG_REG,
CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
}
spin_unlock_irqrestore(&il->lock, flags);
if (eeprom->sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE)
D_RF_KILL("SW RF KILL supported in EEPROM.\n");
if (eeprom->sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE)
D_RF_KILL("HW RF KILL supported in EEPROM.\n");
}
int
il3945_hw_nic_init(struct il_priv *il)
{ int rc; unsignedlong flags; struct il_rx_queue *rxq = &il->rxq;
/* Allocate the RX queue, or reset if it is already allocated */ if (!rxq->bd) {
rc = il_rx_queue_alloc(il); if (rc) {
IL_ERR("Unable to initialize Rx queue\n"); return -ENOMEM;
}
} else
il3945_rx_queue_reset(il, rxq);
il3945_rx_replenish(il);
il3945_rx_init(il, rxq);
/* Look at using this instead: rxq->need_update = 1; il_rx_queue_update_write_ptr(il, rxq);
*/
il_wr(il, FH39_RCSR_WPTR(0), rxq->write & ~7);
rc = il3945_txq_ctx_reset(il); if (rc) return rc;
set_bit(S_INIT, &il->status);
return 0;
}
/* * il3945_hw_txq_ctx_free - Free TXQ Context * * Destroy all TX DMA queues and structures
*/ void
il3945_hw_txq_ctx_free(struct il_priv *il)
{ int txq_id;
/* Tx queues */ if (il->txq) { for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) if (txq_id == IL39_CMD_QUEUE_NUM)
il_cmd_queue_free(il); else
il_tx_queue_free(il, txq_id);
}
/* * il3945_hw_reg_adjust_power_by_temp * return idx delta into power gain settings table
*/ staticint
il3945_hw_reg_adjust_power_by_temp(int new_reading, int old_reading)
{ return (new_reading - old_reading) * (-11) / 100;
}
/* * il3945_hw_reg_temp_out_of_range - Keep temperature in sane range
*/ staticinlineint
il3945_hw_reg_temp_out_of_range(int temperature)
{ return (temperature < -260 || temperature > 25) ? 1 : 0;
}
int
il3945_hw_get_temperature(struct il_priv *il)
{ return _il_rd(il, CSR_UCODE_DRV_GP2);
}
/* * il3945_hw_reg_txpower_get_temperature * get the current temperature by reading from NIC
*/ staticint
il3945_hw_reg_txpower_get_temperature(struct il_priv *il)
{ struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; int temperature;
temperature = il3945_hw_get_temperature(il);
/* driver's okay range is -260 to +25.
* human readable okay range is 0 to +285 */
D_INFO("Temperature: %d\n", temperature + IL_TEMP_CONVERT);
/* handle insane temp reading */ if (il3945_hw_reg_temp_out_of_range(temperature)) {
IL_ERR("Error bad temperature value %d\n", temperature);
/* if really really hot(?),
* substitute the 3rd band/group's temp measured at factory */ if (il->last_temperature > 100)
temperature = eeprom->groups[2].temperature; else/* else use most recent "sane" value from driver */
temperature = il->last_temperature;
}
return temperature; /* raw, not "human readable" */
}
/* Adjust Txpower only if temperature variance is greater than threshold. *
* Both are lower than older versions' 9 degrees */ #define IL_TEMPERATURE_LIMIT_TIMER 6
/* * il3945_is_temp_calib_needed - determines if new calibration is needed * * records new temperature in tx_mgr->temperature. * replaces tx_mgr->last_temperature *only* if calib needed
* (assumes caller will actually do the calibration!). */ staticint
il3945_is_temp_calib_needed(struct il_priv *il)
{ int temp_diff;
/* if we don't need calibration, *don't* update last_temperature */ if (temp_diff < IL_TEMPERATURE_LIMIT_TIMER) {
D_POWER("Timed thermal calib not needed\n"); return 0;
}
D_POWER("Timed thermal calib needed\n");
/* assume that caller will actually do calib ...
* update the "last temperature" value */
il->last_temperature = il->temperature; return 1;
}
/* Kick off thermal recalibration check every 60 seconds */ #define REG_RECALIB_PERIOD (60)
/* * il3945_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.
*/ staticvoid
il3945_hw_reg_set_scan_power(struct il_priv *il, u32 scan_tbl_idx, s32 rate_idx, const s8 *clip_pwrs, struct il_channel_info *ch_info, int band_idx)
{ struct il3945_scan_power_info *scan_power_info;
s8 power;
u8 power_idx;
/* 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[RATE_6M_IDX_TBL]);
power = min(power, il->tx_power_user_lmt);
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 *idx* for * this rate (1Mb or 6Mb) to yield new temp-compensated scan power
* *idx*. */
power_idx =
ch_info->power_info[rate_idx].power_table_idx - (power -
ch_info->
power_info
[RATE_6M_IDX_TBL].
requested_power) *
2;
/* store reference idx 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 idx 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. */
/* * il3945_send_tx_power - 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
*/ staticint
il3945_send_tx_power(struct il_priv *il)
{ int rate_idx, i; conststruct il_channel_info *ch_info = NULL; struct il3945_txpowertable_cmd txpower = {
.channel = il->active.channel,
};
u16 chan;
if (WARN_ONCE
(test_bit(S_SCAN_HW, &il->status), "TX Power requested while scanning!\n")) return -EAGAIN;
chan = le16_to_cpu(il->active.channel);
txpower.band = (il->band == NL80211_BAND_5GHZ) ? 0 : 1;
ch_info = il_get_channel_info(il, il->band, chan); if (!ch_info) {
IL_ERR("Failed to get channel info for channel %d [%d]\n", chan,
il->band); return -EINVAL;
}
if (!il_is_channel_valid(ch_info)) {
D_POWER("Not calling TX_PWR_TBL_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 = IL_FIRST_OFDM_RATE, i = 0;
rate_idx <= IL39_LAST_OFDM_RATE; rate_idx++, i++) {
/* * il3945_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_idx 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
*/ staticint
il3945_hw_reg_set_new_power(struct il_priv *il, struct il_channel_info *ch_info)
{ struct il3945_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 = il->_3945.clip_groups[ch_info->group_idx].clip_powers;
/* Get this channel's rate-to-current-power settings table */
power_info = ch_info->power_info;
/* update OFDM Txpower settings */ for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; 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 idx */
delta_idx = (power - power_info->requested_power) * 2;
power_info->base_power_idx -= 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[RATE_12M_IDX_TBL].requested_power +
IL_CCK_FROM_OFDM_POWER_DIFF;
/* do all CCK rates' il3945_channel_power_info structures */ for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) {
power_info->requested_power = power;
power_info->base_power_idx =
ch_info->power_info[RATE_12M_IDX_TBL].
base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF;
++power_info;
}
}
return 0;
}
/* * il3945_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).
*/ staticint
il3945_hw_reg_get_ch_txpower_limit(struct il_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);
}
/* * il3945_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_idx. * * If RxOn is "associated", this sends the new Txpower to NIC!
*/ staticint
il3945_hw_reg_comp_txpower_temp(struct il_priv *il)
{ struct il_channel_info *ch_info = NULL; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; int delta_idx; const s8 *clip_pwrs; /* array of h/w max power levels for each rate */
u8 a_band;
u8 rate_idx;
u8 scan_tbl_idx;
u8 i; int ref_temp; int temperature = il->temperature;
if (il->disable_tx_power_cal || test_bit(S_SCANNING, &il->status)) { /* do not perform tx power calibration */ return 0;
} /* set up new Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0; i < il->channel_count; i++) {
ch_info = &il->channel_info[i];
a_band = il_is_channel_a_band(ch_info);
/* Get this chnlgrp's factory calibration temperature */
ref_temp = (s16) eeprom->groups[ch_info->group_idx].temperature;
/* get power idx adjustment based on current and factory
* temps */
delta_idx =
il3945_hw_reg_adjust_power_by_temp(temperature, ref_temp);
/* set tx power value for all rates, OFDM and CCK */ for (rate_idx = 0; rate_idx < RATE_COUNT_3945; rate_idx++) { int power_idx =
ch_info->power_info[rate_idx].base_power_idx;
/* temperature compensate */
power_idx += delta_idx;
/* stay within table range */
power_idx = il3945_hw_reg_fix_power_idx(power_idx);
ch_info->power_info[rate_idx].power_table_idx =
(u8) power_idx;
ch_info->power_info[rate_idx].tpc =
power_gain_table[a_band][power_idx];
}
/* Get this chnlgrp's rate-to-max/clip-powers table */
clip_pwrs =
il->_3945.clip_groups[ch_info->group_idx].clip_powers;
/* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES;
scan_tbl_idx++) {
s32 actual_idx =
(scan_tbl_idx ==
0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL;
il3945_hw_reg_set_scan_power(il, scan_tbl_idx,
actual_idx, clip_pwrs,
ch_info, a_band);
}
}
/* send Txpower command for current channel to ucode */ return il->ops->send_tx_power(il);
}
if (il->tx_power_user_lmt == power) {
D_POWER("Requested Tx power same as current ""limit: %ddBm.\n",
power); return 0;
}
D_POWER("Setting upper limit clamp to %ddBm.\n", power);
il->tx_power_user_lmt = power;
/* set up new Tx powers for each and every channel, 2.4 and 5.x */
for (i = 0; i < il->channel_count; i++) {
ch_info = &il->channel_info[i];
/* find minimum power of all user and regulatory constraints
* (does not consider h/w clipping limitations) */
max_power = il3945_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 */
il3945_hw_reg_set_new_power(il, ch_info);
}
}
/* update txpower settings for all channels,
* send to NIC if associated. */
il3945_is_temp_calib_needed(il);
il3945_hw_reg_comp_txpower_temp(il);
rc = il_send_cmd_sync(il, &cmd); if (rc) return rc;
pkt = (struct il_rx_pkt *)cmd.reply_page; if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
IL_ERR("Bad return from C_RXON_ASSOC command\n");
rc = -EIO;
}
il_free_pages(il, cmd.reply_page);
return rc;
}
/* * il3945_commit_rxon - commit staging_rxon to hardware * * The RXON command in staging_rxon is committed to the hardware and * the active_rxon structure is updated with the new data. This * function correctly transitions out of the RXON_ASSOC_MSK state if * a HW tune is required based on the RXON structure changes.
*/ int
il3945_commit_rxon(struct il_priv *il)
{ /* cast away the const for active_rxon in this function */ struct il3945_rxon_cmd *active_rxon = (void *)&il->active; struct il3945_rxon_cmd *staging_rxon = (void *)&il->staging; int rc = 0; bool new_assoc = !!(staging_rxon->filter_flags & RXON_FILTER_ASSOC_MSK);
if (test_bit(S_EXIT_PENDING, &il->status)) return -EINVAL;
if (!il_is_alive(il)) return -1;
/* always get timestamp with Rx frame */
staging_rxon->flags |= RXON_FLG_TSF2HOST_MSK;
rc = il_check_rxon_cmd(il); if (rc) {
IL_ERR("Invalid RXON configuration. Not committing.\n"); return -EINVAL;
}
/* If we don't need to send a full RXON, we can use * il3945_rxon_assoc_cmd which is used to reconfigure filter
* and other flags for the current radio configuration. */ if (!il_full_rxon_required(il)) {
rc = il_send_rxon_assoc(il); if (rc) {
IL_ERR("Error setting RXON_ASSOC " "configuration (%d).\n", rc); return rc;
}
memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); /* * We do not commit tx power settings while channel changing, * do it now if tx power changed.
*/
il_set_tx_power(il, il->tx_power_next, false); return 0;
}
/* If we are currently associated and the new config requires * an RXON_ASSOC and the new config wants the associated mask enabled, * we must clear the associated from the active configuration
* before we apply the new config */ if (il_is_associated(il) && new_assoc) {
D_INFO("Toggling associated bit on current RXON\n");
active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
/* * reserved4 and 5 could have been filled by the iwlcore code. * Let's clear them before pushing to the 3945.
*/
active_rxon->reserved4 = 0;
active_rxon->reserved5 = 0;
rc = il_send_cmd_pdu(il, C_RXON, sizeof(struct il3945_rxon_cmd),
&il->active);
/* If the mask clearing failed then we set
* active_rxon back to what it was previously */ if (rc) {
active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK;
IL_ERR("Error clearing ASSOC_MSK on current " "configuration (%d).\n", rc); return rc;
}
il_clear_ucode_stations(il);
il_restore_stations(il);
}
/* * reserved4 and 5 could have been filled by the iwlcore code. * Let's clear them before pushing to the 3945.
*/
staging_rxon->reserved4 = 0;
staging_rxon->reserved5 = 0;
if (!new_assoc) {
il_clear_ucode_stations(il);
il_restore_stations(il);
}
/* If we issue a new RXON command which required a tune then we must
* send a new TXPOWER command or we won't be able to Tx any frames */
rc = il_set_tx_power(il, il->tx_power_next, true); if (rc) {
IL_ERR("Error setting Tx power (%d).\n", rc); return rc;
}
/* Init the hardware's rate fallback order based on the band */
rc = il3945_init_hw_rate_table(il); if (rc) {
IL_ERR("Error setting HW rate table: %02X\n", rc); return -EIO;
}
return 0;
}
/* * il3945_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
il3945_reg_txpower_periodic(struct il_priv *il)
{ /* This will kick in the "brute force"
* il3945_hw_reg_comp_txpower_temp() below */ if (!il3945_is_temp_calib_needed(il)) 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 */
il3945_hw_reg_comp_txpower_temp(il);
/* * il3945_hw_reg_get_ch_grp_idx - find the channel-group idx (0-4) for 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
il3945_hw_reg_get_ch_grp_idx(struct il_priv *il, conststruct il_channel_info *ch_info)
{ struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; struct il3945_eeprom_txpower_group *ch_grp = &eeprom->groups[0];
u8 group;
u16 group_idx = 0; /* based on factory calib frequencies */
u8 grp_channel;
/* Find the group idx for the channel ... don't use idx 1(?) */ if (il_is_channel_a_band(ch_info)) { for (group = 1; group < 5; group++) {
grp_channel = ch_grp[group].group_channel; if (ch_info->channel <= grp_channel) {
group_idx = group; break;
}
} /* group 4 has a few channels *above* its factory cal freq */ if (group == 5)
group_idx = 4;
} else
group_idx = 0; /* 2.4 GHz, group 0 */
D_POWER("Initializing factory calib info from EEPROM\n");
for (i = 0; i < IL_NUM_TX_CALIB_GROUPS; i++) {
s8 *clip_pwrs; /* table of power levels for each rate */
s8 satur_pwr; /* saturation power for each chnl group */
group = &eeprom->groups[i];
/* sanity check on factory saturation power value */ if (group->saturation_power < 40) {
IL_WARN("Error: saturation power is %d, " "less than minimum expected 40\n",
group->saturation_power); return;
}
/* * Derive requested power levels for each rate, based on * hardware capabilities (saturation power for band). * Basic value is 3dB down from saturation, with further * power reductions for highest 3 data rates. These * backoffs provide headroom for high rate modulation * power peaks, without too much distortion (clipping).
*/ /* we'll fill in this array with h/w max power levels */
clip_pwrs = (s8 *) il->_3945.clip_groups[i].clip_powers;
/* divide factory saturation power by 2 to find -3dB level */
satur_pwr = (s8) (group->saturation_power >> 1);
/* fill in channel group's nominal powers for each rate */ for (rate_idx = 0; rate_idx < RATE_COUNT_3945;
rate_idx++, clip_pwrs++) { switch (rate_idx) { case RATE_36M_IDX_TBL: if (i == 0) /* B/G */
*clip_pwrs = satur_pwr; else/* A */
*clip_pwrs = satur_pwr - 5; break; case RATE_48M_IDX_TBL: if (i == 0)
*clip_pwrs = satur_pwr - 7; else
*clip_pwrs = satur_pwr - 10; break; case RATE_54M_IDX_TBL: if (i == 0)
*clip_pwrs = satur_pwr - 9; else
*clip_pwrs = satur_pwr - 12; break; default:
*clip_pwrs = satur_pwr; break;
}
}
}
}
/* * il3945_txpower_set_from_eeprom - Set channel power info based on EEPROM * * Second pass (during init) to set up il->channel_info * * Set up Tx-power settings in our channel info database for each VALID * (for this geo/SKU) channel, at all Tx data rates, based on eeprom values * and current temperature. * * Since this is based on current temperature (at init time), these values may * not be valid for very long, but it gives us a starting/default point, * and allows us to active (i.e. using Tx) scan. * * This does *not* write values to NIC, just sets up our internal table.
*/ int
il3945_txpower_set_from_eeprom(struct il_priv *il)
{ struct il_channel_info *ch_info = NULL; struct il3945_channel_power_info *pwr_info; struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; int delta_idx;
u8 rate_idx;
u8 scan_tbl_idx; const s8 *clip_pwrs; /* array of power levels for each rate */
u8 gain, dsp_atten;
s8 power;
u8 pwr_idx, base_pwr_idx, a_band;
u8 i; int temperature;
/* save temperature reference,
* so we can determine next time to calibrate */
temperature = il3945_hw_reg_txpower_get_temperature(il);
il->last_temperature = temperature;
il3945_hw_reg_init_channel_groups(il);
/* initialize Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0, ch_info = il->channel_info; i < il->channel_count;
i++, ch_info++) {
a_band = il_is_channel_a_band(ch_info); if (!il_is_channel_valid(ch_info)) continue;
/* find this channel's channel group (*not* "band") idx */
ch_info->group_idx = il3945_hw_reg_get_ch_grp_idx(il, ch_info);
/* Get this chnlgrp's rate->max/clip-powers table */
clip_pwrs =
il->_3945.clip_groups[ch_info->group_idx].clip_powers;
/* calculate power idx *adjustment* value according to
* diff between current temperature and factory temperature */
delta_idx =
il3945_hw_reg_adjust_power_by_temp(temperature,
eeprom->groups[ch_info->
group_idx].
temperature);
D_POWER("Delta idx for channel %d: %d [%d]\n", ch_info->channel,
delta_idx, temperature + IL_TEMP_CONVERT);
/* set tx power value for all OFDM rates */ for (rate_idx = 0; rate_idx < IL_OFDM_RATES; rate_idx++) {
s32 power_idx; int rc;
/* use channel group's clip-power table,
* but don't exceed channel's max power */
s8 pwr = min(ch_info->max_power_avg,
clip_pwrs[rate_idx]);
pwr_info = &ch_info->power_info[rate_idx];
/* get base (i.e. at factory-measured temperature)
* power table idx for this rate's power */
rc = il3945_hw_reg_get_matched_power_idx(il, pwr,
ch_info->
group_idx,
&power_idx); if (rc) {
IL_ERR("Invalid power idx\n"); return rc;
}
pwr_info->base_power_idx = (u8) power_idx;
/* temperature compensate */
power_idx += delta_idx;
/* stay within range of gain table */
power_idx = il3945_hw_reg_fix_power_idx(power_idx);
/* set tx power for CCK rates, based on OFDM 12 Mbit settings */
pwr_info = &ch_info->power_info[RATE_12M_IDX_TBL];
power = pwr_info->requested_power + IL_CCK_FROM_OFDM_POWER_DIFF;
pwr_idx = pwr_info->power_table_idx + IL_CCK_FROM_OFDM_IDX_DIFF;
base_pwr_idx =
pwr_info->base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF;
/* stay within table range */
pwr_idx = il3945_hw_reg_fix_power_idx(pwr_idx);
gain = power_gain_table[a_band][pwr_idx].tx_gain;
dsp_atten = power_gain_table[a_band][pwr_idx].dsp_atten;
/* fill each CCK rate's il3945_channel_power_info structure * NOTE: All CCK-rate Txpwrs are the same for a given chnl!
* NOTE: CCK rates start at end of OFDM rates! */ for (rate_idx = 0; rate_idx < IL_CCK_RATES; rate_idx++) {
pwr_info =
&ch_info->power_info[rate_idx + IL_OFDM_RATES];
pwr_info->requested_power = power;
pwr_info->power_table_idx = pwr_idx;
pwr_info->base_power_idx = base_pwr_idx;
pwr_info->tpc.tx_gain = gain;
pwr_info->tpc.dsp_atten = dsp_atten;
}
/* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES;
scan_tbl_idx++) {
s32 actual_idx =
(scan_tbl_idx ==
0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL;
il3945_hw_reg_set_scan_power(il, scan_tbl_idx,
actual_idx, clip_pwrs,
ch_info, a_band);
}
}
return 0;
}
int
il3945_hw_rxq_stop(struct il_priv *il)
{ int ret;
_il_wr(il, FH39_RCSR_CONFIG(0), 0);
ret = _il_poll_bit(il, FH39_RSSR_STATUS,
FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE,
1000); if (ret < 0)
IL_ERR("Can't stop Rx DMA.\n");
return 0;
}
int
il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq)
{ int txq_id = txq->q.id;
switch (il->band) { case NL80211_BAND_5GHZ:
D_RATE("Select A mode rate scale\n"); /* If one of the following CCK rates is used,
* have it fall back to the 6M OFDM rate */ for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++)
table[i].next_rate_idx =
il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx;
/* Don't fall back to CCK rates */
table[RATE_12M_IDX_TBL].next_rate_idx = RATE_9M_IDX_TBL;
/* Don't drop out of OFDM rates */
table[RATE_6M_IDX_TBL].next_rate_idx =
il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; break;
case NL80211_BAND_2GHZ:
D_RATE("Select B/G mode rate scale\n"); /* If an OFDM rate is used, have it fall back to the
* 1M CCK rates */
if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) &&
il_is_associated(il)) {
idx = IL_FIRST_CCK_RATE; for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++)
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.