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