#include "mld.h"
#include "iface.h"
+#include "link.h"
#include "mlo.h"
#include "fw/api/mac-cfg.h"
+#include "fw/api/mac.h"
+#include "fw/api/rs.h"
#define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512
#define IWL_NAN_RSSI_CLOSE 55
cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
}
+static void iwl_mld_nan_fill_rates(struct iwl_link_config_cmd *cmd)
+{
+ u32 ofdm = 0;
+
+ /* All OFDM rates - NAN uses OFDM only, no CCK */
+ ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
+ ofdm |= IWL_RATE_BIT_MSK(9) >> IWL_FIRST_OFDM_RATE;
+ ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
+ ofdm |= IWL_RATE_BIT_MSK(18) >> IWL_FIRST_OFDM_RATE;
+ ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
+ ofdm |= IWL_RATE_BIT_MSK(36) >> IWL_FIRST_OFDM_RATE;
+ ofdm |= IWL_RATE_BIT_MSK(48) >> IWL_FIRST_OFDM_RATE;
+ ofdm |= IWL_RATE_BIT_MSK(54) >> IWL_FIRST_OFDM_RATE;
+
+ cmd->ofdm_rates = cpu_to_le32(ofdm);
+ cmd->short_slot = cpu_to_le32(1);
+}
+
+static void iwl_mld_nan_fill_qos(struct iwl_ac_qos *ac, __le32 *qos_flags)
+{
+ /* AC_BK: CWmin=15, CWmax=1023, AIFSN=7, TXOP=0 */
+ ac[AC_BK].cw_min = cpu_to_le16(15);
+ ac[AC_BK].cw_max = cpu_to_le16(1023);
+ ac[AC_BK].aifsn = 7;
+ ac[AC_BK].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BK);
+ ac[AC_BK].edca_txop = 0;
+
+ /* AC_BE: CWmin=15, CWmax=1023, AIFSN=3, TXOP=0 */
+ ac[AC_BE].cw_min = cpu_to_le16(15);
+ ac[AC_BE].cw_max = cpu_to_le16(1023);
+ ac[AC_BE].aifsn = 3;
+ ac[AC_BE].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BE);
+ ac[AC_BE].edca_txop = 0;
+
+ /* AC_VI: CWmin=7, CWmax=15, AIFSN=2, TXOP=3008us */
+ ac[AC_VI].cw_min = cpu_to_le16(7);
+ ac[AC_VI].cw_max = cpu_to_le16(15);
+ ac[AC_VI].aifsn = 2;
+ ac[AC_VI].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VI);
+ ac[AC_VI].edca_txop = cpu_to_le16(3008);
+
+ /* AC_VO: CWmin=3, CWmax=7, AIFSN=2, TXOP=1504us */
+ ac[AC_VO].cw_min = cpu_to_le16(3);
+ ac[AC_VO].cw_max = cpu_to_le16(7);
+ ac[AC_VO].aifsn = 2;
+ ac[AC_VO].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VO);
+ ac[AC_VO].edca_txop = cpu_to_le16(1504);
+
+ *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
+}
+
+static void
+iwl_mld_nan_link_prep_cmd(struct iwl_mld *mld,
+ struct iwl_mld_nan_link *nan_link,
+ struct iwl_link_config_cmd *cmd,
+ u32 modify_flags)
+{
+ struct ieee80211_vif *vif = mld->nan_device_vif;
+ struct iwl_mld_vif *mld_vif;
+
+ if (WARN_ON_ONCE(!vif))
+ return;
+
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (!nan_link->chanctx) {
+ cmd->phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
+ } else {
+ struct iwl_mld_phy *mld_phy;
+
+ mld_phy = iwl_mld_phy_from_mac80211(nan_link->chanctx);
+ cmd->phy_id = cpu_to_le32(mld_phy->fw_id);
+ }
+
+ if (modify_flags & LINK_CONTEXT_MODIFY_RATES_INFO)
+ iwl_mld_nan_fill_rates(cmd);
+
+ if (modify_flags & LINK_CONTEXT_MODIFY_QOS_PARAMS)
+ iwl_mld_nan_fill_qos(cmd->ac, &cmd->qos_flags);
+
+ cmd->link_id = cpu_to_le32(nan_link->fw_id);
+ cmd->mac_id = cpu_to_le32(mld_vif->fw_id);
+ cmd->active = cpu_to_le32(nan_link->active);
+
+ ether_addr_copy(cmd->local_link_addr, vif->addr);
+
+ cmd->modify_mask = cpu_to_le32(modify_flags);
+}
+
+static struct iwl_mld_nan_link *
+iwl_mld_nan_link_add(struct iwl_mld *mld,
+ struct iwl_mld_vif *mld_vif,
+ struct ieee80211_chanctx_conf *chanctx)
+{
+ struct iwl_mld_nan_link *nan_link;
+ struct iwl_link_config_cmd cmd;
+ u8 fw_id;
+ int ret;
+
+ ret = iwl_mld_allocate_link_fw_id(mld, &fw_id, ERR_PTR(-ENODEV));
+ if (ret < 0)
+ return NULL;
+
+ nan_link = &mld_vif->nan.links[fw_id];
+
+ if (WARN_ON_ONCE(nan_link->fw_id != FW_CTXT_ID_INVALID))
+ goto err;
+
+ nan_link->fw_id = fw_id;
+ nan_link->chanctx = chanctx;
+
+ iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
+ LINK_CONTEXT_MODIFY_RATES_INFO |
+ LINK_CONTEXT_MODIFY_QOS_PARAMS);
+
+ ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD);
+ if (ret) {
+ nan_link->fw_id = FW_CTXT_ID_INVALID;
+ nan_link->chanctx = NULL;
+ goto err;
+ }
+
+ return nan_link;
+err:
+ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL);
+ return NULL;
+}
+
+static int iwl_mld_nan_link_set_active(struct iwl_mld *mld,
+ struct iwl_mld_nan_link *nan_link,
+ bool active)
+{
+ struct iwl_link_config_cmd cmd;
+ int ret;
+
+ if (nan_link->active == active)
+ return 0;
+
+ nan_link->active = active;
+
+ iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
+ LINK_CONTEXT_MODIFY_ACTIVE);
+
+ ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY);
+ if (ret) {
+ nan_link->active = !nan_link->active;
+ return ret;
+ }
+
+ if (!active)
+ nan_link->chanctx = NULL;
+
+ return 0;
+}
+
+static void iwl_mld_nan_link_remove(struct iwl_mld *mld,
+ struct iwl_mld_nan_link *nan_link)
+{
+ struct iwl_link_config_cmd cmd = {
+ .link_id = cpu_to_le32(nan_link->fw_id),
+ .phy_id = cpu_to_le32(FW_CTXT_ID_INVALID),
+ };
+
+ if (WARN_ON_ONCE(nan_link->fw_id == FW_CTXT_ID_INVALID))
+ return;
+
+ iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
+
+ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[nan_link->fw_id], NULL);
+ nan_link->fw_id = FW_CTXT_ID_INVALID;
+ nan_link->active = false;
+ nan_link->chanctx = NULL;
+}
+
+static bool iwl_mld_nan_have_links(struct iwl_mld_vif *mld_vif)
+{
+ struct iwl_mld_nan_link *nan_link;
+
+ for_each_mld_nan_valid_link(mld_vif, nan_link)
+ return true;
+
+ return false;
+}
+
+static struct iwl_mld_nan_link *
+iwl_mld_nan_find_link(struct iwl_mld_vif *mld_vif,
+ struct ieee80211_chanctx_conf *chanctx)
+{
+ struct iwl_mld_nan_link *nan_link;
+
+ for_each_mld_nan_valid_link(mld_vif, nan_link) {
+ if (nan_link->chanctx == chanctx)
+ return nan_link;
+ }
+
+ return NULL;
+}
+
void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
struct ieee80211_vif *vif,
u64 changes)
{
- struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched;
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
- bool has_sched = memchr_inv(sched_cfg->schedule, 0,
- sizeof(sched_cfg->schedule));
-
- lockdep_assert_wiphy(mld->wiphy);
+ bool previously_empty_schedule = !iwl_mld_nan_have_links(mld_vif);
+ struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched;
+ struct iwl_mld_nan_link *links[ARRAY_SIZE(sched_cfg->channels)] = {};
+ bool link_used[ARRAY_SIZE(mld_vif->nan.links)] = {};
+ struct iwl_mld_nan_link *nan_link;
+ bool empty_schedule = true;
+ int ret;
if (!(changes & BSS_CHANGED_NAN_LOCAL_SCHED))
return;
- if (has_sched && !mld_vif->nan.mac_added) {
- if (iwl_mld_add_nan_vif(mld, vif))
- IWL_ERR(mld, "Failed to add NAN vif\n");
- } else if (!has_sched && mld_vif->nan.mac_added) {
- iwl_mld_rm_vif(mld, vif);
+ for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
+ if (!sched_cfg->channels[i].chanreq.oper.chan)
+ continue;
+ empty_schedule = false;
+ break;
+ }
+
+ /* add the MAC if needed (before adding links) */
+ if (!empty_schedule && previously_empty_schedule) {
+ WARN_ON(mld_vif->nan.mac_added);
+ ret = iwl_mld_add_nan_vif(mld, vif);
+
+ if (ret) {
+ IWL_ERR(mld, "NAN: failed to add MAC (%d)\n", ret);
+ return;
+ }
+ }
+
+ if (!mld_vif->nan.mac_added) {
+ /* nothing to do */
+ return;
+ }
+
+ /* find links we can keep (same chanctx/PHY) */
+ for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
+ struct ieee80211_chanctx_conf *chanctx;
+ struct iwl_mld_nan_link *link;
+
+ chanctx = sched_cfg->channels[i].chanctx_conf;
+ /* ULW */
+ if (!chanctx)
+ continue;
+
+ link = iwl_mld_nan_find_link(mld_vif, chanctx);
+ links[i] = link;
+ if (link)
+ link_used[link->fw_id] = true;
+ }
+
+ /* add/reassign links for new channels */
+ for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
+ struct ieee80211_chanctx_conf *chanctx;
+
+ /* already have an existing active link */
+ if (links[i])
+ continue;
+
+ chanctx = sched_cfg->channels[i].chanctx_conf;
+ /* ULW or unused slot */
+ if (!chanctx)
+ continue;
+
+ /*
+ * if this fails we still update the schedule, but
+ * without a valid link we'll always ULW it
+ */
+ links[i] = iwl_mld_nan_link_add(mld, mld_vif, chanctx);
+
+ /* we have a link, activate it */
+ if (links[i]) {
+ link_used[links[i]->fw_id] = true;
+ iwl_mld_nan_link_set_active(mld, links[i], true);
+ }
+ }
+
+ /* delete unused links */
+ for_each_mld_nan_valid_link(mld_vif, nan_link) {
+ if (!link_used[nan_link->fw_id]) {
+ iwl_mld_nan_link_set_active(mld, nan_link, false);
+ iwl_mld_nan_link_remove(mld, nan_link);
+ }
+ }
+
+ /* remove MAC if needed */
+ if (!previously_empty_schedule && empty_schedule) {
+ /* must have been added */
+ WARN_ON_ONCE(!mld_vif->nan.mac_added);
+
+ /* mac80211 should reconfigure same state */
+ if (!WARN_ON_ONCE(mld->fw_status.in_hw_restart))
+ iwl_mld_rm_vif(mld, vif);
}
}