19e978d8eSIlan Peer // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 29e978d8eSIlan Peer /* 39e978d8eSIlan Peer * Copyright (C) 2025 Intel Corporation 49e978d8eSIlan Peer */ 59e978d8eSIlan Peer 69e978d8eSIlan Peer #include "mld.h" 79e978d8eSIlan Peer #include "iface.h" 853d649aeSMiri Korenblit #include "mlo.h" 99e978d8eSIlan Peer #include "fw/api/mac-cfg.h" 109e978d8eSIlan Peer 119e978d8eSIlan Peer #define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512 129e978d8eSIlan Peer #define IWL_NAN_RSSI_CLOSE 55 139e978d8eSIlan Peer #define IWL_NAN_RSSI_MIDDLE 70 149e978d8eSIlan Peer 159e978d8eSIlan Peer bool iwl_mld_nan_supported(struct iwl_mld *mld) 169e978d8eSIlan Peer { 179e978d8eSIlan Peer return fw_has_capa(&mld->fw->ucode_capa, 189e978d8eSIlan Peer IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT); 199e978d8eSIlan Peer } 209e978d8eSIlan Peer 2189000fe4SIlan Peer static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld, 2289000fe4SIlan Peer struct iwl_nan_config_cmd *cmd, 2389000fe4SIlan Peer u8 *beacon_data, size_t beacon_data_len) 249e978d8eSIlan Peer { 2589000fe4SIlan Peer struct iwl_host_cmd hcmd = { 2689000fe4SIlan Peer .id = WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD), 2789000fe4SIlan Peer }; 289e978d8eSIlan Peer 2989000fe4SIlan Peer hcmd.len[0] = sizeof(*cmd); 3089000fe4SIlan Peer hcmd.data[0] = cmd; 319e978d8eSIlan Peer 3289000fe4SIlan Peer if (beacon_data_len) { 3389000fe4SIlan Peer hcmd.len[1] = beacon_data_len; 3489000fe4SIlan Peer hcmd.data[1] = beacon_data; 3589000fe4SIlan Peer hcmd.dataflags[1] = IWL_HCMD_DFL_DUP; 3689000fe4SIlan Peer } 3789000fe4SIlan Peer 3889000fe4SIlan Peer return iwl_mld_send_cmd(mld, &hcmd); 399e978d8eSIlan Peer } 409e978d8eSIlan Peer 41ab410758SIlan Peer static int iwl_mld_nan_config(struct iwl_mld *mld, 42ab410758SIlan Peer struct ieee80211_vif *vif, 43ab410758SIlan Peer struct cfg80211_nan_conf *conf, 44ab410758SIlan Peer enum iwl_ctxt_action action) 459e978d8eSIlan Peer { 469e978d8eSIlan Peer struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 479e978d8eSIlan Peer struct iwl_nan_config_cmd cmd = { 48ab410758SIlan Peer .action = cpu_to_le32(action), 499e978d8eSIlan Peer }; 5089000fe4SIlan Peer u8 *data __free(kfree) = NULL; 519e978d8eSIlan Peer 529e978d8eSIlan Peer lockdep_assert_wiphy(mld->wiphy); 539e978d8eSIlan Peer 549e978d8eSIlan Peer ether_addr_copy(cmd.nmi_addr, vif->addr); 559e978d8eSIlan Peer cmd.master_pref = conf->master_pref; 5689000fe4SIlan Peer 57*5b31cf3fSBenjamin Berg memcpy(cmd.cluster_id, conf->cluster_id + 4, 58*5b31cf3fSBenjamin Berg sizeof(cmd.cluster_id)); 5989000fe4SIlan Peer 6089000fe4SIlan Peer cmd.scan_period = conf->scan_period < 255 ? conf->scan_period : 255; 6189000fe4SIlan Peer cmd.dwell_time = 6289000fe4SIlan Peer conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255; 6389000fe4SIlan Peer 6489000fe4SIlan Peer if (conf->discovery_beacon_interval) 6589000fe4SIlan Peer cmd.discovery_beacon_interval = 6689000fe4SIlan Peer cpu_to_le32(conf->discovery_beacon_interval); 6789000fe4SIlan Peer else 6889000fe4SIlan Peer cmd.discovery_beacon_interval = 6989000fe4SIlan Peer cpu_to_le32(IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU); 7089000fe4SIlan Peer 7189000fe4SIlan Peer if (conf->enable_dw_notification) 729e978d8eSIlan Peer cmd.flags = IWL_NAN_FLAG_DW_END_NOTIF_ENABLED; 739e978d8eSIlan Peer 7489000fe4SIlan Peer /* 2 GHz band must be supported */ 7589000fe4SIlan Peer cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_close = 7689000fe4SIlan Peer abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close); 7789000fe4SIlan Peer cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_middle = 7889000fe4SIlan Peer abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle); 7989000fe4SIlan Peer cmd.band_config[IWL_NAN_BAND_2GHZ].dw_interval = 8089000fe4SIlan Peer conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval; 819e978d8eSIlan Peer 8289000fe4SIlan Peer /* 5 GHz band operation is optional. Configure its operation if 8389000fe4SIlan Peer * supported. Note that conf->bands might be zero, so we need to check 8489000fe4SIlan Peer * the channel pointer, not the band mask. 8589000fe4SIlan Peer */ 8689000fe4SIlan Peer if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) { 8789000fe4SIlan Peer cmd.hb_channel = 8889000fe4SIlan Peer conf->band_cfgs[NL80211_BAND_5GHZ].chan->hw_value; 8989000fe4SIlan Peer 9089000fe4SIlan Peer cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_close = 9189000fe4SIlan Peer abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close); 9289000fe4SIlan Peer cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_middle = 9389000fe4SIlan Peer abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle); 9489000fe4SIlan Peer cmd.band_config[IWL_NAN_BAND_5GHZ].dw_interval = 9589000fe4SIlan Peer conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval; 969e978d8eSIlan Peer } 9789000fe4SIlan Peer 9889000fe4SIlan Peer if (conf->extra_nan_attrs_len || conf->vendor_elems_len) { 9989000fe4SIlan Peer data = kmalloc(conf->extra_nan_attrs_len + 10089000fe4SIlan Peer conf->vendor_elems_len, GFP_KERNEL); 10189000fe4SIlan Peer if (!data) 10289000fe4SIlan Peer return -ENOMEM; 10389000fe4SIlan Peer 10489000fe4SIlan Peer cmd.nan_attr_len = cpu_to_le32(conf->extra_nan_attrs_len); 10589000fe4SIlan Peer cmd.nan_vendor_elems_len = cpu_to_le32(conf->vendor_elems_len); 10689000fe4SIlan Peer 10789000fe4SIlan Peer if (conf->extra_nan_attrs_len) 10889000fe4SIlan Peer memcpy(data, conf->extra_nan_attrs, 10989000fe4SIlan Peer conf->extra_nan_attrs_len); 11089000fe4SIlan Peer 11189000fe4SIlan Peer if (conf->vendor_elems_len) 11289000fe4SIlan Peer memcpy(data + conf->extra_nan_attrs_len, 11389000fe4SIlan Peer conf->vendor_elems, 11489000fe4SIlan Peer conf->vendor_elems_len); 1159e978d8eSIlan Peer } 1169e978d8eSIlan Peer 117ab410758SIlan Peer cmd.sta_id = mld_vif->aux_sta.sta_id; 118ab410758SIlan Peer return iwl_mld_nan_send_config_cmd(mld, &cmd, data, 119ab410758SIlan Peer conf->extra_nan_attrs_len + 120ab410758SIlan Peer conf->vendor_elems_len); 121ab410758SIlan Peer } 122ab410758SIlan Peer 123ab410758SIlan Peer int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 124ab410758SIlan Peer struct cfg80211_nan_conf *conf) 125ab410758SIlan Peer { 126ab410758SIlan Peer struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 127ab410758SIlan Peer struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 128ab410758SIlan Peer struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta; 129ab410758SIlan Peer int ret; 130ab410758SIlan Peer 131ab410758SIlan Peer IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands); 132ab410758SIlan Peer 13353d649aeSMiri Korenblit ret = iwl_mld_update_emlsr_block(mld, true, IWL_MLD_EMLSR_BLOCKED_NAN); 1349e978d8eSIlan Peer if (ret) 1359e978d8eSIlan Peer return ret; 1369e978d8eSIlan Peer 13753d649aeSMiri Korenblit ret = iwl_mld_add_aux_sta(mld, aux_sta); 13853d649aeSMiri Korenblit if (ret) 13953d649aeSMiri Korenblit goto unblock_emlsr; 14053d649aeSMiri Korenblit 141ab410758SIlan Peer ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD); 1429e978d8eSIlan Peer if (ret) { 1439e978d8eSIlan Peer IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret); 14453d649aeSMiri Korenblit goto remove_aux; 1459e978d8eSIlan Peer } 14653d649aeSMiri Korenblit return 0; 14753d649aeSMiri Korenblit 14853d649aeSMiri Korenblit remove_aux: 14953d649aeSMiri Korenblit iwl_mld_remove_aux_sta(mld, vif); 15053d649aeSMiri Korenblit unblock_emlsr: 15153d649aeSMiri Korenblit iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN); 1529e978d8eSIlan Peer 1539e978d8eSIlan Peer return ret; 1549e978d8eSIlan Peer } 1559e978d8eSIlan Peer 156ab410758SIlan Peer int iwl_mld_nan_change_config(struct ieee80211_hw *hw, 157ab410758SIlan Peer struct ieee80211_vif *vif, 158ab410758SIlan Peer struct cfg80211_nan_conf *conf, 159ab410758SIlan Peer u32 changes) 160ab410758SIlan Peer { 161ab410758SIlan Peer struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 162ab410758SIlan Peer 163ab410758SIlan Peer IWL_DEBUG_MAC80211(mld, "NAN: change: changes=0x%x, bands=0x%x\n", 164ab410758SIlan Peer changes, conf->bands); 165ab410758SIlan Peer 166ab410758SIlan Peer /* Note that we do not use 'changes' as the FW always expects the 167ab410758SIlan Peer * complete configuration, and mac80211 always provides the complete 168ab410758SIlan Peer * configuration. 169ab410758SIlan Peer */ 170ab410758SIlan Peer return iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_MODIFY); 171ab410758SIlan Peer } 172ab410758SIlan Peer 1739e978d8eSIlan Peer int iwl_mld_stop_nan(struct ieee80211_hw *hw, 1749e978d8eSIlan Peer struct ieee80211_vif *vif) 1759e978d8eSIlan Peer { 1769e978d8eSIlan Peer struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 1779e978d8eSIlan Peer struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 1789e978d8eSIlan Peer struct iwl_nan_config_cmd cmd = { 1799e978d8eSIlan Peer .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 1809e978d8eSIlan Peer }; 1819e978d8eSIlan Peer int ret; 1829e978d8eSIlan Peer 1839e978d8eSIlan Peer lockdep_assert_wiphy(mld->wiphy); 1849e978d8eSIlan Peer 1859e978d8eSIlan Peer ret = iwl_mld_send_cmd_pdu(mld, 1869e978d8eSIlan Peer WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD), 1879e978d8eSIlan Peer &cmd); 1889e978d8eSIlan Peer if (ret) 1899e978d8eSIlan Peer IWL_ERR(mld, "NAN: Failed to stop NAN. ret=%d\n", ret); 1909e978d8eSIlan Peer 1919e978d8eSIlan Peer /* assume that higher layer guarantees that no additional frames are 1929e978d8eSIlan Peer * added before calling this callback 1939e978d8eSIlan Peer */ 1949e978d8eSIlan Peer iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); 1959e978d8eSIlan Peer iwl_mld_remove_aux_sta(mld, vif); 1969e978d8eSIlan Peer 19760094146SIlan Peer /* cancel based on object type being NAN, as the NAN objects do 19860094146SIlan Peer * not have a unique identifier associated with them 19960094146SIlan Peer */ 20060094146SIlan Peer iwl_mld_cancel_notifications_of_object(mld, 20160094146SIlan Peer IWL_MLD_OBJECT_TYPE_NAN, 20260094146SIlan Peer 0); 20353d649aeSMiri Korenblit 20453d649aeSMiri Korenblit iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN); 20553d649aeSMiri Korenblit 2069e978d8eSIlan Peer return 0; 2079e978d8eSIlan Peer } 2089e978d8eSIlan Peer 2099e978d8eSIlan Peer void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld, 2109e978d8eSIlan Peer struct iwl_rx_packet *pkt) 2119e978d8eSIlan Peer { 2129e978d8eSIlan Peer struct iwl_nan_cluster_notif *notif = (void *)pkt->data; 21360094146SIlan Peer struct wireless_dev *wdev = mld->nan_device_vif ? 21460094146SIlan Peer ieee80211_vif_to_wdev(mld->nan_device_vif) : NULL; 21560094146SIlan Peer bool new_cluster = !!(notif->flags & 21660094146SIlan Peer IWL_NAN_CLUSTER_NOTIF_FLAG_NEW_CLUSTER); 217*5b31cf3fSBenjamin Berg u8 cluster_id[ETH_ALEN] = { 218*5b31cf3fSBenjamin Berg 0x50, 0x6f, 0x9a, 0x01, 219*5b31cf3fSBenjamin Berg notif->cluster_id[0], notif->cluster_id[1] 22060094146SIlan Peer }; 2219e978d8eSIlan Peer 2229e978d8eSIlan Peer IWL_DEBUG_INFO(mld, 223*5b31cf3fSBenjamin Berg "NAN: cluster event: cluster_id=%pM, flags=0x%x\n", 224*5b31cf3fSBenjamin Berg cluster_id, notif->flags); 22560094146SIlan Peer 22660094146SIlan Peer if (IWL_FW_CHECK(mld, !wdev, "NAN: cluster event without wdev\n")) 22760094146SIlan Peer return; 22860094146SIlan Peer 22960094146SIlan Peer if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif), 23060094146SIlan Peer "NAN: cluster event without NAN started\n")) 23160094146SIlan Peer return; 23260094146SIlan Peer 23360094146SIlan Peer cfg80211_nan_cluster_joined(wdev, cluster_id, new_cluster, GFP_KERNEL); 23460094146SIlan Peer } 23560094146SIlan Peer 23660094146SIlan Peer bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld, 23760094146SIlan Peer struct iwl_rx_packet *pkt, 23860094146SIlan Peer u32 obj_id) 23960094146SIlan Peer { 24060094146SIlan Peer return true; 24160094146SIlan Peer } 24260094146SIlan Peer 24360094146SIlan Peer bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld, 24460094146SIlan Peer struct iwl_rx_packet *pkt, 24560094146SIlan Peer u32 obj_id) 24660094146SIlan Peer { 24760094146SIlan Peer return true; 2489e978d8eSIlan Peer } 2499e978d8eSIlan Peer 2509e978d8eSIlan Peer void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld, 2519e978d8eSIlan Peer struct iwl_rx_packet *pkt) 2529e978d8eSIlan Peer { 2539e978d8eSIlan Peer struct iwl_nan_dw_end_notif *notif = (void *)pkt->data; 2549e978d8eSIlan Peer struct iwl_mld_vif *mld_vif = mld->nan_device_vif ? 2559e978d8eSIlan Peer iwl_mld_vif_from_mac80211(mld->nan_device_vif) : 2569e978d8eSIlan Peer NULL; 25760094146SIlan Peer struct wireless_dev *wdev; 25860094146SIlan Peer struct ieee80211_channel *chan; 2599e978d8eSIlan Peer 2609e978d8eSIlan Peer IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band); 2619e978d8eSIlan Peer 26260094146SIlan Peer if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n")) 26360094146SIlan Peer return; 26460094146SIlan Peer 26560094146SIlan Peer if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif), 26660094146SIlan Peer "NAN: DW end without NAN started\n")) 2679e978d8eSIlan Peer return; 2689e978d8eSIlan Peer 2699e978d8eSIlan Peer if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA)) 2709e978d8eSIlan Peer return; 2719e978d8eSIlan Peer 2729e978d8eSIlan Peer IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n", 2739e978d8eSIlan Peer mld_vif->aux_sta.sta_id); 2749e978d8eSIlan Peer 2759e978d8eSIlan Peer iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); 27660094146SIlan Peer 27760094146SIlan Peer /* TODO: currently the notification specified the band on which the DW 27860094146SIlan Peer * ended. Need to change that to the actual channel on which the next DW 27960094146SIlan Peer * will be started. 28060094146SIlan Peer */ 28160094146SIlan Peer switch (notif->band) { 28260094146SIlan Peer case IWL_NAN_BAND_2GHZ: 28360094146SIlan Peer chan = ieee80211_get_channel(mld->wiphy, 2437); 28460094146SIlan Peer break; 28560094146SIlan Peer case IWL_NAN_BAND_5GHZ: 28660094146SIlan Peer /* TODO: use the actual channel */ 28760094146SIlan Peer chan = ieee80211_get_channel(mld->wiphy, 5745); 28860094146SIlan Peer break; 28960094146SIlan Peer default: 29060094146SIlan Peer IWL_FW_CHECK(mld, false, 29160094146SIlan Peer "NAN: Invalid band %u in DW end notif\n", 29260094146SIlan Peer notif->band); 29360094146SIlan Peer return; 29460094146SIlan Peer } 29560094146SIlan Peer 29660094146SIlan Peer wdev = ieee80211_vif_to_wdev(mld->nan_device_vif); 29760094146SIlan Peer cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL); 2989e978d8eSIlan Peer } 299