Path: blob/main/sys/contrib/dev/iwlwifi/mld/roc.c
101168 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024 - 2025 Intel Corporation3*/4#include <net/cfg80211.h>5#include <net/mac80211.h>67#include "mld.h"8#include "roc.h"9#include "hcmd.h"10#include "iface.h"11#include "sta.h"12#include "mlo.h"1314#include "fw/api/context.h"15#include "fw/api/time-event.h"1617#define AUX_ROC_MAX_DELAY MSEC_TO_TU(200)1819static void20iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif)21{22struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);23int *result = data;24int ret;2526ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,27IWL_MLD_EMLSR_BLOCKED_ROC,28iwl_mld_get_primary_link(vif));29if (ret)30*result = ret;31}3233struct iwl_mld_roc_iter_data {34enum iwl_roc_activity activity;35struct ieee80211_vif *vif;36bool found;37};3839static void iwl_mld_find_roc_vif_iter(void *data, u8 *mac,40struct ieee80211_vif *vif)41{42struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);43struct iwl_mld_roc_iter_data *roc_data = data;4445if (mld_vif->roc_activity != roc_data->activity)46return;4748/* The FW supports one ROC of each type simultaneously */49if (WARN_ON(roc_data->found)) {50roc_data->vif = NULL;51return;52}5354roc_data->found = true;55roc_data->vif = vif;56}5758static struct ieee80211_vif *59iwl_mld_find_roc_vif(struct iwl_mld *mld, enum iwl_roc_activity activity)60{61struct iwl_mld_roc_iter_data roc_data = {62.activity = activity,63.found = false,64};6566ieee80211_iterate_active_interfaces_mtx(mld->hw,67IEEE80211_IFACE_ITER_NORMAL,68iwl_mld_find_roc_vif_iter,69&roc_data);7071return roc_data.vif;72}7374int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,75struct ieee80211_channel *channel, int duration,76enum ieee80211_roc_type type)77{78struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);79struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);80struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;81struct iwl_roc_req cmd = {82.action = cpu_to_le32(FW_CTXT_ACTION_ADD),83};84u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,85WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);86u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);87enum iwl_roc_activity activity;88int ret = 0;8990lockdep_assert_wiphy(mld->wiphy);9192if (vif->type != NL80211_IFTYPE_P2P_DEVICE &&93vif->type != NL80211_IFTYPE_STATION) {94IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n",95vif->type);9697return -EOPNOTSUPP;98}99100if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {101switch (type) {102case IEEE80211_ROC_TYPE_NORMAL:103activity = ROC_ACTIVITY_P2P_DISC;104break;105case IEEE80211_ROC_TYPE_MGMT_TX:106activity = ROC_ACTIVITY_P2P_NEG;107break;108default:109WARN_ONCE(1, "Got an invalid P2P ROC type\n");110return -EINVAL;111}112} else {113activity = ROC_ACTIVITY_HOTSPOT;114}115116/* The FW supports one ROC of each type simultaneously */117if (WARN_ON(iwl_mld_find_roc_vif(mld, activity)))118return -EBUSY;119120ieee80211_iterate_active_interfaces_mtx(mld->hw,121IEEE80211_IFACE_ITER_NORMAL,122iwl_mld_vif_iter_emlsr_block_roc,123&ret);124if (ret)125return ret;126127ret = iwl_mld_add_aux_sta(mld, aux_sta);128if (ret)129return ret;130131cmd.activity = cpu_to_le32(activity);132cmd.sta_id = cpu_to_le32(aux_sta->sta_id);133cmd.channel_info.channel = cpu_to_le32(channel->hw_value);134cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band);135cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20;136cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY);137cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));138139memcpy(cmd.node_addr, vif->addr, ETH_ALEN);140141ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),142&cmd, cmd_len);143if (ret) {144IWL_ERR(mld, "Couldn't send the ROC_CMD\n");145return ret;146}147148mld_vif->roc_activity = activity;149150return 0;151}152153static void154iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac,155struct ieee80211_vif *vif)156{157struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);158159iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC);160}161162static void iwl_mld_destroy_roc(struct iwl_mld *mld,163struct ieee80211_vif *vif,164struct iwl_mld_vif *mld_vif)165{166mld_vif->roc_activity = ROC_NUM_ACTIVITIES;167168ieee80211_iterate_active_interfaces_mtx(mld->hw,169IEEE80211_IFACE_ITER_NORMAL,170iwl_mld_vif_iter_emlsr_unblock_roc,171NULL);172173/* wait until every tx has seen that roc_activity has been reset */174synchronize_net();175/* from here, no new tx will be added176* we can flush the Tx on the queues177*/178179iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);180181iwl_mld_remove_aux_sta(mld, vif);182}183184int iwl_mld_cancel_roc(struct ieee80211_hw *hw,185struct ieee80211_vif *vif)186{187struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);188struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);189struct iwl_roc_req cmd = {190.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),191};192u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,193WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);194u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);195int ret;196197lockdep_assert_wiphy(mld->wiphy);198199if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE &&200vif->type != NL80211_IFTYPE_STATION))201return -EOPNOTSUPP;202203/* No roc activity running it's probably already done */204if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES)205return 0;206207cmd.activity = cpu_to_le32(mld_vif->roc_activity);208209ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),210&cmd, cmd_len);211if (ret)212IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n");213214/* We may have raced with the firmware expiring the ROC instance at215* this very moment. In that case, we can have a notification in the216* async processing queue. However, none can arrive _after_ this as217* ROC_CMD was sent synchronously, i.e. we waited for a response and218* the firmware cannot refer to this ROC after the response. Thus,219* if we just cancel the notification (if there's one) we'll be at a220* clean state for any possible next ROC.221*/222iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC,223mld_vif->roc_activity);224225iwl_mld_destroy_roc(mld, vif, mld_vif);226227return 0;228}229230void iwl_mld_handle_roc_notif(struct iwl_mld *mld,231struct iwl_rx_packet *pkt)232{233const struct iwl_roc_notif *notif = (void *)pkt->data;234u32 activity = le32_to_cpu(notif->activity);235struct iwl_mld_vif *mld_vif;236struct ieee80211_vif *vif;237238vif = iwl_mld_find_roc_vif(mld, activity);239if (WARN_ON(!vif))240return;241242mld_vif = iwl_mld_vif_from_mac80211(vif);243/* It is possible that the ROC was canceled244* but the notification was already fired.245*/246if (mld_vif->roc_activity != activity)247return;248249if (le32_to_cpu(notif->success) &&250le32_to_cpu(notif->started)) {251/* We had a successful start */252ieee80211_ready_on_channel(mld->hw);253} else {254/* ROC was not successful, tell the firmware to remove it */255if (le32_to_cpu(notif->started))256iwl_mld_cancel_roc(mld->hw, vif);257else258iwl_mld_destroy_roc(mld, vif, mld_vif);259/* we need to let know mac80211 about end OR260* an unsuccessful start261*/262ieee80211_remain_on_channel_expired(mld->hw);263}264}265266267