// SPDX-License-Identifier: GPL-2.0-only /****************************************************************************** * * Copyright(c) 2003 - 2014, 2018 - 2022 Intel Corporation. All rights reserved. * Copyright(c) 2024-2025 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files.
*****************************************************************************/
/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ staticvoid iwl_set_beacon_tim(struct iwl_priv *priv, struct iwl_tx_beacon_cmd *tx_beacon_cmd,
u8 *beacon, u32 frame_size)
{
u16 tim_idx; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
/* * The index is relative to frame start but we start looking at the * variable-length part of the beacon.
*/
tim_idx = mgmt->u.beacon.variable - beacon;
/* Parse variable-length elements of beacon to find WLAN_EID_TIM */ while ((tim_idx < (frame_size - 2)) &&
(beacon[tim_idx] != WLAN_EID_TIM))
tim_idx += beacon[tim_idx+1] + 2;
/* If TIM field was found, set variables */ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
tx_beacon_cmd->tim_size = beacon[tim_idx+1];
} else
IWL_WARN(priv, "Unable to find TIM Element in beacon\n");
}
/* Set up TX beacon command fields */
iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data,
frame_size);
/* Set up packet rate and flags */
info = IEEE80211_SKB_CB(priv->beacon_skb);
/* * Let's set up the rate at least somewhat correctly; * it will currently not actually be used by the uCode, * it uses the broadcast station's rate instead.
*/ if (info->control.rates[0].idx < 0 ||
info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
rate = 0; else
rate = info->control.rates[0].idx;
if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) { /* * The ucode will send beacon notifications even in * IBSS mode, but we don't want to process them. But * we need to defer the type check to here due to * requiring locking around the beacon_ctx access.
*/ goto out;
}
/* Pull updated AP beacon from mac80211. will fail if not in AP mode */
beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif, 0); if (!beacon) {
IWL_ERR(priv, "update beacon failed -- keeping old\n"); goto out;
}
/* new beacon skb is allocated every time; dispose previous.*/
dev_kfree_skb(priv->beacon_skb);
/* * iwl_bg_statistics_periodic - Timer callback to queue statistics * * This callback is provided in order to send a statistics request. * * This timer function is continually reset to execute within * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION * was received. We need to ensure we receive the statistics in order * to update the temperature used for calibrating the TXPOWER.
*/ staticvoid iwl_bg_statistics_periodic(struct timer_list *t)
{ struct iwl_priv *priv = timer_container_of(priv, t,
statistics_periodic);
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return;
/* dont send host command if rf-kill is on */ if (!iwl_is_ready_rf(priv)) return;
/* Make sure device is powered up for SRAM reads */ if (!iwl_trans_grab_nic_access(priv->trans)) return;
/* Set starting address; reads will auto-increment */
iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr);
/* * Refuse to read more than would have fit into the log from * the current start_idx. This used to happen due to the race * described below, but now WARN because the code below should * prevent it from happening here.
*/ if (WARN_ON(num_events > capacity - start_idx))
num_events = capacity - start_idx;
/* * "time" is actually "data" for mode 0 (no timestamp). * place event id # at far right for easier visual parsing.
*/ for (i = 0; i < num_events; i++) {
ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); if (mode == 0) {
trace_iwlwifi_dev_ucode_cont_event(
priv->trans->dev, 0, time, ev);
} else {
data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
trace_iwlwifi_dev_ucode_cont_event(
priv->trans->dev, time, data, ev);
}
} /* Allow device to power down */
iwl_trans_release_nic_access(priv->trans);
}
staticvoid iwl_continuous_event_trace(struct iwl_priv *priv)
{
u32 capacity; /* event log capacity in # entries */ struct {
u32 capacity;
u32 mode;
u32 wrap_counter;
u32 write_counter;
} __packed read;
u32 base; /* SRAM byte address of event log header */
u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
u32 num_wraps; /* # times uCode wrapped to top of log */
u32 next_entry; /* index of next entry to be written by uCode */
/* * Unfortunately, the uCode doesn't use temporary variables. * Therefore, it can happen that we read next_entry == capacity, * which really means next_entry == 0.
*/ if (unlikely(next_entry == capacity))
next_entry = 0; /* * Additionally, the uCode increases the write pointer before * the wraps counter, so if the write pointer is smaller than * the old write pointer (wrap occurred) but we read that no * wrap occurred, we actually read between the next_entry and * num_wraps update (this does happen in practice!!) -- take * that into account by increasing num_wraps.
*/ if (unlikely(next_entry < priv->event_log.next_entry &&
num_wraps == priv->event_log.num_wraps))
num_wraps++;
/* * iwl_bg_ucode_trace - Timer callback to log ucode event * * The timer is continually set to execute every * UCODE_TRACE_PERIOD milliseconds after the last timer expired * this function is to perform continuous uCode event logging operation * if enabled
*/ staticvoid iwl_bg_ucode_trace(struct timer_list *t)
{ struct iwl_priv *priv = timer_container_of(priv, t, ucode_trace);
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return;
if (priv->event_log.ucode_trace) {
iwl_continuous_event_trace(priv); /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */
mod_timer(&priv->ucode_trace,
jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
}
}
staticvoid iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
{ int i;
/* * The default context is always valid, * the PAN context depends on uCode.
*/
priv->valid_contexts = BIT(IWL_RXON_CTX_BSS); if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN)
priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
for (i = 0; i < NUM_IWL_RXON_CTX; i++)
priv->contexts[i].ctxid = i;
if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
0, sizeof(struct iwl_bt_cmd), &bt_cmd))
IWL_ERR(priv, "failed to send BT Coex Config\n");
}
/* * iwl_alive_start - called after REPLY_ALIVE notification received * from protocol/runtime uCode (initialization uCode's * Alive gets handled by iwl_init_alive_start()).
*/ int iwl_alive_start(struct iwl_priv *priv)
{ int ret = 0; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
/* After the ALIVE response, we can send host commands to the uCode */
set_bit(STATUS_ALIVE, &priv->status);
if (iwl_is_rfkill(priv)) return -ERFKILL;
if (priv->event_log.ucode_trace) { /* start collecting data now */
mod_timer(&priv->ucode_trace, jiffies);
}
/* download priority table before any calibration request */ if (priv->lib->bt_params &&
priv->lib->bt_params->advanced_bt_coexist) { /* Configure Bluetooth device coexistence support */ if (priv->lib->bt_params->bt_sco_disable)
priv->bt_enable_pspoll = false; else
priv->bt_enable_pspoll = true;
/* FIXME: w/a to force change uCode BT state machine */
ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret;
ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret;
} elseif (priv->lib->bt_params) { /* * default is 2-wire BT coexexistence support
*/
iwl_send_bt_config(priv);
}
/* * Perform runtime calibrations, including DC calibration.
*/
iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX);
ieee80211_wake_queues(priv->hw);
/* Configure Tx antenna selection based on H/W config */
iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant);
/** * iwl_clear_driver_stations - clear knowledge of all stations from driver * @priv: iwl priv struct * * This is called during iwl_down() to make sure that in the case * we're coming there from a hardware restart mac80211 will be * able to reconfigure stations -- if we're getting there in the * normal down flow then the stations will already be cleared.
*/ staticvoid iwl_clear_driver_stations(struct iwl_priv *priv)
{ struct iwl_rxon_context *ctx;
for_each_context(priv, ctx) { /* * Remove all key information that is not stored as part * of station information since mac80211 may not have had * a chance to remove all the keys. When device is * reconfigured by mac80211 after an error all keys will * be reconfigured.
*/
memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys));
ctx->key_mapping_keys = 0;
}
spin_unlock_bh(&priv->sta_lock);
}
void iwl_down(struct iwl_priv *priv)
{ int exit_pending;
IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
/* Wipe out the EXIT_PENDING status bit if we are not actually
* exiting the module */ if (!exit_pending)
clear_bit(STATUS_EXIT_PENDING, &priv->status);
if (priv->mac80211_registered)
ieee80211_stop_queues(priv->hw);
/* Set num_aux_in_flight must be done after the transport is stopped */
atomic_set(&priv->num_aux_in_flight, 0);
/* Clear out all status bits but a few that are stable across reset */
priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) <<
STATUS_RF_KILL_HW |
test_bit(STATUS_FW_ERROR, &priv->status) <<
STATUS_FW_ERROR |
test_bit(STATUS_EXIT_PENDING, &priv->status) <<
STATUS_EXIT_PENDING;
/* * __iwl_down() will clear the BT status variables, * which is correct, but when we restart we really * want to keep them so restore them afterwards. * * The restart process will later pick them up and * re-configure the hw when we reconfigure the BT * command.
*/
bt_full_concurrent = priv->bt_full_concurrent;
bt_ci_compliance = priv->bt_ci_compliance;
bt_load = priv->bt_traffic_load;
bt_status = priv->bt_status;
bt_is_sco = priv->bt_is_sco;
/* reset aggregation queues */ for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++)
priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; /* and stop counts */ for (i = 0; i < IWL_MAX_HW_QUEUES; i++)
atomic_set(&priv->queue_stop_count[i], 0);
staticvoid iwl_set_hw_params(struct iwl_priv *priv)
{ /* there are no devices with HT but without HT40 on all bands */ if (priv->cfg->ht_params.ht40_bands)
priv->hw_params.use_rts_for_aggregation =
priv->cfg->ht_params.use_rts_for_aggregation;
/* all HT devices also have HT40 on at least one band */ if (data->sku_cap_11n_enable &&
!priv->cfg->ht_params.ht40_bands) {
IWL_ERR(priv, "Invalid 11n configuration\n"); return -EINVAL;
}
switch (priv->trans->mac_cfg->device_family) { case IWL_DEVICE_FAMILY_1000: case IWL_DEVICE_FAMILY_100:
priv->lib = &iwl_dvm_1000_cfg; break; case IWL_DEVICE_FAMILY_2000:
priv->lib = &iwl_dvm_2000_cfg; break; case IWL_DEVICE_FAMILY_105:
priv->lib = &iwl_dvm_105_cfg; break; case IWL_DEVICE_FAMILY_2030: case IWL_DEVICE_FAMILY_135:
priv->lib = &iwl_dvm_2030_cfg; break; case IWL_DEVICE_FAMILY_5000:
priv->lib = &iwl_dvm_5000_cfg; break; case IWL_DEVICE_FAMILY_5150:
priv->lib = &iwl_dvm_5150_cfg; break; case IWL_DEVICE_FAMILY_6000: case IWL_DEVICE_FAMILY_6000i:
priv->lib = &iwl_dvm_6000_cfg; break; case IWL_DEVICE_FAMILY_6005:
priv->lib = &iwl_dvm_6005_cfg; break; case IWL_DEVICE_FAMILY_6050: case IWL_DEVICE_FAMILY_6150:
priv->lib = &iwl_dvm_6050_cfg; break; case IWL_DEVICE_FAMILY_6030:
priv->lib = &iwl_dvm_6030_cfg; break; default: break;
}
if (WARN_ON(!priv->lib)) {
err = -ENODEV; goto out_free_hw;
}
/* * Populate the state variables that the transport layer needs * to know about.
*/
BUILD_BUG_ON(sizeof(no_reclaim_cmds) > sizeof(trans->conf.no_reclaim_cmds));
memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds, sizeof(no_reclaim_cmds));
trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
switch (iwlwifi_mod_params.amsdu_size) { case IWL_AMSDU_DEF: case IWL_AMSDU_4K:
trans->conf.rx_buf_size = IWL_AMSDU_4K; break; case IWL_AMSDU_8K:
trans->conf.rx_buf_size = IWL_AMSDU_8K; break; case IWL_AMSDU_12K: default:
trans->conf.rx_buf_size = IWL_AMSDU_4K;
pr_err("Unsupported amsdu_size: %d\n",
iwlwifi_mod_params.amsdu_size);
}
if (!(priv->nvm_data->sku_cap_ipan_enable)) {
IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n");
ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; /* * if not PAN, then don't support P2P -- might be a uCode * packaging bug or due to the eeprom check above
*/
priv->sta_key_max_num = STA_KEY_MAX_NUM;
trans->conf.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
}
/******************* * 5. Setup priv
*******************/ for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; if (i < IWLAGN_FIRST_AMPDU_QUEUE &&
i != IWL_DEFAULT_CMD_QUEUE_NUM &&
i != IWL_IPAN_CMD_QUEUE_NUM)
priv->queue_to_mac80211[i] = i;
atomic_set(&priv->queue_stop_count[i], 0);
}
err = iwl_init_drv(priv); if (err) goto out_free_eeprom;
/* At this point both hw and priv are initialized. */
/* initialize all valid contexts */
iwl_init_context(priv, ucode_flags);
/************************************************** * This is still part of probe() in a sense... * * 7. Setup and register with mac80211 and debugfs
**************************************************/
err = iwlagn_mac_setup_register(priv, &fw->ucode_capa); if (err) goto out_destroy_workqueue;
/* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes * priv->workqueue... so we can't take down the workqueue
* until now... */
destroy_workqueue(priv->workqueue);
priv->workqueue = NULL;
base = priv->device_pointers.error_event_table; if (priv->cur_ucode == IWL_UCODE_INIT) { if (!base)
base = priv->fw->init_errlog_ptr;
} else { if (!base)
base = priv->fw->inst_errlog_ptr;
}
/* * iwl_print_event_log - Dump error event log to syslog
*/ staticint iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
u32 num_events, u32 mode, int pos, char **buf, size_t bufsz)
{
u32 i;
u32 base; /* SRAM byte address of event log header */
u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
u32 ptr; /* SRAM byte address of log data */
u32 ev, time, data; /* event log data */
struct iwl_trans *trans = priv->trans;
if (num_events == 0) return pos;
base = priv->device_pointers.log_event_table; if (priv->cur_ucode == IWL_UCODE_INIT) { if (!base)
base = priv->fw->init_evtlog_ptr;
} else { if (!base)
base = priv->fw->inst_evtlog_ptr;
}
ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
/* Make sure device is powered up for SRAM reads */ if (!iwl_trans_grab_nic_access(trans)) return pos;
/* Set starting address; reads will auto-increment */
iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
/* "time" is actually "data" for mode 0 (no timestamp).
* place event id # at far right for easier visual parsing. */ for (i = 0; i < num_events; i++) {
ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
time = iwl_read32(trans, HBUS_TARG_MEM_RDAT); if (mode == 0) { /* data, ev */ if (bufsz) {
pos += scnprintf(*buf + pos, bufsz - pos, "EVT_LOG:0x%08x:%04u\n",
time, ev);
} else {
trace_iwlwifi_dev_ucode_event(trans->dev, 0,
time, ev);
IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
time, ev);
}
} else {
data = iwl_read32(trans, HBUS_TARG_MEM_RDAT); if (bufsz) {
pos += scnprintf(*buf + pos, bufsz - pos, "EVT_LOGT:%010u:0x%08x:%04u\n",
time, data, ev);
} else {
IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
time, data, ev);
trace_iwlwifi_dev_ucode_event(trans->dev, time,
data, ev);
}
}
}
/* Allow device to power down */
iwl_trans_release_nic_access(trans); return pos;
}
/* * iwl_print_last_event_logs - Dump the newest # of event log to syslog
*/ staticint iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
u32 num_wraps, u32 next_entry,
u32 size, u32 mode, int pos, char **buf, size_t bufsz)
{ /* * display the newest DEFAULT_LOG_ENTRIES entries * i.e the entries just before the next ont that uCode would fill.
*/ if (num_wraps) { if (next_entry < size) {
pos = iwl_print_event_log(priv,
capacity - (size - next_entry),
size - next_entry, mode,
pos, buf, bufsz);
pos = iwl_print_event_log(priv, 0,
next_entry, mode,
pos, buf, bufsz);
} else
pos = iwl_print_event_log(priv, next_entry - size,
size, mode, pos, buf, bufsz);
} else { if (next_entry < size) {
pos = iwl_print_event_log(priv, 0, next_entry,
mode, pos, buf, bufsz);
} else {
pos = iwl_print_event_log(priv, next_entry - size,
size, mode, pos, buf, bufsz);
}
} return pos;
}
#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, char **buf)
{
u32 base; /* SRAM byte address of event log header */
u32 capacity; /* event log capacity in # entries */
u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
u32 num_wraps; /* # times uCode wrapped to top of log */
u32 next_entry; /* index of next entry to be written by uCode */
u32 size; /* # entries that we'll print */
u32 logsize; int pos = 0;
size_t bufsz = 0; struct iwl_trans *trans = priv->trans;
base = priv->device_pointers.log_event_table; if (priv->cur_ucode == IWL_UCODE_INIT) {
logsize = priv->fw->init_evtlog_size; if (!base)
base = priv->fw->init_evtlog_ptr;
} else {
logsize = priv->fw->inst_evtlog_size; if (!base)
base = priv->fw->inst_evtlog_ptr;
}
/* uCode is no longer loaded. */
priv->ucode_loaded = false;
/* Keep the restart process from trying to send host
* commands by clearing the ready bit */
clear_bit(STATUS_READY, &priv->status);
if (!ondemand) { /* * If firmware keep reloading, then it indicate something * serious wrong and firmware having problem to recover * from it. Instead of keep trying which will fill the syslog * and hang the system, let's just stop it
*/
reload_jiffies = jiffies;
reload_msec = jiffies_to_msecs((long) reload_jiffies -
(long) priv->reload_jiffies);
priv->reload_jiffies = reload_jiffies; if (reload_msec <= IWL_MIN_RELOAD_DURATION) {
priv->reload_count++; if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) {
IWL_ERR(priv, "BUG_ON, Stop restarting\n"); return;
}
} else
priv->reload_count = 0;
}
if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { if (iwlwifi_mod_params.fw_restart) {
IWL_DEBUG_FW(priv, "Restarting adapter due to uCode error.\n");
queue_work(priv->workqueue, &priv->restart);
} else
IWL_DEBUG_FW(priv, "Detected FW error, but not restarting\n");
}
}
/* set CSR_HW_CONFIG_REG for uCode use */
iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
/* W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), * causing ME FW to lose ownership and not being able to obtain it back.
*/
iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG,
APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
if (priv->lib->nic_config)
priv->lib->nic_config(priv);
}
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.