2246 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2012-2014, 2018-2023 Intel Corporation4 * Copyright (C) 2013-2015 Intel Mobile Communications GmbH5 * Copyright (C) 2016-2017 Intel Deutschland GmbH6 */7#include <linux/vmalloc.h>8#include <linux/err.h>9#include <linux/ieee80211.h>10#include <linux/netdevice.h>11#include <linux/dmi.h>12 13#include "mvm.h"14#include "sta.h"15#include "iwl-io.h"16#include "debugfs.h"17#include "iwl-modparams.h"18#include "iwl-drv.h"19#include "fw/error-dump.h"20#include "fw/api/phy-ctxt.h"21 22static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file,23 char __user *user_buf,24 size_t count, loff_t *ppos)25{26 struct iwl_mvm *mvm = file->private_data;27 char buf[16];28 int pos, budget;29 30 if (!iwl_mvm_is_ctdp_supported(mvm))31 return -EOPNOTSUPP;32 33 if (!iwl_mvm_firmware_running(mvm) ||34 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)35 return -EIO;36 37 mutex_lock(&mvm->mutex);38 budget = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_REPORT, 0);39 mutex_unlock(&mvm->mutex);40 41 if (budget < 0)42 return budget;43 44 pos = scnprintf(buf, sizeof(buf), "%d\n", budget);45 46 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);47}48 49static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf,50 size_t count, loff_t *ppos)51{52 int ret;53 bool force;54 55 if (!kstrtobool(buf, &force))56 IWL_DEBUG_INFO(mvm,57 "force start is %d [0=disabled, 1=enabled]\n",58 force);59 60 /* we allow skipping cap support check and force stop ctdp61 * statistics collection and with guerantee that it is62 * safe to use.63 */64 if (!force && !iwl_mvm_is_ctdp_supported(mvm))65 return -EOPNOTSUPP;66 67 if (!iwl_mvm_firmware_running(mvm) ||68 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)69 return -EIO;70 71 mutex_lock(&mvm->mutex);72 ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_STOP, 0);73 mutex_unlock(&mvm->mutex);74 75 return ret ?: count;76}77 78static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mvm *mvm,79 char *buf, size_t count,80 loff_t *ppos)81{82 int ret;83 bool force;84 85 if (!kstrtobool(buf, &force))86 IWL_DEBUG_INFO(mvm,87 "force start is %d [0=disabled, 1=enabled]\n",88 force);89 90 /* we allow skipping cap support check and force enable ctdp91 * for statistics collection and with guerantee that it is92 * safe to use.93 */94 if (!force && !iwl_mvm_is_ctdp_supported(mvm))95 return -EOPNOTSUPP;96 97 if (!iwl_mvm_firmware_running(mvm) ||98 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)99 return -EIO;100 101 mutex_lock(&mvm->mutex);102 ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START, 0);103 mutex_unlock(&mvm->mutex);104 105 return ret ?: count;106}107 108static ssize_t iwl_dbgfs_force_ctkill_write(struct iwl_mvm *mvm, char *buf,109 size_t count, loff_t *ppos)110{111 if (!iwl_mvm_firmware_running(mvm) ||112 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)113 return -EIO;114 115 iwl_mvm_enter_ctkill(mvm);116 117 return count;118}119 120static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,121 size_t count, loff_t *ppos)122{123 int ret;124 u32 flush_arg;125 126 if (!iwl_mvm_firmware_running(mvm) ||127 mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)128 return -EIO;129 130 if (kstrtou32(buf, 0, &flush_arg))131 return -EINVAL;132 133 if (iwl_mvm_has_new_tx_api(mvm)) {134 IWL_DEBUG_TX_QUEUES(mvm,135 "FLUSHING all tids queues on sta_id = %d\n",136 flush_arg);137 mutex_lock(&mvm->mutex);138 ret = iwl_mvm_flush_sta_tids(mvm, flush_arg, 0xFFFF)139 ? : count;140 mutex_unlock(&mvm->mutex);141 return ret;142 }143 144 IWL_DEBUG_TX_QUEUES(mvm, "FLUSHING queues mask to flush = 0x%x\n",145 flush_arg);146 147 mutex_lock(&mvm->mutex);148 ret = iwl_mvm_flush_tx_path(mvm, flush_arg) ? : count;149 mutex_unlock(&mvm->mutex);150 151 return ret;152}153 154static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,155 size_t count, loff_t *ppos)156{157 struct iwl_mvm *mvm = file->private_data;158 const struct fw_img *img;159 unsigned int ofs, len;160 size_t ret;161 u8 *ptr;162 163 if (!iwl_mvm_firmware_running(mvm))164 return -EINVAL;165 166 /* default is to dump the entire data segment */167 img = &mvm->fw->img[mvm->fwrt.cur_fw_img];168 ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;169 len = img->sec[IWL_UCODE_SECTION_DATA].len;170 171 if (mvm->dbgfs_sram_len) {172 ofs = mvm->dbgfs_sram_offset;173 len = mvm->dbgfs_sram_len;174 }175 176 ptr = kzalloc(len, GFP_KERNEL);177 if (!ptr)178 return -ENOMEM;179 180 iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len);181 182 ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len);183 184 kfree(ptr);185 186 return ret;187}188 189static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf,190 size_t count, loff_t *ppos)191{192 const struct fw_img *img;193 u32 offset, len;194 u32 img_offset, img_len;195 196 if (!iwl_mvm_firmware_running(mvm))197 return -EINVAL;198 199 img = &mvm->fw->img[mvm->fwrt.cur_fw_img];200 img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset;201 img_len = img->sec[IWL_UCODE_SECTION_DATA].len;202 203 if (sscanf(buf, "%x,%x", &offset, &len) == 2) {204 if ((offset & 0x3) || (len & 0x3))205 return -EINVAL;206 207 if (offset + len > img_offset + img_len)208 return -EINVAL;209 210 mvm->dbgfs_sram_offset = offset;211 mvm->dbgfs_sram_len = len;212 } else {213 mvm->dbgfs_sram_offset = 0;214 mvm->dbgfs_sram_len = 0;215 }216 217 return count;218}219 220static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file,221 char __user *user_buf,222 size_t count, loff_t *ppos)223{224 struct iwl_mvm *mvm = file->private_data;225 char buf[16];226 int pos;227 228 if (!mvm->temperature_test)229 pos = scnprintf(buf, sizeof(buf), "disabled\n");230 else231 pos = scnprintf(buf, sizeof(buf), "%d\n", mvm->temperature);232 233 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);234}235 236/*237 * Set NIC Temperature238 * Cause the driver to ignore the actual NIC temperature reported by the FW239 * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -240 * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX241 * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE242 */243static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm,244 char *buf, size_t count,245 loff_t *ppos)246{247 int temperature;248 249 if (!iwl_mvm_firmware_running(mvm) && !mvm->temperature_test)250 return -EIO;251 252 if (kstrtoint(buf, 10, &temperature))253 return -EINVAL;254 /* not a legal temperature */255 if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX &&256 temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) ||257 temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN)258 return -EINVAL;259 260 mutex_lock(&mvm->mutex);261 if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) {262 if (!mvm->temperature_test)263 goto out;264 265 mvm->temperature_test = false;266 /* Since we can't read the temp while awake, just set267 * it to zero until we get the next RX stats from the268 * firmware.269 */270 mvm->temperature = 0;271 } else {272 mvm->temperature_test = true;273 mvm->temperature = temperature;274 }275 IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n",276 mvm->temperature_test ? "En" : "Dis",277 mvm->temperature);278 /* handle the temperature change */279 iwl_mvm_tt_handler(mvm);280 281out:282 mutex_unlock(&mvm->mutex);283 284 return count;285}286 287static ssize_t iwl_dbgfs_nic_temp_read(struct file *file,288 char __user *user_buf,289 size_t count, loff_t *ppos)290{291 struct iwl_mvm *mvm = file->private_data;292 char buf[16];293 int pos, ret;294 s32 temp;295 296 if (!iwl_mvm_firmware_running(mvm))297 return -EIO;298 299 mutex_lock(&mvm->mutex);300 ret = iwl_mvm_get_temp(mvm, &temp);301 mutex_unlock(&mvm->mutex);302 303 if (ret)304 return -EIO;305 306 pos = scnprintf(buf, sizeof(buf), "%d\n", temp);307 308 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);309}310 311#ifdef CONFIG_ACPI312static ssize_t iwl_dbgfs_sar_geo_profile_read(struct file *file,313 char __user *user_buf,314 size_t count, loff_t *ppos)315{316 struct iwl_mvm *mvm = file->private_data;317 char buf[256];318 int pos = 0;319 int bufsz = sizeof(buf);320 int tbl_idx;321 322 if (!iwl_mvm_firmware_running(mvm))323 return -EIO;324 325 mutex_lock(&mvm->mutex);326 tbl_idx = iwl_mvm_get_sar_geo_profile(mvm);327 if (tbl_idx < 0) {328 mutex_unlock(&mvm->mutex);329 return tbl_idx;330 }331 332 if (!tbl_idx) {333 pos = scnprintf(buf, bufsz,334 "SAR geographic profile disabled\n");335 } else {336 pos += scnprintf(buf + pos, bufsz - pos,337 "Use geographic profile %d\n", tbl_idx);338 pos += scnprintf(buf + pos, bufsz - pos,339 "2.4GHz:\n\tChain A offset: %u dBm\n\tChain B offset: %u dBm\n\tmax tx power: %u dBm\n",340 mvm->fwrt.geo_profiles[tbl_idx - 1].bands[0].chains[0],341 mvm->fwrt.geo_profiles[tbl_idx - 1].bands[0].chains[1],342 mvm->fwrt.geo_profiles[tbl_idx - 1].bands[0].max);343 pos += scnprintf(buf + pos, bufsz - pos,344 "5.2GHz:\n\tChain A offset: %u dBm\n\tChain B offset: %u dBm\n\tmax tx power: %u dBm\n",345 mvm->fwrt.geo_profiles[tbl_idx - 1].bands[1].chains[0],346 mvm->fwrt.geo_profiles[tbl_idx - 1].bands[1].chains[1],347 mvm->fwrt.geo_profiles[tbl_idx - 1].bands[1].max);348 }349 mutex_unlock(&mvm->mutex);350 351 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);352}353 354static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct file *file,355 char __user *user_buf,356 size_t count, loff_t *ppos)357{358 struct iwl_mvm *mvm = file->private_data;359 int err, pos;360 char buf[12];361 u32 value;362 363 err = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_ENABLE_6E, &value);364 if (err)365 return err;366 367 pos = sprintf(buf, "0x%08x\n", value);368 369 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);370}371#endif372 373static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,374 size_t count, loff_t *ppos)375{376 struct iwl_mvm *mvm = file->private_data;377 struct ieee80211_sta *sta;378 char buf[400];379 int i, pos = 0, bufsz = sizeof(buf);380 381 mutex_lock(&mvm->mutex);382 383 for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {384 pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);385 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],386 lockdep_is_held(&mvm->mutex));387 if (!sta)388 pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");389 else if (IS_ERR(sta))390 pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",391 PTR_ERR(sta));392 else393 pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",394 sta->addr);395 }396 397 mutex_unlock(&mvm->mutex);398 399 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);400}401 402static ssize_t iwl_dbgfs_rs_data_read(struct ieee80211_link_sta *link_sta,403 struct iwl_mvm_sta *mvmsta,404 struct iwl_mvm *mvm,405 struct iwl_mvm_link_sta *mvm_link_sta,406 char __user *user_buf,407 size_t count, loff_t *ppos)408{409 struct iwl_lq_sta_rs_fw *lq_sta = &mvm_link_sta->lq_sta.rs_fw;410 static const size_t bufsz = 2048;411 char *buff;412 int desc = 0;413 ssize_t ret;414 415 buff = kmalloc(bufsz, GFP_KERNEL);416 if (!buff)417 return -ENOMEM;418 419 desc += scnprintf(buff + desc, bufsz - desc, "sta_id %d\n",420 lq_sta->pers.sta_id);421 desc += scnprintf(buff + desc, bufsz - desc,422 "fixed rate 0x%X\n",423 lq_sta->pers.dbg_fixed_rate);424 desc += scnprintf(buff + desc, bufsz - desc,425 "A-MPDU size limit %d\n",426 lq_sta->pers.dbg_agg_frame_count_lim);427 desc += scnprintf(buff + desc, bufsz - desc,428 "valid_tx_ant %s%s\n",429 (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "",430 (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "");431 desc += scnprintf(buff + desc, bufsz - desc,432 "last tx rate=0x%X ",433 lq_sta->last_rate_n_flags);434 435 desc += rs_pretty_print_rate(buff + desc, bufsz - desc,436 lq_sta->last_rate_n_flags);437 if (desc < bufsz - 1)438 buff[desc++] = '\n';439 440 ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);441 kfree(buff);442 return ret;443}444 445static ssize_t iwl_dbgfs_amsdu_len_write(struct ieee80211_link_sta *link_sta,446 struct iwl_mvm_sta *mvmsta,447 struct iwl_mvm *mvm,448 struct iwl_mvm_link_sta *mvm_link_sta,449 char *buf, size_t count,450 loff_t *ppos)451{452 int i;453 u16 amsdu_len;454 455 if (kstrtou16(buf, 0, &amsdu_len))456 return -EINVAL;457 458 /* only change from debug set <-> debug unset */459 if (amsdu_len && mvm_link_sta->orig_amsdu_len)460 return -EBUSY;461 462 if (amsdu_len) {463 mvm_link_sta->orig_amsdu_len = link_sta->agg.max_amsdu_len;464 link_sta->agg.max_amsdu_len = amsdu_len;465 link_sta->agg.max_amsdu_len = amsdu_len;466 for (i = 0; i < ARRAY_SIZE(link_sta->agg.max_tid_amsdu_len); i++)467 link_sta->agg.max_tid_amsdu_len[i] = amsdu_len;468 } else {469 link_sta->agg.max_amsdu_len = mvm_link_sta->orig_amsdu_len;470 mvm_link_sta->orig_amsdu_len = 0;471 }472 473 ieee80211_sta_recalc_aggregates(link_sta->sta);474 475 return count;476}477 478static ssize_t iwl_dbgfs_amsdu_len_read(struct ieee80211_link_sta *link_sta,479 struct iwl_mvm_sta *mvmsta,480 struct iwl_mvm *mvm,481 struct iwl_mvm_link_sta *mvm_link_sta,482 char __user *user_buf,483 size_t count, loff_t *ppos)484{485 char buf[32];486 int pos;487 488 pos = scnprintf(buf, sizeof(buf), "current %d ",489 link_sta->agg.max_amsdu_len);490 pos += scnprintf(buf + pos, sizeof(buf) - pos, "stored %d\n",491 mvm_link_sta->orig_amsdu_len);492 493 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);494}495 496static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file,497 char __user *user_buf,498 size_t count, loff_t *ppos)499{500 struct iwl_mvm *mvm = file->private_data;501 char buf[64];502 int bufsz = sizeof(buf);503 int pos = 0;504 505 pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n",506 mvm->disable_power_off);507 pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n",508 mvm->disable_power_off_d3);509 510 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);511}512 513static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,514 size_t count, loff_t *ppos)515{516 int ret, val;517 518 if (!iwl_mvm_firmware_running(mvm))519 return -EIO;520 521 if (!strncmp("disable_power_off_d0=", buf, 21)) {522 if (sscanf(buf + 21, "%d", &val) != 1)523 return -EINVAL;524 mvm->disable_power_off = val;525 } else if (!strncmp("disable_power_off_d3=", buf, 21)) {526 if (sscanf(buf + 21, "%d", &val) != 1)527 return -EINVAL;528 mvm->disable_power_off_d3 = val;529 } else {530 return -EINVAL;531 }532 533 mutex_lock(&mvm->mutex);534 ret = iwl_mvm_power_update_device(mvm);535 mutex_unlock(&mvm->mutex);536 537 return ret ?: count;538}539 540static ssize_t iwl_dbgfs_fw_ver_read(struct file *file, char __user *user_buf,541 size_t count, loff_t *ppos)542{543 struct iwl_mvm *mvm = file->private_data;544 char *buff, *pos, *endpos;545 static const size_t bufsz = 1024;546 int ret;547 548 buff = kmalloc(bufsz, GFP_KERNEL);549 if (!buff)550 return -ENOMEM;551 552 pos = buff;553 endpos = pos + bufsz;554 555 pos += scnprintf(pos, endpos - pos, "FW id: %s\n",556 mvm->fwrt.fw->fw_version);557 pos += scnprintf(pos, endpos - pos, "FW: %s\n",558 mvm->fwrt.fw->human_readable);559 pos += scnprintf(pos, endpos - pos, "Device: %s\n",560 mvm->fwrt.trans->name);561 pos += scnprintf(pos, endpos - pos, "Bus: %s\n",562 mvm->fwrt.dev->bus->name);563 564 ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);565 kfree(buff);566 567 return ret;568}569 570static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file,571 char __user *user_buf,572 size_t count, loff_t *ppos)573{574 struct iwl_mvm *mvm = file->private_data;575 struct iwl_mvm_tas_status_resp tas_rsp;576 struct iwl_mvm_tas_status_resp *rsp = &tas_rsp;577 static const size_t bufsz = 1024;578 char *buff, *pos, *endpos;579 const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = {580 [TAS_DISABLED_DUE_TO_BIOS] =581 "Due To BIOS",582 [TAS_DISABLED_DUE_TO_SAR_6DBM] =583 "Due To SAR Limit Less Than 6 dBm",584 [TAS_DISABLED_REASON_INVALID] =585 "N/A",586 };587 const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = {588 [TAS_DYNA_INACTIVE] = "INACTIVE",589 [TAS_DYNA_INACTIVE_MVM_MODE] =590 "inactive due to mvm mode",591 [TAS_DYNA_INACTIVE_TRIGGER_MODE] =592 "inactive due to trigger mode",593 [TAS_DYNA_INACTIVE_BLOCK_LISTED] =594 "inactive due to block listed",595 [TAS_DYNA_INACTIVE_UHB_NON_US] =596 "inactive due to uhb non US",597 [TAS_DYNA_ACTIVE] = "ACTIVE",598 };599 struct iwl_host_cmd hcmd = {600 .id = WIDE_ID(DEBUG_GROUP, GET_TAS_STATUS),601 .flags = CMD_WANT_SKB,602 .len = { 0, },603 .data = { NULL, },604 };605 int ret, i, tmp;606 bool tas_enabled = false;607 unsigned long dyn_status;608 609 if (!iwl_mvm_firmware_running(mvm))610 return -ENODEV;611 612 mutex_lock(&mvm->mutex);613 ret = iwl_mvm_send_cmd(mvm, &hcmd);614 mutex_unlock(&mvm->mutex);615 if (ret < 0)616 return ret;617 618 buff = kzalloc(bufsz, GFP_KERNEL);619 if (!buff)620 return -ENOMEM;621 pos = buff;622 endpos = pos + bufsz;623 624 rsp = (void *)hcmd.resp_pkt->data;625 626 pos += scnprintf(pos, endpos - pos, "TAS Conclusion:\n");627 for (i = 0; i < rsp->in_dual_radio + 1; i++) {628 if (rsp->tas_status_mac[i].band != TAS_LMAC_BAND_INVALID &&629 rsp->tas_status_mac[i].dynamic_status & BIT(TAS_DYNA_ACTIVE)) {630 pos += scnprintf(pos, endpos - pos, "\tON for ");631 switch (rsp->tas_status_mac[i].band) {632 case TAS_LMAC_BAND_HB:633 pos += scnprintf(pos, endpos - pos, "HB\n");634 break;635 case TAS_LMAC_BAND_LB:636 pos += scnprintf(pos, endpos - pos, "LB\n");637 break;638 case TAS_LMAC_BAND_UHB:639 pos += scnprintf(pos, endpos - pos, "UHB\n");640 break;641 case TAS_LMAC_BAND_INVALID:642 pos += scnprintf(pos, endpos - pos,643 "INVALID BAND\n");644 break;645 default:646 pos += scnprintf(pos, endpos - pos,647 "Unsupported band (%d)\n",648 rsp->tas_status_mac[i].band);649 goto out;650 }651 tas_enabled = true;652 }653 }654 if (!tas_enabled)655 pos += scnprintf(pos, endpos - pos, "\tOFF\n");656 657 pos += scnprintf(pos, endpos - pos, "TAS Report\n");658 pos += scnprintf(pos, endpos - pos, "TAS FW version: %d\n",659 rsp->tas_fw_version);660 pos += scnprintf(pos, endpos - pos, "Is UHB enabled for USA?: %s\n",661 rsp->is_uhb_for_usa_enable ? "True" : "False");662 pos += scnprintf(pos, endpos - pos, "Current MCC: 0x%x\n",663 le16_to_cpu(rsp->curr_mcc));664 665 pos += scnprintf(pos, endpos - pos, "Block list entries:");666 for (i = 0; i < IWL_WTAS_BLACK_LIST_MAX; i++)667 pos += scnprintf(pos, endpos - pos, " 0x%x",668 le16_to_cpu(rsp->block_list[i]));669 670 pos += scnprintf(pos, endpos - pos, "\nOEM name: %s\n",671 dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");672 pos += scnprintf(pos, endpos - pos, "\tVendor In Approved List: %s\n",673 iwl_is_tas_approved() ? "YES" : "NO");674 pos += scnprintf(pos, endpos - pos,675 "\tDo TAS Support Dual Radio?: %s\n",676 rsp->in_dual_radio ? "TRUE" : "FALSE");677 678 for (i = 0; i < rsp->in_dual_radio + 1; i++) {679 if (rsp->tas_status_mac[i].static_status == 0) {680 pos += scnprintf(pos, endpos - pos,681 "Static status: disabled\n");682 pos += scnprintf(pos, endpos - pos,683 "Static disabled reason: %s (0)\n",684 tas_dis_reason[0]);685 goto out;686 }687 688 pos += scnprintf(pos, endpos - pos, "TAS status for ");689 switch (rsp->tas_status_mac[i].band) {690 case TAS_LMAC_BAND_HB:691 pos += scnprintf(pos, endpos - pos, "High band\n");692 break;693 case TAS_LMAC_BAND_LB:694 pos += scnprintf(pos, endpos - pos, "Low band\n");695 break;696 case TAS_LMAC_BAND_UHB:697 pos += scnprintf(pos, endpos - pos,698 "Ultra high band\n");699 break;700 case TAS_LMAC_BAND_INVALID:701 pos += scnprintf(pos, endpos - pos,702 "INVALID band\n");703 break;704 default:705 pos += scnprintf(pos, endpos - pos,706 "Unsupported band (%d)\n",707 rsp->tas_status_mac[i].band);708 goto out;709 }710 pos += scnprintf(pos, endpos - pos, "Static status: %sabled\n",711 rsp->tas_status_mac[i].static_status ?712 "En" : "Dis");713 pos += scnprintf(pos, endpos - pos,714 "\tStatic Disabled Reason: ");715 if (rsp->tas_status_mac[i].static_dis_reason < TAS_DISABLED_REASON_MAX)716 pos += scnprintf(pos, endpos - pos, "%s (%d)\n",717 tas_dis_reason[rsp->tas_status_mac[i].static_dis_reason],718 rsp->tas_status_mac[i].static_dis_reason);719 else720 pos += scnprintf(pos, endpos - pos,721 "unsupported value (%d)\n",722 rsp->tas_status_mac[i].static_dis_reason);723 724 pos += scnprintf(pos, endpos - pos, "Dynamic status:\n");725 dyn_status = (rsp->tas_status_mac[i].dynamic_status);726 for_each_set_bit(tmp, &dyn_status, sizeof(dyn_status)) {727 if (tmp >= 0 && tmp < TAS_DYNA_STATUS_MAX)728 pos += scnprintf(pos, endpos - pos,729 "\t%s (%d)\n",730 tas_current_status[tmp], tmp);731 }732 733 pos += scnprintf(pos, endpos - pos,734 "Is near disconnection?: %s\n",735 rsp->tas_status_mac[i].near_disconnection ?736 "True" : "False");737 tmp = le16_to_cpu(rsp->tas_status_mac[i].max_reg_pwr_limit);738 pos += scnprintf(pos, endpos - pos,739 "Max. regulatory pwr limit (dBm): %d.%03d\n",740 tmp / 8, 125 * (tmp % 8));741 tmp = le16_to_cpu(rsp->tas_status_mac[i].sar_limit);742 pos += scnprintf(pos, endpos - pos,743 "SAR limit (dBm): %d.%03d\n",744 tmp / 8, 125 * (tmp % 8));745 }746 747out:748 ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);749 kfree(buff);750 iwl_free_resp(&hcmd);751 return ret;752}753 754static ssize_t iwl_dbgfs_phy_integration_ver_read(struct file *file,755 char __user *user_buf,756 size_t count, loff_t *ppos)757{758 struct iwl_mvm *mvm = file->private_data;759 char *buf;760 size_t bufsz;761 int pos;762 ssize_t ret;763 764 bufsz = mvm->fw->phy_integration_ver_len + 2;765 buf = kmalloc(bufsz, GFP_KERNEL);766 if (!buf)767 return -ENOMEM;768 769 pos = scnprintf(buf, bufsz, "%.*s\n", mvm->fw->phy_integration_ver_len,770 mvm->fw->phy_integration_ver);771 772 ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);773 774 kfree(buf);775 return ret;776}777 778#define PRINT_STATS_LE32(_struct, _memb) \779 pos += scnprintf(buf + pos, bufsz - pos, \780 fmt_table, #_memb, \781 le32_to_cpu(_struct->_memb))782 783static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,784 char __user *user_buf, size_t count,785 loff_t *ppos)786{787 struct iwl_mvm *mvm = file->private_data;788 static const char *fmt_table = "\t%-30s %10u\n";789 static const char *fmt_header = "%-32s\n";790 int pos = 0;791 char *buf;792 int ret;793 size_t bufsz;794 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,795 WIDE_ID(SYSTEM_GROUP,796 SYSTEM_STATISTICS_CMD),797 IWL_FW_CMD_VER_UNKNOWN);798 799 if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN)800 return -EOPNOTSUPP;801 802 if (iwl_mvm_has_new_rx_stats_api(mvm))803 bufsz = ((sizeof(struct mvm_statistics_rx) /804 sizeof(__le32)) * 43) + (4 * 33) + 1;805 else806 /* 43 = size of each data line; 33 = size of each header */807 bufsz = ((sizeof(struct mvm_statistics_rx_v3) /808 sizeof(__le32)) * 43) + (4 * 33) + 1;809 810 buf = kzalloc(bufsz, GFP_KERNEL);811 if (!buf)812 return -ENOMEM;813 814 mutex_lock(&mvm->mutex);815 816 if (iwl_mvm_firmware_running(mvm))817 iwl_mvm_request_statistics(mvm, false);818 819 pos += scnprintf(buf + pos, bufsz - pos, fmt_header,820 "Statistics_Rx - OFDM");821 if (!iwl_mvm_has_new_rx_stats_api(mvm)) {822 struct mvm_statistics_rx_phy_v2 *ofdm = &mvm->rx_stats_v3.ofdm;823 824 PRINT_STATS_LE32(ofdm, ina_cnt);825 PRINT_STATS_LE32(ofdm, fina_cnt);826 PRINT_STATS_LE32(ofdm, plcp_err);827 PRINT_STATS_LE32(ofdm, crc32_err);828 PRINT_STATS_LE32(ofdm, overrun_err);829 PRINT_STATS_LE32(ofdm, early_overrun_err);830 PRINT_STATS_LE32(ofdm, crc32_good);831 PRINT_STATS_LE32(ofdm, false_alarm_cnt);832 PRINT_STATS_LE32(ofdm, fina_sync_err_cnt);833 PRINT_STATS_LE32(ofdm, sfd_timeout);834 PRINT_STATS_LE32(ofdm, fina_timeout);835 PRINT_STATS_LE32(ofdm, unresponded_rts);836 PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun);837 PRINT_STATS_LE32(ofdm, sent_ack_cnt);838 PRINT_STATS_LE32(ofdm, sent_cts_cnt);839 PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt);840 PRINT_STATS_LE32(ofdm, dsp_self_kill);841 PRINT_STATS_LE32(ofdm, mh_format_err);842 PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum);843 PRINT_STATS_LE32(ofdm, reserved);844 } else {845 struct mvm_statistics_rx_phy *ofdm = &mvm->rx_stats.ofdm;846 847 PRINT_STATS_LE32(ofdm, unresponded_rts);848 PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun);849 PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt);850 PRINT_STATS_LE32(ofdm, dsp_self_kill);851 PRINT_STATS_LE32(ofdm, reserved);852 }853 854 pos += scnprintf(buf + pos, bufsz - pos, fmt_header,855 "Statistics_Rx - CCK");856 if (!iwl_mvm_has_new_rx_stats_api(mvm)) {857 struct mvm_statistics_rx_phy_v2 *cck = &mvm->rx_stats_v3.cck;858 859 PRINT_STATS_LE32(cck, ina_cnt);860 PRINT_STATS_LE32(cck, fina_cnt);861 PRINT_STATS_LE32(cck, plcp_err);862 PRINT_STATS_LE32(cck, crc32_err);863 PRINT_STATS_LE32(cck, overrun_err);864 PRINT_STATS_LE32(cck, early_overrun_err);865 PRINT_STATS_LE32(cck, crc32_good);866 PRINT_STATS_LE32(cck, false_alarm_cnt);867 PRINT_STATS_LE32(cck, fina_sync_err_cnt);868 PRINT_STATS_LE32(cck, sfd_timeout);869 PRINT_STATS_LE32(cck, fina_timeout);870 PRINT_STATS_LE32(cck, unresponded_rts);871 PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun);872 PRINT_STATS_LE32(cck, sent_ack_cnt);873 PRINT_STATS_LE32(cck, sent_cts_cnt);874 PRINT_STATS_LE32(cck, sent_ba_rsp_cnt);875 PRINT_STATS_LE32(cck, dsp_self_kill);876 PRINT_STATS_LE32(cck, mh_format_err);877 PRINT_STATS_LE32(cck, re_acq_main_rssi_sum);878 PRINT_STATS_LE32(cck, reserved);879 } else {880 struct mvm_statistics_rx_phy *cck = &mvm->rx_stats.cck;881 882 PRINT_STATS_LE32(cck, unresponded_rts);883 PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun);884 PRINT_STATS_LE32(cck, sent_ba_rsp_cnt);885 PRINT_STATS_LE32(cck, dsp_self_kill);886 PRINT_STATS_LE32(cck, reserved);887 }888 889 pos += scnprintf(buf + pos, bufsz - pos, fmt_header,890 "Statistics_Rx - GENERAL");891 if (!iwl_mvm_has_new_rx_stats_api(mvm)) {892 struct mvm_statistics_rx_non_phy_v3 *general =893 &mvm->rx_stats_v3.general;894 895 PRINT_STATS_LE32(general, bogus_cts);896 PRINT_STATS_LE32(general, bogus_ack);897 PRINT_STATS_LE32(general, non_bssid_frames);898 PRINT_STATS_LE32(general, filtered_frames);899 PRINT_STATS_LE32(general, non_channel_beacons);900 PRINT_STATS_LE32(general, channel_beacons);901 PRINT_STATS_LE32(general, num_missed_bcon);902 PRINT_STATS_LE32(general, adc_rx_saturation_time);903 PRINT_STATS_LE32(general, ina_detection_search_time);904 PRINT_STATS_LE32(general, beacon_silence_rssi_a);905 PRINT_STATS_LE32(general, beacon_silence_rssi_b);906 PRINT_STATS_LE32(general, beacon_silence_rssi_c);907 PRINT_STATS_LE32(general, interference_data_flag);908 PRINT_STATS_LE32(general, channel_load);909 PRINT_STATS_LE32(general, dsp_false_alarms);910 PRINT_STATS_LE32(general, beacon_rssi_a);911 PRINT_STATS_LE32(general, beacon_rssi_b);912 PRINT_STATS_LE32(general, beacon_rssi_c);913 PRINT_STATS_LE32(general, beacon_energy_a);914 PRINT_STATS_LE32(general, beacon_energy_b);915 PRINT_STATS_LE32(general, beacon_energy_c);916 PRINT_STATS_LE32(general, num_bt_kills);917 PRINT_STATS_LE32(general, mac_id);918 PRINT_STATS_LE32(general, directed_data_mpdu);919 } else {920 struct mvm_statistics_rx_non_phy *general =921 &mvm->rx_stats.general;922 923 PRINT_STATS_LE32(general, bogus_cts);924 PRINT_STATS_LE32(general, bogus_ack);925 PRINT_STATS_LE32(general, non_channel_beacons);926 PRINT_STATS_LE32(general, channel_beacons);927 PRINT_STATS_LE32(general, num_missed_bcon);928 PRINT_STATS_LE32(general, adc_rx_saturation_time);929 PRINT_STATS_LE32(general, ina_detection_search_time);930 PRINT_STATS_LE32(general, beacon_silence_rssi_a);931 PRINT_STATS_LE32(general, beacon_silence_rssi_b);932 PRINT_STATS_LE32(general, beacon_silence_rssi_c);933 PRINT_STATS_LE32(general, interference_data_flag);934 PRINT_STATS_LE32(general, channel_load);935 PRINT_STATS_LE32(general, beacon_rssi_a);936 PRINT_STATS_LE32(general, beacon_rssi_b);937 PRINT_STATS_LE32(general, beacon_rssi_c);938 PRINT_STATS_LE32(general, beacon_energy_a);939 PRINT_STATS_LE32(general, beacon_energy_b);940 PRINT_STATS_LE32(general, beacon_energy_c);941 PRINT_STATS_LE32(general, num_bt_kills);942 PRINT_STATS_LE32(general, mac_id);943 }944 945 pos += scnprintf(buf + pos, bufsz - pos, fmt_header,946 "Statistics_Rx - HT");947 if (!iwl_mvm_has_new_rx_stats_api(mvm)) {948 struct mvm_statistics_rx_ht_phy_v1 *ht =949 &mvm->rx_stats_v3.ofdm_ht;950 951 PRINT_STATS_LE32(ht, plcp_err);952 PRINT_STATS_LE32(ht, overrun_err);953 PRINT_STATS_LE32(ht, early_overrun_err);954 PRINT_STATS_LE32(ht, crc32_good);955 PRINT_STATS_LE32(ht, crc32_err);956 PRINT_STATS_LE32(ht, mh_format_err);957 PRINT_STATS_LE32(ht, agg_crc32_good);958 PRINT_STATS_LE32(ht, agg_mpdu_cnt);959 PRINT_STATS_LE32(ht, agg_cnt);960 PRINT_STATS_LE32(ht, unsupport_mcs);961 } else {962 struct mvm_statistics_rx_ht_phy *ht =963 &mvm->rx_stats.ofdm_ht;964 965 PRINT_STATS_LE32(ht, mh_format_err);966 PRINT_STATS_LE32(ht, agg_mpdu_cnt);967 PRINT_STATS_LE32(ht, agg_cnt);968 PRINT_STATS_LE32(ht, unsupport_mcs);969 }970 971 mutex_unlock(&mvm->mutex);972 973 ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);974 kfree(buf);975 976 return ret;977}978#undef PRINT_STAT_LE32979 980static ssize_t iwl_dbgfs_fw_system_stats_read(struct file *file,981 char __user *user_buf,982 size_t count, loff_t *ppos)983{984 char *buff, *pos, *endpos;985 int ret;986 size_t bufsz;987 int i;988 struct iwl_mvm_vif *mvmvif;989 struct ieee80211_vif *vif;990 struct iwl_mvm *mvm = file->private_data;991 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,992 WIDE_ID(SYSTEM_GROUP,993 SYSTEM_STATISTICS_CMD),994 IWL_FW_CMD_VER_UNKNOWN);995 996 /* in case of a wrong cmd version, allocate buffer only for error msg */997 bufsz = (cmd_ver == 1) ? 4096 : 64;998 999 buff = kzalloc(bufsz, GFP_KERNEL);1000 if (!buff)1001 return -ENOMEM;1002 1003 pos = buff;1004 endpos = pos + bufsz;1005 1006 if (cmd_ver != 1) {1007 pos += scnprintf(pos, endpos - pos,1008 "System stats not supported:%d\n", cmd_ver);1009 goto send_out;1010 }1011 1012 mutex_lock(&mvm->mutex);1013 if (iwl_mvm_firmware_running(mvm))1014 iwl_mvm_request_statistics(mvm, false);1015 1016 for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) {1017 vif = iwl_mvm_rcu_dereference_vif_id(mvm, i, false);1018 if (!vif)1019 continue;1020 1021 if (vif->type == NL80211_IFTYPE_STATION)1022 break;1023 }1024 1025 if (i == NUM_MAC_INDEX_DRIVER || !vif) {1026 pos += scnprintf(pos, endpos - pos, "vif is NULL\n");1027 goto release_send_out;1028 }1029 1030 mvmvif = iwl_mvm_vif_from_mac80211(vif);1031 if (!mvmvif) {1032 pos += scnprintf(pos, endpos - pos, "mvmvif is NULL\n");1033 goto release_send_out;1034 }1035 1036 for_each_mvm_vif_valid_link(mvmvif, i) {1037 struct iwl_mvm_vif_link_info *link_info = mvmvif->link[i];1038 1039 pos += scnprintf(pos, endpos - pos,1040 "link_id %d", i);1041 pos += scnprintf(pos, endpos - pos,1042 " num_beacons %d",1043 link_info->beacon_stats.num_beacons);1044 pos += scnprintf(pos, endpos - pos,1045 " accu_num_beacons %d",1046 link_info->beacon_stats.accu_num_beacons);1047 pos += scnprintf(pos, endpos - pos,1048 " avg_signal %d\n",1049 link_info->beacon_stats.avg_signal);1050 }1051 1052 pos += scnprintf(pos, endpos - pos,1053 "radio_stats.rx_time %lld\n",1054 mvm->radio_stats.rx_time);1055 pos += scnprintf(pos, endpos - pos,1056 "radio_stats.tx_time %lld\n",1057 mvm->radio_stats.tx_time);1058 pos += scnprintf(pos, endpos - pos,1059 "accu_radio_stats.rx_time %lld\n",1060 mvm->accu_radio_stats.rx_time);1061 pos += scnprintf(pos, endpos - pos,1062 "accu_radio_stats.tx_time %lld\n",1063 mvm->accu_radio_stats.tx_time);1064 1065release_send_out:1066 mutex_unlock(&mvm->mutex);1067 1068send_out:1069 ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);1070 kfree(buff);1071 1072 return ret;1073}1074 1075static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm,1076 char __user *user_buf, size_t count,1077 loff_t *ppos,1078 struct iwl_mvm_frame_stats *stats)1079{1080 char *buff, *pos, *endpos;1081 int idx, i;1082 int ret;1083 static const size_t bufsz = 1024;1084 1085 buff = kmalloc(bufsz, GFP_KERNEL);1086 if (!buff)1087 return -ENOMEM;1088 1089 spin_lock_bh(&mvm->drv_stats_lock);1090 1091 pos = buff;1092 endpos = pos + bufsz;1093 1094 pos += scnprintf(pos, endpos - pos,1095 "Legacy/HT/VHT\t:\t%d/%d/%d\n",1096 stats->legacy_frames,1097 stats->ht_frames,1098 stats->vht_frames);1099 pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n",1100 stats->bw_20_frames,1101 stats->bw_40_frames,1102 stats->bw_80_frames);1103 pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n",1104 stats->ngi_frames,1105 stats->sgi_frames);1106 pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n",1107 stats->siso_frames,1108 stats->mimo2_frames);1109 pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n",1110 stats->fail_frames,1111 stats->success_frames);1112 pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n",1113 stats->agg_frames);1114 pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n",1115 stats->ampdu_count);1116 pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n",1117 stats->ampdu_count > 0 ?1118 (stats->agg_frames / stats->ampdu_count) : 0);1119 1120 pos += scnprintf(pos, endpos - pos, "Last Rates\n");1121 1122 idx = stats->last_frame_idx - 1;1123 for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) {1124 idx = (idx + 1) % ARRAY_SIZE(stats->last_rates);1125 if (stats->last_rates[idx] == 0)1126 continue;1127 pos += scnprintf(pos, endpos - pos, "Rate[%d]: ",1128 (int)(ARRAY_SIZE(stats->last_rates) - i));1129 pos += rs_pretty_print_rate_v1(pos, endpos - pos,1130 stats->last_rates[idx]);1131 if (pos < endpos - 1)1132 *pos++ = '\n';1133 }1134 spin_unlock_bh(&mvm->drv_stats_lock);1135 1136 ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);1137 kfree(buff);1138 1139 return ret;1140}1141 1142static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file,1143 char __user *user_buf, size_t count,1144 loff_t *ppos)1145{1146 struct iwl_mvm *mvm = file->private_data;1147 1148 return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos,1149 &mvm->drv_rx_stats);1150}1151 1152static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,1153 size_t count, loff_t *ppos)1154{1155 int __maybe_unused ret;1156 1157 if (!iwl_mvm_firmware_running(mvm))1158 return -EIO;1159 1160 mutex_lock(&mvm->mutex);1161 1162 /* allow one more restart that we're provoking here */1163 if (mvm->fw_restart >= 0)1164 mvm->fw_restart++;1165 1166 if (count == 6 && !strcmp(buf, "nolog\n")) {1167 set_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status);1168 set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mvm->trans->status);1169 }1170 1171 /* take the return value to make compiler happy - it will fail anyway */1172 ret = iwl_mvm_send_cmd_pdu(mvm,1173 WIDE_ID(LONG_GROUP, REPLY_ERROR),1174 0, 0, NULL);1175 1176 mutex_unlock(&mvm->mutex);1177 1178 return count;1179}1180 1181static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,1182 size_t count, loff_t *ppos)1183{1184 if (!iwl_mvm_firmware_running(mvm))1185 return -EIO;1186 1187 IWL_ERR(mvm, "Triggering an NMI from debugfs\n");1188 1189 if (count == 6 && !strcmp(buf, "nolog\n"))1190 set_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status);1191 1192 iwl_force_nmi(mvm->trans);1193 1194 return count;1195}1196 1197static ssize_t1198iwl_dbgfs_scan_ant_rxchain_read(struct file *file,1199 char __user *user_buf,1200 size_t count, loff_t *ppos)1201{1202 struct iwl_mvm *mvm = file->private_data;1203 int pos = 0;1204 char buf[32];1205 const size_t bufsz = sizeof(buf);1206 1207 /* print which antennas were set for the scan command by the user */1208 pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: ");1209 if (mvm->scan_rx_ant & ANT_A)1210 pos += scnprintf(buf + pos, bufsz - pos, "A");1211 if (mvm->scan_rx_ant & ANT_B)1212 pos += scnprintf(buf + pos, bufsz - pos, "B");1213 pos += scnprintf(buf + pos, bufsz - pos, " (%x)\n", mvm->scan_rx_ant);1214 1215 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);1216}1217 1218static ssize_t1219iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,1220 size_t count, loff_t *ppos)1221{1222 u8 scan_rx_ant;1223 1224 if (!iwl_mvm_firmware_running(mvm))1225 return -EIO;1226 1227 if (sscanf(buf, "%hhx", &scan_rx_ant) != 1)1228 return -EINVAL;1229 if (scan_rx_ant > ANT_ABC)1230 return -EINVAL;1231 if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm)))1232 return -EINVAL;1233 1234 if (mvm->scan_rx_ant != scan_rx_ant) {1235 mvm->scan_rx_ant = scan_rx_ant;1236 if (fw_has_capa(&mvm->fw->ucode_capa,1237 IWL_UCODE_TLV_CAPA_UMAC_SCAN))1238 iwl_mvm_config_scan(mvm);1239 }1240 1241 return count;1242}1243 1244static ssize_t iwl_dbgfs_indirection_tbl_write(struct iwl_mvm *mvm,1245 char *buf, size_t count,1246 loff_t *ppos)1247{1248 struct iwl_rss_config_cmd cmd = {1249 .flags = cpu_to_le32(IWL_RSS_ENABLE),1250 .hash_mask = IWL_RSS_HASH_TYPE_IPV4_TCP |1251 IWL_RSS_HASH_TYPE_IPV4_UDP |1252 IWL_RSS_HASH_TYPE_IPV4_PAYLOAD |1253 IWL_RSS_HASH_TYPE_IPV6_TCP |1254 IWL_RSS_HASH_TYPE_IPV6_UDP |1255 IWL_RSS_HASH_TYPE_IPV6_PAYLOAD,1256 };1257 int ret, i, num_repeats, nbytes = count / 2;1258 1259 ret = hex2bin(cmd.indirection_table, buf, nbytes);1260 if (ret)1261 return ret;1262 1263 /*1264 * The input is the redirection table, partial or full.1265 * Repeat the pattern if needed.1266 * For example, input of 01020F will be repeated 42 times,1267 * indirecting RSS hash results to queues 1, 2, 15 (skipping1268 * queues 3 - 14).1269 */1270 num_repeats = ARRAY_SIZE(cmd.indirection_table) / nbytes;1271 for (i = 1; i < num_repeats; i++)1272 memcpy(&cmd.indirection_table[i * nbytes],1273 cmd.indirection_table, nbytes);1274 /* handle cut in the middle pattern for the last places */1275 memcpy(&cmd.indirection_table[i * nbytes], cmd.indirection_table,1276 ARRAY_SIZE(cmd.indirection_table) % nbytes);1277 1278 netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key));1279 1280 mutex_lock(&mvm->mutex);1281 if (iwl_mvm_firmware_running(mvm))1282 ret = iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0,1283 sizeof(cmd), &cmd);1284 else1285 ret = 0;1286 mutex_unlock(&mvm->mutex);1287 1288 return ret ?: count;1289}1290 1291static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mvm *mvm,1292 char *buf, size_t count,1293 loff_t *ppos)1294{1295 struct iwl_op_mode *opmode = container_of((void *)mvm,1296 struct iwl_op_mode,1297 op_mode_specific);1298 struct iwl_rx_cmd_buffer rxb = {1299 ._rx_page_order = 0,1300 .truesize = 0, /* not used */1301 ._offset = 0,1302 };1303 struct iwl_rx_packet *pkt;1304 int bin_len = count / 2;1305 int ret = -EINVAL;1306 1307 if (!iwl_mvm_firmware_running(mvm))1308 return -EIO;1309 1310 /* supporting only MQ RX */1311 if (!mvm->trans->trans_cfg->mq_rx_supported)1312 return -EOPNOTSUPP;1313 1314 rxb._page = alloc_pages(GFP_ATOMIC, 0);1315 if (!rxb._page)1316 return -ENOMEM;1317 pkt = rxb_addr(&rxb);1318 1319 ret = hex2bin(page_address(rxb._page), buf, bin_len);1320 if (ret)1321 goto out;1322 1323 /* avoid invalid memory access and malformed packet */1324 if (bin_len < sizeof(*pkt) ||1325 bin_len != sizeof(*pkt) + iwl_rx_packet_payload_len(pkt))1326 goto out;1327 1328 local_bh_disable();1329 iwl_mvm_rx_mq(opmode, NULL, &rxb);1330 local_bh_enable();1331 ret = 0;1332 1333out:1334 iwl_free_rxb(&rxb);1335 1336 return ret ?: count;1337}1338 1339static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len)1340{1341 struct ieee80211_vif *vif;1342 struct iwl_mvm_vif *mvmvif;1343 struct sk_buff *beacon;1344 struct ieee80211_tx_info *info;1345 struct iwl_mac_beacon_cmd beacon_cmd = {};1346 unsigned int link_id;1347 u8 rate;1348 int i;1349 1350 len /= 2;1351 1352 /* Element len should be represented by u8 */1353 if (len >= U8_MAX)1354 return -EINVAL;1355 1356 if (!iwl_mvm_firmware_running(mvm))1357 return -EIO;1358 1359 if (!iwl_mvm_has_new_tx_api(mvm) &&1360 !fw_has_api(&mvm->fw->ucode_capa,1361 IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE))1362 return -EINVAL;1363 1364 mutex_lock(&mvm->mutex);1365 1366 for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) {1367 vif = iwl_mvm_rcu_dereference_vif_id(mvm, i, false);1368 if (!vif)1369 continue;1370 1371 if (vif->type == NL80211_IFTYPE_AP)1372 break;1373 }1374 1375 if (i == NUM_MAC_INDEX_DRIVER || !vif)1376 goto out_err;1377 1378 mvm->hw->extra_beacon_tailroom = len;1379 1380 beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL, 0);1381 if (!beacon)1382 goto out_err;1383 1384 if (len && hex2bin(skb_put_zero(beacon, len), bin, len)) {1385 dev_kfree_skb(beacon);1386 goto out_err;1387 }1388 1389 mvm->beacon_inject_active = true;1390 1391 mvmvif = iwl_mvm_vif_from_mac80211(vif);1392 info = IEEE80211_SKB_CB(beacon);1393 rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif);1394 1395 for_each_mvm_vif_valid_link(mvmvif, link_id) {1396 beacon_cmd.flags =1397 cpu_to_le16(iwl_mvm_mac_ctxt_get_beacon_flags(mvm->fw,1398 rate));1399 beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len);1400 if (iwl_fw_lookup_cmd_ver(mvm->fw, BEACON_TEMPLATE_CMD, 0) > 12)1401 beacon_cmd.link_id =1402 cpu_to_le32(mvmvif->link[link_id]->fw_link_id);1403 else1404 beacon_cmd.link_id = cpu_to_le32((u32)mvmvif->id);1405 1406 iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx,1407 &beacon_cmd.tim_size,1408 beacon->data, beacon->len);1409 1410 if (iwl_fw_lookup_cmd_ver(mvm->fw,1411 BEACON_TEMPLATE_CMD, 0) >= 14) {1412 u32 offset = iwl_mvm_find_ie_offset(beacon->data,1413 WLAN_EID_S1G_TWT,1414 beacon->len);1415 1416 beacon_cmd.btwt_offset = cpu_to_le32(offset);1417 }1418 1419 iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd,1420 sizeof(beacon_cmd));1421 }1422 mutex_unlock(&mvm->mutex);1423 1424 dev_kfree_skb(beacon);1425 1426 return 0;1427 1428out_err:1429 mutex_unlock(&mvm->mutex);1430 return -EINVAL;1431}1432 1433static ssize_t iwl_dbgfs_inject_beacon_ie_write(struct iwl_mvm *mvm,1434 char *buf, size_t count,1435 loff_t *ppos)1436{1437 int ret = _iwl_dbgfs_inject_beacon_ie(mvm, buf, count);1438 1439 mvm->hw->extra_beacon_tailroom = 0;1440 return ret ?: count;1441}1442 1443static ssize_t iwl_dbgfs_inject_beacon_ie_restore_write(struct iwl_mvm *mvm,1444 char *buf,1445 size_t count,1446 loff_t *ppos)1447{1448 int ret = _iwl_dbgfs_inject_beacon_ie(mvm, NULL, 0);1449 1450 mvm->hw->extra_beacon_tailroom = 0;1451 mvm->beacon_inject_active = false;1452 return ret ?: count;1453}1454 1455static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,1456 char __user *user_buf,1457 size_t count, loff_t *ppos)1458{1459 struct iwl_mvm *mvm = file->private_data;1460 int conf;1461 char buf[8];1462 const size_t bufsz = sizeof(buf);1463 int pos = 0;1464 1465 mutex_lock(&mvm->mutex);1466 conf = mvm->fwrt.dump.conf;1467 mutex_unlock(&mvm->mutex);1468 1469 pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf);1470 1471 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);1472}1473 1474static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,1475 char *buf, size_t count,1476 loff_t *ppos)1477{1478 unsigned int conf_id;1479 int ret;1480 1481 if (!iwl_mvm_firmware_running(mvm))1482 return -EIO;1483 1484 ret = kstrtouint(buf, 0, &conf_id);1485 if (ret)1486 return ret;1487 1488 if (WARN_ON(conf_id >= FW_DBG_CONF_MAX))1489 return -EINVAL;1490 1491 mutex_lock(&mvm->mutex);1492 ret = iwl_fw_start_dbg_conf(&mvm->fwrt, conf_id);1493 mutex_unlock(&mvm->mutex);1494 1495 return ret ?: count;1496}1497 1498static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,1499 char *buf, size_t count,1500 loff_t *ppos)1501{1502 if (count == 0)1503 return 0;1504 1505 iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_USER_TRIGGER,1506 NULL);1507 1508 iwl_fw_dbg_collect(&mvm->fwrt, FW_DBG_TRIGGER_USER, buf,1509 (count - 1), NULL);1510 1511 return count;1512}1513 1514static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mvm *mvm,1515 char *buf, size_t count,1516 loff_t *ppos)1517{1518 if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)1519 return -EOPNOTSUPP;1520 1521 mutex_lock(&mvm->mutex);1522 iwl_fw_dbg_clear_monitor_buf(&mvm->fwrt);1523 mutex_unlock(&mvm->mutex);1524 1525 return count;1526}1527 1528static ssize_t iwl_dbgfs_dbg_time_point_write(struct iwl_mvm *mvm,1529 char *buf, size_t count,1530 loff_t *ppos)1531{1532 u32 timepoint;1533 1534 if (kstrtou32(buf, 0, &timepoint))1535 return -EINVAL;1536 1537 if (timepoint == IWL_FW_INI_TIME_POINT_INVALID ||1538 timepoint >= IWL_FW_INI_TIME_POINT_NUM)1539 return -EINVAL;1540 1541 iwl_dbg_tlv_time_point(&mvm->fwrt, timepoint, NULL);1542 1543 return count;1544}1545 1546#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \1547 _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)1548#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \1549 _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)1550#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \1551 debugfs_create_file(alias, mode, parent, mvm, \1552 &iwl_dbgfs_##name##_ops); \1553 } while (0)1554#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \1555 MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)1556 1557static ssize_t1558_iwl_dbgfs_link_sta_wrap_write(ssize_t (*real)(struct ieee80211_link_sta *,1559 struct iwl_mvm_sta *,1560 struct iwl_mvm *,1561 struct iwl_mvm_link_sta *,1562 char *,1563 size_t, loff_t *),1564 struct file *file,1565 char *buf, size_t buf_size, loff_t *ppos)1566{1567 struct ieee80211_link_sta *link_sta = file->private_data;1568 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(link_sta->sta);1569 struct iwl_mvm *mvm = iwl_mvm_vif_from_mac80211(mvmsta->vif)->mvm;1570 struct iwl_mvm_link_sta *mvm_link_sta;1571 ssize_t ret;1572 1573 mutex_lock(&mvm->mutex);1574 1575 mvm_link_sta = rcu_dereference_protected(mvmsta->link[link_sta->link_id],1576 lockdep_is_held(&mvm->mutex));1577 if (WARN_ON(!mvm_link_sta)) {1578 mutex_unlock(&mvm->mutex);1579 return -ENODEV;1580 }1581 1582 ret = real(link_sta, mvmsta, mvm, mvm_link_sta, buf, buf_size, ppos);1583 1584 mutex_unlock(&mvm->mutex);1585 1586 return ret;1587}1588 1589static ssize_t1590_iwl_dbgfs_link_sta_wrap_read(ssize_t (*real)(struct ieee80211_link_sta *,1591 struct iwl_mvm_sta *,1592 struct iwl_mvm *,1593 struct iwl_mvm_link_sta *,1594 char __user *,1595 size_t, loff_t *),1596 struct file *file,1597 char __user *user_buf, size_t count, loff_t *ppos)1598{1599 struct ieee80211_link_sta *link_sta = file->private_data;1600 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(link_sta->sta);1601 struct iwl_mvm *mvm = iwl_mvm_vif_from_mac80211(mvmsta->vif)->mvm;1602 struct iwl_mvm_link_sta *mvm_link_sta;1603 ssize_t ret;1604 1605 mutex_lock(&mvm->mutex);1606 1607 mvm_link_sta = rcu_dereference_protected(mvmsta->link[link_sta->link_id],1608 lockdep_is_held(&mvm->mutex));1609 if (WARN_ON(!mvm_link_sta)) {1610 mutex_unlock(&mvm->mutex);1611 return -ENODEV;1612 }1613 1614 ret = real(link_sta, mvmsta, mvm, mvm_link_sta, user_buf, count, ppos);1615 1616 mutex_unlock(&mvm->mutex);1617 1618 return ret;1619}1620 1621#define MVM_DEBUGFS_LINK_STA_WRITE_WRAPPER(name, buflen) \1622static ssize_t _iwl_dbgfs_link_sta_##name##_write(struct file *file, \1623 const char __user *user_buf, \1624 size_t count, loff_t *ppos) \1625{ \1626 char buf[buflen] = {}; \1627 size_t buf_size = min(count, sizeof(buf) - 1); \1628 \1629 if (copy_from_user(buf, user_buf, buf_size)) \1630 return -EFAULT; \1631 \1632 return _iwl_dbgfs_link_sta_wrap_write(iwl_dbgfs_##name##_write, \1633 file, \1634 buf, buf_size, ppos); \1635} \1636 1637#define MVM_DEBUGFS_LINK_STA_READ_WRAPPER(name) \1638static ssize_t _iwl_dbgfs_link_sta_##name##_read(struct file *file, \1639 char __user *user_buf, \1640 size_t count, loff_t *ppos) \1641{ \1642 return _iwl_dbgfs_link_sta_wrap_read(iwl_dbgfs_##name##_read, \1643 file, \1644 user_buf, count, ppos); \1645} \1646 1647#define MVM_DEBUGFS_WRITE_LINK_STA_FILE_OPS(name, bufsz) \1648MVM_DEBUGFS_LINK_STA_WRITE_WRAPPER(name, bufsz) \1649static const struct file_operations iwl_dbgfs_link_sta_##name##_ops = { \1650 .write = _iwl_dbgfs_link_sta_##name##_write, \1651 .open = simple_open, \1652 .llseek = generic_file_llseek, \1653}1654 1655#define MVM_DEBUGFS_READ_LINK_STA_FILE_OPS(name) \1656MVM_DEBUGFS_LINK_STA_READ_WRAPPER(name) \1657static const struct file_operations iwl_dbgfs_link_sta_##name##_ops = { \1658 .read = _iwl_dbgfs_link_sta_##name##_read, \1659 .open = simple_open, \1660 .llseek = generic_file_llseek, \1661}1662 1663#define MVM_DEBUGFS_READ_WRITE_LINK_STA_FILE_OPS(name, bufsz) \1664MVM_DEBUGFS_LINK_STA_READ_WRAPPER(name) \1665MVM_DEBUGFS_LINK_STA_WRITE_WRAPPER(name, bufsz) \1666static const struct file_operations iwl_dbgfs_link_sta_##name##_ops = { \1667 .read = _iwl_dbgfs_link_sta_##name##_read, \1668 .write = _iwl_dbgfs_link_sta_##name##_write, \1669 .open = simple_open, \1670 .llseek = generic_file_llseek, \1671}1672 1673#define MVM_DEBUGFS_ADD_LINK_STA_FILE_ALIAS(alias, name, parent, mode) \1674 debugfs_create_file(alias, mode, parent, link_sta, \1675 &iwl_dbgfs_link_sta_##name##_ops)1676#define MVM_DEBUGFS_ADD_LINK_STA_FILE(name, parent, mode) \1677 MVM_DEBUGFS_ADD_LINK_STA_FILE_ALIAS(#name, name, parent, mode)1678 1679static ssize_t1680iwl_dbgfs_prph_reg_read(struct file *file,1681 char __user *user_buf,1682 size_t count, loff_t *ppos)1683{1684 struct iwl_mvm *mvm = file->private_data;1685 int pos = 0;1686 char buf[32];1687 const size_t bufsz = sizeof(buf);1688 1689 if (!mvm->dbgfs_prph_reg_addr)1690 return -EINVAL;1691 1692 pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n",1693 mvm->dbgfs_prph_reg_addr,1694 iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr));1695 1696 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);1697}1698 1699static ssize_t1700iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,1701 size_t count, loff_t *ppos)1702{1703 u8 args;1704 u32 value;1705 1706 args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value);1707 /* if we only want to set the reg address - nothing more to do */1708 if (args == 1)1709 goto out;1710 1711 /* otherwise, make sure we have both address and value */1712 if (args != 2)1713 return -EINVAL;1714 1715 iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value);1716 1717out:1718 return count;1719}1720 1721static ssize_t1722iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,1723 size_t count, loff_t *ppos)1724{1725 int ret;1726 1727 if (!iwl_mvm_firmware_running(mvm))1728 return -EIO;1729 1730 mutex_lock(&mvm->mutex);1731 ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL);1732 mutex_unlock(&mvm->mutex);1733 1734 return ret ?: count;1735}1736 1737struct iwl_mvm_sniffer_apply {1738 struct iwl_mvm *mvm;1739 u8 *bssid;1740 u16 aid;1741};1742 1743static bool iwl_mvm_sniffer_apply(struct iwl_notif_wait_data *notif_data,1744 struct iwl_rx_packet *pkt, void *data)1745{1746 struct iwl_mvm_sniffer_apply *apply = data;1747 1748 apply->mvm->cur_aid = cpu_to_le16(apply->aid);1749 memcpy(apply->mvm->cur_bssid, apply->bssid,1750 sizeof(apply->mvm->cur_bssid));1751 1752 return true;1753}1754 1755static ssize_t1756iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm *mvm, char *buf,1757 size_t count, loff_t *ppos)1758{1759 struct iwl_notification_wait wait;1760 struct iwl_he_monitor_cmd he_mon_cmd = {};1761 struct iwl_mvm_sniffer_apply apply = {1762 .mvm = mvm,1763 };1764 u16 wait_cmds[] = {1765 WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD),1766 };1767 u32 aid;1768 int ret;1769 1770 if (!iwl_mvm_firmware_running(mvm))1771 return -EIO;1772 1773 ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid,1774 &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1],1775 &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3],1776 &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]);1777 if (ret != 7)1778 return -EINVAL;1779 1780 he_mon_cmd.aid = cpu_to_le16(aid);1781 1782 apply.aid = aid;1783 apply.bssid = (void *)he_mon_cmd.bssid;1784 1785 mutex_lock(&mvm->mutex);1786 1787 /*1788 * Use the notification waiter to get our function triggered1789 * in sequence with other RX. This ensures that frames we get1790 * on the RX queue _before_ the new configuration is applied1791 * still have mvm->cur_aid pointing to the old AID, and that1792 * frames on the RX queue _after_ the firmware processed the1793 * new configuration (and sent the response, synchronously)1794 * get mvm->cur_aid correctly set to the new AID.1795 */1796 iwl_init_notification_wait(&mvm->notif_wait, &wait,1797 wait_cmds, ARRAY_SIZE(wait_cmds),1798 iwl_mvm_sniffer_apply, &apply);1799 1800 ret = iwl_mvm_send_cmd_pdu(mvm,1801 WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD),1802 0,1803 sizeof(he_mon_cmd), &he_mon_cmd);1804 1805 /* no need to really wait, we already did anyway */1806 iwl_remove_notification(&mvm->notif_wait, &wait);1807 1808 mutex_unlock(&mvm->mutex);1809 1810 return ret ?: count;1811}1812 1813static ssize_t1814iwl_dbgfs_he_sniffer_params_read(struct file *file, char __user *user_buf,1815 size_t count, loff_t *ppos)1816{1817 struct iwl_mvm *mvm = file->private_data;1818 u8 buf[32];1819 int len;1820 1821 len = scnprintf(buf, sizeof(buf),1822 "%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",1823 le16_to_cpu(mvm->cur_aid), mvm->cur_bssid[0],1824 mvm->cur_bssid[1], mvm->cur_bssid[2], mvm->cur_bssid[3],1825 mvm->cur_bssid[4], mvm->cur_bssid[5]);1826 1827 return simple_read_from_buffer(user_buf, count, ppos, buf, len);1828}1829 1830static ssize_t1831iwl_dbgfs_uapsd_noagg_bssids_read(struct file *file, char __user *user_buf,1832 size_t count, loff_t *ppos)1833{1834 struct iwl_mvm *mvm = file->private_data;1835 u8 buf[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM * ETH_ALEN * 3 + 1];1836 unsigned int pos = 0;1837 size_t bufsz = sizeof(buf);1838 int i;1839 1840 mutex_lock(&mvm->mutex);1841 1842 for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++)1843 pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",1844 mvm->uapsd_noagg_bssids[i].addr);1845 1846 mutex_unlock(&mvm->mutex);1847 1848 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);1849}1850 1851static ssize_t1852iwl_dbgfs_ltr_config_write(struct iwl_mvm *mvm,1853 char *buf, size_t count, loff_t *ppos)1854{1855 int ret;1856 struct iwl_ltr_config_cmd ltr_config = {0};1857 1858 if (!iwl_mvm_firmware_running(mvm))1859 return -EIO;1860 1861 if (sscanf(buf, "%x,%x,%x,%x,%x,%x,%x",1862 <r_config.flags,1863 <r_config.static_long,1864 <r_config.static_short,1865 <r_config.ltr_cfg_values[0],1866 <r_config.ltr_cfg_values[1],1867 <r_config.ltr_cfg_values[2],1868 <r_config.ltr_cfg_values[3]) != 7) {1869 return -EINVAL;1870 }1871 1872 mutex_lock(&mvm->mutex);1873 ret = iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, sizeof(ltr_config),1874 <r_config);1875 mutex_unlock(&mvm->mutex);1876 1877 if (ret)1878 IWL_ERR(mvm, "failed to send ltr configuration cmd\n");1879 1880 return ret ?: count;1881}1882 1883static ssize_t iwl_dbgfs_rfi_freq_table_write(struct iwl_mvm *mvm, char *buf,1884 size_t count, loff_t *ppos)1885{1886 int ret = 0;1887 u16 op_id;1888 1889 if (kstrtou16(buf, 10, &op_id))1890 return -EINVAL;1891 1892 /* value zero triggers re-sending the default table to the device */1893 if (!op_id) {1894 mutex_lock(&mvm->mutex);1895 ret = iwl_rfi_send_config_cmd(mvm, NULL);1896 mutex_unlock(&mvm->mutex);1897 } else {1898 ret = -EOPNOTSUPP; /* in the future a new table will be added */1899 }1900 1901 return ret ?: count;1902}1903 1904/* The size computation is as follows:1905 * each number needs at most 3 characters, number of rows is the size of1906 * the table; So, need 5 chars for the "freq: " part and each tuple afterwards1907 * needs 6 characters for numbers and 5 for the punctuation around.1908 */1909#define IWL_RFI_BUF_SIZE (IWL_RFI_LUT_INSTALLED_SIZE *\1910 (5 + IWL_RFI_LUT_ENTRY_CHANNELS_NUM * (6 + 5)))1911 1912static ssize_t iwl_dbgfs_rfi_freq_table_read(struct file *file,1913 char __user *user_buf,1914 size_t count, loff_t *ppos)1915{1916 struct iwl_mvm *mvm = file->private_data;1917 struct iwl_rfi_freq_table_resp_cmd *resp;1918 u32 status;1919 char buf[IWL_RFI_BUF_SIZE];1920 int i, j, pos = 0;1921 1922 resp = iwl_rfi_get_freq_table(mvm);1923 if (IS_ERR(resp))1924 return PTR_ERR(resp);1925 1926 status = le32_to_cpu(resp->status);1927 if (status != RFI_FREQ_TABLE_OK) {1928 scnprintf(buf, IWL_RFI_BUF_SIZE, "status = %d\n", status);1929 goto out;1930 }1931 1932 for (i = 0; i < ARRAY_SIZE(resp->table); i++) {1933 pos += scnprintf(buf + pos, IWL_RFI_BUF_SIZE - pos, "%d: ",1934 resp->table[i].freq);1935 1936 for (j = 0; j < ARRAY_SIZE(resp->table[i].channels); j++)1937 pos += scnprintf(buf + pos, IWL_RFI_BUF_SIZE - pos,1938 "(%d, %d) ",1939 resp->table[i].channels[j],1940 resp->table[i].bands[j]);1941 pos += scnprintf(buf + pos, IWL_RFI_BUF_SIZE - pos, "\n");1942 }1943 1944out:1945 kfree(resp);1946 return simple_read_from_buffer(user_buf, count, ppos, buf, pos);1947}1948 1949MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);1950 1951/* Device wide debugfs entries */1952MVM_DEBUGFS_READ_FILE_OPS(ctdp_budget);1953MVM_DEBUGFS_WRITE_FILE_OPS(stop_ctdp, 8);1954MVM_DEBUGFS_WRITE_FILE_OPS(start_ctdp, 8);1955MVM_DEBUGFS_WRITE_FILE_OPS(force_ctkill, 8);1956MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);1957MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8);1958MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64);1959MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64);1960MVM_DEBUGFS_READ_FILE_OPS(nic_temp);1961MVM_DEBUGFS_READ_FILE_OPS(stations);1962MVM_DEBUGFS_READ_LINK_STA_FILE_OPS(rs_data);1963MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64);1964MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);1965MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);1966MVM_DEBUGFS_READ_FILE_OPS(fw_system_stats);1967MVM_DEBUGFS_READ_FILE_OPS(fw_ver);1968MVM_DEBUGFS_READ_FILE_OPS(phy_integration_ver);1969MVM_DEBUGFS_READ_FILE_OPS(tas_get_status);1970MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);1971MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);1972MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);1973MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);1974MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64);1975MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_clear, 64);1976MVM_DEBUGFS_WRITE_FILE_OPS(dbg_time_point, 64);1977MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl,1978 (IWL_RSS_INDIRECTION_TABLE_SIZE * 2));1979MVM_DEBUGFS_WRITE_FILE_OPS(inject_packet, 512);1980MVM_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512);1981MVM_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512);1982 1983MVM_DEBUGFS_READ_FILE_OPS(uapsd_noagg_bssids);1984 1985#ifdef CONFIG_ACPI1986MVM_DEBUGFS_READ_FILE_OPS(sar_geo_profile);1987MVM_DEBUGFS_READ_FILE_OPS(wifi_6e_enable);1988#endif1989 1990MVM_DEBUGFS_READ_WRITE_LINK_STA_FILE_OPS(amsdu_len, 16);1991 1992MVM_DEBUGFS_READ_WRITE_FILE_OPS(he_sniffer_params, 32);1993 1994MVM_DEBUGFS_WRITE_FILE_OPS(ltr_config, 512);1995MVM_DEBUGFS_READ_WRITE_FILE_OPS(rfi_freq_table, 16);1996 1997static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf,1998 size_t count, loff_t *ppos)1999{2000 struct iwl_mvm *mvm = file->private_data;2001 struct iwl_dbg_mem_access_cmd cmd = {};2002 struct iwl_dbg_mem_access_rsp *rsp;2003 struct iwl_host_cmd hcmd = {2004 .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,2005 .data = { &cmd, },2006 .len = { sizeof(cmd) },2007 };2008 size_t delta;2009 ssize_t ret, len;2010 2011 if (!iwl_mvm_firmware_running(mvm))2012 return -EIO;2013 2014 hcmd.id = WIDE_ID(DEBUG_GROUP, *ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR);2015 cmd.op = cpu_to_le32(DEBUG_MEM_OP_READ);2016 2017 /* Take care of alignment of both the position and the length */2018 delta = *ppos & 0x3;2019 cmd.addr = cpu_to_le32(*ppos - delta);2020 cmd.len = cpu_to_le32(min(ALIGN(count + delta, 4) / 4,2021 (size_t)DEBUG_MEM_MAX_SIZE_DWORDS));2022 2023 mutex_lock(&mvm->mutex);2024 ret = iwl_mvm_send_cmd(mvm, &hcmd);2025 mutex_unlock(&mvm->mutex);2026 2027 if (ret < 0)2028 return ret;2029 2030 if (iwl_rx_packet_payload_len(hcmd.resp_pkt) < sizeof(*rsp)) {2031 ret = -EIO;2032 goto out;2033 }2034 2035 rsp = (void *)hcmd.resp_pkt->data;2036 if (le32_to_cpu(rsp->status) != DEBUG_MEM_STATUS_SUCCESS) {2037 ret = -ENXIO;2038 goto out;2039 }2040 2041 len = min((size_t)le32_to_cpu(rsp->len) << 2,2042 iwl_rx_packet_payload_len(hcmd.resp_pkt) - sizeof(*rsp));2043 len = min(len - delta, count);2044 if (len < 0) {2045 ret = -EFAULT;2046 goto out;2047 }2048 2049 ret = len - copy_to_user(user_buf, (u8 *)rsp->data + delta, len);2050 *ppos += ret;2051 2052out:2053 iwl_free_resp(&hcmd);2054 return ret;2055}2056 2057static ssize_t iwl_dbgfs_mem_write(struct file *file,2058 const char __user *user_buf, size_t count,2059 loff_t *ppos)2060{2061 struct iwl_mvm *mvm = file->private_data;2062 struct iwl_dbg_mem_access_cmd *cmd;2063 struct iwl_dbg_mem_access_rsp *rsp;2064 struct iwl_host_cmd hcmd = {};2065 size_t cmd_size;2066 size_t data_size;2067 u32 op, len;2068 ssize_t ret;2069 2070 if (!iwl_mvm_firmware_running(mvm))2071 return -EIO;2072 2073 hcmd.id = WIDE_ID(DEBUG_GROUP, *ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR);2074 2075 if (*ppos & 0x3 || count < 4) {2076 op = DEBUG_MEM_OP_WRITE_BYTES;2077 len = min(count, (size_t)(4 - (*ppos & 0x3)));2078 data_size = len;2079 } else {2080 op = DEBUG_MEM_OP_WRITE;2081 len = min(count >> 2, (size_t)DEBUG_MEM_MAX_SIZE_DWORDS);2082 data_size = len << 2;2083 }2084 2085 cmd_size = sizeof(*cmd) + ALIGN(data_size, 4);2086 cmd = kzalloc(cmd_size, GFP_KERNEL);2087 if (!cmd)2088 return -ENOMEM;2089 2090 cmd->op = cpu_to_le32(op);2091 cmd->len = cpu_to_le32(len);2092 cmd->addr = cpu_to_le32(*ppos);2093 if (copy_from_user((void *)cmd->data, user_buf, data_size)) {2094 kfree(cmd);2095 return -EFAULT;2096 }2097 2098 hcmd.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,2099 hcmd.data[0] = (void *)cmd;2100 hcmd.len[0] = cmd_size;2101 2102 mutex_lock(&mvm->mutex);2103 ret = iwl_mvm_send_cmd(mvm, &hcmd);2104 mutex_unlock(&mvm->mutex);2105 2106 kfree(cmd);2107 2108 if (ret < 0)2109 return ret;2110 2111 if (iwl_rx_packet_payload_len(hcmd.resp_pkt) < sizeof(*rsp)) {2112 ret = -EIO;2113 goto out;2114 }2115 2116 rsp = (void *)hcmd.resp_pkt->data;2117 if (rsp->status != DEBUG_MEM_STATUS_SUCCESS) {2118 ret = -ENXIO;2119 goto out;2120 }2121 2122 ret = data_size;2123 *ppos += ret;2124 2125out:2126 iwl_free_resp(&hcmd);2127 return ret;2128}2129 2130static const struct file_operations iwl_dbgfs_mem_ops = {2131 .read = iwl_dbgfs_mem_read,2132 .write = iwl_dbgfs_mem_write,2133 .open = simple_open,2134 .llseek = default_llseek,2135};2136 2137void iwl_mvm_link_sta_add_debugfs(struct ieee80211_hw *hw,2138 struct ieee80211_vif *vif,2139 struct ieee80211_link_sta *link_sta,2140 struct dentry *dir)2141{2142 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);2143 2144 if (iwl_mvm_has_tlc_offload(mvm)) {2145 MVM_DEBUGFS_ADD_LINK_STA_FILE(rs_data, dir, 0400);2146 }2147 2148 MVM_DEBUGFS_ADD_LINK_STA_FILE(amsdu_len, dir, 0600);2149}2150 2151void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm)2152{2153 struct dentry *bcast_dir __maybe_unused;2154 2155 spin_lock_init(&mvm->drv_stats_lock);2156 2157 MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, 0200);2158 MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, 0600);2159 MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir, 0600);2160 MVM_DEBUGFS_ADD_FILE(nic_temp, mvm->debugfs_dir, 0400);2161 MVM_DEBUGFS_ADD_FILE(ctdp_budget, mvm->debugfs_dir, 0400);2162 MVM_DEBUGFS_ADD_FILE(stop_ctdp, mvm->debugfs_dir, 0200);2163 MVM_DEBUGFS_ADD_FILE(start_ctdp, mvm->debugfs_dir, 0200);2164 MVM_DEBUGFS_ADD_FILE(force_ctkill, mvm->debugfs_dir, 0200);2165 MVM_DEBUGFS_ADD_FILE(stations, mvm->debugfs_dir, 0400);2166 MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, 0600);2167 MVM_DEBUGFS_ADD_FILE(fw_ver, mvm->debugfs_dir, 0400);2168 MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, 0400);2169 MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, 0400);2170 MVM_DEBUGFS_ADD_FILE(fw_system_stats, mvm->debugfs_dir, 0400);2171 MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, 0200);2172 MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, 0200);2173 MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, 0600);2174 MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, 0600);2175 MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, 0600);2176 MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, 0200);2177 MVM_DEBUGFS_ADD_FILE(fw_dbg_clear, mvm->debugfs_dir, 0200);2178 MVM_DEBUGFS_ADD_FILE(dbg_time_point, mvm->debugfs_dir, 0200);2179 MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, 0200);2180 MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, 0200);2181 MVM_DEBUGFS_ADD_FILE(inject_packet, mvm->debugfs_dir, 0200);2182 MVM_DEBUGFS_ADD_FILE(inject_beacon_ie, mvm->debugfs_dir, 0200);2183 MVM_DEBUGFS_ADD_FILE(inject_beacon_ie_restore, mvm->debugfs_dir, 0200);2184 MVM_DEBUGFS_ADD_FILE(rfi_freq_table, mvm->debugfs_dir, 0600);2185 2186 if (mvm->fw->phy_integration_ver)2187 MVM_DEBUGFS_ADD_FILE(phy_integration_ver, mvm->debugfs_dir, 0400);2188 MVM_DEBUGFS_ADD_FILE(tas_get_status, mvm->debugfs_dir, 0400);2189#ifdef CONFIG_ACPI2190 MVM_DEBUGFS_ADD_FILE(sar_geo_profile, mvm->debugfs_dir, 0400);2191 MVM_DEBUGFS_ADD_FILE(wifi_6e_enable, mvm->debugfs_dir, 0400);2192#endif2193 MVM_DEBUGFS_ADD_FILE(he_sniffer_params, mvm->debugfs_dir, 0600);2194 2195 if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_LTR_GEN2))2196 MVM_DEBUGFS_ADD_FILE(ltr_config, mvm->debugfs_dir, 0200);2197 2198 debugfs_create_bool("enable_scan_iteration_notif", 0600,2199 mvm->debugfs_dir, &mvm->scan_iter_notif_enabled);2200 debugfs_create_bool("drop_bcn_ap_mode", 0600, mvm->debugfs_dir,2201 &mvm->drop_bcn_ap_mode);2202 2203 MVM_DEBUGFS_ADD_FILE(uapsd_noagg_bssids, mvm->debugfs_dir, S_IRUSR);2204 2205#ifdef CONFIG_PM_SLEEP2206 MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, 0400);2207 debugfs_create_bool("d3_wake_sysassert", 0600, mvm->debugfs_dir,2208 &mvm->d3_wake_sysassert);2209 debugfs_create_u32("last_netdetect_scans", 0400, mvm->debugfs_dir,2210 &mvm->last_netdetect_scans);2211#endif2212 2213 debugfs_create_u8("ps_disabled", 0400, mvm->debugfs_dir,2214 &mvm->ps_disabled);2215 debugfs_create_blob("nvm_hw", 0400, mvm->debugfs_dir,2216 &mvm->nvm_hw_blob);2217 debugfs_create_blob("nvm_sw", 0400, mvm->debugfs_dir,2218 &mvm->nvm_sw_blob);2219 debugfs_create_blob("nvm_calib", 0400, mvm->debugfs_dir,2220 &mvm->nvm_calib_blob);2221 debugfs_create_blob("nvm_prod", 0400, mvm->debugfs_dir,2222 &mvm->nvm_prod_blob);2223 debugfs_create_blob("nvm_phy_sku", 0400, mvm->debugfs_dir,2224 &mvm->nvm_phy_sku_blob);2225 debugfs_create_blob("nvm_reg", S_IRUSR,2226 mvm->debugfs_dir, &mvm->nvm_reg_blob);2227 2228 debugfs_create_file("mem", 0600, mvm->debugfs_dir, mvm,2229 &iwl_dbgfs_mem_ops);2230 2231 debugfs_create_bool("rx_ts_ptp", 0600, mvm->debugfs_dir,2232 &mvm->rx_ts_ptp);2233 2234 /*2235 * Create a symlink with mac80211. It will be removed when mac802112236 * exists (before the opmode exists which removes the target.)2237 */2238 if (!IS_ERR(mvm->debugfs_dir)) {2239 char buf[100];2240 2241 snprintf(buf, 100, "../../%pd2", mvm->debugfs_dir->d_parent);2242 debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir,2243 buf);2244 }2245}2246