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" 17d1e879ecSMiri Korenblit #ifdef CONFIG_THERMAL 18d1e879ecSMiri Korenblit #include "thermal.h" 19d1e879ecSMiri Korenblit #endif 20d1e879ecSMiri Korenblit 21d1e879ecSMiri Korenblit #include "fw/api/rs.h" 22d1e879ecSMiri Korenblit #include "fw/api/dhc.h" 23d1e879ecSMiri Korenblit #include "fw/api/rfi.h" 24d1e879ecSMiri Korenblit 25d1e879ecSMiri Korenblit #define MLD_DEBUGFS_READ_FILE_OPS(name, bufsz) \ 26d1e879ecSMiri Korenblit _MLD_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_mld) 27d1e879ecSMiri Korenblit 28d1e879ecSMiri Korenblit #define MLD_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 29d1e879ecSMiri Korenblit debugfs_create_file(alias, mode, parent, mld, \ 30d1e879ecSMiri Korenblit &iwl_dbgfs_##name##_ops) 31d1e879ecSMiri Korenblit #define MLD_DEBUGFS_ADD_FILE(name, parent, mode) \ 32d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 33d1e879ecSMiri Korenblit 34d1e879ecSMiri Korenblit static bool iwl_mld_dbgfs_fw_cmd_disabled(struct iwl_mld *mld) 35d1e879ecSMiri Korenblit { 36d1e879ecSMiri Korenblit #ifdef CONFIG_PM_SLEEP 37d1e879ecSMiri Korenblit return !mld->fw_status.running || mld->fw_status.in_d3; 38d1e879ecSMiri Korenblit #else 39d1e879ecSMiri Korenblit return !mld->fw_status.running; 40d1e879ecSMiri Korenblit #endif /* CONFIG_PM_SLEEP */ 41d1e879ecSMiri Korenblit } 42d1e879ecSMiri Korenblit 43d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mld *mld, 44d1e879ecSMiri Korenblit char *buf, size_t count) 45d1e879ecSMiri Korenblit { 46d1e879ecSMiri Korenblit /* If the firmware is not running, silently succeed since there is 47d1e879ecSMiri Korenblit * no data to clear. 48d1e879ecSMiri Korenblit */ 49d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 50d1e879ecSMiri Korenblit return 0; 51d1e879ecSMiri Korenblit 52d1e879ecSMiri Korenblit iwl_fw_dbg_clear_monitor_buf(&mld->fwrt); 53d1e879ecSMiri Korenblit 54d1e879ecSMiri Korenblit return count; 55d1e879ecSMiri Korenblit } 56d1e879ecSMiri Korenblit 57d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mld *mld, char *buf, 58d1e879ecSMiri Korenblit size_t count) 59d1e879ecSMiri Korenblit { 60d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 61d1e879ecSMiri Korenblit return -EIO; 62d1e879ecSMiri Korenblit 63d1e879ecSMiri Korenblit IWL_ERR(mld, "Triggering an NMI from debugfs\n"); 64d1e879ecSMiri Korenblit 65d1e879ecSMiri Korenblit if (count == 6 && !strcmp(buf, "nolog\n")) 66d1e879ecSMiri Korenblit mld->fw_status.do_not_dump_once = true; 67d1e879ecSMiri Korenblit 68d1e879ecSMiri Korenblit iwl_force_nmi(mld->trans); 69d1e879ecSMiri Korenblit 70d1e879ecSMiri Korenblit return count; 71d1e879ecSMiri Korenblit } 72d1e879ecSMiri Korenblit 73d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf, 74d1e879ecSMiri Korenblit size_t count) 75d1e879ecSMiri Korenblit { 76d1e879ecSMiri Korenblit int __maybe_unused ret; 77d1e879ecSMiri Korenblit 78d1e879ecSMiri Korenblit if (!iwlwifi_mod_params.fw_restart) 79d1e879ecSMiri Korenblit return -EPERM; 80d1e879ecSMiri Korenblit 81d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 82d1e879ecSMiri Korenblit return -EIO; 83d1e879ecSMiri Korenblit 84d1e879ecSMiri Korenblit if (count == 6 && !strcmp(buf, "nolog\n")) { 85d1e879ecSMiri Korenblit mld->fw_status.do_not_dump_once = true; 86d1e879ecSMiri Korenblit set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mld->trans->status); 87d1e879ecSMiri Korenblit } 88d1e879ecSMiri Korenblit 89d1e879ecSMiri Korenblit /* take the return value to make compiler happy - it will 90d1e879ecSMiri Korenblit * fail anyway 91d1e879ecSMiri Korenblit */ 92d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_empty(mld, WIDE_ID(LONG_GROUP, REPLY_ERROR)); 93d1e879ecSMiri Korenblit 94d1e879ecSMiri Korenblit return count; 95d1e879ecSMiri Korenblit } 96d1e879ecSMiri Korenblit 97d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_send_echo_cmd_write(struct iwl_mld *mld, char *buf, 98d1e879ecSMiri Korenblit size_t count) 99d1e879ecSMiri Korenblit { 100d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 101d1e879ecSMiri Korenblit return -EIO; 102d1e879ecSMiri Korenblit 103d1e879ecSMiri Korenblit return iwl_mld_send_cmd_empty(mld, ECHO_CMD) ?: count; 104d1e879ecSMiri Korenblit } 105d1e879ecSMiri Korenblit 106d1e879ecSMiri Korenblit struct iwl_mld_sniffer_apply { 107d1e879ecSMiri Korenblit struct iwl_mld *mld; 108d1e879ecSMiri Korenblit const u8 *bssid; 109d1e879ecSMiri Korenblit u16 aid; 110d1e879ecSMiri Korenblit }; 111d1e879ecSMiri Korenblit 112d1e879ecSMiri Korenblit static bool iwl_mld_sniffer_apply(struct iwl_notif_wait_data *notif_data, 113d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt, void *data) 114d1e879ecSMiri Korenblit { 115d1e879ecSMiri Korenblit struct iwl_mld_sniffer_apply *apply = data; 116d1e879ecSMiri Korenblit 117d1e879ecSMiri Korenblit apply->mld->monitor.cur_aid = cpu_to_le16(apply->aid); 118d1e879ecSMiri Korenblit memcpy(apply->mld->monitor.cur_bssid, apply->bssid, 119d1e879ecSMiri Korenblit sizeof(apply->mld->monitor.cur_bssid)); 120d1e879ecSMiri Korenblit 121d1e879ecSMiri Korenblit return true; 122d1e879ecSMiri Korenblit } 123d1e879ecSMiri Korenblit 124d1e879ecSMiri Korenblit static ssize_t 125d1e879ecSMiri Korenblit iwl_dbgfs_he_sniffer_params_write(struct iwl_mld *mld, char *buf, 126d1e879ecSMiri Korenblit size_t count) 127d1e879ecSMiri Korenblit { 128d1e879ecSMiri Korenblit struct iwl_notification_wait wait; 129d1e879ecSMiri Korenblit struct iwl_he_monitor_cmd he_mon_cmd = {}; 130d1e879ecSMiri Korenblit struct iwl_mld_sniffer_apply apply = { 131d1e879ecSMiri Korenblit .mld = mld, 132d1e879ecSMiri Korenblit }; 133d1e879ecSMiri Korenblit u16 wait_cmds[] = { 134d1e879ecSMiri Korenblit WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD), 135d1e879ecSMiri Korenblit }; 136d1e879ecSMiri Korenblit u32 aid; 137d1e879ecSMiri Korenblit int ret; 138d1e879ecSMiri Korenblit 139d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 140d1e879ecSMiri Korenblit return -EIO; 141d1e879ecSMiri Korenblit 142d1e879ecSMiri Korenblit if (!mld->monitor.on) 143d1e879ecSMiri Korenblit return -ENODEV; 144d1e879ecSMiri Korenblit 145d1e879ecSMiri Korenblit ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid, 146d1e879ecSMiri Korenblit &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1], 147d1e879ecSMiri Korenblit &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3], 148d1e879ecSMiri Korenblit &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]); 149d1e879ecSMiri Korenblit if (ret != 7) 150d1e879ecSMiri Korenblit return -EINVAL; 151d1e879ecSMiri Korenblit 152d1e879ecSMiri Korenblit he_mon_cmd.aid = cpu_to_le16(aid); 153d1e879ecSMiri Korenblit 154d1e879ecSMiri Korenblit apply.aid = aid; 155d1e879ecSMiri Korenblit apply.bssid = (void *)he_mon_cmd.bssid; 156d1e879ecSMiri Korenblit 157d1e879ecSMiri Korenblit /* Use the notification waiter to get our function triggered 158d1e879ecSMiri Korenblit * in sequence with other RX. This ensures that frames we get 159d1e879ecSMiri Korenblit * on the RX queue _before_ the new configuration is applied 160d1e879ecSMiri Korenblit * still have mld->cur_aid pointing to the old AID, and that 161d1e879ecSMiri Korenblit * frames on the RX queue _after_ the firmware processed the 162d1e879ecSMiri Korenblit * new configuration (and sent the response, synchronously) 163d1e879ecSMiri Korenblit * get mld->cur_aid correctly set to the new AID. 164d1e879ecSMiri Korenblit */ 165d1e879ecSMiri Korenblit iwl_init_notification_wait(&mld->notif_wait, &wait, 166d1e879ecSMiri Korenblit wait_cmds, ARRAY_SIZE(wait_cmds), 167d1e879ecSMiri Korenblit iwl_mld_sniffer_apply, &apply); 168d1e879ecSMiri Korenblit 169d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_pdu(mld, 170d1e879ecSMiri Korenblit WIDE_ID(DATA_PATH_GROUP, 171d1e879ecSMiri Korenblit HE_AIR_SNIFFER_CONFIG_CMD), 172d1e879ecSMiri Korenblit &he_mon_cmd); 173d1e879ecSMiri Korenblit 174d1e879ecSMiri Korenblit /* no need to really wait, we already did anyway */ 175d1e879ecSMiri Korenblit iwl_remove_notification(&mld->notif_wait, &wait); 176d1e879ecSMiri Korenblit 177d1e879ecSMiri Korenblit return ret ?: count; 178d1e879ecSMiri Korenblit } 179d1e879ecSMiri Korenblit 180d1e879ecSMiri Korenblit static ssize_t 181d1e879ecSMiri Korenblit iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, size_t count, 182d1e879ecSMiri Korenblit char *buf) 183d1e879ecSMiri Korenblit { 184d1e879ecSMiri Korenblit return scnprintf(buf, count, 185d1e879ecSMiri Korenblit "%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", 186d1e879ecSMiri Korenblit le16_to_cpu(mld->monitor.cur_aid), 187d1e879ecSMiri Korenblit mld->monitor.cur_bssid[0], mld->monitor.cur_bssid[1], 188d1e879ecSMiri Korenblit mld->monitor.cur_bssid[2], mld->monitor.cur_bssid[3], 189d1e879ecSMiri Korenblit mld->monitor.cur_bssid[4], mld->monitor.cur_bssid[5]); 190d1e879ecSMiri Korenblit } 191d1e879ecSMiri Korenblit 192d1e879ecSMiri Korenblit /* The size computation is as follows: 193d1e879ecSMiri Korenblit * each number needs at most 3 characters, number of rows is the size of 194d1e879ecSMiri Korenblit * the table; So, need 5 chars for the "freq: " part and each tuple afterwards 195d1e879ecSMiri Korenblit * needs 6 characters for numbers and 5 for the punctuation around. 32 bytes 196d1e879ecSMiri Korenblit * for feature support message. 197d1e879ecSMiri Korenblit */ 198d1e879ecSMiri Korenblit #define IWL_RFI_DDR_BUF_SIZE (IWL_RFI_DDR_LUT_INSTALLED_SIZE *\ 199d1e879ecSMiri Korenblit (5 + IWL_RFI_DDR_LUT_ENTRY_CHANNELS_NUM *\ 200d1e879ecSMiri Korenblit (6 + 5)) + 32) 201d1e879ecSMiri Korenblit #define IWL_RFI_DLVR_BUF_SIZE (IWL_RFI_DLVR_LUT_INSTALLED_SIZE *\ 202d1e879ecSMiri Korenblit (5 + IWL_RFI_DLVR_LUT_ENTRY_CHANNELS_NUM *\ 203d1e879ecSMiri Korenblit (6 + 5)) + 32) 204d1e879ecSMiri Korenblit #define IWL_RFI_DESENSE_BUF_SIZE IWL_RFI_DDR_BUF_SIZE 205d1e879ecSMiri Korenblit 206d1e879ecSMiri Korenblit /* Extra 32 for "DDR and DLVR table" message */ 207d1e879ecSMiri Korenblit #define IWL_RFI_BUF_SIZE (IWL_RFI_DDR_BUF_SIZE + IWL_RFI_DLVR_BUF_SIZE +\ 208d1e879ecSMiri Korenblit IWL_RFI_DESENSE_BUF_SIZE + 32) 209d1e879ecSMiri Korenblit 210d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_nmi, 10); 211d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_restart, 10); 212d1e879ecSMiri Korenblit WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(he_sniffer_params, 32); 213d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_dbg_clear, 10); 214d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(send_echo_cmd, 8); 215d1e879ecSMiri Korenblit 216d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld *mld, 217d1e879ecSMiri Korenblit size_t count, u8 *buf) 218d1e879ecSMiri Korenblit { 219d1e879ecSMiri Korenblit int err; 220d1e879ecSMiri Korenblit u32 value; 221d1e879ecSMiri Korenblit 222d1e879ecSMiri Korenblit err = iwl_bios_get_dsm(&mld->fwrt, DSM_FUNC_ENABLE_6E, &value); 223d1e879ecSMiri Korenblit if (err) 224d1e879ecSMiri Korenblit return err; 225d1e879ecSMiri Korenblit 226d1e879ecSMiri Korenblit return scnprintf(buf, count, "0x%08x\n", value); 227d1e879ecSMiri Korenblit } 228d1e879ecSMiri Korenblit 229d1e879ecSMiri Korenblit MLD_DEBUGFS_READ_FILE_OPS(wifi_6e_enable, 64); 230d1e879ecSMiri Korenblit 231d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mld *mld, 232d1e879ecSMiri Korenblit char *buf, size_t count) 233d1e879ecSMiri Korenblit { 234d1e879ecSMiri Korenblit struct iwl_op_mode *opmode = container_of((void *)mld, 235d1e879ecSMiri Korenblit struct iwl_op_mode, 236d1e879ecSMiri Korenblit op_mode_specific); 237d1e879ecSMiri Korenblit struct iwl_rx_cmd_buffer rxb = {}; 238d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt; 239d1e879ecSMiri Korenblit int n_bytes = count / 2; 240d1e879ecSMiri Korenblit int ret = -EINVAL; 241d1e879ecSMiri Korenblit 242d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 243d1e879ecSMiri Korenblit return -EIO; 244d1e879ecSMiri Korenblit 245d1e879ecSMiri Korenblit rxb._page = alloc_pages(GFP_KERNEL, 0); 246d1e879ecSMiri Korenblit if (!rxb._page) 247d1e879ecSMiri Korenblit return -ENOMEM; 248d1e879ecSMiri Korenblit pkt = rxb_addr(&rxb); 249d1e879ecSMiri Korenblit 250d1e879ecSMiri Korenblit ret = hex2bin(page_address(rxb._page), buf, n_bytes); 251d1e879ecSMiri Korenblit if (ret) 252d1e879ecSMiri Korenblit goto out; 253d1e879ecSMiri Korenblit 254d1e879ecSMiri Korenblit /* avoid invalid memory access and malformed packet */ 255d1e879ecSMiri Korenblit if (n_bytes < sizeof(*pkt) || 256d1e879ecSMiri Korenblit n_bytes != sizeof(*pkt) + iwl_rx_packet_payload_len(pkt)) 257d1e879ecSMiri Korenblit goto out; 258d1e879ecSMiri Korenblit 259d1e879ecSMiri Korenblit local_bh_disable(); 260d1e879ecSMiri Korenblit iwl_mld_rx(opmode, NULL, &rxb); 261d1e879ecSMiri Korenblit local_bh_enable(); 262d1e879ecSMiri Korenblit ret = 0; 263d1e879ecSMiri Korenblit 264d1e879ecSMiri Korenblit out: 265d1e879ecSMiri Korenblit iwl_free_rxb(&rxb); 266d1e879ecSMiri Korenblit 267d1e879ecSMiri Korenblit return ret ?: count; 268d1e879ecSMiri Korenblit } 269d1e879ecSMiri Korenblit 270d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(inject_packet, 512); 271d1e879ecSMiri Korenblit 272d1e879ecSMiri Korenblit #ifdef CONFIG_THERMAL 273d1e879ecSMiri Korenblit 274d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mld *mld, 275d1e879ecSMiri Korenblit char *buf, size_t count) 276d1e879ecSMiri Korenblit { 277d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 278d1e879ecSMiri Korenblit return -EIO; 279d1e879ecSMiri Korenblit 280d1e879ecSMiri Korenblit return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, 281d1e879ecSMiri Korenblit CTDP_CMD_OPERATION_STOP) ? : count; 282d1e879ecSMiri Korenblit } 283d1e879ecSMiri Korenblit 284d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(stop_ctdp, 8); 285d1e879ecSMiri Korenblit 286d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mld *mld, 287d1e879ecSMiri Korenblit char *buf, size_t count) 288d1e879ecSMiri Korenblit { 289d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 290d1e879ecSMiri Korenblit return -EIO; 291d1e879ecSMiri Korenblit 292d1e879ecSMiri Korenblit return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, 293d1e879ecSMiri Korenblit CTDP_CMD_OPERATION_START) ? : count; 294d1e879ecSMiri Korenblit } 295d1e879ecSMiri Korenblit 296d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(start_ctdp, 8); 297d1e879ecSMiri Korenblit 298d1e879ecSMiri Korenblit #endif /* CONFIG_THERMAL */ 299d1e879ecSMiri Korenblit 300d1e879ecSMiri Korenblit void 301d1e879ecSMiri Korenblit iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) 302d1e879ecSMiri Korenblit { 303d1e879ecSMiri Korenblit /* Add debugfs files here */ 304d1e879ecSMiri Korenblit 305d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(fw_nmi, debugfs_dir, 0200); 306d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(fw_restart, debugfs_dir, 0200); 307d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(wifi_6e_enable, debugfs_dir, 0400); 308d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(he_sniffer_params, debugfs_dir, 0600); 309d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(fw_dbg_clear, debugfs_dir, 0200); 310d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(send_echo_cmd, debugfs_dir, 0200); 311d1e879ecSMiri Korenblit #ifdef CONFIG_THERMAL 312d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(start_ctdp, debugfs_dir, 0200); 313d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(stop_ctdp, debugfs_dir, 0200); 314d1e879ecSMiri Korenblit #endif 315d1e879ecSMiri Korenblit MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200); 316d1e879ecSMiri Korenblit 317d1e879ecSMiri Korenblit /* Create a symlink with mac80211. It will be removed when mac80211 318d1e879ecSMiri Korenblit * exits (before the opmode exits which removes the target.) 319d1e879ecSMiri Korenblit */ 320d1e879ecSMiri Korenblit if (!IS_ERR(debugfs_dir)) { 321d1e879ecSMiri Korenblit char buf[100]; 322d1e879ecSMiri Korenblit 323d1e879ecSMiri Korenblit snprintf(buf, 100, "../../%pd2", debugfs_dir->d_parent); 324d1e879ecSMiri Korenblit debugfs_create_symlink("iwlwifi", mld->wiphy->debugfsdir, 325d1e879ecSMiri Korenblit buf); 326d1e879ecSMiri Korenblit } 327d1e879ecSMiri Korenblit } 328d1e879ecSMiri Korenblit 329d1e879ecSMiri Korenblit #define VIF_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ 330d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS(vif_##name, bufsz, vif) 331d1e879ecSMiri Korenblit 332d1e879ecSMiri Korenblit #define VIF_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ 333d1e879ecSMiri Korenblit IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(vif_##name, bufsz, vif) \ 334d1e879ecSMiri Korenblit 335d1e879ecSMiri Korenblit #define VIF_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 336d1e879ecSMiri Korenblit debugfs_create_file(alias, mode, parent, vif, \ 337d1e879ecSMiri Korenblit &iwl_dbgfs_vif_##name##_ops) 338d1e879ecSMiri Korenblit #define VIF_DEBUGFS_ADD_FILE(name, parent, mode) \ 339d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 340d1e879ecSMiri Korenblit 341d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_vif_bf_params_write(struct iwl_mld *mld, char *buf, 342d1e879ecSMiri Korenblit size_t count, void *data) 343d1e879ecSMiri Korenblit { 344d1e879ecSMiri Korenblit struct ieee80211_vif *vif = data; 345d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 346d1e879ecSMiri Korenblit int link_id = vif->active_links ? __ffs(vif->active_links) : 0; 347d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf; 348d1e879ecSMiri Korenblit int val; 349d1e879ecSMiri Korenblit 350d1e879ecSMiri Korenblit if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { 351d1e879ecSMiri Korenblit if (sscanf(buf + 24, "%d", &val) != 1) 352d1e879ecSMiri Korenblit return -EINVAL; 353d1e879ecSMiri Korenblit } else { 354d1e879ecSMiri Korenblit return -EINVAL; 355d1e879ecSMiri Korenblit } 356d1e879ecSMiri Korenblit 357d1e879ecSMiri Korenblit if (val != 0 && val != 1) 358d1e879ecSMiri Korenblit return -EINVAL; 359d1e879ecSMiri Korenblit 360d1e879ecSMiri Korenblit link_conf = link_conf_dereference_protected(vif, link_id); 361d1e879ecSMiri Korenblit if (WARN_ON(!link_conf)) 362d1e879ecSMiri Korenblit return -ENODEV; 363d1e879ecSMiri Korenblit 364d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 365d1e879ecSMiri Korenblit return -EIO; 366d1e879ecSMiri Korenblit 367d1e879ecSMiri Korenblit mld_vif->disable_bf = !val; 368d1e879ecSMiri Korenblit 369d1e879ecSMiri Korenblit if (val) 370d1e879ecSMiri Korenblit return iwl_mld_enable_beacon_filter(mld, link_conf, 371d1e879ecSMiri Korenblit false) ?: count; 372d1e879ecSMiri Korenblit else 373d1e879ecSMiri Korenblit return iwl_mld_disable_beacon_filter(mld, vif) ?: count; 374d1e879ecSMiri Korenblit } 375d1e879ecSMiri Korenblit 376d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_vif_pm_params_write(struct iwl_mld *mld, 377d1e879ecSMiri Korenblit char *buf, 378d1e879ecSMiri Korenblit size_t count, void *data) 379d1e879ecSMiri Korenblit { 380d1e879ecSMiri Korenblit struct ieee80211_vif *vif = data; 381d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 382d1e879ecSMiri Korenblit int val; 383d1e879ecSMiri Korenblit 384d1e879ecSMiri Korenblit if (!strncmp("use_ps_poll=", buf, 12)) { 385d1e879ecSMiri Korenblit if (sscanf(buf + 12, "%d", &val) != 1) 386d1e879ecSMiri Korenblit return -EINVAL; 387d1e879ecSMiri Korenblit } else { 388d1e879ecSMiri Korenblit return -EINVAL; 389d1e879ecSMiri Korenblit } 390d1e879ecSMiri Korenblit 391d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 392d1e879ecSMiri Korenblit return -EIO; 393d1e879ecSMiri Korenblit 394d1e879ecSMiri Korenblit mld_vif->use_ps_poll = val; 395d1e879ecSMiri Korenblit 396d1e879ecSMiri Korenblit return iwl_mld_update_mac_power(mld, vif, false) ?: count; 397d1e879ecSMiri Korenblit } 398d1e879ecSMiri Korenblit 399d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_vif_low_latency_write(struct iwl_mld *mld, 400d1e879ecSMiri Korenblit char *buf, size_t count, 401d1e879ecSMiri Korenblit void *data) 402d1e879ecSMiri Korenblit { 403d1e879ecSMiri Korenblit struct ieee80211_vif *vif = data; 404d1e879ecSMiri Korenblit u8 value; 405d1e879ecSMiri Korenblit int ret; 406d1e879ecSMiri Korenblit 407d1e879ecSMiri Korenblit ret = kstrtou8(buf, 0, &value); 408d1e879ecSMiri Korenblit if (ret) 409d1e879ecSMiri Korenblit return ret; 410d1e879ecSMiri Korenblit 411d1e879ecSMiri Korenblit if (value > 1) 412d1e879ecSMiri Korenblit return -EINVAL; 413d1e879ecSMiri Korenblit 414d1e879ecSMiri Korenblit iwl_mld_vif_update_low_latency(mld, vif, value, LOW_LATENCY_DEBUGFS); 415d1e879ecSMiri Korenblit 416d1e879ecSMiri Korenblit return count; 417d1e879ecSMiri Korenblit } 418d1e879ecSMiri Korenblit 419d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif *vif, 420d1e879ecSMiri Korenblit size_t count, char *buf) 421d1e879ecSMiri Korenblit { 422d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 423d1e879ecSMiri Korenblit char format[] = "traffic=%d\ndbgfs=%d\nvif_type=%d\nactual=%d\n"; 424d1e879ecSMiri Korenblit u8 ll_causes; 425d1e879ecSMiri Korenblit 426d1e879ecSMiri Korenblit if (WARN_ON(count < sizeof(format))) 427d1e879ecSMiri Korenblit return -EINVAL; 428d1e879ecSMiri Korenblit 429d1e879ecSMiri Korenblit ll_causes = READ_ONCE(mld_vif->low_latency_causes); 430d1e879ecSMiri Korenblit 431d1e879ecSMiri Korenblit /* all values in format are boolean so the size of format is enough 432d1e879ecSMiri Korenblit * for holding the result string 433d1e879ecSMiri Korenblit */ 434d1e879ecSMiri Korenblit return scnprintf(buf, count, format, 435d1e879ecSMiri Korenblit !!(ll_causes & LOW_LATENCY_TRAFFIC), 436d1e879ecSMiri Korenblit !!(ll_causes & LOW_LATENCY_DEBUGFS), 437d1e879ecSMiri Korenblit !!(ll_causes & LOW_LATENCY_VIF_TYPE), 438d1e879ecSMiri Korenblit !!(ll_causes)); 439d1e879ecSMiri Korenblit } 440d1e879ecSMiri Korenblit 441d1e879ecSMiri Korenblit VIF_DEBUGFS_WRITE_FILE_OPS(pm_params, 32); 442d1e879ecSMiri Korenblit VIF_DEBUGFS_WRITE_FILE_OPS(bf_params, 32); 443d1e879ecSMiri Korenblit VIF_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 45); 444d1e879ecSMiri Korenblit 445d1e879ecSMiri Korenblit static int 446d1e879ecSMiri Korenblit _iwl_dbgfs_inject_beacon_ie(struct iwl_mld *mld, struct ieee80211_vif *vif, 447d1e879ecSMiri Korenblit char *bin, ssize_t len, 448d1e879ecSMiri Korenblit bool restore) 449d1e879ecSMiri Korenblit { 450d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif; 451d1e879ecSMiri Korenblit struct iwl_mld_link *mld_link; 452d1e879ecSMiri Korenblit struct iwl_mac_beacon_cmd beacon_cmd = {}; 453d1e879ecSMiri Korenblit int n_bytes = len / 2; 454d1e879ecSMiri Korenblit 455d1e879ecSMiri Korenblit /* Element len should be represented by u8 */ 456d1e879ecSMiri Korenblit if (n_bytes >= U8_MAX) 457d1e879ecSMiri Korenblit return -EINVAL; 458d1e879ecSMiri Korenblit 459d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 460d1e879ecSMiri Korenblit return -EIO; 461d1e879ecSMiri Korenblit 462d1e879ecSMiri Korenblit if (!vif) 463d1e879ecSMiri Korenblit return -EINVAL; 464d1e879ecSMiri Korenblit 465d1e879ecSMiri Korenblit mld_vif = iwl_mld_vif_from_mac80211(vif); 466d1e879ecSMiri Korenblit mld_vif->beacon_inject_active = true; 467d1e879ecSMiri Korenblit mld->hw->extra_beacon_tailroom = n_bytes; 468d1e879ecSMiri Korenblit 469d1e879ecSMiri Korenblit for_each_mld_vif_valid_link(mld_vif, mld_link) { 470d1e879ecSMiri Korenblit u32 offset; 471d1e879ecSMiri Korenblit struct ieee80211_tx_info *info; 472d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf = 473d1e879ecSMiri Korenblit link_conf_dereference_protected(vif, link_id); 474d1e879ecSMiri Korenblit struct ieee80211_chanctx_conf *ctx = 475d1e879ecSMiri Korenblit wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); 476d1e879ecSMiri Korenblit struct sk_buff *beacon = 477d1e879ecSMiri Korenblit ieee80211_beacon_get_template(mld->hw, vif, 478d1e879ecSMiri Korenblit NULL, link_id); 479d1e879ecSMiri Korenblit 480d1e879ecSMiri Korenblit if (!beacon) 481d1e879ecSMiri Korenblit return -EINVAL; 482d1e879ecSMiri Korenblit 483d1e879ecSMiri Korenblit if (!restore && (WARN_ON(!n_bytes || !bin) || 484d1e879ecSMiri Korenblit hex2bin(skb_put_zero(beacon, n_bytes), 485d1e879ecSMiri Korenblit bin, n_bytes))) { 486d1e879ecSMiri Korenblit dev_kfree_skb(beacon); 487d1e879ecSMiri Korenblit return -EINVAL; 488d1e879ecSMiri Korenblit } 489d1e879ecSMiri Korenblit 490d1e879ecSMiri Korenblit info = IEEE80211_SKB_CB(beacon); 491d1e879ecSMiri Korenblit 492d1e879ecSMiri Korenblit beacon_cmd.flags = 493d1e879ecSMiri Korenblit cpu_to_le16(iwl_mld_get_rate_flags(mld, info, vif, 494d1e879ecSMiri Korenblit link_conf, 495d1e879ecSMiri Korenblit ctx->def.chan->band)); 496d1e879ecSMiri Korenblit beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); 497d1e879ecSMiri Korenblit beacon_cmd.link_id = 498d1e879ecSMiri Korenblit cpu_to_le32(mld_link->fw_id); 499d1e879ecSMiri Korenblit 500d1e879ecSMiri Korenblit iwl_mld_set_tim_idx(mld, &beacon_cmd.tim_idx, 501d1e879ecSMiri Korenblit beacon->data, beacon->len); 502d1e879ecSMiri Korenblit 503d1e879ecSMiri Korenblit offset = iwl_find_ie_offset(beacon->data, 504d1e879ecSMiri Korenblit WLAN_EID_S1G_TWT, 505d1e879ecSMiri Korenblit beacon->len); 506d1e879ecSMiri Korenblit 507d1e879ecSMiri Korenblit beacon_cmd.btwt_offset = cpu_to_le32(offset); 508d1e879ecSMiri Korenblit 509d1e879ecSMiri Korenblit iwl_mld_send_beacon_template_cmd(mld, beacon, &beacon_cmd); 510d1e879ecSMiri Korenblit dev_kfree_skb(beacon); 511d1e879ecSMiri Korenblit } 512d1e879ecSMiri Korenblit 513d1e879ecSMiri Korenblit if (restore) 514d1e879ecSMiri Korenblit mld_vif->beacon_inject_active = false; 515d1e879ecSMiri Korenblit 516d1e879ecSMiri Korenblit return 0; 517d1e879ecSMiri Korenblit } 518d1e879ecSMiri Korenblit 519d1e879ecSMiri Korenblit static ssize_t 520d1e879ecSMiri Korenblit iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld *mld, 521d1e879ecSMiri Korenblit char *buf, size_t count, 522d1e879ecSMiri Korenblit void *data) 523d1e879ecSMiri Korenblit { 524d1e879ecSMiri Korenblit struct ieee80211_vif *vif = data; 525d1e879ecSMiri Korenblit int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, buf, 526d1e879ecSMiri Korenblit count, false); 527d1e879ecSMiri Korenblit 528d1e879ecSMiri Korenblit mld->hw->extra_beacon_tailroom = 0; 529d1e879ecSMiri Korenblit return ret ?: count; 530d1e879ecSMiri Korenblit } 531d1e879ecSMiri Korenblit 532d1e879ecSMiri Korenblit VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512); 533d1e879ecSMiri Korenblit 534d1e879ecSMiri Korenblit static ssize_t 535d1e879ecSMiri Korenblit iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld *mld, 536d1e879ecSMiri Korenblit char *buf, 537d1e879ecSMiri Korenblit size_t count, 538d1e879ecSMiri Korenblit void *data) 539d1e879ecSMiri Korenblit { 540d1e879ecSMiri Korenblit struct ieee80211_vif *vif = data; 541d1e879ecSMiri Korenblit int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, NULL, 542d1e879ecSMiri Korenblit 0, true); 543d1e879ecSMiri Korenblit 544d1e879ecSMiri Korenblit mld->hw->extra_beacon_tailroom = 0; 545d1e879ecSMiri Korenblit return ret ?: count; 546d1e879ecSMiri Korenblit } 547d1e879ecSMiri Korenblit 548d1e879ecSMiri Korenblit VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512); 549d1e879ecSMiri Korenblit 550d1e879ecSMiri Korenblit static ssize_t 551d1e879ecSMiri Korenblit iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count, 552d1e879ecSMiri Korenblit void *data) 553d1e879ecSMiri Korenblit { 554d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = { 555d1e879ecSMiri Korenblit .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, DEBUG_HOST_COMMAND), 556d1e879ecSMiri Korenblit }; 557d1e879ecSMiri Korenblit struct ieee80211_vif *vif = data; 558d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 559*54be64fdSDan Carpenter struct iwl_dhc_cmd *cmd __free(kfree) = NULL; 560d1e879ecSMiri Korenblit struct iwl_dhc_twt_operation *dhc_twt_cmd; 561d1e879ecSMiri Korenblit u64 target_wake_time; 562d1e879ecSMiri Korenblit u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration; 563d1e879ecSMiri Korenblit u8 trigger, flow_type, flow_id, protection, tenth_param; 564d1e879ecSMiri Korenblit u8 twt_request = 1, broadcast = 0; 565d1e879ecSMiri Korenblit int ret; 566d1e879ecSMiri Korenblit 567d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 568d1e879ecSMiri Korenblit return -EIO; 569d1e879ecSMiri Korenblit 570d1e879ecSMiri Korenblit ret = sscanf(buf, "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu", 571d1e879ecSMiri Korenblit &twt_operation, &target_wake_time, &interval_exp, 572d1e879ecSMiri Korenblit &interval_mantissa, &min_wake_duration, &trigger, 573d1e879ecSMiri Korenblit &flow_type, &flow_id, &protection, &tenth_param); 574d1e879ecSMiri Korenblit 575d1e879ecSMiri Korenblit /* the new twt_request parameter is optional for station */ 576d1e879ecSMiri Korenblit if ((ret != 9 && ret != 10) || 577d1e879ecSMiri Korenblit (ret == 10 && vif->type != NL80211_IFTYPE_STATION && 578d1e879ecSMiri Korenblit tenth_param == 1)) 579d1e879ecSMiri Korenblit return -EINVAL; 580d1e879ecSMiri Korenblit 581d1e879ecSMiri Korenblit /* The 10th parameter: 582d1e879ecSMiri Korenblit * In STA mode - the TWT type (broadcast or individual) 583d1e879ecSMiri Korenblit * In AP mode - the role (0 responder, 2 unsolicited) 584d1e879ecSMiri Korenblit */ 585d1e879ecSMiri Korenblit if (ret == 10) { 586d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_STATION) 587d1e879ecSMiri Korenblit broadcast = tenth_param; 588d1e879ecSMiri Korenblit else 589d1e879ecSMiri Korenblit twt_request = tenth_param; 590d1e879ecSMiri Korenblit } 591d1e879ecSMiri Korenblit 592d1e879ecSMiri Korenblit cmd = kzalloc(sizeof(*cmd) + sizeof(*dhc_twt_cmd), GFP_KERNEL); 593d1e879ecSMiri Korenblit if (!cmd) 594d1e879ecSMiri Korenblit return -ENOMEM; 595d1e879ecSMiri Korenblit 596d1e879ecSMiri Korenblit dhc_twt_cmd = (void *)cmd->data; 597d1e879ecSMiri Korenblit dhc_twt_cmd->mac_id = cpu_to_le32(mld_vif->fw_id); 598d1e879ecSMiri Korenblit dhc_twt_cmd->twt_operation = cpu_to_le32(twt_operation); 599d1e879ecSMiri Korenblit dhc_twt_cmd->target_wake_time = cpu_to_le64(target_wake_time); 600d1e879ecSMiri Korenblit dhc_twt_cmd->interval_exp = cpu_to_le32(interval_exp); 601d1e879ecSMiri Korenblit dhc_twt_cmd->interval_mantissa = cpu_to_le32(interval_mantissa); 602d1e879ecSMiri Korenblit dhc_twt_cmd->min_wake_duration = cpu_to_le32(min_wake_duration); 603d1e879ecSMiri Korenblit dhc_twt_cmd->trigger = trigger; 604d1e879ecSMiri Korenblit dhc_twt_cmd->flow_type = flow_type; 605d1e879ecSMiri Korenblit dhc_twt_cmd->flow_id = flow_id; 606d1e879ecSMiri Korenblit dhc_twt_cmd->protection = protection; 607d1e879ecSMiri Korenblit dhc_twt_cmd->twt_request = twt_request; 608d1e879ecSMiri Korenblit dhc_twt_cmd->negotiation_type = broadcast ? 3 : 0; 609d1e879ecSMiri Korenblit 610d1e879ecSMiri Korenblit cmd->length = cpu_to_le32(sizeof(*dhc_twt_cmd) >> 2); 611d1e879ecSMiri Korenblit cmd->index_and_mask = 612d1e879ecSMiri Korenblit cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC | 613d1e879ecSMiri Korenblit DHC_INT_UMAC_TWT_OPERATION); 614d1e879ecSMiri Korenblit 615d1e879ecSMiri Korenblit hcmd.len[0] = sizeof(*cmd) + sizeof(*dhc_twt_cmd); 616d1e879ecSMiri Korenblit hcmd.data[0] = cmd; 617d1e879ecSMiri Korenblit 618d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &hcmd); 619d1e879ecSMiri Korenblit 620d1e879ecSMiri Korenblit return ret ?: count; 621d1e879ecSMiri Korenblit } 622d1e879ecSMiri Korenblit 623d1e879ecSMiri Korenblit VIF_DEBUGFS_WRITE_FILE_OPS(twt_setup, 256); 624d1e879ecSMiri Korenblit 625d1e879ecSMiri Korenblit static ssize_t 626d1e879ecSMiri Korenblit iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count, 627d1e879ecSMiri Korenblit void *data) 628d1e879ecSMiri Korenblit { 629d1e879ecSMiri Korenblit struct ieee80211_vif *vif = data; 630d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 631d1e879ecSMiri Korenblit struct iwl_twt_operation_cmd twt_cmd = {}; 632d1e879ecSMiri Korenblit int link_id = vif->active_links ? __ffs(vif->active_links) : 0; 633d1e879ecSMiri Korenblit struct iwl_mld_link *mld_link = iwl_mld_link_dereference_check(mld_vif, 634d1e879ecSMiri Korenblit link_id); 635d1e879ecSMiri Korenblit int ret; 636d1e879ecSMiri Korenblit 637d1e879ecSMiri Korenblit if (WARN_ON(!mld_link)) 638d1e879ecSMiri Korenblit return -ENODEV; 639d1e879ecSMiri Korenblit 640d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 641d1e879ecSMiri Korenblit return -EIO; 642d1e879ecSMiri Korenblit 643d1e879ecSMiri Korenblit if (hweight16(vif->active_links) > 1) 644d1e879ecSMiri Korenblit return -EOPNOTSUPP; 645d1e879ecSMiri Korenblit 646d1e879ecSMiri Korenblit ret = sscanf(buf, 647d1e879ecSMiri Korenblit "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu", 648d1e879ecSMiri Korenblit &twt_cmd.twt_operation, &twt_cmd.target_wake_time, 649d1e879ecSMiri Korenblit &twt_cmd.interval_exponent, &twt_cmd.interval_mantissa, 650d1e879ecSMiri Korenblit &twt_cmd.minimum_wake_duration, &twt_cmd.trigger, 651d1e879ecSMiri Korenblit &twt_cmd.flow_type, &twt_cmd.flow_id, 652d1e879ecSMiri Korenblit &twt_cmd.twt_protection, &twt_cmd.ndp_paging_indicator, 653d1e879ecSMiri Korenblit &twt_cmd.responder_pm_mode, &twt_cmd.negotiation_type, 654d1e879ecSMiri Korenblit &twt_cmd.twt_request, &twt_cmd.implicit, 655d1e879ecSMiri Korenblit &twt_cmd.twt_group_assignment, &twt_cmd.twt_channel, 656d1e879ecSMiri Korenblit &twt_cmd.restricted_info_present, &twt_cmd.dl_bitmap_valid, 657d1e879ecSMiri Korenblit &twt_cmd.ul_bitmap_valid, &twt_cmd.dl_tid_bitmap, 658d1e879ecSMiri Korenblit &twt_cmd.ul_tid_bitmap); 659d1e879ecSMiri Korenblit 660d1e879ecSMiri Korenblit if (ret != 21) 661d1e879ecSMiri Korenblit return -EINVAL; 662d1e879ecSMiri Korenblit 663d1e879ecSMiri Korenblit twt_cmd.link_id = cpu_to_le32(mld_link->fw_id); 664d1e879ecSMiri Korenblit 665d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_pdu(mld, 666d1e879ecSMiri Korenblit WIDE_ID(MAC_CONF_GROUP, TWT_OPERATION_CMD), 667d1e879ecSMiri Korenblit &twt_cmd); 668d1e879ecSMiri Korenblit return ret ?: count; 669d1e879ecSMiri Korenblit } 670d1e879ecSMiri Korenblit 671d1e879ecSMiri Korenblit VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256); 672d1e879ecSMiri Korenblit 673d1e879ecSMiri Korenblit void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, 674d1e879ecSMiri Korenblit struct ieee80211_vif *vif) 675d1e879ecSMiri Korenblit { 676d1e879ecSMiri Korenblit struct dentry *mld_vif_dbgfs = 677d1e879ecSMiri Korenblit debugfs_create_dir("iwlmld", vif->debugfs_dir); 678d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 679d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); 680d1e879ecSMiri Korenblit char target[3 * 3 + 11 + (NL80211_WIPHY_NAME_MAXLEN + 1) + 681d1e879ecSMiri Korenblit (7 + IFNAMSIZ + 1) + 6 + 1]; 682d1e879ecSMiri Korenblit char name[7 + IFNAMSIZ + 1]; 683d1e879ecSMiri Korenblit 684d1e879ecSMiri Korenblit /* Create symlink for convenience pointing to interface specific 685d1e879ecSMiri Korenblit * debugfs entries for the driver. For example, under 686d1e879ecSMiri Korenblit * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmld/ 687d1e879ecSMiri Korenblit * find 688d1e879ecSMiri Korenblit * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmld/ 689d1e879ecSMiri Korenblit */ 690d1e879ecSMiri Korenblit snprintf(name, sizeof(name), "%pd", vif->debugfs_dir); 691d1e879ecSMiri Korenblit snprintf(target, sizeof(target), "../../../%pd3/iwlmld", 692d1e879ecSMiri Korenblit vif->debugfs_dir); 693d1e879ecSMiri Korenblit mld_vif->dbgfs_slink = 694d1e879ecSMiri Korenblit debugfs_create_symlink(name, mld->debugfs_dir, target); 695d1e879ecSMiri Korenblit 696d1e879ecSMiri Korenblit if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && 697d1e879ecSMiri Korenblit vif->type == NL80211_IFTYPE_STATION) { 698d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE(pm_params, mld_vif_dbgfs, 0200); 699d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE(bf_params, mld_vif_dbgfs, 0200); 700d1e879ecSMiri Korenblit } 701d1e879ecSMiri Korenblit 702d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_AP) { 703d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE(inject_beacon_ie, mld_vif_dbgfs, 0200); 704d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE(inject_beacon_ie_restore, 705d1e879ecSMiri Korenblit mld_vif_dbgfs, 0200); 706d1e879ecSMiri Korenblit } 707d1e879ecSMiri Korenblit 708d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600); 709d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200); 710d1e879ecSMiri Korenblit VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200); 711d1e879ecSMiri Korenblit } 712d1e879ecSMiri Korenblit 713d1e879ecSMiri Korenblit #define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ 714d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf) 715d1e879ecSMiri Korenblit 716d1e879ecSMiri Korenblit #define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 717d1e879ecSMiri Korenblit debugfs_create_file(alias, mode, parent, link_conf, \ 718d1e879ecSMiri Korenblit &iwl_dbgfs_link_##name##_ops) 719d1e879ecSMiri Korenblit #define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \ 720d1e879ecSMiri Korenblit LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 721d1e879ecSMiri Korenblit 722d1e879ecSMiri Korenblit void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, 723d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 724d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf, 725d1e879ecSMiri Korenblit struct dentry *dir) 726d1e879ecSMiri Korenblit { 727d1e879ecSMiri Korenblit struct dentry *mld_link_dir; 728d1e879ecSMiri Korenblit 729d1e879ecSMiri Korenblit mld_link_dir = debugfs_lookup("iwlmld", dir); 730d1e879ecSMiri Korenblit 731d1e879ecSMiri Korenblit /* For non-MLO vifs, the dir of deflink is the same as the vif's one. 732d1e879ecSMiri Korenblit * so if iwlmld dir already exists, this means that this is deflink. 733d1e879ecSMiri Korenblit * If not, this is a per-link dir of a MLO vif, add in it the iwlmld 734d1e879ecSMiri Korenblit * dir. 735d1e879ecSMiri Korenblit */ 736d1e879ecSMiri Korenblit if (!mld_link_dir) 737d1e879ecSMiri Korenblit mld_link_dir = debugfs_create_dir("iwlmld", dir); 738d1e879ecSMiri Korenblit } 739d1e879ecSMiri Korenblit 740d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, 741d1e879ecSMiri Korenblit size_t count, void *data) 742d1e879ecSMiri Korenblit { 743d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta = data; 744d1e879ecSMiri Korenblit struct iwl_mld_link_sta *mld_link_sta; 745d1e879ecSMiri Korenblit u32 rate; 746d1e879ecSMiri Korenblit u32 partial = false; 747d1e879ecSMiri Korenblit char pretty_rate[100]; 748d1e879ecSMiri Korenblit int ret; 749d1e879ecSMiri Korenblit u8 fw_sta_id; 750d1e879ecSMiri Korenblit 751d1e879ecSMiri Korenblit mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); 752d1e879ecSMiri Korenblit if (WARN_ON(!mld_link_sta)) 753d1e879ecSMiri Korenblit return -EINVAL; 754d1e879ecSMiri Korenblit 755d1e879ecSMiri Korenblit fw_sta_id = mld_link_sta->fw_id; 756d1e879ecSMiri Korenblit 757d1e879ecSMiri Korenblit if (sscanf(buf, "%i %i", &rate, &partial) == 0) 758d1e879ecSMiri Korenblit return -EINVAL; 759d1e879ecSMiri Korenblit 760d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 761d1e879ecSMiri Korenblit return -EIO; 762d1e879ecSMiri Korenblit 763d1e879ecSMiri Korenblit ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, 764d1e879ecSMiri Korenblit partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE : 765d1e879ecSMiri Korenblit IWL_TLC_DEBUG_FIXED_RATE, 766d1e879ecSMiri Korenblit rate); 767d1e879ecSMiri Korenblit 768d1e879ecSMiri Korenblit rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), rate); 769d1e879ecSMiri Korenblit 770d1e879ecSMiri Korenblit IWL_DEBUG_RATE(mld, "sta_id %d rate %s partial: %d, ret:%d\n", 771d1e879ecSMiri Korenblit fw_sta_id, pretty_rate, partial, ret); 772d1e879ecSMiri Korenblit 773d1e879ecSMiri Korenblit return ret ? : count; 774d1e879ecSMiri Korenblit } 775d1e879ecSMiri Korenblit 776d1e879ecSMiri Korenblit static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, 777d1e879ecSMiri Korenblit size_t count, void *data) 778d1e879ecSMiri Korenblit { 779d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta = data; 780d1e879ecSMiri Korenblit struct iwl_mld_link_sta *mld_link_sta; 781d1e879ecSMiri Korenblit u32 type, value; 782d1e879ecSMiri Korenblit int ret; 783d1e879ecSMiri Korenblit u8 fw_sta_id; 784d1e879ecSMiri Korenblit 785d1e879ecSMiri Korenblit mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); 786d1e879ecSMiri Korenblit if (WARN_ON(!mld_link_sta)) 787d1e879ecSMiri Korenblit return -EINVAL; 788d1e879ecSMiri Korenblit 789d1e879ecSMiri Korenblit fw_sta_id = mld_link_sta->fw_id; 790d1e879ecSMiri Korenblit 791d1e879ecSMiri Korenblit if (sscanf(buf, "%i %i", &type, &value) != 2) { 792d1e879ecSMiri Korenblit IWL_DEBUG_RATE(mld, "usage <type> <value>\n"); 793d1e879ecSMiri Korenblit return -EINVAL; 794d1e879ecSMiri Korenblit } 795d1e879ecSMiri Korenblit 796d1e879ecSMiri Korenblit if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) 797d1e879ecSMiri Korenblit return -EIO; 798d1e879ecSMiri Korenblit 799d1e879ecSMiri Korenblit ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, type, value); 800d1e879ecSMiri Korenblit 801d1e879ecSMiri Korenblit return ret ? : count; 802d1e879ecSMiri Korenblit } 803d1e879ecSMiri Korenblit 804d1e879ecSMiri Korenblit #define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ 805d1e879ecSMiri Korenblit debugfs_create_file(alias, mode, parent, link_sta, \ 806d1e879ecSMiri Korenblit &iwl_dbgfs_##name##_ops) 807d1e879ecSMiri Korenblit #define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \ 808d1e879ecSMiri Korenblit LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 809d1e879ecSMiri Korenblit 810d1e879ecSMiri Korenblit #define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \ 811d1e879ecSMiri Korenblit WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta) 812d1e879ecSMiri Korenblit 813d1e879ecSMiri Korenblit LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64); 814d1e879ecSMiri Korenblit LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64); 815d1e879ecSMiri Korenblit 816d1e879ecSMiri Korenblit void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, 817d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 818d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta, 819d1e879ecSMiri Korenblit struct dentry *dir) 820d1e879ecSMiri Korenblit { 821d1e879ecSMiri Korenblit LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200); 822d1e879ecSMiri Korenblit LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200); 823d1e879ecSMiri Korenblit } 824