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 * capability is set. * TDLS config data is used currently only in TDLS channel switch code. * Supposed to serve also TDLS buffer station which is not implemneted
* 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;
/* * Update state and possibly switch again after this is over (DTIM). * Also convert TU to msec.
*/
delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
/* 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: /* * might be spurious packet from the peer after the switch is * already done
*/ 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) /* * We received a ch-switch request while an outgoing * one is pending. Allow it if the peer is the link * 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: /* * we are waiting for the FW to give an "active" notification, * so ignore requests in the meantime
*/
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: /* * the only valid request when active is a request to return * to the base channel by the current off-channel peer
*/ 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;
/* * Mark the peer as "in tdls switch" for this vif. We only allow a * single such peer per vif.
*/
mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL); if (!mvm->tdls_cs.peer.skb) return -ENOMEM;
/* * Wait for 2 DTIM periods before attempting the next switch. The next * switch will be made sooner if the current one completes before that.
*/
delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
vif->bss_conf.beacon_int);
mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay)); return 0;
}
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;
/* * If we're currently in a switch because of the now canceled peer, * wait a DTIM here to make sure the phy is back on the base channel. * We can't otherwise force it.
*/ if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id &&
mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE)
wait_for_phy = true;
IWL_DEBUG_TDLS(mvm, "Received TDLS ch switch action %s from %pM status %d\n",
action_str, params->sta->addr, params->status);
/* * we got a non-zero status from a peer we were switching to - move to * the idle state and retry again later
*/ if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE &&
params->status != 0 &&
mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
mvm->tdls_cs.cur_sta_id != IWL_INVALID_STA) { struct ieee80211_sta *cur_sta;
/* make sure it's the same peer */
cur_sta = rcu_dereference_protected(
mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
lockdep_is_held(&mvm->mutex)); if (cur_sta == params->sta) {
iwl_mvm_tdls_update_cs_state(mvm,
IWL_MVM_TDLS_SW_IDLE); goto retry;
}
}
type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ?
TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH;
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.