/* * Will return 0 even if the cmd failed when RFKILL is asserted unless * CMD_WANT_SKB is set in cmd->flags.
*/ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
{ int ret;
#ifdefined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) if (WARN_ON(mvm->d3_test_active)) return -EIO; #endif
/* * Synchronous commands from this op-mode must hold * the mutex, this ensures we don't try to send two * (or more) synchronous commands at a time.
*/ if (!(cmd->flags & CMD_ASYNC))
lockdep_assert_held(&mvm->mutex);
ret = iwl_trans_send_cmd(mvm->trans, cmd);
/* * If the caller wants the SKB, then don't hide any problems, the * caller might access the response buffer which will be NULL if * the command failed.
*/ if (cmd->flags & CMD_WANT_SKB) return ret;
/* * Silently ignore failures if RFKILL is asserted or * we are in suspend\resume process
*/ if (!ret || ret == -ERFKILL || ret == -EHOSTDOWN) return 0; return ret;
}
/* * We assume that the caller set the status to the success value
*/ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
u32 *status)
{ struct iwl_rx_packet *pkt; struct iwl_cmd_response *resp; int ret, resp_len;
lockdep_assert_held(&mvm->mutex);
#ifdefined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) if (WARN_ON(mvm->d3_test_active)) return -EIO; #endif
/* * Only synchronous commands can wait for status, * we use WANT_SKB so the caller can't.
*/ if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB), "cmd flags %x", cmd->flags)) return -EINVAL;
cmd->flags |= CMD_WANT_SKB;
ret = iwl_trans_send_cmd(mvm->trans, cmd); if (ret == -ERFKILL) { /* * The command failed because of RFKILL, don't update * the status, leave it as success and return 0.
*/ return 0;
} elseif (ret) { return ret;
}
pkt = cmd->resp_pkt;
resp_len = iwl_rx_packet_payload_len(pkt); if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
ret = -EIO; goto out_free_resp;
}
/* * We assume that the caller set the status to the sucess value
*/ int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, constvoid *data, u32 *status)
{ struct iwl_host_cmd cmd = {
.id = id,
.len = { len, },
.data = { data, },
};
int iwl_mvm_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band)
{ int format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; bool is_LB = band == NL80211_BAND_2GHZ;
/* CCK is not allowed in HB */ return is_LB ? rate : -1;
}
int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band)
{ int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1; int idx; int band_offset = 0;
/* Legacy rate format, search for match in table */ if (band != NL80211_BAND_2GHZ)
band_offset = IWL_FIRST_OFDM_RATE; for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) if (iwl_fw_rate_idx_to_plcp(idx) == rate) return idx - band_offset;
return -1;
}
u8 iwl_mvm_mac80211_idx_to_hwrate(conststruct iwl_fw *fw, int rate_idx)
{ if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) /* In the new rate legacy rates are indexed: * 0 - 3 for CCK and 0 - 7 for OFDM.
*/ return (rate_idx >= IWL_FIRST_OFDM_RATE ?
rate_idx - IWL_FIRST_OFDM_RATE :
rate_idx);
/* * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h. * The parameter should also be a combination of ANT_[ABC].
*/
u8 first_antenna(u8 mask)
{
BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */ if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */ return BIT(0); return BIT(ffs(mask) - 1);
}
#define MAX_ANT_NUM 2 /* * Toggles between TX antennas to send the probe request on. * Receives the bitmask of valid TX antennas and the *index* used * for the last TX, and returns the next valid *index* to use. * In order to set it in the tx_cmd, must do BIT(idx).
*/
u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
{
u8 ind = last_idx; int i;
for (i = 0; i < MAX_ANT_NUM; i++) {
ind = (ind + 1) % MAX_ANT_NUM; if (valid & BIT(ind)) return ind;
}
WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid); return last_idx;
}
/** * iwl_mvm_send_lq_cmd() - Send link quality command * @mvm: Driver data. * @lq: Link quality command to send. * * The link quality command is sent as the last step of station creation. * This is the special case in which init is set and we call a callback in * this case to clear the state indicating that station creation is in * progress. * * Returns: an error code indicating success or failure
*/ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq)
{ struct iwl_host_cmd cmd = {
.id = LQ_CMD,
.len = { sizeof(struct iwl_lq_cmd), },
.flags = CMD_ASYNC,
.data = { lq, },
};
if (WARN_ON(lq->sta_id == IWL_INVALID_STA ||
iwl_mvm_has_tlc_offload(mvm))) return -EINVAL;
return iwl_mvm_send_cmd(mvm, &cmd);
}
/** * iwl_mvm_update_smps - Get a request to change the SMPS mode * @mvm: Driver data. * @vif: Pointer to the ieee80211_vif structure * @req_type: The part of the driver who call for a change. * @smps_request: The request to change the SMPS mode. * @link_id: for MLO link_id, otherwise 0 (deflink) * * Get a requst to change the SMPS mode, * and change it according to all other requests in the driver.
*/ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum iwl_mvm_smps_type_request req_type, enum ieee80211_smps_mode smps_request, unsignedint link_id)
{ struct iwl_mvm_vif *mvmvif; enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; int i;
lockdep_assert_held(&mvm->mutex);
/* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) return;
if (vif->type != NL80211_IFTYPE_STATION) return;
/* SMPS is handled by firmware */ if (iwl_mvm_has_rlc_offload(mvm)) return;
mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (WARN_ON_ONCE(!mvmvif->link[link_id])) return;
mvmvif->link[link_id]->smps_requests[req_type] = smps_request; for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { if (mvmvif->link[link_id]->smps_requests[i] ==
IEEE80211_SMPS_STATIC) {
smps_mode = IEEE80211_SMPS_STATIC; break;
} if (mvmvif->link[link_id]->smps_requests[i] ==
IEEE80211_SMPS_DYNAMIC)
smps_mode = IEEE80211_SMPS_DYNAMIC;
}
/* SMPS is disabled in eSR */ if (mvmvif->esr_active)
smps_mode = IEEE80211_SMPS_OFF;
mvm->statistics_clear = clear;
ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret) {
iwl_remove_notification(&mvm->notif_wait, &stats_wait); return ret;
}
/* 500ms for OPERATIONAL, PART1 and END notification should be enough * for FW to collect data from all LMACs and send * STATISTICS_NOTIFICATION to host
*/
ret = iwl_wait_notification(&mvm->notif_wait, &stats_wait, HZ / 2); if (ret) return ret;
/* * Don't request statistics during restart, they'll not have any useful * information right after restart, nor is clearing needed
*/ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) return 0;
if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN) return iwl_mvm_request_system_statistics(mvm, clear, cmd_ver);
/* From version 15 - STATISTICS_NOTIFICATION, the reply for * STATISTICS_CMD is empty, and the response is with * STATISTICS_NOTIFICATION notification
*/ if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
STATISTICS_NOTIFICATION, 0) < 15) {
cmd.flags = CMD_WANT_SKB;
ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret) return ret;
ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret) {
iwl_remove_notification(&mvm->notif_wait, &stats_wait); return ret;
}
/* 200ms should be enough for FW to collect data from all * LMACs and send STATISTICS_NOTIFICATION to host
*/
ret = iwl_wait_notification(&mvm->notif_wait, &stats_wait, HZ / 5); if (ret) return ret;
}
/* * We can't know when the station is asleep or awake, so we * must disable the queue hang detection.
*/ if (fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_STA_PM_NOTIF) &&
vif->type == NL80211_IFTYPE_AP) return IWL_WATCHDOG_DISABLED; return default_timeout;
}
if (mvm->tcm.data[mvmvif->id].opened_rx_ba_sessions) return;
/* remember that this AP is broken */
memcpy(mvm->uapsd_noagg_bssids[mvm->uapsd_noagg_bssid_write_idx].addr,
vif->bss_conf.bssid, ETH_ALEN);
mvm->uapsd_noagg_bssid_write_idx++; if (mvm->uapsd_noagg_bssid_write_idx >= IWL_MVM_UAPSD_NOAGG_LIST_LEN)
mvm->uapsd_noagg_bssid_write_idx = 0;
iwl_mvm_connection_loss(mvm, vif, "AP isn't using AMPDU with uAPSD enabled");
}
if (!mvmvif->deflink.queue_params[IEEE80211_AC_VO].uapsd &&
!mvmvif->deflink.queue_params[IEEE80211_AC_VI].uapsd &&
!mvmvif->deflink.queue_params[IEEE80211_AC_BE].uapsd &&
!mvmvif->deflink.queue_params[IEEE80211_AC_BK].uapsd) return;
if (mvm->tcm.data[mvmvif->id].uapsd_nonagg_detect.detected) return;
mvm->tcm.data[mvmvif->id].uapsd_nonagg_detect.detected = true;
IWL_INFO(mvm, "detected AP should do aggregation but isn't, likely due to U-APSD\n");
schedule_delayed_work(&mvmvif->uapsd_nonagg_detected_wk,
15 * HZ);
}
for (i = 0; i < NUM_NL80211_BANDS; i++) {
band_load = iwl_mvm_tcm_load(mvm, band_airtime[i], elapsed);
mvm->tcm.result.band_load[i] = band_load;
}
/* * If the current load isn't low we need to force re-evaluation * in the TCM period, so that we can return to low load if there * was no traffic at all (and thus iwl_mvm_recalc_tcm didn't get * triggered by traffic).
*/ if (load != IWL_MVM_TRAFFIC_LOW) return MVM_TCM_PERIOD; /* * If low-latency is active we need to force re-evaluation after * (the longer) MVM_LL_PERIOD, so that we can disable low-latency * when there's no traffic at all.
*/ if (low_latency) return MVM_LL_PERIOD; /* * Otherwise, we don't need to run the work struct because we're * in the default "idle" state - traffic indication is low (which * also covers the "no traffic" case) and low-latency is disabled * so there's no state that may need to be disabled when there's * no traffic at all. * * Note that this has no impact on the regular scheduling of the * updates triggered by traffic - those happen whenever one of the * two timeouts expire (if there's traffic at all.)
*/ return 0;
}
if (handle_uapsd && iwl_mvm_has_new_rx_api(mvm)) {
guard(mvm)(mvm); if (iwl_mvm_request_statistics(mvm, true))
handle_uapsd = false;
}
spin_lock(&mvm->tcm.lock); /* re-check if somebody else won the recheck race */ if (!mvm->tcm.paused && time_after(ts, mvm->tcm.ts + MVM_TCM_PERIOD)) { /* calculate statistics */ unsignedlong work_delay = iwl_mvm_calc_tcm_stats(mvm, ts,
handle_uapsd);
/* the memset needs to be visible before the timestamp */
smp_mb();
mvm->tcm.ts = ts; if (work_delay)
schedule_delayed_work(&mvm->tcm.work, work_delay);
}
spin_unlock(&mvm->tcm.lock);
if (mvm->tcm.result.low_latency[mac])
low_latency = true;
} /* The TCM data needs to be reset before "paused" flag changes */
smp_mb();
mvm->tcm.paused = false;
/* * if the current load is not low or low latency is active, force * re-evaluation to cover the case of no traffic.
*/ if (mvm->tcm.result.global_load > IWL_MVM_TRAFFIC_LOW)
schedule_delayed_work(&mvm->tcm.work, MVM_TCM_PERIOD); elseif (low_latency)
schedule_delayed_work(&mvm->tcm.work, MVM_LL_PERIOD);
/* Disable power save when reading GP2 */
ps_disabled = mvm->ps_disabled; if (!ps_disabled) {
mvm->ps_disabled = true;
iwl_mvm_power_update_device(mvm);
}
if (!ps_disabled) {
mvm->ps_disabled = ps_disabled;
iwl_mvm_power_update_device(mvm);
}
}
/* Find if at least two links from different vifs use same channel * FIXME: consider having a refcount array in struct iwl_mvm_vif for * used phy_ctxt ids.
*/ bool iwl_mvm_have_links_same_channel(struct iwl_mvm_vif *vif1, struct iwl_mvm_vif *vif2)
{ unsignedint i, j;
/* FIXME: can it fail when phy_ctxt is assigned? */
for_each_mvm_vif_valid_link(mvmvif, i) { if (mvmvif->link[i]->phy_ctxt &&
mvmvif->link[i]->phy_ctxt->id < NUM_PHY_CTX) returntrue;
}
returnfalse;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.4 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.