/* Skip the interface for which we are trying to assign a tsf_id */ if (vif == data->vif) return;
/* * The TSF is a hardware/firmware resource, there are 4 and * the driver should assign and free them as needed. However, * there are cases where 2 MACs should share the same TSF ID * for the purpose of clock sync, an optimization to avoid * clock drift causing overlapping TBTTs/DTIMs for a GO and * client in the system. * * The firmware will decide according to the MAC type which * will be the leader and follower. Clients that need to sync * with a remote station will be the leader, and an AP or GO * will be the follower. * * Depending on the new interface type it can be following * or become the leader of an existing interface.
*/ switch (data->vif->type) { case NL80211_IFTYPE_STATION: /* * The new interface is a client, so if the one we're iterating * is an AP, and the beacon interval of the AP is a multiple or * divisor of the beacon interval of the client, the same TSF * should be used to avoid drift between the new client and * existing AP. The existing AP will get drift updates from the * new client context in this case.
*/ if (vif->type != NL80211_IFTYPE_AP ||
data->preferred_tsf != NUM_TSF_IDS ||
!test_bit(mvmvif->tsf_id, data->available_tsf_ids)) break;
case NL80211_IFTYPE_AP: /* * The new interface is AP/GO, so if its beacon interval is a * multiple or a divisor of the beacon interval of an existing * interface, it should get drift updates from an existing * client or use the same TSF as an existing GO. There's no * drift between TSFs internally but if they used different * TSFs then a new client MAC could update one of them and * cause drift that way.
*/ if ((vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_STATION) ||
data->preferred_tsf != NUM_TSF_IDS ||
!test_bit(mvmvif->tsf_id, data->available_tsf_ids)) break;
if ((data->vif->bss_conf.beacon_int -
vif->bss_conf.beacon_int) % min_bi == 0) {
data->preferred_tsf = mvmvif->tsf_id; return;
} break; default: /* * For all other interface types there's no need to * take drift into account. Either they're exclusive * like IBSS and monitor, or we don't care much about * their TSF (like P2P Device), but we won't be able * to share the TSF resource.
*/ break;
}
/* * Unless we exited above, we can't share the TSF resource * that the virtual interface we're iterating over is using * with the new one, so clear the available bit and if this * was the preferred one, reset that as well.
*/
__clear_bit(mvmvif->tsf_id, data->available_tsf_ids);
if (data->preferred_tsf == mvmvif->tsf_id)
data->preferred_tsf = NUM_TSF_IDS;
}
/* Iterator may already find the interface being added -- skip it */ if (vif == data->vif) {
data->found_vif = true; return;
}
/* Mark MAC IDs as used by clearing the available bit, and * (below) mark TSFs as used if their existing use is not * compatible with the new interface type. * No locking or atomic bit operations are needed since the * data is on the stack of the caller function.
*/
__clear_bit(mvmvif->id, data->available_mac_ids);
/* find a suitable tsf_id */
iwl_mvm_mac_tsf_id_iter(_data, mac, vif);
}
/* * Allocate a MAC ID and a TSF for this MAC, along with the queues * and other resources.
*/
/* * Before the iterator, we start with all MAC IDs and TSFs available. * * During iteration, all MAC IDs are cleared that are in use by other * virtual interfaces, and all TSF IDs are cleared that can't be used * by this new virtual interface because they're used by an interface * that can't share it with the new one. * At the same time, we check if there's a preferred TSF in the case * that we should share it with another interface.
*/
/* MAC ID 0 should be used only for the managed/IBSS vif with non-MLO * FW API
*/ if (!mvm->mld_api_is_used) { switch (vif->type) { case NL80211_IFTYPE_ADHOC: break; case NL80211_IFTYPE_STATION: if (!vif->p2p) break;
fallthrough; default:
__clear_bit(0, data.available_mac_ids);
}
}
/* * In the case we're getting here during resume, it's similar to * firmware restart, and with RESUME_ALL the iterator will find * the vif being added already. * We don't want to reassign any IDs in either case since doing * so would probably assign different IDs (as interfaces aren't * necessarily added in the same order), but the old IDs were * preserved anyway, so skip ID assignment for both resume and * recovery.
*/ if (data.found_vif) return 0;
/* Therefore, in recovery, we can't get here */ if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) return -EBUSY;
mvmvif->id = find_first_bit(data.available_mac_ids,
NUM_MAC_INDEX_DRIVER); if (mvmvif->id == NUM_MAC_INDEX_DRIVER) {
IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n");
ret = -EIO; goto exit_fail;
}
if (data.preferred_tsf != NUM_TSF_IDS)
mvmvif->tsf_id = data.preferred_tsf; else
mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
NUM_TSF_IDS); if (mvmvif->tsf_id == NUM_TSF_IDS) {
IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n");
ret = -EIO; goto exit_fail;
}
/* No need to allocate data queues to P2P Device MAC */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) return 0;
/* Allocate the CAB queue for softAP and GO interfaces */ if (vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_ADHOC) { /* * For TVQM this will be overwritten later with the FW assigned * queue value (when queue is enabled).
*/
mvmvif->deflink.cab_queue = IWL_MVM_DQA_GCAST_QUEUE;
}
/* * Now we've got the basic rates as bitmaps in the ofdm and cck * variables. This isn't sufficient though, as there might not * be all the right rates in the bitmap. E.g. if the only basic * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps * and 6 Mbps because the 802.11-2007 standard says in 9.6: * * [...] a STA responding to a received frame shall transmit * its Control Response frame [...] at the highest rate in the * BSSBasicRateSet parameter that is less than or equal to the * rate of the immediately previous frame in the frame exchange * sequence ([...]) and that is of the same modulation class * ([...]) as the received frame. If no rate contained in the * BSSBasicRateSet parameter meets these conditions, then the * control frame sent in response to a received frame shall be * transmitted at the highest mandatory rate of the PHY that is * less than or equal to the rate of the received frame, and * that is of the same modulation class as the received frame. * * As a consequence, we need to add all mandatory rates that are * lower than all of the basic rates to these bitmaps.
*/
if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE; if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE; /* 6M already there or needed so always add */
ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
/* * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. * Note, however: * - if no CCK rates are basic, it must be ERP since there must * be some basic rates at all, so they're OFDM => ERP PHY * (or we're in 5 GHz, and the cck bitmap will never be used) * - if 11M is a basic rate, it must be ERP as well, so add 5.5M * - if 5.5M is basic, 1M and 2M are mandatory * - if 2M is basic, 1M is mandatory * - if 1M is basic, that's the only valid ACK rate. * As a consequence, it's not as complicated as it sounds, just add * any lower rates to the ACK rate bitmap.
*/ if (IWL_RATE_11M_INDEX < lowest_present_cck)
cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE; if (IWL_RATE_5M_INDEX < lowest_present_cck)
cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE; if (IWL_RATE_2M_INDEX < lowest_present_cck)
cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE; /* 1M already there or needed so always add */
cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE;
/* The fw does not distinguish between ht and fat */
ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT;
iwl_mvm_set_fw_protection_flags(mvm, vif, &vif->bss_conf,
&cmd->protection_flags,
ht_flag, MAC_PROT_FLG_TGG_PROTECT);
}
staticint iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, struct iwl_mac_ctx_cmd *cmd)
{ int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, sizeof(*cmd), cmd); if (ret)
IWL_ERR(mvm, "Failed to send MAC_CONTEXT_CMD (action:%d): %d\n",
le32_to_cpu(cmd->action), ret); return ret;
}
/* * The DTIM count counts down, so when it is N that means N * more beacon intervals happen until the DTIM TBTT. Therefore * add this to the current time. If that ends up being in the * future, the firmware will handle it. * * Also note that the system_timestamp (which we get here as * "sync_device_ts") and TSF timestamp aren't at exactly the * same offset in the frame -- the TSF is at the first symbol * of the TSF, the system timestamp is at signal acquisition * time. This means there's an offset between them of at most * a few hundred microseconds (24 * 8 bits + PLCP time gives * 384us in the longest case), this is currently not relevant * as the firmware wakes up around 2ms before the TBTT.
*/
dtim_offs = link_conf->sync_dtim_count *
link_conf->beacon_int; /* convert TU to usecs */
dtim_offs *= 1024;
/* We need the dtim_period to set the MAC as associated */ if (vif->cfg.assoc && vif->bss_conf.dtim_period &&
!force_assoc_off) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
/* Allow beacons to pass through as long as we are not * associated, or we do not have dtim period information.
*/
cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
}
/* * the queue mask is only relevant for old TX API, and * mvm->snif_queue isn't set here (it's still set to * IWL_MVM_INVALID_QUEUE so the BIT() of it is UB)
*/ if (!iwl_mvm_has_new_tx_api(mvm))
tfd_queue_msk = BIT(mvm->snif_queue);
/* Allocate sniffer station */
ret = iwl_mvm_allocate_int_sta(mvm, &mvm->snif_sta, tfd_queue_msk,
vif->type, IWL_STA_GENERAL_PURPOSE); if (ret) return ret;
/* * This flag should be set to true when the P2P Device is * discoverable and there is at least another active P2P GO. Settings * this flag will allow the P2P Device to be discoverable on other * channels in addition to its listen channel. * Note that this flag should not be set in other cases as it opens the * Rx filters on all MAC and increases the number of interrupts.
*/
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
iwl_mvm_go_iterator, &data);
/* 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)) {
*tim_index = cpu_to_le32(tim_idx);
*tim_size = cpu_to_le32((u32)beacon[tim_idx + 1]);
} else {
IWL_WARN(mvm, "Unable to find TIM Element in beacon\n");
}
}
if (link_id == IEEE80211_LINK_UNSPECIFIED && ieee80211_vif_is_mld(vif)) { for (i = 0; i < ARRAY_SIZE(mvmvif->link); i++) { if (!mvmvif->link[i]) continue; /* shouldn't do this when >1 link is active */
WARN_ON_ONCE(link_id != IEEE80211_LINK_UNSPECIFIED);
link_id = i;
}
}
if (link_id < IEEE80211_LINK_UNSPECIFIED) { struct ieee80211_bss_conf *link_conf;
rcu_read_lock();
link_conf = rcu_dereference(vif->link_conf[link_id]); if (link_conf) {
basic = link_conf->basic_rates; if (link_conf->chanreq.oper.chan)
band = link_conf->chanreq.oper.chan->band;
}
rcu_read_unlock();
}
/* The beacon template for the AP/GO/IBSS has changed and needs update */ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf)
{ struct sk_buff *beacon; int ret;
/* * Fill the filter flags for mac context of type AP or P2P GO.
*/ void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif,
__le32 *filter_flags, int accept_probe_req_flag, int accept_beacon_flag)
{ /* * in AP mode, pass probe requests and beacons from other APs * (needed for ht protection); when there're no any associated * station don't ask FW to pass beacons to prevent unnecessary * wake-ups.
*/
*filter_flags |= cpu_to_le32(accept_probe_req_flag); if (mvmvif->ap_assoc_sta_count || !mvm->drop_bcn_ap_mode) {
*filter_flags |= cpu_to_le32(accept_beacon_flag);
IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n");
} else {
IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
}
}
/* * Fill the specific data for mac context of type AP of P2P GO
*/ staticvoid iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mac_ctx_cmd *cmd, struct iwl_mac_data_ap *ctxt_ap, bool add)
{ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_mac_ap_iterator_data data = {
.mvm = mvm,
.vif = vif,
.beacon_device_ts = 0
};
/* in AP mode, the MCAST FIFO takes the EDCA params from VO */
cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST);
if (!fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_STA_TYPE))
ctxt_ap->mcast_qid = cpu_to_le32(mvmvif->deflink.cab_queue);
/* * Only set the beacon time when the MAC is being added, when we * just modify the MAC then we should keep the time -- the firmware * can otherwise have a "jumping" TBTT.
*/ if (add) { /* * If there is a station/P2P client interface which is * associated, set the AP's TBTT far enough from the station's * TBTT. Otherwise, set it to the current system time
*/
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
iwl_mvm_mac_ap_iterator, &data);
/* * The channel switch is started and we have blocked the * stations. If this is the first beacon (the timeout wasn't * set), set the unblock timeout, otherwise countdown
*/ if (!mvm->csa_tx_block_bcn_timeout)
mvm->csa_tx_block_bcn_timeout =
IWL_MVM_CS_UNBLOCK_TX_TIMEOUT; else
mvm->csa_tx_block_bcn_timeout--;
/* Check if the timeout is expired, and unblock tx */ if (mvm->csa_tx_block_bcn_timeout == 0) {
iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
}
}
}
staticvoid
iwl_mvm_handle_missed_beacons_notif(struct iwl_mvm *mvm, conststruct iwl_missed_beacons_notif *mb, struct iwl_rx_packet *pkt)
{ struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig; struct iwl_fw_dbg_trigger_tlv *trigger;
u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx;
u32 rx_missed_bcon, rx_missed_bcon_since_rx; struct ieee80211_vif *vif; /* Id can be mac/link id depending on the notification version */
u32 id = le32_to_cpu(mb->link_id); union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt };
u32 mac_type; int link_id;
u8 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
MISSED_BEACONS_NOTIFICATION,
0);
u8 new_notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP,
MISSED_BEACONS_NOTIF, 0); struct ieee80211_bss_conf *bss_conf;
/* If the firmware uses the new notification (from MAC_CONF_GROUP), * refer to that notification's version. * Note that the new notification from MAC_CONF_GROUP starts from * version 5.
*/ if (new_notif_ver)
notif_ver = new_notif_ver;
/* * starting from version 4 the ID is link ID, but driver * uses link ID == MAC ID, so always treat as MAC ID
*/
vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, false); if (!vif) return;
rx_missed_bcon = le32_to_cpu(mb->consec_missed_beacons);
rx_missed_bcon_since_rx =
le32_to_cpu(mb->consec_missed_beacons_since_last_rx); /* * TODO: the threshold should be adjusted based on latency conditions, * and/or in case of a CS flow on one of the other AP vifs.
*/ if (rx_missed_bcon >= IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG) { if (rx_missed_bcon_since_rx >= IWL_MVM_MISSED_BEACONS_SINCE_RX_THOLD) {
iwl_mvm_connection_loss(mvm, vif, "missed beacons");
} else {
IWL_WARN(mvm, "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n");
IWL_WARN(mvm, "missed_beacons:%d, missed_beacons_since_rx:%d\n",
rx_missed_bcon, rx_missed_bcon_since_rx);
}
} elseif (link_id >= 0 && hweight16(vif->active_links) > 1) {
u32 bss_param_ch_cnt_link_id =
bss_conf->bss_param_ch_cnt_link_id;
u32 scnd_lnk_bcn_lost = 0;
if (notif_ver >= 5 &&
!IWL_FW_CHECK(mvm,
le32_to_cpu(mb->other_link_id) == IWL_MVM_FW_LINK_ID_INVALID, "No data for other link id but we are in EMLSR active_links: 0x%x\n",
vif->active_links))
scnd_lnk_bcn_lost =
le32_to_cpu(mb->consec_missed_beacons_other_link);
/* Exit EMLSR if we lost more than * IWL_MVM_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links * OR more than IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH on any link. * OR more than IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED * and the link's bss_param_ch_count has changed.
*/ if ((rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
scnd_lnk_bcn_lost >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) ||
rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH ||
(bss_param_ch_cnt_link_id != link_id &&
rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED))
iwl_mvm_exit_esr(mvm, vif,
IWL_MVM_ESR_EXIT_MISSED_BEACON,
iwl_mvm_get_primary_link(vif));
} elseif (rx_missed_bcon_since_rx > IWL_MVM_MISSED_BEACONS_THRESHOLD) { if (!iwl_mvm_has_new_tx_api(mvm))
ieee80211_beacon_loss(vif); else
ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC);
/* try to switch links, no-op if we don't have MLO */
iwl_mvm_int_mlo_scan(mvm, vif);
}
/* update rx_status according to the notification's metadata */
memset(&rx_status, 0, sizeof(rx_status));
rx_status.mactime = le64_to_cpu(sb->tsf); /* TSF as indicated by the firmware is at INA time */
rx_status.flag |= RX_FLAG_MACTIME_PLCP_START;
rx_status.device_timestamp = le32_to_cpu(sb->system_time);
rx_status.band =
(sb->band & 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(sb->channel),
rx_status.band);
/* copy the data */
skb_put_data(skb, data, size);
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
/* pass it as regular rx to mac80211 */
ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);
}
/* noa_attr contains 1 reserved byte, need to substruct it */
new_data->noa_len = sizeof(struct ieee80211_vendor_ie) + sizeof(new_data->notif.noa_attr) - 1;
/* * If it's a one time NoA, only one descriptor is needed, * adjust the length according to len_low.
*/ if (new_data->notif.noa_attr.len_low == sizeof(struct ieee80211_p2p_noa_desc) + 2)
new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc);
RCU_INIT_POINTER(mvm->csa_vif, NULL); return; case NL80211_IFTYPE_STATION: /* * if we don't know about an ongoing channel switch, * make sure FW cancels it
*/ if (iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP,
CHANNEL_SWITCH_ERROR_NOTIF,
0) && !csa_active) {
IWL_DEBUG_INFO(mvm, "Channel Switch was canceled\n");
iwl_mvm_cancel_channel_switch(mvm, vif, id); break;
}
iwl_mvm_csa_client_absent(mvm, vif);
cancel_delayed_work(&mvmvif->csa_work);
ieee80211_chswitch_done(vif, true, mac_link_id); break; default: /* should never happen */
WARN_ON_ONCE(1); break;
}
out_unlock:
rcu_read_unlock();
}
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.