1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2024-2025 Intel Corporation 4 */ 5 6 #include "mld.h" 7 #include "stats.h" 8 #include "sta.h" 9 #include "mlo.h" 10 #include "hcmd.h" 11 #include "iface.h" 12 #include "scan.h" 13 #include "phy.h" 14 #include "fw/api/stats.h" 15 16 static int iwl_mld_send_fw_stats_cmd(struct iwl_mld *mld, u32 cfg_mask, 17 u32 cfg_time, u32 type_mask) 18 { 19 u32 cmd_id = WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_CMD); 20 struct iwl_system_statistics_cmd stats_cmd = { 21 .cfg_mask = cpu_to_le32(cfg_mask), 22 .config_time_sec = cpu_to_le32(cfg_time), 23 .type_id_mask = cpu_to_le32(type_mask), 24 }; 25 26 return iwl_mld_send_cmd_pdu(mld, cmd_id, &stats_cmd); 27 } 28 29 int iwl_mld_clear_stats_in_fw(struct iwl_mld *mld) 30 { 31 u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK; 32 u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER | 33 IWL_STATS_NTFY_TYPE_ID_OPER_PART1; 34 35 return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask); 36 } 37 38 static void 39 iwl_mld_fill_stats_from_oper_notif(struct iwl_mld *mld, 40 struct iwl_rx_packet *pkt, 41 u8 fw_sta_id, struct station_info *sinfo) 42 { 43 const struct iwl_system_statistics_notif_oper *notif = 44 (void *)&pkt->data; 45 const struct iwl_stats_ntfy_per_sta *per_sta = 46 ¬if->per_sta[fw_sta_id]; 47 struct ieee80211_link_sta *link_sta; 48 struct iwl_mld_link_sta *mld_link_sta; 49 50 /* 0 isn't a valid value, but FW might send 0. 51 * In that case, set the latest non-zero value we stored 52 */ 53 rcu_read_lock(); 54 55 link_sta = rcu_dereference(mld->fw_id_to_link_sta[fw_sta_id]); 56 if (IS_ERR_OR_NULL(link_sta)) 57 goto unlock; 58 59 mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); 60 if (WARN_ON(!mld_link_sta)) 61 goto unlock; 62 63 if (per_sta->average_energy) 64 mld_link_sta->signal_avg = 65 -(s8)le32_to_cpu(per_sta->average_energy); 66 67 sinfo->signal_avg = mld_link_sta->signal_avg; 68 sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); 69 70 unlock: 71 rcu_read_unlock(); 72 } 73 74 struct iwl_mld_stats_data { 75 u8 fw_sta_id; 76 struct station_info *sinfo; 77 struct iwl_mld *mld; 78 }; 79 80 static bool iwl_mld_wait_stats_handler(struct iwl_notif_wait_data *notif_data, 81 struct iwl_rx_packet *pkt, void *data) 82 { 83 struct iwl_mld_stats_data *stats_data = data; 84 u16 cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); 85 86 switch (cmd) { 87 case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF): 88 iwl_mld_fill_stats_from_oper_notif(stats_data->mld, pkt, 89 stats_data->fw_sta_id, 90 stats_data->sinfo); 91 break; 92 case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF): 93 break; 94 case WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF): 95 return true; 96 } 97 98 return false; 99 } 100 101 static int 102 iwl_mld_fw_stats_to_mac80211(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta, 103 struct station_info *sinfo) 104 { 105 u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK | 106 IWL_STATS_CFG_FLG_RESET_MSK; 107 u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER | 108 IWL_STATS_NTFY_TYPE_ID_OPER_PART1; 109 static const u16 notifications[] = { 110 WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF), 111 WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF), 112 WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF), 113 }; 114 struct iwl_mld_stats_data wait_stats_data = { 115 /* We don't support drv_sta_statistics in EMLSR */ 116 .fw_sta_id = mld_sta->deflink.fw_id, 117 .sinfo = sinfo, 118 .mld = mld, 119 }; 120 struct iwl_notification_wait stats_wait; 121 int ret; 122 123 iwl_init_notification_wait(&mld->notif_wait, &stats_wait, 124 notifications, ARRAY_SIZE(notifications), 125 iwl_mld_wait_stats_handler, 126 &wait_stats_data); 127 128 ret = iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask); 129 if (ret) { 130 iwl_remove_notification(&mld->notif_wait, &stats_wait); 131 return ret; 132 } 133 134 /* Wait 500ms for OPERATIONAL, PART1, and END notifications, 135 * which should be sufficient for the firmware to gather data 136 * from all LMACs and send notifications to the host. 137 */ 138 ret = iwl_wait_notification(&mld->notif_wait, &stats_wait, HZ / 2); 139 if (ret) 140 return ret; 141 142 /* When periodic statistics are sent, FW will clear its statistics DB. 143 * If the statistics request here happens shortly afterwards, 144 * the response will contain data collected over a short time 145 * interval. The response we got here shouldn't be processed by 146 * the general statistics processing because it's incomplete. 147 * So, we delete it from the list so it won't be processed. 148 */ 149 iwl_mld_delete_handlers(mld, notifications, ARRAY_SIZE(notifications)); 150 151 return 0; 152 } 153 154 #define PERIODIC_STATS_SECONDS 5 155 156 int iwl_mld_request_periodic_fw_stats(struct iwl_mld *mld, bool enable) 157 { 158 u32 cfg_mask = enable ? 0 : IWL_STATS_CFG_FLG_DISABLE_NTFY_MSK; 159 u32 type_mask = enable ? (IWL_STATS_NTFY_TYPE_ID_OPER | 160 IWL_STATS_NTFY_TYPE_ID_OPER_PART1) : 0; 161 u32 cfg_time = enable ? PERIODIC_STATS_SECONDS : 0; 162 163 return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, cfg_time, type_mask); 164 } 165 166 static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, 167 struct station_info *sinfo) 168 { 169 struct rate_info *rinfo = &sinfo->txrate; 170 u32 rate_n_flags = mld_sta->deflink.last_rate_n_flags; 171 u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; 172 u32 gi_ltf; 173 174 sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 175 176 switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { 177 case RATE_MCS_CHAN_WIDTH_20: 178 rinfo->bw = RATE_INFO_BW_20; 179 break; 180 case RATE_MCS_CHAN_WIDTH_40: 181 rinfo->bw = RATE_INFO_BW_40; 182 break; 183 case RATE_MCS_CHAN_WIDTH_80: 184 rinfo->bw = RATE_INFO_BW_80; 185 break; 186 case RATE_MCS_CHAN_WIDTH_160: 187 rinfo->bw = RATE_INFO_BW_160; 188 break; 189 case RATE_MCS_CHAN_WIDTH_320: 190 rinfo->bw = RATE_INFO_BW_320; 191 break; 192 } 193 194 if (format == RATE_MCS_MOD_TYPE_CCK || 195 format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) { 196 int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK); 197 198 /* add the offset needed to get to the legacy ofdm indices */ 199 if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) 200 rate += IWL_FIRST_OFDM_RATE; 201 202 switch (rate) { 203 case IWL_RATE_1M_INDEX: 204 rinfo->legacy = 10; 205 break; 206 case IWL_RATE_2M_INDEX: 207 rinfo->legacy = 20; 208 break; 209 case IWL_RATE_5M_INDEX: 210 rinfo->legacy = 55; 211 break; 212 case IWL_RATE_11M_INDEX: 213 rinfo->legacy = 110; 214 break; 215 case IWL_RATE_6M_INDEX: 216 rinfo->legacy = 60; 217 break; 218 case IWL_RATE_9M_INDEX: 219 rinfo->legacy = 90; 220 break; 221 case IWL_RATE_12M_INDEX: 222 rinfo->legacy = 120; 223 break; 224 case IWL_RATE_18M_INDEX: 225 rinfo->legacy = 180; 226 break; 227 case IWL_RATE_24M_INDEX: 228 rinfo->legacy = 240; 229 break; 230 case IWL_RATE_36M_INDEX: 231 rinfo->legacy = 360; 232 break; 233 case IWL_RATE_48M_INDEX: 234 rinfo->legacy = 480; 235 break; 236 case IWL_RATE_54M_INDEX: 237 rinfo->legacy = 540; 238 } 239 return; 240 } 241 242 rinfo->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; 243 244 if (format == RATE_MCS_MOD_TYPE_HT) 245 rinfo->mcs = RATE_HT_MCS_INDEX(rate_n_flags); 246 else 247 rinfo->mcs = u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK); 248 249 if (rate_n_flags & RATE_MCS_SGI_MSK) 250 rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; 251 252 switch (format) { 253 case RATE_MCS_MOD_TYPE_EHT: 254 rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS; 255 break; 256 case RATE_MCS_MOD_TYPE_HE: 257 gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK); 258 259 rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; 260 261 if (rate_n_flags & RATE_MCS_HE_106T_MSK) { 262 rinfo->bw = RATE_INFO_BW_HE_RU; 263 rinfo->he_ru_alloc = NL80211_RATE_INFO_HE_RU_ALLOC_106; 264 } 265 266 switch (rate_n_flags & RATE_MCS_HE_TYPE_MSK) { 267 case RATE_MCS_HE_TYPE_SU: 268 case RATE_MCS_HE_TYPE_EXT_SU: 269 if (gi_ltf == 0 || gi_ltf == 1) 270 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; 271 else if (gi_ltf == 2) 272 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; 273 else if (gi_ltf == 3) 274 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; 275 else 276 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; 277 break; 278 case RATE_MCS_HE_TYPE_MU: 279 if (gi_ltf == 0 || gi_ltf == 1) 280 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; 281 else if (gi_ltf == 2) 282 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; 283 else 284 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; 285 break; 286 case RATE_MCS_HE_TYPE_TRIG: 287 if (gi_ltf == 0 || gi_ltf == 1) 288 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; 289 else 290 rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; 291 break; 292 } 293 294 if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK) 295 rinfo->he_dcm = 1; 296 break; 297 case RATE_MCS_MOD_TYPE_HT: 298 rinfo->flags |= RATE_INFO_FLAGS_MCS; 299 break; 300 case RATE_MCS_MOD_TYPE_VHT: 301 rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; 302 break; 303 } 304 } 305 306 void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw, 307 struct ieee80211_vif *vif, 308 struct ieee80211_sta *sta, 309 struct station_info *sinfo) 310 { 311 struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); 312 313 /* This API is not EMLSR ready, so we cannot provide complete 314 * information if EMLSR is active 315 */ 316 if (hweight16(vif->active_links) > 1) 317 return; 318 319 if (iwl_mld_fw_stats_to_mac80211(mld_sta->mld, mld_sta, sinfo)) 320 return; 321 322 iwl_mld_sta_stats_fill_txrate(mld_sta, sinfo); 323 324 /* TODO: NL80211_STA_INFO_BEACON_RX */ 325 326 /* TODO: NL80211_STA_INFO_BEACON_SIGNAL_AVG */ 327 } 328 329 #define IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH 10 /* percentage */ 330 #define IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH 50 /* percentage */ 331 #define IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC (500 * 1000) 332 333 static u8 iwl_mld_stats_load_percentage(u32 last_ts_usec, u32 curr_ts_usec, 334 u32 total_airtime_usec) 335 { 336 u32 elapsed_usec = curr_ts_usec - last_ts_usec; 337 338 if (elapsed_usec < IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC) 339 return 0; 340 341 return (100 * total_airtime_usec / elapsed_usec); 342 } 343 344 static void iwl_mld_stats_recalc_traffic_load(struct iwl_mld *mld, 345 u32 total_airtime_usec, 346 u32 curr_ts_usec) 347 { 348 u32 last_ts_usec = mld->scan.traffic_load.last_stats_ts_usec; 349 u8 load_prec; 350 351 /* Skip the calculation as this is the first notification received */ 352 if (!last_ts_usec) 353 goto out; 354 355 load_prec = iwl_mld_stats_load_percentage(last_ts_usec, curr_ts_usec, 356 total_airtime_usec); 357 358 if (load_prec > IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH) 359 mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_HIGH; 360 else if (load_prec > IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH) 361 mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_MEDIUM; 362 else 363 mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_LOW; 364 365 out: 366 mld->scan.traffic_load.last_stats_ts_usec = curr_ts_usec; 367 } 368 369 static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig, 370 struct ieee80211_bss_conf *bss_conf) 371 { 372 struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld; 373 int exit_emlsr_thresh; 374 375 if (sig == 0) { 376 IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n"); 377 return; 378 } 379 380 /* TODO: task=statistics handle CQM notifications */ 381 382 if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH) 383 iwl_mld_int_mlo_scan(mld, vif); 384 385 if (!iwl_mld_emlsr_active(vif)) 386 return; 387 388 /* We are in EMLSR, check if we need to exit */ 389 exit_emlsr_thresh = 390 iwl_mld_get_emlsr_rssi_thresh(mld, &bss_conf->chanreq.oper, 391 true); 392 393 if (sig < exit_emlsr_thresh) 394 iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LOW_RSSI, 395 iwl_mld_get_other_link(vif, 396 bss_conf->link_id)); 397 } 398 399 static void 400 iwl_mld_process_per_link_stats(struct iwl_mld *mld, 401 const struct iwl_stats_ntfy_per_link *per_link, 402 u32 curr_ts_usec) 403 { 404 u32 total_airtime_usec = 0; 405 406 for (u32 fw_id = 0; 407 fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf); 408 fw_id++) { 409 const struct iwl_stats_ntfy_per_link *link_stats; 410 struct ieee80211_bss_conf *bss_conf; 411 int sig; 412 413 bss_conf = wiphy_dereference(mld->wiphy, 414 mld->fw_id_to_bss_conf[fw_id]); 415 if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION) 416 continue; 417 418 link_stats = &per_link[fw_id]; 419 420 total_airtime_usec += le32_to_cpu(link_stats->air_time); 421 422 sig = -le32_to_cpu(link_stats->beacon_filter_average_energy); 423 iwl_mld_update_link_sig(bss_conf->vif, sig, bss_conf); 424 425 /* TODO: parse more fields here (task=statistics)*/ 426 } 427 428 iwl_mld_stats_recalc_traffic_load(mld, total_airtime_usec, 429 curr_ts_usec); 430 } 431 432 static void 433 iwl_mld_process_per_sta_stats(struct iwl_mld *mld, 434 const struct iwl_stats_ntfy_per_sta *per_sta) 435 { 436 for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { 437 struct ieee80211_link_sta *link_sta = 438 wiphy_dereference(mld->wiphy, 439 mld->fw_id_to_link_sta[i]); 440 struct iwl_mld_link_sta *mld_link_sta; 441 s8 avg_energy = 442 -(s8)le32_to_cpu(per_sta[i].average_energy); 443 444 if (IS_ERR_OR_NULL(link_sta) || !avg_energy) 445 continue; 446 447 mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); 448 if (WARN_ON(!mld_link_sta)) 449 continue; 450 451 mld_link_sta->signal_avg = avg_energy; 452 } 453 } 454 455 static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, 456 struct ieee80211_chanctx_conf *ctx, 457 void *data) 458 { 459 struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); 460 const struct iwl_stats_ntfy_per_phy *per_phy = data; 461 u32 new_load, old_load; 462 463 if (WARN_ON(phy->fw_id >= IWL_STATS_MAX_PHY_OPERATIONAL)) 464 return; 465 466 phy->channel_load_by_us = 467 le32_to_cpu(per_phy[phy->fw_id].channel_load_by_us); 468 469 old_load = phy->avg_channel_load_not_by_us; 470 new_load = le32_to_cpu(per_phy[phy->fw_id].channel_load_not_by_us); 471 472 if (IWL_FW_CHECK(phy->mld, 473 new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD && 474 new_load > 100, 475 "Invalid channel load %u\n", new_load)) 476 return; 477 478 if (new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD) { 479 /* update giving a weight of 0.5 for the old value */ 480 phy->avg_channel_load_not_by_us = (new_load >> 1) + 481 (old_load >> 1); 482 } 483 484 iwl_mld_emlsr_check_chan_load(hw, phy, old_load); 485 } 486 487 static void 488 iwl_mld_process_per_phy_stats(struct iwl_mld *mld, 489 const struct iwl_stats_ntfy_per_phy *per_phy) 490 { 491 ieee80211_iter_chan_contexts_mtx(mld->hw, 492 iwl_mld_fill_chanctx_stats, 493 (void *)(uintptr_t)per_phy); 494 495 } 496 497 void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, 498 struct iwl_rx_packet *pkt) 499 { 500 const struct iwl_system_statistics_notif_oper *stats = 501 (void *)&pkt->data; 502 u32 curr_ts_usec = le32_to_cpu(stats->time_stamp); 503 504 BUILD_BUG_ON(ARRAY_SIZE(stats->per_sta) != IWL_STATION_COUNT_MAX); 505 BUILD_BUG_ON(ARRAY_SIZE(stats->per_link) < 506 ARRAY_SIZE(mld->fw_id_to_bss_conf)); 507 508 iwl_mld_process_per_link_stats(mld, stats->per_link, curr_ts_usec); 509 iwl_mld_process_per_sta_stats(mld, stats->per_sta); 510 iwl_mld_process_per_phy_stats(mld, stats->per_phy); 511 512 iwl_mld_check_omi_bw_reduction(mld); 513 } 514 515 void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld, 516 struct iwl_rx_packet *pkt) 517 { 518 /* TODO */ 519 } 520 521