for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta) || !sta->tdls) continue;
int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{ struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; int count = 0; int i;
lockdep_assert_held(&mvm->mutex);
for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta) || !sta->tdls) continue;
if (vif) {
mvmsta = iwl_mvm_sta_from_mac80211(sta); if (mvmsta->vif != vif) continue;
}
tdls_cfg_cmd.id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID;
tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */
/* for now the Tx cmd is empty and unused */
/* populate TDLS peer data */
cnt = 0; for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta) || !sta->tdls) continue;
/* when the first peer joins, send a power update first */ if (tdls_sta_cnt == 1 && sta_added)
iwl_mvm_power_update_mac(mvm);
/* Configure the FW with TDLS peer info only if TDLS channel switch *capabilityisset. *TDLSconfigdataisusedcurrentlyonlyinTDLSchannelswitchcode. *SupposedtoservealsoTDLSbufferstationwhichisnotimplemneted
* yet in FW*/ if (fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH))
iwl_mvm_tdls_config(mvm, vif);
/* when the last peer leaves, send a power update last */ if (tdls_sta_cnt == 0 && !sta_added)
iwl_mvm_power_update_mac(mvm);
}
/* we only send requests to our switching peer - update sent time */ if (state == IWL_MVM_TDLS_SW_REQ_SENT)
mvm->tdls_cs.peer.sent_timestamp = iwl_mvm_get_systime(mvm);
if (state == IWL_MVM_TDLS_SW_IDLE)
mvm->tdls_cs.cur_sta_id = IWL_INVALID_STA;
}
/* can fail sometimes */ if (!le32_to_cpu(notif->status)) {
iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); return;
}
if (WARN_ON(sta_id >= mvm->fw->ucode_capa.num_stations)) return;
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex)); /* the station may not be here, but if it is, it must be a TDLS peer */ if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls)) return;
/* get the existing peer if it's there */ if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE &&
mvm->tdls_cs.cur_sta_id != IWL_INVALID_STA) { struct ieee80211_sta *sta = rcu_dereference_protected(
mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
lockdep_is_held(&mvm->mutex)); if (!IS_ERR_OR_NULL(sta))
same_peer = ether_addr_equal(peer, sta->addr);
}
switch (mvm->tdls_cs.state) { case IWL_MVM_TDLS_SW_IDLE: /* *mightbespuriouspacketfromthepeeraftertheswitchis *alreadydone
*/ if (type == TDLS_MOVE_CH)
ret = -EINVAL; break; case IWL_MVM_TDLS_SW_REQ_SENT: /* only allow requests from the same peer */ if (!same_peer)
ret = -EBUSY; elseif (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH &&
!peer_initiator) /* *Wereceivedach-switchrequestwhileanoutgoing *oneispending.Allowitifthepeeristhelink *initiator.
*/
ret = -EBUSY; elseif (type == TDLS_SEND_CHAN_SW_REQ) /* wait for idle before sending another request */
ret = -EBUSY; elseif (timestamp <= mvm->tdls_cs.peer.sent_timestamp) /* we got a stale response - ignore it */
ret = -EINVAL; break; case IWL_MVM_TDLS_SW_RESP_RCVD: /* *wearewaitingfortheFWtogivean"active"notification, *soignorerequestsinthemeantime
*/
ret = -EBUSY; break; case IWL_MVM_TDLS_SW_REQ_RCVD: /* as above, allow the link initiator to proceed */ if (type == TDLS_SEND_CHAN_SW_REQ) { if (!same_peer)
ret = -EBUSY; elseif (peer_initiator) /* they are the initiator */
ret = -EBUSY;
} elseif (type == TDLS_MOVE_CH) {
ret = -EINVAL;
} break; case IWL_MVM_TDLS_SW_ACTIVE: /* *theonlyvalidrequestwhenactiveisarequesttoreturn *tothebasechannelbythecurrentoff-channelpeer
*/ if (type != TDLS_MOVE_CH || !same_peer)
ret = -EBUSY; break;
}
if (ret)
IWL_DEBUG_TDLS(mvm, "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n",
type, mvm->tdls_cs.state, peer, same_peer,
peer_initiator);
rcu_read_lock();
sta = ieee80211_find_sta(vif, peer); if (!sta) {
rcu_read_unlock();
ret = -ENOENT; goto out;
}
mvmsta = iwl_mvm_sta_from_mac80211(sta);
cmd.peer_sta_id = cpu_to_le32(mvmsta->deflink.sta_id);
if (!chandef) { if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
mvm->tdls_cs.peer.chandef.chan) { /* actually moving to the channel */
chandef = &mvm->tdls_cs.peer.chandef;
} elseif (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE &&
type == TDLS_MOVE_CH) { /* we need to return to base channel */ struct ieee80211_chanctx_conf *chanctx =
rcu_dereference(vif->bss_conf.chanctx_conf);
if (WARN_ON_ONCE(!chanctx)) {
rcu_read_unlock(); goto out;
}
chandef = &chanctx->def;
}
}
if (chandef)
iwl_mvm_set_chan_info_chandef(mvm, &cmd.ci, chandef);
/* keep quota calculation simple for now - 50% of DTIM for TDLS */
tail->timing.max_offchan_duration =
cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
vif->bss_conf.beacon_int) / 2);
/* Switch time is the first element in the switch-timing IE. */
tail->frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
info = IEEE80211_SKB_CB(skb);
hdr = (void *)skb->data; if (info->control.hw_key) { if (info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP) {
rcu_read_unlock();
ret = -EINVAL; goto out;
}
iwl_mvm_set_tx_cmd_ccmp(info, &tail->frame.tx_cmd);
}
/* called after an active channel switch has finished or timed-out */
iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
/* station might be gone, in that case do nothing */ if (mvm->tdls_cs.peer.sta_id == IWL_INVALID_STA) return;
sta = rcu_dereference_protected(
mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
lockdep_is_held(&mvm->mutex)); /* the station may not be here, but if it is, it must be a TDLS peer */ if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls)) return;
/* retry after a DTIM if we failed sending now */
delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
schedule_delayed_work(&mvm->tdls_cs.dwork, msecs_to_jiffies(delay));
}
/* we only support a single peer for channel switching */ if (mvm->tdls_cs.peer.sta_id != IWL_INVALID_STA) {
IWL_DEBUG_TDLS(mvm, "Existing peer. Can't start switch with %pM\n",
sta->addr); return -EBUSY;
}
ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
TDLS_SEND_CHAN_SW_REQ,
sta->addr, sta->tdls_initiator,
oper_class, chandef, 0, 0, 0,
tmpl_skb, ch_sw_tm_ie); if (ret) return ret;
IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr);
/* we only support a single peer for channel switching */ if (mvm->tdls_cs.peer.sta_id == IWL_INVALID_STA) {
IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr); goto out;
}
cur_sta = rcu_dereference_protected(
mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
lockdep_is_held(&mvm->mutex)); /* make sure it's the same peer */ if (cur_sta != sta) goto out;
retry: /* register a timeout in case we don't succeed in switching */
delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int * 1024 / 1000;
mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
}
Messung V0.5 in Prozent
¤ 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.0.12Bemerkung:
(vorverarbeitet am 2026-10-11)
¤
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.