/* This function should only be used with the wiphy lock held, * In other cases, it is not guaranteed that the link_sta will exist * in the driver too, and it is checked here.
*/
lockdep_assert_wiphy(mld->wiphy);
/* This is not meant to be called with a NULL pointer */ if (WARN_ON(!link_sta)) return -ENOENT;
mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); if (!mld_link_sta) {
WARN_ON(!iwl_mld_error_before_recovery(mld)); return -ENOENT;
}
/* Note that we always use only legacy & highest supported PPDUs, so * of Draft P802.11be D.30 Table 10-12a--Fields used for calculating * the maximum A-MPDU size of various PPDU types in different bands, * we only need to worry about the highest supported PPDU type here.
*/
if (link_sta->ht_cap.ht_supported) {
agg_size = link_sta->ht_cap.ampdu_factor;
mpdu_dens = link_sta->ht_cap.ampdu_density;
}
if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { /* overwrite HT values on 6 GHz */
mpdu_dens =
le16_get_bits(link_sta->he_6ghz_capa.capa,
IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
agg_size =
le16_get_bits(link_sta->he_6ghz_capa.capa,
IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
} elseif (link_sta->vht_cap.vht_supported) { /* if VHT supported overwrite HT value */
agg_size =
u32_get_bits(link_sta->vht_cap.cap,
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
}
/* D6.0 10.12.2 A-MPDU length limit rules * A STA indicates the maximum length of the A-MPDU preEOF padding * that it can receive in an HE PPDU in the Maximum A-MPDU Length * Exponent field in its HT Capabilities, VHT Capabilities, * and HE 6 GHz Band Capabilities elements (if present) and the * Maximum AMPDU Length Exponent Extension field in its HE * Capabilities element
*/ if (link_sta->he_cap.has_he)
agg_size +=
u8_get_bits(link_sta->he_cap.he_cap_elem.mac_cap_info[3],
IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK);
if (link_sta->eht_cap.has_eht)
agg_size +=
u8_get_bits(link_sta->eht_cap.eht_cap_elem.mac_cap_info[1],
IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK);
/* Limit to max A-MPDU supported by FW */
agg_size = min_t(u32, agg_size,
STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT);
/* If bit_num > 5, we have to combine bits with next byte. * Calculate how many bits we need to take from current byte (called * here "residue_bits"), and add them to bits from next byte.
*/
staticvoid iwl_mld_parse_ppe(struct iwl_mld *mld, struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss,
u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit, bool inheritance)
{ /* FW currently supports only nss == MAX_HE_SUPP_NSS * * If nss > MAX: we can ignore values we don't support * If nss < MAX: we can set zeros in other streams
*/ if (nss > MAX_HE_SUPP_NSS) {
IWL_DEBUG_INFO(mld, "Got NSS = %d - trimming to %d\n", nss,
MAX_HE_SUPP_NSS);
nss = MAX_HE_SUPP_NSS;
}
for (int i = 0; i < nss; i++) {
u8 ru_index_tmp = ru_index_bitmap << 1;
u8 low_th = IWL_HE_PKT_EXT_NONE, high_th = IWL_HE_PKT_EXT_NONE;
/* According to the 11be spec, if for a specific BW the PPE Thresholds * isn't present - it should inherit the thresholds from the last * BW for which we had PPE Thresholds. In 11ax though, we don't have * this inheritance - continue in this case
*/ if (!(ru_index_tmp & 1)) { if (inheritance) goto set_thresholds; else continue;
}
staticint
iwl_mld_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext,
u8 nominal_padding)
{ int low_th = -1; int high_th = -1;
/* all the macros are the same for EHT and HE */ switch (nominal_padding) { case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US:
low_th = IWL_HE_PKT_EXT_NONE;
high_th = IWL_HE_PKT_EXT_NONE; break; case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US:
low_th = IWL_HE_PKT_EXT_BPSK;
high_th = IWL_HE_PKT_EXT_NONE; break; case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US: case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US:
low_th = IWL_HE_PKT_EXT_NONE;
high_th = IWL_HE_PKT_EXT_BPSK; break;
}
if (low_th < 0 || high_th < 0) return -EINVAL;
/* Set the PPE thresholds accordingly */ for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { for (u8 bw = 0;
bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
bw++) {
pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th;
pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;
}
}
return 0;
}
staticvoid iwl_mld_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext,
u8 nominal_padding)
{ for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { for (u8 bw = 0; bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]);
bw++) {
u8 *qam_th = &pkt_ext->pkt_ext_qam_th[i][bw][0];
/* Initialize the PPE thresholds to "None" (7), as described in Table * 9-262ac of 80211.ax/D3.0.
*/
memset(pkt_ext, IWL_HE_PKT_EXT_NONE, sizeof(*pkt_ext));
if (link_sta->eht_cap.has_eht) {
u8 nominal_padding =
u8_get_bits(link_sta->eht_cap.eht_cap_elem.phy_cap_info[5],
IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK);
/* If PPE Thresholds exists, parse them into a FW-familiar * format.
*/ if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) {
u8 nss = (link_sta->eht_cap.eht_ppe_thres[0] &
IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1;
u8 *ppe = &link_sta->eht_cap.eht_ppe_thres[0];
u8 ru_index_bitmap =
u16_get_bits(*ppe,
IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); /* Starting after PPE header */
u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE;
iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap,
ppe, ppe_pos_bit, true); /* EHT PPE Thresholds doesn't exist - set the API according to * HE PPE Tresholds
*/
} elseif (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { /* Even though HE Capabilities IE doesn't contain PPE * Thresholds for BW 320Mhz, thresholds for this BW will * be filled in with the same values as 160Mhz, due to * the inheritance, as required.
*/
iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext, true);
/* According to the requirements, for MCSs 12-13 the * maximum value between HE PPE Threshold and Common * Nominal Packet Padding needs to be taken
*/
iwl_mld_get_optimal_ppe_info(pkt_ext, nominal_padding);
/* if PPE Thresholds doesn't present in both EHT IE and HE IE - * take the Thresholds from Common Nominal Packet Padding field
*/
} else {
iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext,
nominal_padding);
}
} elseif (link_sta->he_cap.has_he) { /* If PPE Thresholds exist, parse them into a FW-familiar format. */ if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext, false); /* PPE Thresholds doesn't exist - set the API PPE values * according to Common Nominal Packet Padding field.
*/
} else {
u8 nominal_padding =
u8_get_bits(link_sta->he_cap.he_cap_elem.phy_cap_info[9],
IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED)
iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext,
nominal_padding);
}
}
for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { for (int bw = 0;
bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]);
bw++) {
u8 *qam_th =
&pkt_ext->pkt_ext_qam_th[i][bw][0];
/* We need to preserve the fw sta ids during a restart, since the fw * will recover SN/PN for them, this is why the mld_link_sta exists.
*/ if (mld_link_sta) { /* But if we are not restarting, this is not OK */
WARN_ON(!mld->fw_status.in_hw_restart);
/* Avoid adding a STA that is already in FW to avoid an assert */ if (WARN_ON(mld_link_sta->in_fw)) return -EINVAL;
fw_id = mld_link_sta->fw_id; goto add_to_fw;
}
/* Allocate a fw id and map it to the link_sta */
ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta); if (ret) return ret;
if (link_sta == &link_sta->sta->deflink) {
mld_link_sta = &mld_sta->deflink;
} else {
mld_link_sta = kzalloc(sizeof(*mld_link_sta), GFP_KERNEL); if (!mld_link_sta) return -ENOMEM;
}
/* Now that the STA doesn't exist in FW, we don't expect any new * notifications for it. Cancel the ones that are already pending
*/
iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_STA,
mld_link_sta->fw_id);
/* This will not be done upon reconfig, so do it also when * failed to remove from fw
*/
RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id], NULL);
RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL); if (mld_link_sta != &mld_sta->deflink)
kfree_rcu(mld_link_sta, rcu_head);
}
/* For EHT, HE and VHT we can use the value as it was calculated by * mac80211. For HT, mac80211 doesn't enforce to 4095, so force it * here
*/ if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he ||
link_sta->vht_cap.vht_supported ||
!ht_cap->ht_supported ||
!(ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU)) return;
dup_data = kcalloc(mld->trans->info.num_rxqs, sizeof(*dup_data),
GFP_KERNEL); if (!dup_data) return -ENOMEM;
/* Initialize all the last_seq values to 0xffff which can never * compare equal to the frame's seq_ctrl in the check in * iwl_mld_is_dup() since the lower 4 bits are the fragment * number and fragmented packets don't reach that function. * * This thus allows receiving a packet with seqno 0 and the * retry bit set as the very first packet on a new TID.
*/ for (int q = 0; q < mld->trans->info.num_rxqs; q++)
memset(dup_data[q].last_seq, 0xff, sizeof(dup_data[q].last_seq));
mld_sta->dup_data = dup_data;
/* MPDUs are counted only when EMLSR is possible */ if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION ||
sta->tdls || !ieee80211_vif_is_mld(vif)) return;
mld_sta->mpdu_counters = kcalloc(mld->trans->info.num_rxqs, sizeof(*mld_sta->mpdu_counters),
GFP_KERNEL); if (!mld_sta->mpdu_counters) return;
int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
{ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); struct ieee80211_link_sta *link_sta; int link_id; int ret;
ret = iwl_mld_init_sta(mld, sta, vif, type); if (ret) return ret;
/* We could have add only the deflink link_sta, but it will not work * in the restart case if the single link that is active during * reconfig is not the deflink one.
*/
for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) {
ret = iwl_mld_add_link_sta(mld, link_sta); if (ret) goto destroy_sta;
}
for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) { int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
if (fw_sta_id < 0) continue;
iwl_mld_flush_link_sta_txqs(mld, fw_sta_id);
}
}
void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, struct ieee80211_sta *sta)
{ /* Avoid a warning in iwl_trans_wait_txq_empty if are anyway on the way * to a restart.
*/ if (iwl_mld_error_before_recovery(mld)) return;
for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) { struct iwl_mld_txq *mld_txq =
iwl_mld_txq_from_mac80211(sta->txq[i]);
/* Tell the HW to flush the queues */
iwl_mld_flush_sta_txqs(mld, sta);
/* Wait for trans to empty its queues */
iwl_mld_wait_sta_txqs_empty(mld, sta);
/* Now we can remove the queues */ for (int i = 0; i < ARRAY_SIZE(sta->txq); i++)
iwl_mld_remove_txq(mld, sta->txq[i]);
for_each_sta_active_link(vif, sta, link_sta, link_id) { /* Mac8011 will remove the groupwise keys after the sta is * removed, but FW expects all the keys to be removed before * the STA is, so remove them all here.
*/ if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
iwl_mld_remove_ap_keys(mld, vif, sta, link_id);
/* Remove the link_sta */
iwl_mld_remove_link_sta(mld, link_sta);
}
/* This function should only be used with the wiphy lock held, * In other cases, it is not guaranteed that the link_sta will exist * in the driver too, and it is checked in * iwl_mld_fw_sta_id_from_link_sta.
*/
lockdep_assert_wiphy(mld->wiphy);
for_each_sta_active_link(vif, sta, link_sta, link_id) { int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
/* If it the window is over, first clear the counters. * When we are not blocked by TPT, the window is managed by check_tpt_wk
*/ if ((mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT) &&
time_is_before_jiffies(queue_counter->window_start_time +
IWL_MLD_TPT_COUNT_WINDOW)) {
memset(queue_counter->per_link, 0, sizeof(queue_counter->per_link));
queue_counter->window_start_time = jiffies;
IWL_DEBUG_INFO(mld, "MPDU counters are cleared\n");
}
/* Update the statistics for this TPT measurement window */ if (tx)
link_counter->tx += count; else
link_counter->rx += count;
/* * Next, evaluate whether we should queue an unblock, * skip this if we are not blocked due to low throughput.
*/ if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT)) goto unlock;
for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++)
total_mpdus += tx ? queue_counter->per_link[i].tx :
queue_counter->per_link[i].rx;
/* Unblock is already queued if the threshold was reached before */ if (total_mpdus - count >= IWL_MLD_ENTER_EMLSR_TPT_THRESH) goto unlock;
if (total_mpdus >= IWL_MLD_ENTER_EMLSR_TPT_THRESH)
wiphy_work_queue(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk);
unlock:
spin_unlock_bh(&queue_counter->lock);
}
/* must be called under rcu_read_lock() */ void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue,
u32 count)
{
iwl_mld_count_mpdu(link_sta, queue, count, false);
}
/* must be called under rcu_read_lock() */ void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count)
{ /* use queue 0 for all TX */
iwl_mld_count_mpdu(link_sta, 0, count, true);
}
/* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP. * On the other hand, FW will never check this flag during RX since * an AP/GO doesn't receive protected broadcast management frames. * So, we can set it unconditionally.
*/ if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT)
cmd.mfp = cpu_to_le32(1);
if (addr) {
memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
memcpy(cmd.peer_link_address, addr, ETH_ALEN);
}
if (sta_mask_added) {
ret = iwl_mld_update_sta_resources(mld, vif, sta,
current_sta_mask,
current_sta_mask |
sta_mask_added); if (ret) goto remove_added_link_stas;
}
/* We couldn't activate the links before it has a STA. Now we can */
for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_bss_conf *link =
link_conf_dereference_protected(mld_sta->vif, link_id);
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.