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