/* * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler * * Copies the phy information in mvm->last_phy_info, it will be used when the * actual data will come from the fw in the next packet.
*/ void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
{ struct iwl_rx_packet *pkt = rxb_addr(rxb); unsignedint pkt_len = iwl_rx_packet_payload_len(pkt);
if (unlikely(pkt_len < sizeof(mvm->last_phy_info))) return;
/* * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211 * * Adds the rxb to a new skb and give it to mac80211
*/ staticvoid iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct napi_struct *napi, struct sk_buff *skb, struct ieee80211_hdr *hdr, u16 len,
u8 crypt_len, struct iwl_rx_cmd_buffer *rxb)
{ unsignedint hdrlen = ieee80211_hdrlen(hdr->frame_control); unsignedint fraglen;
/* * The 'hdrlen' (plus the 8 bytes for the SNAP and the crypt_len, * but those are all multiples of 4 long) all goes away, but we * want the *end* of it, which is going to be the start of the IP * header, to be aligned when it gets pulled in. * The beginning of the skb->data is aligned on at least a 4-byte * boundary after allocation. Everything here is aligned at least * on a 2-byte boundary so we can just take hdrlen & 3 and pad by * the result.
*/
skb_reserve(skb, hdrlen & 3);
/* If frame is small enough to fit in skb->head, pull it completely. * If not, only pull ieee80211_hdr (including crypto if present, and * an additional 8 bytes for SNAP/ethertype, see below) so that * splice() or TCP coalesce are more efficient. * * Since, in addition, ieee80211_data_to_8023() always pull in at * least 8 bytes (possibly more for mesh) we can do the same here * to save the cost of doing it later. That still doesn't pull in * the actual IP header since the typical case has a SNAP header. * If the latter changes (there are efforts in the standards group * to do so) we should revisit this and ieee80211_data_to_8023().
*/
hdrlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8;
skb_put_data(skb, hdr, hdrlen);
fraglen = len - hdrlen;
if (fraglen) { int offset = (u8 *)hdr + hdrlen -
(u8 *)rxb_addr(rxb) + rxb_offset(rxb);
/* * iwl_mvm_get_signal_strength - use new rx PHY INFO API * values are reported by the fw as positive values - need to negate * to obtain their dBM. Account for missing antennas by replacing 0 * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
*/ staticvoid iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, struct iwl_rx_phy_info *phy_info, struct ieee80211_rx_status *rx_status)
{ int energy_a, energy_b, max_energy;
u32 val;
/* * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format * @mvm: the mvm object * @hdr: 80211 header * @stats: status in mac80211's format * @rx_pkt_status: status coming from fw * * returns non 0 value if the packet should be dropped
*/ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *stats,
u32 rx_pkt_status,
u8 *crypt_len)
{ if (!ieee80211_has_protected(hdr->frame_control) ||
(rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
RX_MPDU_RES_STATUS_SEC_NO_ENC) return 0;
/* packet was encrypted with unknown alg */ if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
RX_MPDU_RES_STATUS_SEC_ENC_ERR) return 0;
switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) { case RX_MPDU_RES_STATUS_SEC_CCM_ENC: /* alg is CCM: check MIC only */ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) return -1;
case RX_MPDU_RES_STATUS_SEC_TKIP_ENC: /* Don't drop the frame and decrypt it in SW */ if (!fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_DEPRECATE_TTAK) &&
!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK)) return 0;
*crypt_len = IEEE80211_TKIP_IV_LEN;
fallthrough;
case RX_MPDU_RES_STATUS_SEC_WEP_ENC: if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK)) return -1;
/* count the airtime only once for each ampdu */ if (mdata->rx.last_ampdu_ref != mvm->ampdu_ref) {
mdata->rx.last_ampdu_ref = mvm->ampdu_ref;
mdata->rx.airtime += le16_to_cpu(phy_info->frame_time);
}
if (!(rate_n_flags & (RATE_MCS_HT_MSK_V1 | RATE_MCS_VHT_MSK_V1))) return;
/* Dont use dev_alloc_skb(), we'll have enough headroom once * ieee80211_hdr pulled.
*/
skb = alloc_skb(128, GFP_ATOMIC); if (!skb) {
IWL_ERR(mvm, "alloc_skb failed\n"); return;
}
rx_status = IEEE80211_SKB_RXCB(skb);
/* * Keep packets with CRC errors (and with overrun) for monitor mode * (otherwise the firmware discards them) but mark them as bad.
*/ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
!(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
}
/* This will be used in several places later */
rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
/* rx_status carries information about the packet to mac80211 */
rx_status->mactime = le64_to_cpu(phy_info->timestamp);
rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
rx_status->band =
(phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
rx_status->freq =
ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
rx_status->band);
/* TSF as indicated by the firmware is at INA time */
rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
rcu_read_lock(); if (rx_pkt_status & RX_MPDU_RES_STATUS_SRC_STA_FOUND) {
u32 id = rx_pkt_status & RX_MPDU_RES_STATUS_STA_ID_MSK;
id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT;
if (!WARN_ON_ONCE(id >= mvm->fw->ucode_capa.num_stations)) {
sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); if (IS_ERR(sta))
sta = NULL;
}
} elseif (!is_multicast_ether_addr(hdr->addr2)) { /* This is fine since we prevent two stations with the same * address from being added.
*/
sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
}
/* * Don't even try to decrypt a MCAST frame that was received * before the managed vif is authorized, we'd fail anyway.
*/ if (is_multicast_ether_addr(hdr->addr1) &&
vif->type == NL80211_IFTYPE_STATION &&
!mvmvif->authorized &&
ieee80211_has_protected(hdr->frame_control)) {
IWL_DEBUG_DROP(mvm, "MCAST before the vif is authorized\n");
kfree_skb(skb);
rcu_read_unlock(); return;
}
}
/* * drop the packet if it has failed being decrypted by HW
*/ if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
&crypt_len)) {
IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
rx_pkt_status);
kfree_skb(skb);
rcu_read_unlock(); return;
}
/* We have tx blocked stations (with CS bit). If we heard * frames from a blocked station on a new channel we can * TX to it again.
*/ if (unlikely(tx_blocked_vif) && vif == tx_blocked_vif) { struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(tx_blocked_vif);
if (mvmvif->csa_target_freq == rx_status->freq)
iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
}
if (rx_status->signal < rssi)
iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
NULL);
}
if (!mvm->tcm.paused && len >= sizeof(*hdr) &&
!is_multicast_ether_addr(hdr->addr1) &&
ieee80211_is_data(hdr->frame_control))
iwl_mvm_rx_handle_tcm(mvm, sta, hdr, len, phy_info,
rate_n_flags);
if (ieee80211_is_data(hdr->frame_control))
iwl_mvm_rx_csum(sta, skb, rx_pkt_status);
}
rcu_read_unlock();
/* set the preamble flag if appropriate */ if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { /* * We know which subframes of an A-MPDU belong * together since we get a single PHY response * from the firmware for all of them
*/
rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
rx_status->ampdu_reference = mvm->ampdu_ref;
}
/* ESR recalculation */ if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) return;
/* We're not in EMLSR and our signal is bad, try to switch link maybe */ if (sig < IWL_MVM_LOW_RSSI_MLO_SCAN_THRESH && !mvmvif->esr_active) {
iwl_mvm_int_mlo_scan(mvm, vif); return;
}
/* We are in EMLSR, check if we need to exit */
exit_esr_thresh =
iwl_mvm_get_esr_rssi_thresh(mvm,
&bss_conf->chanreq.oper, true);
if (sig < exit_esr_thresh)
iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_LOW_RSSI,
iwl_mvm_get_other_link(vif,
bss_conf->link_id));
}
/* This doesn't need the MAC ID check since it's not taking the * data copied into the "data" struct, but rather the data from * the notification directly.
*/
mvmvif->deflink.beacon_stats.num_beacons =
le32_to_cpu(data->beacon_counter[vif_id]);
mvmvif->deflink.beacon_stats.avg_signal =
-data->beacon_average_energy[vif_id];
if (mvmvif->id != id) return;
if (vif->type != NL80211_IFTYPE_STATION) return;
/* make sure that beacon statistics don't go backwards with TCM * request to clear statistics
*/ if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
mvmvif->deflink.beacon_stats.accu_num_beacons +=
mvmvif->deflink.beacon_stats.num_beacons;
/* This is used in pre-MLO API so use deflink */
iwl_mvm_update_link_sig(vif, sig, &mvmvif->deflink, &vif->bss_conf);
}
/* make sure that beacon statistics don't go backwards with TCM * request to clear statistics
*/ if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
mvmvif->deflink.beacon_stats.accu_num_beacons +=
mvmvif->deflink.beacon_stats.num_beacons;
sig = -le32_to_cpu(mac_stats->beacon_filter_average_energy);
/* This is used in pre-MLO API so use deflink */
iwl_mvm_update_link_sig(vif, sig, &mvmvif->deflink, &vif->bss_conf);
}
spin_lock(&mvm->tcm.lock); for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) { struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[i];
u32 rx_bytes = le32_to_cpu(rx_bytes_le[i]);
u32 airtime = le32_to_cpu(air_time_le[i]);
mdata->rx.airtime += airtime;
mdata->uapsd_nonagg_detect.rx_bytes += rx_bytes; if (airtime) { /* re-init every time to store rate from FW */
ewma_rate_init(&mdata->uapsd_nonagg_detect.rate);
ewma_rate_add(&mdata->uapsd_nonagg_detect.rate,
rx_bytes * 8 / airtime);
}
}
spin_unlock(&mvm->tcm.lock);
}
staticvoid iwl_mvm_handle_per_phy_stats(struct iwl_mvm *mvm, struct iwl_stats_ntfy_per_phy *per_phy)
{ int i;
for (i = 0; i < NUM_PHY_CTX; i++) { if (!mvm->phy_ctxts[i].ref) continue;
mvm->phy_ctxts[i].channel_load_by_us =
le32_to_cpu(per_phy[i].channel_load_by_us);
mvm->phy_ctxts[i].channel_load_not_by_us =
le32_to_cpu(per_phy[i].channel_load_not_by_us);
}
}
/* driver uses link ID == MAC ID */ for (fw_link_id = 0; fw_link_id < ARRAY_SIZE(mvm->vif_id_to_mac);
fw_link_id++) { struct iwl_stats_ntfy_per_link *link_stats; struct iwl_mvm_vif_link_info *link_info; struct iwl_mvm_vif *mvmvif; struct ieee80211_vif *vif; int link_id; int sig;
vif = iwl_mvm_rcu_dereference_vif_id(mvm, fw_link_id, false); if (!vif) continue;
if (vif->type != NL80211_IFTYPE_STATION) continue;
link_id = vif->bss_conf.link_id; if (link_id >= ARRAY_SIZE(mvmvif->link)) continue;
mvmvif = iwl_mvm_vif_from_mac80211(vif);
link_info = mvmvif->link[link_id]; if (!link_info) continue;
/* we basically just use the u8 to store 8 bits and then treat * it as a s8 whenever we take it out to a different type.
*/
link_info->beacon_stats.avg_signal =
-le32_to_cpu(link_stats->beacon_average_energy);
if (link_info->phy_ctxt &&
link_info->phy_ctxt->channel->band == NL80211_BAND_2GHZ)
iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id);
/* make sure that beacon statistics don't go backwards with TCM * request to clear statistics
*/ if (mvm->statistics_clear)
mvmvif->link[link_id]->beacon_stats.accu_num_beacons +=
mvmvif->link[link_id]->beacon_stats.num_beacons;
sig = -le32_to_cpu(link_stats->beacon_filter_average_energy);
iwl_mvm_update_link_sig(vif, sig, link_info, &vif->bss_conf);
if (WARN_ONCE(mvmvif->id >= MAC_INDEX_AUX, "invalid mvmvif id: %d", mvmvif->id)) continue;
/* Don't update in case the statistics are not cleared, since * we will end up counting twice the same airtime, once in TCM * request and once in statistics notification.
*/ if (mvm->statistics_clear) {
__le32 air_time_le[MAC_INDEX_AUX];
__le32 rx_bytes_le[MAC_INDEX_AUX]; int vif_id;
if (!mvmvif->esr_active || !mvmvif->ap_sta) return;
mvmsta = iwl_mvm_sta_from_mac80211(mvmvif->ap_sta); /* We only count for the AP sta in a MLO connection */ if (!mvmsta->mpdu_counters) return;
/* Get the FW ID of the secondary link */
sec_link = iwl_mvm_get_other_link(bss_vif,
iwl_mvm_get_primary_link(bss_vif)); if (WARN_ON(!mvmvif->link[sec_link])) return;
sec_link = mvmvif->link[sec_link]->fw_link_id;
/* Sum up RX and TX MPDUs from the different queues/links */ for (int q = 0; q < mvm->trans->info.num_rxqs; q++) {
spin_lock_bh(&mvmsta->mpdu_counters[q].lock);
/* The link IDs that doesn't exist will contain 0 */ for (int link = 0; link < IWL_FW_MAX_LINK_ID; link++) {
total_tx += mvmsta->mpdu_counters[q].per_link[link].tx;
total_rx += mvmsta->mpdu_counters[q].per_link[link].rx;
}
/* * In EMLSR we have statistics every 5 seconds, so we can reset * the counters upon every statistics notification. * The FW sends the notification regularly, but it will be * misaligned at the start. Skipping the measurement if it is * short will synchronize us.
*/ if (jiffies - mvmsta->mpdu_counters[q].window_start <
IWL_MVM_TPT_MIN_COUNT_WINDOW)
skip = true;
mvmsta->mpdu_counters[q].window_start = jiffies;
memset(mvmsta->mpdu_counters[q].per_link, 0, sizeof(mvmsta->mpdu_counters[q].per_link));
spin_unlock_bh(&mvmsta->mpdu_counters[q].lock);
}
if (skip) {
IWL_DEBUG_INFO(mvm, "MPDU statistics window was short\n"); return;
}
/* Calculate the percentage of the secondary link TX/RX */
sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0;
sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0;
/* * The TX/RX percentage is checked only if it exceeds the required * minimum. In addition, RX is checked only if the TX check failed.
*/ if ((total_tx > SEC_LINK_MIN_TX &&
sec_link_tx_perc < SEC_LINK_MIN_PERC) ||
(total_rx > SEC_LINK_MIN_RX &&
sec_link_rx_perc < SEC_LINK_MIN_PERC))
iwl_mvm_exit_esr(mvm, bss_vif, IWL_MVM_ESR_EXIT_LINK_USAGE,
iwl_mvm_get_primary_link(bss_vif));
}
for (i = 0; i < ARRAY_SIZE(average_energy); i++)
average_energy[i] =
le32_to_cpu(stats->per_sta[i].average_energy);
for (i = 0; i < ARRAY_SIZE(air_time); i++) {
air_time[i] = stats->per_mac[i].air_time;
rx_bytes[i] = stats->per_mac[i].rx_bytes;
}
}
iwl_mvm_rx_stats_check_trigger(mvm, pkt);
ieee80211_iterate_stations_atomic(mvm->hw, iwl_mvm_stats_energy_iter,
average_energy); /* * Don't update in case the statistics are not cleared, since * we will end up counting twice the same airtime, once in TCM * request and once in statistics notification.
*/ if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
iwl_mvm_update_tcm_from_stats(mvm, air_time, rx_bytes);
}
/* From ver 14 and up we use TLV statistics format */ if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
STATISTICS_NOTIFICATION, 0) >= 14) return iwl_mvm_handle_rx_statistics_tlv(mvm, pkt);
/* * Don't update in case the statistics are not cleared, since * we will end up counting twice the same airtime, once in TCM * request and once in statistics notification.
*/ if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
iwl_mvm_update_tcm_from_stats(mvm, air_time, bytes);
rcu_read_lock(); for (i = 0; i < BA_WINDOW_STREAMS_MAX; i++) { struct ieee80211_sta *sta;
u8 sta_id, tid;
u64 bitmap;
u32 ssn;
u16 ratid;
u16 received_mpdu;
ratid = le16_to_cpu(notif->ra_tid[i]); /* check that this TID is valid */ if (!(ratid & BA_WINDOW_STATUS_VALID_MSK)) continue;
received_mpdu = le16_to_cpu(notif->mpdu_rx_count[i]); if (received_mpdu == 0) continue;
tid = ratid & BA_WINDOW_STATUS_TID_MSK; /* get the station */
sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK)
>> BA_WINDOW_STATUS_STA_ID_POS;
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); if (IS_ERR_OR_NULL(sta)) continue;
bitmap = le64_to_cpu(notif->bitmap[i]);
ssn = le32_to_cpu(notif->start_seq_num[i]);
/* update mac80211 with the bitmap for the reordering buffer */
ieee80211_mark_rx_ba_filtered_frames(sta, tid, ssn, bitmap,
received_mpdu);
}
rcu_read_unlock();
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.18 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.