1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2025 Intel Corporation 4 */ 5 6 #include "mld.h" 7 #include "iface.h" 8 #include "mlo.h" 9 #include "fw/api/mac-cfg.h" 10 11 #define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512 12 #define IWL_NAN_RSSI_CLOSE 55 13 #define IWL_NAN_RSSI_MIDDLE 70 14 15 bool iwl_mld_nan_supported(struct iwl_mld *mld) 16 { 17 return fw_has_capa(&mld->fw->ucode_capa, 18 IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT); 19 } 20 21 static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld, 22 struct iwl_nan_config_cmd *cmd, 23 u8 *beacon_data, size_t beacon_data_len) 24 { 25 struct iwl_host_cmd hcmd = { 26 .id = WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD), 27 }; 28 29 hcmd.len[0] = sizeof(*cmd); 30 hcmd.data[0] = cmd; 31 32 if (beacon_data_len) { 33 hcmd.len[1] = beacon_data_len; 34 hcmd.data[1] = beacon_data; 35 hcmd.dataflags[1] = IWL_HCMD_DFL_DUP; 36 } 37 38 return iwl_mld_send_cmd(mld, &hcmd); 39 } 40 41 static int iwl_mld_nan_config(struct iwl_mld *mld, 42 struct ieee80211_vif *vif, 43 struct cfg80211_nan_conf *conf, 44 enum iwl_ctxt_action action) 45 { 46 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 47 struct iwl_nan_config_cmd cmd = { 48 .action = cpu_to_le32(action), 49 }; 50 u8 *data __free(kfree) = NULL; 51 52 lockdep_assert_wiphy(mld->wiphy); 53 54 ether_addr_copy(cmd.nmi_addr, vif->addr); 55 cmd.master_pref = conf->master_pref; 56 57 memcpy(cmd.cluster_id, conf->cluster_id + 4, 58 sizeof(cmd.cluster_id)); 59 60 cmd.scan_period = conf->scan_period < 255 ? conf->scan_period : 255; 61 cmd.dwell_time = 62 conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255; 63 64 if (conf->discovery_beacon_interval) 65 cmd.discovery_beacon_interval = 66 cpu_to_le32(conf->discovery_beacon_interval); 67 else 68 cmd.discovery_beacon_interval = 69 cpu_to_le32(IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU); 70 71 if (conf->enable_dw_notification) 72 cmd.flags = IWL_NAN_FLAG_DW_END_NOTIF_ENABLED; 73 74 /* 2 GHz band must be supported */ 75 cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_close = 76 abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close); 77 cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_middle = 78 abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle); 79 cmd.band_config[IWL_NAN_BAND_2GHZ].dw_interval = 80 conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval; 81 82 /* 5 GHz band operation is optional. Configure its operation if 83 * supported. Note that conf->bands might be zero, so we need to check 84 * the channel pointer, not the band mask. 85 */ 86 if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) { 87 cmd.hb_channel = 88 conf->band_cfgs[NL80211_BAND_5GHZ].chan->hw_value; 89 90 cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_close = 91 abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close); 92 cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_middle = 93 abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle); 94 cmd.band_config[IWL_NAN_BAND_5GHZ].dw_interval = 95 conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval; 96 } 97 98 if (conf->extra_nan_attrs_len || conf->vendor_elems_len) { 99 data = kmalloc(conf->extra_nan_attrs_len + 100 conf->vendor_elems_len, GFP_KERNEL); 101 if (!data) 102 return -ENOMEM; 103 104 cmd.nan_attr_len = cpu_to_le32(conf->extra_nan_attrs_len); 105 cmd.nan_vendor_elems_len = cpu_to_le32(conf->vendor_elems_len); 106 107 if (conf->extra_nan_attrs_len) 108 memcpy(data, conf->extra_nan_attrs, 109 conf->extra_nan_attrs_len); 110 111 if (conf->vendor_elems_len) 112 memcpy(data + conf->extra_nan_attrs_len, 113 conf->vendor_elems, 114 conf->vendor_elems_len); 115 } 116 117 cmd.sta_id = mld_vif->aux_sta.sta_id; 118 return iwl_mld_nan_send_config_cmd(mld, &cmd, data, 119 conf->extra_nan_attrs_len + 120 conf->vendor_elems_len); 121 } 122 123 int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 124 struct cfg80211_nan_conf *conf) 125 { 126 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 127 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 128 struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta; 129 int ret; 130 131 IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands); 132 133 ret = iwl_mld_update_emlsr_block(mld, true, IWL_MLD_EMLSR_BLOCKED_NAN); 134 if (ret) 135 return ret; 136 137 ret = iwl_mld_add_aux_sta(mld, aux_sta); 138 if (ret) 139 goto unblock_emlsr; 140 141 ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD); 142 if (ret) { 143 IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret); 144 goto remove_aux; 145 } 146 return 0; 147 148 remove_aux: 149 iwl_mld_remove_aux_sta(mld, vif); 150 unblock_emlsr: 151 iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN); 152 153 return ret; 154 } 155 156 int iwl_mld_nan_change_config(struct ieee80211_hw *hw, 157 struct ieee80211_vif *vif, 158 struct cfg80211_nan_conf *conf, 159 u32 changes) 160 { 161 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 162 163 IWL_DEBUG_MAC80211(mld, "NAN: change: changes=0x%x, bands=0x%x\n", 164 changes, conf->bands); 165 166 /* Note that we do not use 'changes' as the FW always expects the 167 * complete configuration, and mac80211 always provides the complete 168 * configuration. 169 */ 170 return iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_MODIFY); 171 } 172 173 int iwl_mld_stop_nan(struct ieee80211_hw *hw, 174 struct ieee80211_vif *vif) 175 { 176 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 177 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 178 struct iwl_nan_config_cmd cmd = { 179 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 180 }; 181 int ret; 182 183 lockdep_assert_wiphy(mld->wiphy); 184 185 ret = iwl_mld_send_cmd_pdu(mld, 186 WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD), 187 &cmd); 188 if (ret) 189 IWL_ERR(mld, "NAN: Failed to stop NAN. ret=%d\n", ret); 190 191 /* assume that higher layer guarantees that no additional frames are 192 * added before calling this callback 193 */ 194 iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); 195 iwl_mld_remove_aux_sta(mld, vif); 196 197 /* cancel based on object type being NAN, as the NAN objects do 198 * not have a unique identifier associated with them 199 */ 200 iwl_mld_cancel_notifications_of_object(mld, 201 IWL_MLD_OBJECT_TYPE_NAN, 202 0); 203 204 iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN); 205 206 return 0; 207 } 208 209 void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld, 210 struct iwl_rx_packet *pkt) 211 { 212 struct iwl_nan_cluster_notif *notif = (void *)pkt->data; 213 struct wireless_dev *wdev = mld->nan_device_vif ? 214 ieee80211_vif_to_wdev(mld->nan_device_vif) : NULL; 215 bool new_cluster = !!(notif->flags & 216 IWL_NAN_CLUSTER_NOTIF_FLAG_NEW_CLUSTER); 217 u8 cluster_id[ETH_ALEN] = { 218 0x50, 0x6f, 0x9a, 0x01, 219 notif->cluster_id[0], notif->cluster_id[1] 220 }; 221 222 IWL_DEBUG_INFO(mld, 223 "NAN: cluster event: cluster_id=%pM, flags=0x%x\n", 224 cluster_id, notif->flags); 225 226 if (IWL_FW_CHECK(mld, !wdev, "NAN: cluster event without wdev\n")) 227 return; 228 229 if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif), 230 "NAN: cluster event without NAN started\n")) 231 return; 232 233 cfg80211_nan_cluster_joined(wdev, cluster_id, new_cluster, GFP_KERNEL); 234 } 235 236 bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld, 237 struct iwl_rx_packet *pkt, 238 u32 obj_id) 239 { 240 return true; 241 } 242 243 bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld, 244 struct iwl_rx_packet *pkt, 245 u32 obj_id) 246 { 247 return true; 248 } 249 250 void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld, 251 struct iwl_rx_packet *pkt) 252 { 253 struct iwl_nan_dw_end_notif *notif = (void *)pkt->data; 254 struct iwl_mld_vif *mld_vif = mld->nan_device_vif ? 255 iwl_mld_vif_from_mac80211(mld->nan_device_vif) : 256 NULL; 257 struct wireless_dev *wdev; 258 struct ieee80211_channel *chan; 259 260 IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band); 261 262 if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n")) 263 return; 264 265 if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif), 266 "NAN: DW end without NAN started\n")) 267 return; 268 269 if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA)) 270 return; 271 272 IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n", 273 mld_vif->aux_sta.sta_id); 274 275 iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); 276 277 /* TODO: currently the notification specified the band on which the DW 278 * ended. Need to change that to the actual channel on which the next DW 279 * will be started. 280 */ 281 switch (notif->band) { 282 case IWL_NAN_BAND_2GHZ: 283 chan = ieee80211_get_channel(mld->wiphy, 2437); 284 break; 285 case IWL_NAN_BAND_5GHZ: 286 /* TODO: use the actual channel */ 287 chan = ieee80211_get_channel(mld->wiphy, 5745); 288 break; 289 default: 290 IWL_FW_CHECK(mld, false, 291 "NAN: Invalid band %u in DW end notif\n", 292 notif->band); 293 return; 294 } 295 296 wdev = ieee80211_vif_to_wdev(mld->nan_device_vif); 297 cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL); 298 } 299