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 "debugfs.h" 8 #include "iwl-io.h" 9 #include "hcmd.h" 10 #include "iface.h" 11 #include "sta.h" 12 #include "tlc.h" 13 #include "power.h" 14 #include "notif.h" 15 #include "ap.h" 16 #include "iwl-utils.h" 17 #include "scan.h" 18 #ifdef CONFIG_THERMAL 19 #include "thermal.h" 20 #endif 21 22 #include "fw/api/rs.h" 23 #include "fw/api/dhc.h" 24 #include "fw/api/rfi.h" 25 #include "fw/dhc-utils.h" 26 #include <linux/dmi.h> 27 #include <linux/hex.h> 28 29 #define MLD_DEBUGFS_READ_FILE_OPS(name, bufsz) \ 30 _MLD_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_mld) 31 32 #define MLD_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 33 debugfs_create_file(alias, mode, parent, mld, \ 34 &iwl_dbgfs_##name##_ops) 35 #define MLD_DEBUGFS_ADD_FILE(name, parent, mode) \ 36 MLD_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 37 38 static bool iwl_mld_dbgfs_fw_cmd_disabled(struct iwl_mld *mld) 39 { 40 #ifdef CONFIG_PM_SLEEP 41 return !mld->fw_status.running || mld->fw_status.in_d3; 42 #else 43 return !mld->fw_status.running; 44 #endif /* CONFIG_PM_SLEEP */ 45 } 46 47 static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mld *mld, 48 char *buf, size_t count) 49 { 50 /* If the firmware is not running, silently succeed since there is 51 * no data to clear. 52 */ 53 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 54 return 0; 55 56 iwl_fw_dbg_clear_monitor_buf(&mld->fwrt); 57 58 return count; 59 } 60 61 static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mld *mld, char *buf, 62 size_t count) 63 { 64 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 65 return -EIO; 66 67 IWL_ERR(mld, "Triggering an NMI from debugfs\n"); 68 69 if (count == 6 && !strcmp(buf, "nolog\n")) 70 mld->fw_status.do_not_dump_once = true; 71 72 iwl_force_nmi(mld->trans); 73 74 return count; 75 } 76 77 static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf, 78 size_t count) 79 { 80 int __maybe_unused ret; 81 82 if (!iwlwifi_mod_params.fw_restart) 83 return -EPERM; 84 85 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 86 return -EIO; 87 88 if (count == 6 && !strcmp(buf, "nolog\n")) { 89 mld->fw_status.do_not_dump_once = true; 90 mld->trans->suppress_cmd_error_once = true; 91 } 92 93 /* take the return value to make compiler happy - it will 94 * fail anyway 95 */ 96 ret = iwl_mld_send_cmd_empty(mld, WIDE_ID(LONG_GROUP, REPLY_ERROR)); 97 98 return count; 99 } 100 101 static ssize_t iwl_dbgfs_send_echo_cmd_write(struct iwl_mld *mld, char *buf, 102 size_t count) 103 { 104 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 105 return -EIO; 106 107 return iwl_mld_send_cmd_empty(mld, ECHO_CMD) ?: count; 108 } 109 110 struct iwl_mld_sniffer_apply { 111 struct iwl_mld *mld; 112 const u8 *bssid; 113 u16 aid; 114 }; 115 116 static bool iwl_mld_sniffer_apply(struct iwl_notif_wait_data *notif_data, 117 struct iwl_rx_packet *pkt, void *data) 118 { 119 struct iwl_mld_sniffer_apply *apply = data; 120 121 apply->mld->monitor.cur_aid = cpu_to_le16(apply->aid); 122 memcpy(apply->mld->monitor.cur_bssid, apply->bssid, 123 sizeof(apply->mld->monitor.cur_bssid)); 124 125 return true; 126 } 127 128 static ssize_t 129 iwl_dbgfs_he_sniffer_params_write(struct iwl_mld *mld, char *buf, 130 size_t count) 131 { 132 struct iwl_notification_wait wait; 133 struct iwl_he_monitor_cmd he_mon_cmd = {}; 134 struct iwl_mld_sniffer_apply apply = { 135 .mld = mld, 136 }; 137 u16 wait_cmds[] = { 138 WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD), 139 }; 140 u32 aid; 141 int ret; 142 143 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 144 return -EIO; 145 146 if (!mld->monitor.on) 147 return -ENODEV; 148 149 ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid, 150 &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1], 151 &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3], 152 &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]); 153 if (ret != 7) 154 return -EINVAL; 155 156 he_mon_cmd.aid = cpu_to_le16(aid); 157 158 apply.aid = aid; 159 apply.bssid = (void *)he_mon_cmd.bssid; 160 161 /* Use the notification waiter to get our function triggered 162 * in sequence with other RX. This ensures that frames we get 163 * on the RX queue _before_ the new configuration is applied 164 * still have mld->cur_aid pointing to the old AID, and that 165 * frames on the RX queue _after_ the firmware processed the 166 * new configuration (and sent the response, synchronously) 167 * get mld->cur_aid correctly set to the new AID. 168 */ 169 iwl_init_notification_wait(&mld->notif_wait, &wait, 170 wait_cmds, ARRAY_SIZE(wait_cmds), 171 iwl_mld_sniffer_apply, &apply); 172 173 ret = iwl_mld_send_cmd_pdu(mld, 174 WIDE_ID(DATA_PATH_GROUP, 175 HE_AIR_SNIFFER_CONFIG_CMD), 176 &he_mon_cmd); 177 178 /* no need to really wait, we already did anyway */ 179 iwl_remove_notification(&mld->notif_wait, &wait); 180 181 return ret ?: count; 182 } 183 184 static ssize_t 185 iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, char *buf, size_t count) 186 { 187 return scnprintf(buf, count, 188 "%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", 189 le16_to_cpu(mld->monitor.cur_aid), 190 mld->monitor.cur_bssid[0], mld->monitor.cur_bssid[1], 191 mld->monitor.cur_bssid[2], mld->monitor.cur_bssid[3], 192 mld->monitor.cur_bssid[4], mld->monitor.cur_bssid[5]); 193 } 194 195 /* The size computation is as follows: 196 * each number needs at most 3 characters, number of rows is the size of 197 * the table; So, need 5 chars for the "freq: " part and each tuple afterwards 198 * needs 6 characters for numbers and 5 for the punctuation around. 32 bytes 199 * for feature support message. 200 */ 201 #define IWL_RFI_DDR_BUF_SIZE (IWL_RFI_DDR_LUT_INSTALLED_SIZE *\ 202 (5 + IWL_RFI_DDR_LUT_ENTRY_CHANNELS_NUM *\ 203 (6 + 5)) + 32) 204 #define IWL_RFI_DLVR_BUF_SIZE (IWL_RFI_DLVR_LUT_INSTALLED_SIZE *\ 205 (5 + IWL_RFI_DLVR_LUT_ENTRY_CHANNELS_NUM *\ 206 (6 + 5)) + 32) 207 #define IWL_RFI_DESENSE_BUF_SIZE IWL_RFI_DDR_BUF_SIZE 208 209 /* Extra 32 for "DDR and DLVR table" message */ 210 #define IWL_RFI_BUF_SIZE (IWL_RFI_DDR_BUF_SIZE + IWL_RFI_DLVR_BUF_SIZE +\ 211 IWL_RFI_DESENSE_BUF_SIZE + 32) 212 213 static size_t iwl_mld_dump_tas_resp(struct iwl_dhc_tas_status_resp *resp, 214 size_t count, u8 *buf) 215 { 216 const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = { 217 [TAS_DISABLED_DUE_TO_BIOS] = 218 "Due To BIOS", 219 [TAS_DISABLED_DUE_TO_SAR_6DBM] = 220 "Due To SAR Limit Less Than 6 dBm", 221 [TAS_DISABLED_REASON_INVALID] = 222 "N/A", 223 [TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] = 224 "Due to table source invalid" 225 }; 226 const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = { 227 [TAS_DYNA_INACTIVE] = "INACTIVE", 228 [TAS_DYNA_INACTIVE_MVM_MODE] = 229 "inactive due to mvm mode", 230 [TAS_DYNA_INACTIVE_TRIGGER_MODE] = 231 "inactive due to trigger mode", 232 [TAS_DYNA_INACTIVE_BLOCK_LISTED] = 233 "inactive due to block listed", 234 [TAS_DYNA_INACTIVE_UHB_NON_US] = 235 "inactive due to uhb non US", 236 [TAS_DYNA_ACTIVE] = "ACTIVE", 237 }; 238 ssize_t pos = 0; 239 240 if (resp->header.version != 1) { 241 pos += scnprintf(buf + pos, count - pos, 242 "Unsupported TAS response version:%d", 243 resp->header.version); 244 return pos; 245 } 246 247 pos += scnprintf(buf + pos, count - pos, "TAS Report\n"); 248 switch (resp->tas_config_info.table_source) { 249 case BIOS_SOURCE_NONE: 250 pos += scnprintf(buf + pos, count - pos, 251 "BIOS SOURCE NONE "); 252 break; 253 case BIOS_SOURCE_ACPI: 254 pos += scnprintf(buf + pos, count - pos, 255 "BIOS SOURCE ACPI "); 256 break; 257 case BIOS_SOURCE_UEFI: 258 pos += scnprintf(buf + pos, count - pos, 259 "BIOS SOURCE UEFI "); 260 break; 261 default: 262 pos += scnprintf(buf + pos, count - pos, 263 "BIOS SOURCE UNKNOWN (%d) ", 264 resp->tas_config_info.table_source); 265 break; 266 } 267 268 pos += scnprintf(buf + pos, count - pos, 269 "revision is: %d data is: 0x%08x\n", 270 resp->tas_config_info.table_revision, 271 resp->tas_config_info.value); 272 pos += scnprintf(buf + pos, count - pos, "Current MCC: 0x%x\n", 273 le16_to_cpu(resp->curr_mcc)); 274 275 pos += scnprintf(buf + pos, count - pos, "Block list entries:"); 276 for (int i = 0; i < ARRAY_SIZE(resp->mcc_block_list); i++) 277 pos += scnprintf(buf + pos, count - pos, " 0x%x", 278 le16_to_cpu(resp->mcc_block_list[i])); 279 280 pos += scnprintf(buf + pos, count - pos, 281 "\nDo TAS Support Dual Radio?: %s\n", 282 hweight8(resp->valid_radio_mask) > 1 ? 283 "TRUE" : "FALSE"); 284 285 for (int i = 0; i < ARRAY_SIZE(resp->tas_status_radio); i++) { 286 int tmp; 287 unsigned long dynamic_status; 288 289 if (!(resp->valid_radio_mask & BIT(i))) 290 continue; 291 292 pos += scnprintf(buf + pos, count - pos, 293 "TAS report for radio:%d\n", i + 1); 294 pos += scnprintf(buf + pos, count - pos, 295 "Static status: %sabled\n", 296 resp->tas_status_radio[i].static_status ? 297 "En" : "Dis"); 298 if (!resp->tas_status_radio[i].static_status) { 299 u8 static_disable_reason = 300 resp->tas_status_radio[i].static_disable_reason; 301 302 pos += scnprintf(buf + pos, count - pos, 303 "\tStatic Disabled Reason: "); 304 if (static_disable_reason >= TAS_DISABLED_REASON_MAX) { 305 pos += scnprintf(buf + pos, count - pos, 306 "unsupported value (%d)\n", 307 static_disable_reason); 308 continue; 309 } 310 311 pos += scnprintf(buf + pos, count - pos, 312 "%s (%d)\n", 313 tas_dis_reason[static_disable_reason], 314 static_disable_reason); 315 continue; 316 } 317 318 pos += scnprintf(buf + pos, count - pos, "\tANT A %s and ", 319 (resp->tas_status_radio[i].dynamic_status_ant_a 320 & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF"); 321 322 pos += scnprintf(buf + pos, count - pos, "ANT B %s for ", 323 (resp->tas_status_radio[i].dynamic_status_ant_b 324 & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF"); 325 326 switch (resp->tas_status_radio[i].band) { 327 case PHY_BAND_5: 328 pos += scnprintf(buf + pos, count - pos, "HB\n"); 329 break; 330 case PHY_BAND_24: 331 pos += scnprintf(buf + pos, count - pos, "LB\n"); 332 break; 333 case PHY_BAND_6: 334 pos += scnprintf(buf + pos, count - pos, "UHB\n"); 335 break; 336 default: 337 pos += scnprintf(buf + pos, count - pos, 338 "Unsupported band (%d)\n", 339 resp->tas_status_radio[i].band); 340 break; 341 } 342 343 pos += scnprintf(buf + pos, count - pos, 344 "Is near disconnection?: %s\n", 345 resp->tas_status_radio[i].near_disconnection ? 346 "True" : "False"); 347 348 pos += scnprintf(buf + pos, count - pos, 349 "Dynamic status antenna A:\n"); 350 dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_a; 351 for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) { 352 pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n", 353 tas_current_status[tmp], tmp); 354 } 355 pos += scnprintf(buf + pos, count - pos, 356 "\nDynamic status antenna B:\n"); 357 dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_b; 358 for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) { 359 pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n", 360 tas_current_status[tmp], tmp); 361 } 362 363 tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_a); 364 pos += scnprintf(buf + pos, count - pos, 365 "Max antenna A regulatory pwr limit (dBm): %d.%03d\n", 366 tmp / 8, 125 * (tmp % 8)); 367 tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_b); 368 pos += scnprintf(buf + pos, count - pos, 369 "Max antenna B regulatory pwr limit (dBm): %d.%03d\n", 370 tmp / 8, 125 * (tmp % 8)); 371 372 tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_a); 373 pos += scnprintf(buf + pos, count - pos, 374 "Antenna A SAR limit (dBm): %d.%03d\n", 375 tmp / 8, 125 * (tmp % 8)); 376 tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_b); 377 pos += scnprintf(buf + pos, count - pos, 378 "Antenna B SAR limit (dBm): %d.%03d\n", 379 tmp / 8, 125 * (tmp % 8)); 380 } 381 382 return pos; 383 } 384 385 static ssize_t iwl_dbgfs_tas_get_status_read(struct iwl_mld *mld, char *buf, 386 size_t count) 387 { 388 struct iwl_dhc_cmd cmd = { 389 .index_and_mask = cpu_to_le32(DHC_TABLE_TOOLS | 390 DHC_TARGET_UMAC | 391 DHC_TOOLS_UMAC_GET_TAS_STATUS), 392 }; 393 struct iwl_host_cmd hcmd = { 394 .id = WIDE_ID(LEGACY_GROUP, DEBUG_HOST_COMMAND), 395 .flags = CMD_WANT_SKB, 396 .len[0] = sizeof(cmd), 397 .data[0] = &cmd, 398 }; 399 struct iwl_dhc_tas_status_resp *resp = NULL; 400 u32 resp_len = 0; 401 ssize_t pos = 0; 402 u32 status; 403 int ret; 404 405 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 406 return -EIO; 407 408 ret = iwl_mld_send_cmd(mld, &hcmd); 409 if (ret) 410 return ret; 411 412 pos += scnprintf(buf + pos, count - pos, "\nOEM name: %s\n", 413 dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); 414 pos += scnprintf(buf + pos, count - pos, 415 "\tVendor In Approved List: %s\n", 416 iwl_is_tas_approved() ? "YES" : "NO"); 417 418 status = iwl_dhc_resp_status(mld->fwrt.fw, hcmd.resp_pkt); 419 if (status != 1) { 420 pos += scnprintf(buf + pos, count - pos, 421 "response status is not success: %d\n", 422 status); 423 goto out; 424 } 425 426 resp = iwl_dhc_resp_data(mld->fwrt.fw, hcmd.resp_pkt, &resp_len); 427 if (IS_ERR(resp) || resp_len != sizeof(*resp)) { 428 pos += scnprintf(buf + pos, count - pos, 429 "Invalid size for TAS response (%u instead of %zd)\n", 430 resp_len, sizeof(*resp)); 431 goto out; 432 } 433 434 pos += iwl_mld_dump_tas_resp(resp, count - pos, buf + pos); 435 436 out: 437 iwl_free_resp(&hcmd); 438 return pos; 439 } 440 441 WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_nmi, 10); 442 WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_restart, 10); 443 WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(he_sniffer_params, 32); 444 WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_dbg_clear, 10); 445 WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(send_echo_cmd, 8); 446 WIPHY_DEBUGFS_READ_FILE_OPS_MLD(tas_get_status, 2048); 447 448 static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld *mld, 449 size_t count, u8 *buf) 450 { 451 int err; 452 u32 value; 453 454 err = iwl_bios_get_dsm(&mld->fwrt, DSM_FUNC_ENABLE_6E, &value); 455 if (err) 456 return err; 457 458 return scnprintf(buf, count, "0x%08x\n", value); 459 } 460 461 MLD_DEBUGFS_READ_FILE_OPS(wifi_6e_enable, 64); 462 463 static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mld *mld, 464 char *buf, size_t count) 465 { 466 struct iwl_op_mode *opmode = container_of((void *)mld, 467 struct iwl_op_mode, 468 op_mode_specific); 469 struct iwl_rx_cmd_buffer rxb = {}; 470 struct iwl_rx_packet *pkt; 471 int n_bytes = count / 2; 472 int ret = -EINVAL; 473 474 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 475 return -EIO; 476 477 rxb._page = alloc_pages(GFP_KERNEL, 0); 478 if (!rxb._page) 479 return -ENOMEM; 480 pkt = rxb_addr(&rxb); 481 482 ret = hex2bin(page_address(rxb._page), buf, n_bytes); 483 if (ret) 484 goto out; 485 486 /* avoid invalid memory access and malformed packet */ 487 if (n_bytes < sizeof(*pkt) || 488 n_bytes != sizeof(*pkt) + iwl_rx_packet_payload_len(pkt)) 489 goto out; 490 491 local_bh_disable(); 492 iwl_mld_rx(opmode, NULL, &rxb); 493 local_bh_enable(); 494 ret = 0; 495 496 out: 497 iwl_free_rxb(&rxb); 498 499 return ret ?: count; 500 } 501 502 WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(inject_packet, 512); 503 504 #ifdef CONFIG_THERMAL 505 506 static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mld *mld, 507 char *buf, size_t count) 508 { 509 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 510 return -EIO; 511 512 return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, 513 CTDP_CMD_OPERATION_STOP) ? : count; 514 } 515 516 WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(stop_ctdp, 8); 517 518 static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mld *mld, 519 char *buf, size_t count) 520 { 521 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 522 return -EIO; 523 524 return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, 525 CTDP_CMD_OPERATION_START) ? : count; 526 } 527 528 WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(start_ctdp, 8); 529 530 #endif /* CONFIG_THERMAL */ 531 532 void 533 iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) 534 { 535 /* Add debugfs files here */ 536 537 MLD_DEBUGFS_ADD_FILE(fw_nmi, debugfs_dir, 0200); 538 MLD_DEBUGFS_ADD_FILE(fw_restart, debugfs_dir, 0200); 539 MLD_DEBUGFS_ADD_FILE(wifi_6e_enable, debugfs_dir, 0400); 540 MLD_DEBUGFS_ADD_FILE(he_sniffer_params, debugfs_dir, 0600); 541 MLD_DEBUGFS_ADD_FILE(fw_dbg_clear, debugfs_dir, 0200); 542 MLD_DEBUGFS_ADD_FILE(send_echo_cmd, debugfs_dir, 0200); 543 MLD_DEBUGFS_ADD_FILE(tas_get_status, debugfs_dir, 0400); 544 #ifdef CONFIG_THERMAL 545 MLD_DEBUGFS_ADD_FILE(start_ctdp, debugfs_dir, 0200); 546 MLD_DEBUGFS_ADD_FILE(stop_ctdp, debugfs_dir, 0200); 547 #endif 548 MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200); 549 550 #ifdef CONFIG_PM_SLEEP 551 debugfs_create_u32("max_sleep", 0600, debugfs_dir, 552 &mld->debug_max_sleep); 553 #endif 554 555 debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir, 556 &mld->monitor.ptp_time); 557 558 /* Create a symlink with mac80211. It will be removed when mac80211 559 * exits (before the opmode exits which removes the target.) 560 */ 561 if (!IS_ERR(debugfs_dir)) { 562 char buf[100]; 563 564 snprintf(buf, 100, "../../%pd2", debugfs_dir->d_parent); 565 debugfs_create_symlink("iwlwifi", mld->wiphy->debugfsdir, 566 buf); 567 } 568 } 569 570 #define VIF_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ 571 WIPHY_DEBUGFS_WRITE_FILE_OPS(vif_##name, bufsz, vif) 572 573 #define VIF_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ 574 IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(vif_##name, bufsz, vif) \ 575 576 #define VIF_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 577 debugfs_create_file(alias, mode, parent, vif, \ 578 &iwl_dbgfs_vif_##name##_ops) 579 #define VIF_DEBUGFS_ADD_FILE(name, parent, mode) \ 580 VIF_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 581 582 static ssize_t iwl_dbgfs_vif_bf_params_write(struct iwl_mld *mld, char *buf, 583 size_t count, void *data) 584 { 585 struct ieee80211_vif *vif = data; 586 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 587 int link_id = vif->active_links ? __ffs(vif->active_links) : 0; 588 struct ieee80211_bss_conf *link_conf; 589 int val; 590 591 if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { 592 if (sscanf(buf + 24, "%d", &val) != 1) 593 return -EINVAL; 594 } else { 595 return -EINVAL; 596 } 597 598 if (val != 0 && val != 1) 599 return -EINVAL; 600 601 link_conf = link_conf_dereference_protected(vif, link_id); 602 if (WARN_ON(!link_conf)) 603 return -ENODEV; 604 605 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 606 return -EIO; 607 608 mld_vif->disable_bf = !val; 609 610 if (val) 611 return iwl_mld_enable_beacon_filter(mld, link_conf, 612 false) ?: count; 613 else 614 return iwl_mld_disable_beacon_filter(mld, vif) ?: count; 615 } 616 617 static ssize_t iwl_dbgfs_vif_pm_params_write(struct iwl_mld *mld, 618 char *buf, 619 size_t count, void *data) 620 { 621 struct ieee80211_vif *vif = data; 622 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 623 int val; 624 625 if (!strncmp("use_ps_poll=", buf, 12)) { 626 if (sscanf(buf + 12, "%d", &val) != 1) 627 return -EINVAL; 628 } else { 629 return -EINVAL; 630 } 631 632 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 633 return -EIO; 634 635 mld_vif->use_ps_poll = val; 636 637 return iwl_mld_update_mac_power(mld, vif, false) ?: count; 638 } 639 640 static ssize_t iwl_dbgfs_vif_low_latency_write(struct iwl_mld *mld, 641 char *buf, size_t count, 642 void *data) 643 { 644 struct ieee80211_vif *vif = data; 645 u8 value; 646 int ret; 647 648 ret = kstrtou8(buf, 0, &value); 649 if (ret) 650 return ret; 651 652 if (value > 1) 653 return -EINVAL; 654 655 iwl_mld_vif_update_low_latency(mld, vif, value, LOW_LATENCY_DEBUGFS); 656 657 return count; 658 } 659 660 static ssize_t iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif *vif, 661 size_t count, char *buf) 662 { 663 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 664 char format[] = "traffic=%d\ndbgfs=%d\nvif_type=%d\nactual=%d\n"; 665 u8 ll_causes; 666 667 if (WARN_ON(count < sizeof(format))) 668 return -EINVAL; 669 670 ll_causes = READ_ONCE(mld_vif->low_latency_causes); 671 672 /* all values in format are boolean so the size of format is enough 673 * for holding the result string 674 */ 675 return scnprintf(buf, count, format, 676 !!(ll_causes & LOW_LATENCY_TRAFFIC), 677 !!(ll_causes & LOW_LATENCY_DEBUGFS), 678 !!(ll_causes & LOW_LATENCY_VIF_TYPE), 679 !!(ll_causes)); 680 } 681 682 VIF_DEBUGFS_WRITE_FILE_OPS(pm_params, 32); 683 VIF_DEBUGFS_WRITE_FILE_OPS(bf_params, 32); 684 VIF_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 45); 685 686 static int 687 _iwl_dbgfs_inject_beacon_ie(struct iwl_mld *mld, struct ieee80211_vif *vif, 688 char *bin, ssize_t len, 689 bool restore) 690 { 691 struct iwl_mld_vif *mld_vif; 692 struct iwl_mld_link *mld_link; 693 struct iwl_mac_beacon_cmd beacon_cmd = {}; 694 int n_bytes = len / 2; 695 696 /* Element len should be represented by u8 */ 697 if (n_bytes >= U8_MAX) 698 return -EINVAL; 699 700 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 701 return -EIO; 702 703 if (!vif) 704 return -EINVAL; 705 706 mld_vif = iwl_mld_vif_from_mac80211(vif); 707 mld_vif->beacon_inject_active = true; 708 mld->hw->extra_beacon_tailroom = n_bytes; 709 710 for_each_mld_vif_valid_link(mld_vif, mld_link) { 711 u32 offset; 712 struct ieee80211_tx_info *info; 713 struct ieee80211_bss_conf *link_conf = 714 link_conf_dereference_protected(vif, link_id); 715 struct ieee80211_chanctx_conf *ctx = 716 wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); 717 struct sk_buff *beacon = 718 ieee80211_beacon_get_template(mld->hw, vif, 719 NULL, link_id); 720 721 if (!beacon) 722 return -EINVAL; 723 724 if (!restore && (WARN_ON(!n_bytes || !bin) || 725 hex2bin(skb_put_zero(beacon, n_bytes), 726 bin, n_bytes))) { 727 dev_kfree_skb(beacon); 728 return -EINVAL; 729 } 730 731 info = IEEE80211_SKB_CB(beacon); 732 733 beacon_cmd.flags = 734 cpu_to_le16(iwl_mld_get_rate_flags(mld, info, vif, 735 link_conf, 736 ctx->def.chan->band)); 737 beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); 738 beacon_cmd.link_id = 739 cpu_to_le32(mld_link->fw_id); 740 741 iwl_mld_set_tim_idx(mld, &beacon_cmd.tim_idx, 742 beacon->data, beacon->len); 743 744 offset = iwl_find_ie_offset(beacon->data, 745 WLAN_EID_S1G_TWT, 746 beacon->len); 747 748 beacon_cmd.btwt_offset = cpu_to_le32(offset); 749 750 iwl_mld_send_beacon_template_cmd(mld, beacon, &beacon_cmd); 751 dev_kfree_skb(beacon); 752 } 753 754 if (restore) 755 mld_vif->beacon_inject_active = false; 756 757 return 0; 758 } 759 760 static ssize_t 761 iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld *mld, 762 char *buf, size_t count, 763 void *data) 764 { 765 struct ieee80211_vif *vif = data; 766 int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, buf, 767 count, false); 768 769 mld->hw->extra_beacon_tailroom = 0; 770 return ret ?: count; 771 } 772 773 VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512); 774 775 static ssize_t 776 iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld *mld, 777 char *buf, 778 size_t count, 779 void *data) 780 { 781 struct ieee80211_vif *vif = data; 782 int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, NULL, 783 0, true); 784 785 mld->hw->extra_beacon_tailroom = 0; 786 return ret ?: count; 787 } 788 789 VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512); 790 791 static ssize_t 792 iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count, 793 void *data) 794 { 795 struct iwl_host_cmd hcmd = { 796 .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, DEBUG_HOST_COMMAND), 797 }; 798 struct ieee80211_vif *vif = data; 799 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 800 struct iwl_dhc_cmd *cmd __free(kfree) = NULL; 801 struct iwl_dhc_twt_operation *dhc_twt_cmd; 802 u64 target_wake_time; 803 u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration; 804 u8 trigger, flow_type, flow_id, protection, tenth_param; 805 u8 twt_request = 1, broadcast = 0; 806 int ret; 807 808 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 809 return -EIO; 810 811 ret = sscanf(buf, "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu", 812 &twt_operation, &target_wake_time, &interval_exp, 813 &interval_mantissa, &min_wake_duration, &trigger, 814 &flow_type, &flow_id, &protection, &tenth_param); 815 816 /* the new twt_request parameter is optional for station */ 817 if ((ret != 9 && ret != 10) || 818 (ret == 10 && vif->type != NL80211_IFTYPE_STATION && 819 tenth_param == 1)) 820 return -EINVAL; 821 822 /* The 10th parameter: 823 * In STA mode - the TWT type (broadcast or individual) 824 * In AP mode - the role (0 responder, 2 unsolicited) 825 */ 826 if (ret == 10) { 827 if (vif->type == NL80211_IFTYPE_STATION) 828 broadcast = tenth_param; 829 else 830 twt_request = tenth_param; 831 } 832 833 cmd = kzalloc(sizeof(*cmd) + sizeof(*dhc_twt_cmd), GFP_KERNEL); 834 if (!cmd) 835 return -ENOMEM; 836 837 dhc_twt_cmd = (void *)cmd->data; 838 dhc_twt_cmd->mac_id = cpu_to_le32(mld_vif->fw_id); 839 dhc_twt_cmd->twt_operation = cpu_to_le32(twt_operation); 840 dhc_twt_cmd->target_wake_time = cpu_to_le64(target_wake_time); 841 dhc_twt_cmd->interval_exp = cpu_to_le32(interval_exp); 842 dhc_twt_cmd->interval_mantissa = cpu_to_le32(interval_mantissa); 843 dhc_twt_cmd->min_wake_duration = cpu_to_le32(min_wake_duration); 844 dhc_twt_cmd->trigger = trigger; 845 dhc_twt_cmd->flow_type = flow_type; 846 dhc_twt_cmd->flow_id = flow_id; 847 dhc_twt_cmd->protection = protection; 848 dhc_twt_cmd->twt_request = twt_request; 849 dhc_twt_cmd->negotiation_type = broadcast ? 3 : 0; 850 851 cmd->length = cpu_to_le32(sizeof(*dhc_twt_cmd) >> 2); 852 cmd->index_and_mask = 853 cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC | 854 DHC_INT_UMAC_TWT_OPERATION); 855 856 hcmd.len[0] = sizeof(*cmd) + sizeof(*dhc_twt_cmd); 857 hcmd.data[0] = cmd; 858 859 ret = iwl_mld_send_cmd(mld, &hcmd); 860 861 return ret ?: count; 862 } 863 864 VIF_DEBUGFS_WRITE_FILE_OPS(twt_setup, 256); 865 866 static ssize_t 867 iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count, 868 void *data) 869 { 870 struct ieee80211_vif *vif = data; 871 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 872 struct iwl_twt_operation_cmd twt_cmd = {}; 873 int link_id = vif->active_links ? __ffs(vif->active_links) : 0; 874 struct iwl_mld_link *mld_link = iwl_mld_link_dereference_check(mld_vif, 875 link_id); 876 int ret; 877 878 if (WARN_ON(!mld_link)) 879 return -ENODEV; 880 881 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 882 return -EIO; 883 884 if (hweight16(vif->active_links) > 1) 885 return -EOPNOTSUPP; 886 887 ret = sscanf(buf, 888 "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu", 889 &twt_cmd.twt_operation, &twt_cmd.target_wake_time, 890 &twt_cmd.interval_exponent, &twt_cmd.interval_mantissa, 891 &twt_cmd.minimum_wake_duration, &twt_cmd.trigger, 892 &twt_cmd.flow_type, &twt_cmd.flow_id, 893 &twt_cmd.twt_protection, &twt_cmd.ndp_paging_indicator, 894 &twt_cmd.responder_pm_mode, &twt_cmd.negotiation_type, 895 &twt_cmd.twt_request, &twt_cmd.implicit, 896 &twt_cmd.twt_group_assignment, &twt_cmd.twt_channel, 897 &twt_cmd.restricted_info_present, &twt_cmd.dl_bitmap_valid, 898 &twt_cmd.ul_bitmap_valid, &twt_cmd.dl_tid_bitmap, 899 &twt_cmd.ul_tid_bitmap); 900 901 if (ret != 21) 902 return -EINVAL; 903 904 twt_cmd.link_id = cpu_to_le32(mld_link->fw_id); 905 906 ret = iwl_mld_send_cmd_pdu(mld, 907 WIDE_ID(MAC_CONF_GROUP, TWT_OPERATION_CMD), 908 &twt_cmd); 909 return ret ?: count; 910 } 911 912 VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256); 913 914 static ssize_t iwl_dbgfs_vif_int_mlo_scan_write(struct iwl_mld *mld, char *buf, 915 size_t count, void *data) 916 { 917 struct ieee80211_vif *vif = data; 918 u32 action; 919 int ret; 920 921 if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) 922 return -EINVAL; 923 924 if (kstrtou32(buf, 0, &action)) 925 return -EINVAL; 926 927 if (action == 0) { 928 ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_INT_MLO, false); 929 } else if (action == 1) { 930 iwl_mld_int_mlo_scan(mld, vif); 931 ret = 0; 932 } else { 933 ret = -EINVAL; 934 } 935 936 return ret ?: count; 937 } 938 939 VIF_DEBUGFS_WRITE_FILE_OPS(int_mlo_scan, 32); 940 941 void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, 942 struct ieee80211_vif *vif) 943 { 944 struct dentry *mld_vif_dbgfs = 945 debugfs_create_dir("iwlmld", vif->debugfs_dir); 946 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 947 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 948 char target[3 * 3 + 11 + (NL80211_WIPHY_NAME_MAXLEN + 1) + 949 (7 + IFNAMSIZ + 1) + 6 + 1]; 950 char name[7 + IFNAMSIZ + 1]; 951 952 /* Create symlink for convenience pointing to interface specific 953 * debugfs entries for the driver. For example, under 954 * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmld/ 955 * find 956 * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmld/ 957 */ 958 snprintf(name, sizeof(name), "%pd", vif->debugfs_dir); 959 snprintf(target, sizeof(target), "../../../%pd3/iwlmld", 960 vif->debugfs_dir); 961 if (!mld_vif->dbgfs_slink) 962 mld_vif->dbgfs_slink = 963 debugfs_create_symlink(name, mld->debugfs_dir, target); 964 965 if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && 966 vif->type == NL80211_IFTYPE_STATION) { 967 VIF_DEBUGFS_ADD_FILE(pm_params, mld_vif_dbgfs, 0200); 968 VIF_DEBUGFS_ADD_FILE(bf_params, mld_vif_dbgfs, 0200); 969 } 970 971 if (vif->type == NL80211_IFTYPE_AP) { 972 VIF_DEBUGFS_ADD_FILE(inject_beacon_ie, mld_vif_dbgfs, 0200); 973 VIF_DEBUGFS_ADD_FILE(inject_beacon_ie_restore, 974 mld_vif_dbgfs, 0200); 975 } 976 977 VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600); 978 VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200); 979 VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200); 980 VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200); 981 } 982 #define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ 983 WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf) 984 985 #define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 986 debugfs_create_file(alias, mode, parent, link_conf, \ 987 &iwl_dbgfs_link_##name##_ops) 988 #define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \ 989 LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 990 991 void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, 992 struct ieee80211_vif *vif, 993 struct ieee80211_bss_conf *link_conf, 994 struct dentry *dir) 995 { 996 struct dentry *mld_link_dir; 997 998 mld_link_dir = debugfs_lookup("iwlmld", dir); 999 1000 /* For non-MLO vifs, the dir of deflink is the same as the vif's one. 1001 * so if iwlmld dir already exists, this means that this is deflink. 1002 * If not, this is a per-link dir of a MLO vif, add in it the iwlmld 1003 * dir. 1004 */ 1005 if (!mld_link_dir) { 1006 mld_link_dir = debugfs_create_dir("iwlmld", dir); 1007 } else { 1008 /* Release the reference from debugfs_lookup */ 1009 dput(mld_link_dir); 1010 } 1011 } 1012 1013 static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, 1014 size_t count, void *data, bool v3) 1015 { 1016 struct ieee80211_link_sta *link_sta = data; 1017 struct iwl_mld_link_sta *mld_link_sta; 1018 u32 rate; 1019 u32 partial = false; 1020 char pretty_rate[100]; 1021 int ret; 1022 u8 fw_sta_id; 1023 1024 mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); 1025 if (WARN_ON(!mld_link_sta)) 1026 return -EINVAL; 1027 1028 fw_sta_id = mld_link_sta->fw_id; 1029 1030 if (sscanf(buf, "%i %i", &rate, &partial) == 0) 1031 return -EINVAL; 1032 1033 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 1034 return -EIO; 1035 1036 /* input is in FW format (v2 or v3) so convert to v3 */ 1037 rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3); 1038 rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3)); 1039 1040 ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, 1041 partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE : 1042 IWL_TLC_DEBUG_FIXED_RATE, 1043 rate); 1044 1045 rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), rate); 1046 1047 IWL_DEBUG_RATE(mld, "sta_id %d rate %s partial: %d, ret:%d\n", 1048 fw_sta_id, pretty_rate, partial, ret); 1049 1050 return ret ? : count; 1051 } 1052 1053 static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, 1054 size_t count, void *data) 1055 { 1056 return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false); 1057 } 1058 1059 static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf, 1060 size_t count, void *data) 1061 { 1062 return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true); 1063 } 1064 1065 static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, 1066 size_t count, void *data) 1067 { 1068 struct ieee80211_link_sta *link_sta = data; 1069 struct iwl_mld_link_sta *mld_link_sta; 1070 u32 type, value; 1071 int ret; 1072 u8 fw_sta_id; 1073 1074 mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); 1075 if (WARN_ON(!mld_link_sta)) 1076 return -EINVAL; 1077 1078 fw_sta_id = mld_link_sta->fw_id; 1079 1080 if (sscanf(buf, "%i %i", &type, &value) != 2) { 1081 IWL_DEBUG_RATE(mld, "usage <type> <value>\n"); 1082 return -EINVAL; 1083 } 1084 1085 if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 1086 return -EIO; 1087 1088 ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, type, value); 1089 1090 return ret ? : count; 1091 } 1092 1093 #define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 1094 debugfs_create_file(alias, mode, parent, link_sta, \ 1095 &iwl_dbgfs_##name##_ops) 1096 #define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \ 1097 LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 1098 1099 #define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \ 1100 WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta) 1101 1102 LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64); 1103 LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64); 1104 LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64); 1105 1106 void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, 1107 struct ieee80211_vif *vif, 1108 struct ieee80211_link_sta *link_sta, 1109 struct dentry *dir) 1110 { 1111 LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200); 1112 LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200); 1113 LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200); 1114 } 1115